diff --git a/django/db/models/fields/json.py b/django/db/models/fields/json.py index d544caf3e501..e0aa5c622b0d 100644 --- a/django/db/models/fields/json.py +++ b/django/db/models/fields/json.py @@ -424,8 +424,12 @@ class KeyTextTransform(KeyTransform): output_field = TextField() def as_mysql(self, compiler, connection): - if connection.mysql_is_mariadb: - # MariaDB doesn't support -> and ->> operators (see MDEV-13594). + # The ->> operator is not supported on MariaDB (see MDEV-13594) and + # only supported against columns on MySQL. + if ( + connection.mysql_is_mariadb + or getattr(self.lhs.output_field, "model", None) is None + ): sql, params = super().as_mysql(compiler, connection) return "JSON_UNQUOTE(%s)" % sql, params else: diff --git a/django/views/generic/base.py b/django/views/generic/base.py index 8412288be1ce..485b74d377bf 100644 --- a/django/views/generic/base.py +++ b/django/views/generic/base.py @@ -1,4 +1,5 @@ import logging +from urllib.parse import urlparse from asgiref.sync import iscoroutinefunction, markcoroutinefunction @@ -252,7 +253,10 @@ def get_redirect_url(self, *args, **kwargs): args = self.request.META.get("QUERY_STRING", "") if args and self.query_string: - url = "%s?%s" % (url, args) + if urlparse(url).query: + url = f"{url}&{args}" + else: + url = f"{url}?{args}" return url def get(self, request, *args, **kwargs): diff --git a/tests/generic_views/test_base.py b/tests/generic_views/test_base.py index cc5dcf4e39d4..9d6cef8a4644 100644 --- a/tests/generic_views/test_base.py +++ b/tests/generic_views/test_base.py @@ -587,6 +587,31 @@ def test_gone_response_logged(self): handler, f"Gone: {escaped}", logging.WARNING, 410, request ) + def test_redirect_with_querry_string_in_destination(self): + response = RedirectView.as_view(url="/bar/?pork=spam", query_string=True)( + self.rf.get("/foo") + ) + self.assertEqual(response.status_code, 302) + self.assertEqual(response.headers["Location"], "/bar/?pork=spam") + + def test_redirect_with_query_string_in_destination_and_request(self): + response = RedirectView.as_view(url="/bar/?pork=spam", query_string=True)( + self.rf.get("/foo/?utm_source=social") + ) + self.assertEqual(response.status_code, 302) + self.assertEqual( + response.headers["Location"], "/bar/?pork=spam&utm_source=social" + ) + + def test_redirect_with_same_query_string_param_will_append_not_replace(self): + response = RedirectView.as_view(url="/bar/?pork=spam", query_string=True)( + self.rf.get("/foo/?utm_source=social&pork=ham") + ) + self.assertEqual(response.status_code, 302) + self.assertEqual( + response.headers["Location"], "/bar/?pork=spam&utm_source=social&pork=ham" + ) + class GetContextDataTest(SimpleTestCase): def test_get_context_data_super(self): diff --git a/tests/model_fields/test_jsonfield.py b/tests/model_fields/test_jsonfield.py index 16ab8887a940..b16499d1980b 100644 --- a/tests/model_fields/test_jsonfield.py +++ b/tests/model_fields/test_jsonfield.py @@ -1160,6 +1160,12 @@ def test_lookups_with_key_transform(self): True, ) + def test_cast_with_key_text_transform(self): + obj = NullableJSONModel.objects.annotate( + json_data=Cast(Value({"foo": "bar"}, JSONField()), JSONField()) + ).get(pk=self.objs[0].pk, json_data__foo__icontains="bar") + self.assertEqual(obj, self.objs[0]) + @skipUnlessDBFeature("supports_json_field_contains") def test_contains_contained_by_with_key_transform(self): tests = [