Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed #26511 -- Documented KeyTransform and KeyTextTransform #15956

Closed
wants to merge 15 commits into from

Conversation

AllenJonathan
Copy link
Contributor

No description provided.

@AllenJonathan
Copy link
Contributor Author

AllenJonathan commented Aug 12, 2022

Looks to me that there is no use in using KeyTextTransform alone. I think it should be Cast to TextField for it to have any functionality of text. Seems to me that this could be done implicitly.

@felixxm
Copy link
Member

felixxm commented Aug 13, 2022

Looks to me that there is no use in using KeyTextTransform alone. I think it should be Cast to TextField for it to have any functionality of text. Seems to me that this could be done implicitly.

That's not true, see e.g. comment.

Copy link
Member

@felixxm felixxm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AllenJonathan Thanks 👍 I left initial comments.

@@ -1009,6 +1009,63 @@ Unless you are sure you wish to work with SQL ``NULL`` values, consider setting
Key, index, and path transforms
-------------------------------

.. class:: KeyTransform(key_name, *args, **kwargs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.. class:: KeyTransform(key_name, *args, **kwargs)
.. class:: json.KeyTransform(key_name, *args, **kwargs)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would move it to the end this paragraph (before the closing notes, warnings, and admonitions).

in ``'"labrador"'`` instead of ``'labrador'``. So filtering
``breed_name__contains='dor"'`` would also be true.

.. class:: KeyTextTransform
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would move it to the end this paragraph (before the closing notes, warnings, and admonitions). Also, add the full signature.

in ``'"labrador"'`` instead of ``'labrador'``. So filtering
``breed_name__contains='dor"'`` would also be true.

.. class:: KeyTextTransform
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.. class:: KeyTextTransform
.. class:: json.KeyTextTransform(key_name, *args, **kwargs)

Comment on lines 1065 to 1067
.. note::

This feature is currently supported only in postgresql databases.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should rather document that on non-PostgreSQL backends it's and alias to KeyTransform.

Comment on lines 1055 to 1058
Casting a resulting KeyTransform might still be in resemble json format.
For example, ``Cast(KeyTransform('breed', 'data'), TextField())`` would result
in ``'"labrador"'`` instead of ``'labrador'``. So filtering
``breed_name__contains='dor"'`` would also be true.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not true for all databases.


.. warning::

Casting a resulting KeyTransform might still be in resemble json format.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

KeyTransform should be backticked everywhere.

Comment on lines 1049 to 1050
... breed_name=Cast(KeyTransform('breed', 'data'), TextField())
... ).filter(breed_name__contains='lab')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix indentation (everywhere):

Suggested change
... breed_name=Cast(KeyTransform('breed', 'data'), TextField())
... ).filter(breed_name__contains='lab')
... breed_name=Cast(KeyTransform('breed', 'data'), TextField())
... ).filter(breed_name__contains='lab')

... )).filter(owner_other_pet_name__exact=[{'name': 'Fishy'}])
<QuerySet [<Dog: Rufus>]>

To query the resulting json as text, the .. class:: Cast function needs to be
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.. class:: Cast is not a valid directive in Sphinx.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please fix all invalid directives, e.g. .. class:: json.KeyTextTransform or .. class:: json.KeyTransform.

@AllenJonathan
Copy link
Contributor Author

Is there a way to run all the doc tests locally?

@felixxm
Copy link
Member

felixxm commented Aug 16, 2022

Is there a way to run all the doc tests locally?

You can use make html or make spelling, see docs.

Comment on lines 1045 to 1051
To query the resulting json as text, the :class:`~django.db.models.functions.Cast`
function needs to be used. This is done as follows::

>>> Dogs.objects.annotate(
... breed_name=Cast(KeyTransform('breed', 'data'), TextField())
... ).filter(breed_name__contains='lab')
<QuerySet [<Dog: Rufus>]>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is bad advice as it will result in quotes around the string being included? Only KeyTextTransform should be used for that really.

Comment on lines 1066 to 1069
.. note::

