Repository: wagtail/wagtail

Issue:
Tests fail against Django's `main` (5.0) branch
<!--
Found a bug? Please fill out the sections below. 👍
-->

### Issue Summary

Our tests seem to fail against Django's `main` branch with `AttributeError: 'IndexView' object has no attribute 'add_facets'` as of a few days ago.

Possible cause: https://github.com/django/django/pull/16495

Will investigate later.


Discussion:
Sounds like it's failing again for a different reason this time, but it fails *really hard*. I wonder what's causing it...
Yep, very weird. Looks like the new failure was introduced in [dde2537f](https://github.com/django/django/commit/dde2537fbb04ad78a673092a931b449245a2d6ae) and is specific to Postgres. Was wondering if outdated postgres / psycopg versions might be the cause (i.e. the new Django commit is a breaking change against older versions, intentionally or not) but it doesn't seem so - Django's test suite passes in the same environment that Wagtail's test suite is massively failing on.
Thanks for verifying the commit, I had guessed it was that one but I hadn't tested it. It's strange though as the commit seems harmless.

One of the reasons why it's massively failing is because the user login for the tests, either with the utilities provided by `WagtailTestUtil` or `self.client.login`, doesn't seem to work. There's a signal handler that updates the last login timestamp, and it calls `save(update_fields=[...])` but it doesn't affect any rows.

I'm reopening this until we figure it out.
This seems to have to do with the use of [`ConvertedValueField`](https://github.com/wagtail/wagtail/blob/d5e4ac5590c3bc7fb6c49d3f0aa377724c369654/wagtail/test/customuser/fields.py#L38) as the primary key of [`CustomUser`](https://github.com/wagtail/wagtail/blob/d5e4ac5590c3bc7fb6c49d3f0aa377724c369654/wagtail/test/customuser/models.py#L56). CVF was missing a definition of `get_prep_value()` that would save the actual primary key integer value to the database; instead it saves a "shifted" version of the integer value which, with the new Django commit, can exceed the supported integer range. I've opened #10224 in an attempt to fix this.

Resolving patch:
diff --git a/wagtail/contrib/modeladmin/views.py b/wagtail/contrib/modeladmin/views.py
--- a/wagtail/contrib/modeladmin/views.py
+++ b/wagtail/contrib/modeladmin/views.py
@@ -1,6 +1,7 @@
 import warnings
 from collections import OrderedDict
 
+from django import VERSION as DJANGO_VERSION
 from django import forms
 from django.conf import settings
 from django.contrib.admin import FieldListFilter
@@ -343,7 +344,11 @@ def dispatch(self, request, *args, **kwargs):
         except ValueError:
             self.page_num = 0
 
-        self.params = dict(request.GET.items())
+        if DJANGO_VERSION >= (5, 0):
+            self.params = request.GET.copy()
+        else:
+            self.params = request.GET.dict()
+
         if self.PAGE_VAR in self.params:
             del self.params[self.PAGE_VAR]
         if self.ERROR_FLAG in self.params:
@@ -632,7 +637,16 @@ def get_queryset(self, request=None):
             # Finally, we apply the remaining lookup parameters from the query
             # string (i.e. those that haven't already been processed by the
             # filters).
-            qs = qs.filter(**remaining_lookup_params)
+            if DJANGO_VERSION >= (5, 0):
+                from django.contrib.admin.utils import (
+                    build_q_object_from_lookup_parameters,
+                )
+
+                qs = qs.filter(
+                    build_q_object_from_lookup_parameters(remaining_lookup_params)
+                )
+            else:
+                qs = qs.filter(**remaining_lookup_params)
         except (SuspiciousOperation, ImproperlyConfigured):
             # Allow certain types of errors to be re-raised as-is so that the
             # caller can treat them in a special way.


Test patch:
diff --git a/wagtail/contrib/modeladmin/tests/test_page_modeladmin.py b/wagtail/contrib/modeladmin/tests/test_page_modeladmin.py
--- a/wagtail/contrib/modeladmin/tests/test_page_modeladmin.py
+++ b/wagtail/contrib/modeladmin/tests/test_page_modeladmin.py
@@ -1,3 +1,4 @@
+from django import VERSION as DJANGO_VERSION
 from django.contrib.auth.models import Group, Permission
 from django.test import TestCase
 
@@ -38,6 +39,25 @@ def test_filter(self):
         for eventpage in response.context["object_list"]:
             self.assertEqual(eventpage.audience, "public")
 
+    def test_filter_multivalue(self):
+        # Filter by audience
+        response = self.get(audience__exact=["public", "private"])
+
+        self.assertEqual(response.status_code, 200)
+
+        if DJANGO_VERSION >= (5, 0):
+            # Multi-valued query parameters are supported as of
+            # https://github.com/django/django/pull/16621
+            # Should return both public and private events
+            self.assertEqual(response.context["result_count"], 4)
+            for eventpage in response.context["object_list"]:
+                self.assertIn(eventpage.audience, ["public", "private"])
+        else:
+            # Should use the last value, thus only return private events
+            self.assertEqual(response.context["result_count"], 1)
+            for eventpage in response.context["object_list"]:
+                self.assertEqual(eventpage.audience, "private")
+
     def test_search(self):
         response = self.get(q="Someone")