Fixed #33966 -- Added support for using KeyTextTransform from lookup.
This commit is contained in:
parent
3ba7f2e906
commit
10178197d5
|
@ -4,6 +4,7 @@ from django import forms
|
|||
from django.core import checks, exceptions
|
||||
from django.db import NotSupportedError, connections, router
|
||||
from django.db.models import lookups
|
||||
from django.db.models.constants import LOOKUP_SEP
|
||||
from django.db.models.fields import TextField
|
||||
from django.db.models.lookups import PostgresOperatorLookup, Transform
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
@ -379,6 +380,18 @@ class KeyTextTransform(KeyTransform):
|
|||
json_path = compile_json_path(key_transforms)
|
||||
return "(%s ->> %%s)" % lhs, tuple(params) + (json_path,)
|
||||
|
||||
@classmethod
|
||||
def from_lookup(cls, lookup):
|
||||
transform, *keys = lookup.split(LOOKUP_SEP)
|
||||
if not keys:
|
||||
raise ValueError("Lookup must contain key or index transforms.")
|
||||
for key in keys:
|
||||
transform = cls(key, transform)
|
||||
return transform
|
||||
|
||||
|
||||
KT = KeyTextTransform.from_lookup
|
||||
|
||||
|
||||
class KeyTransformTextLookupMixin:
|
||||
"""
|
||||
|
|
|
@ -216,6 +216,10 @@ Models
|
|||
allows performing actions that can fail after a database transaction is
|
||||
successfully committed.
|
||||
|
||||
* The new :class:`KT() <django.db.models.fields.json.KT>` expression represents
|
||||
the text value of a key, index, or path transform of
|
||||
:class:`~django.db.models.JSONField`.
|
||||
|
||||
Requests and Responses
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -1059,6 +1059,33 @@ To query for missing keys, use the ``isnull`` lookup::
|
|||
:lookup:`istartswith`, :lookup:`lt`, :lookup:`lte`, :lookup:`gt`, and
|
||||
:lookup:`gte`, as well as with :ref:`containment-and-key-lookups`.
|
||||
|
||||
``KT()`` expressions
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionadded:: 4.2
|
||||
|
||||
.. module:: django.db.models.fields.json
|
||||
|
||||
.. class:: KT(lookup)
|
||||
|
||||
Represents the text value of a key, index, or path transform of
|
||||
:class:`~django.db.models.JSONField`. You can use the double underscore
|
||||
notation in ``lookup`` to chain dictionary key and index transforms.
|
||||
|
||||
For example::
|
||||
|
||||
>>> from django.db.models.fields.json import KT
|
||||
>>> Dog.objects.create(name="Shep", data={
|
||||
... "owner": {"name": "Bob"},
|
||||
... "breed": ["collie", "lhasa apso"],
|
||||
... })
|
||||
<Dog: Shep>
|
||||
>>> Dogs.objects.annotate(
|
||||
... first_breed=KT("data__breed__1"),
|
||||
... owner_name=KT("data__owner__name")
|
||||
... ).filter(first_breed__startswith="lhasa", owner_name="Bob")
|
||||
<QuerySet [<Dog: Shep>]>
|
||||
|
||||
.. note::
|
||||
|
||||
Due to the way in which key-path queries work,
|
||||
|
|
|
@ -27,6 +27,7 @@ from django.db.models import (
|
|||
)
|
||||
from django.db.models.expressions import RawSQL
|
||||
from django.db.models.fields.json import (
|
||||
KT,
|
||||
KeyTextTransform,
|
||||
KeyTransform,
|
||||
KeyTransformFactory,
|
||||
|
@ -374,11 +375,7 @@ class TestQuerying(TestCase):
|
|||
qs = NullableJSONModel.objects.filter(value__isnull=False)
|
||||
self.assertQuerysetEqual(
|
||||
qs.filter(value__isnull=False)
|
||||
.annotate(
|
||||
key=KeyTextTransform(
|
||||
"f", KeyTransform("1", KeyTransform("d", "value"))
|
||||
),
|
||||
)
|
||||
.annotate(key=KT("value__d__1__f"))
|
||||
.values("key")
|
||||
.annotate(count=Count("key"))
|
||||
.order_by("count"),
|
||||
|
@ -1078,3 +1075,20 @@ class TestQuerying(TestCase):
|
|||
).filter(chain=F("related_key__0")),
|
||||
[related_obj],
|
||||
)
|
||||
|
||||
def test_key_text_transform_from_lookup(self):
|
||||
qs = NullableJSONModel.objects.annotate(b=KT("value__bax__foo")).filter(
|
||||
b__contains="ar",
|
||||
)
|
||||
self.assertSequenceEqual(qs, [self.objs[7]])
|
||||
qs = NullableJSONModel.objects.annotate(c=KT("value__o")).filter(
|
||||
c__contains="uot",
|
||||
)
|
||||
self.assertSequenceEqual(qs, [self.objs[4]])
|
||||
|
||||
def test_key_text_transform_from_lookup_invalid(self):
|
||||
msg = "Lookup must contain key or index transforms."
|
||||
with self.assertRaisesMessage(ValueError, msg):
|
||||
KT("value")
|
||||
with self.assertRaisesMessage(ValueError, msg):
|
||||
KT("")
|
||||
|
|
Loading…
Reference in New Issue