.. class:: json.KeyTextTransform is alias to .. class:: json.KeyTransform for
non-postgresql databases.
Copy link
Member

@charettes charettes Aug 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this an oversight? I'm surprised to learn that.

Should we make sure that KeyTextTransform actually results in text (or whatever database specific type is used to represent strings) before making it public?

It seems the ->> operator is supported on MySQL 5.7+ and on other backend we could make sure that the return value is automatically wrapped in a CAST (e.g. a CAST over json_value on Oracle)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works on SQLite and Oracle as a side-effect of the ticket-32213. I've prepared a fix for MySQL, see #15970.

@AllenJonathan
Copy link
Contributor Author

AllenJonathan commented Aug 23, 2022

Should the earlier ways to do path, index, and key transforms on JSONfield be deleted? Or at least it should be specified which way is recommended.

@charettes
Copy link
Member

charettes commented Aug 24, 2022

Last minute check but should we consider changing the signature of KeyTransform and KeyTextTransform to (source, *keys) instead of (key, source)? It has always seemed backward to me and once the API is public it's hard to change things of this nature.

If this is not possible for backward compatibility reasons should we consider exposing aliases à la F to hide this weird implementation detail?

What about

class KeyTransform(Transform):
    ...
    @classmethod
    def from_lookup(cls, lookup):
        transform, *keys = lookup.split(LOOKUP_SEP)
        if not keys:
            raise ValueError("Missing keys")
        for key in keys:
            transform = cls(key, transform)
        return transform

K = KeyTransform.from_lookup
KT = KeyTextTransform.from_lookup

It seems like these K and KT helpers would make for a way nicer public API that mimics the way values, filter and friends allow a __ notation for key accesses.

e.g. to take the documented examples

Dogs.objects.annotate(
    owner_name=KT('data__owner__name')
).filter(owner_name="Bob")

@charettes
Copy link
Member

charettes commented Aug 24, 2022

Actually it seems like K = KeyTransform.from_lookup would not provide any value over F as the latter can already be used for the examples added here in a more ergonomic way through JSONField.get_transform.

The usefulness of KT = KeyTextTransform.from_lookup remains though as that's not something that can be achieved through F. Maybe we only need to introduce KT and document it then?

Any thoughts @AllenJonathan and @felixxm? Allen, can you confirm that using F("data__breed") produces the same thing as KeyTransform('breed', 'data') in your examples.

@AllenJonathan
Copy link
Contributor Author

Yes, F(data_breed) uses KeyTransform to build the query and has the same results. I think the changes you suggested would be good. Nesting KeyTransforms didn't look so good, and using __ makes it easier to users.

I could do the above changes. Should that be a new ticket and a PR? I think this ticket could be put on hold till these changes are made.

@charettes
Copy link
Member

Agreed, F already supports transforms so no need for K but exposing KT still seems like something worth doing one way or the other. Even only documenting KeyTextTransform.from_lookup seems like a better UX than nesting KeyTextTransform with the reverse (key, expr).

Any thoughts on the subject @felixxm?

@felixxm
Copy link
Member

felixxm commented Aug 29, 2022

Agreed, let's add and document only the alias KT.

Also, we recently documented hstore.KeyTransform(). I think we should revert 7faf25d and wontfix'ed ticket-30711 as we can achieved the same with F() so there is no need to expose an extra API.

@AllenJonathan
Copy link
Contributor Author

AllenJonathan commented Sep 9, 2022

  1. Is assuming KT as a class correct? Should I add that KT is a constructor for KeyTextTransform?
  2. Is there anything else that has to be added?

@AllenJonathan
Copy link
Contributor Author

Final Work Submission - Link

This is going to be my final work submission for GSOC. Please go through it and suggest changes or additions if any.

@felixxm
Copy link
Member

felixxm commented Sep 12, 2022

@AllenJonathan Thanks 👍 Please move docs changes to the #16016, we should add and documented KT in a single commit.

@felixxm felixxm closed this Sep 12, 2022
@AllenJonathan AllenJonathan deleted the ticket_26511 branch May 10, 2023 15:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants