-
-
Notifications
You must be signed in to change notification settings - Fork 32k
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 #35718 -- Add JSONArray to django.db.models.functions. #18541
base: main
Are you sure you want to change the base?
Conversation
d8bba27
to
ef4943e
Compare
It turned out to be pretty straightforward to get a basic implementation. I did essentially copy/paste the existing Additionally, I reused the I wrote tests for |
fd8af12
to
fd328af
Compare
Added very basic docs, again basically copy/pasted from JSONObject with necessary tweaks to make it make sense. Force pushed update Few tiny notes:
Some motivation for this feature Let's say you want to build out a payload for an API https://shopify.dev/docs/api/admin-graphql/2024-07/mutations/productvariantupdate You might be able to have your database construct some deeply nested objects both more efficiently and more "declarative" than if you did so as a post-processing step. Consider def metafield(namespace: str, key: str, value) -> JSONObject:
return JSONObject(
namespace=Value(namespace),
key=Value(key),
value=value
)
ProductVariant.objects.all().annotate(
metafields=JSONArray(
metafield(
"search",
"width",
value=Cast("width", output_field=TextField())
),
metafield(
"specs",
"color",
value="attributes__color",
)
)
) vs def get_metafields(variant: ProductVariant) -> list[dict[str, Any]]:
return [{
# Did you spell all your keys correctly here?
"namespace": "search",
"key": "width",
"value": str(variant.width),
}, {
"namespace": "specs",
"key": "color",
# Did you make sure to include `select_related("attributes")`?
# If not, this will result in an extra database call and will
# also fail in an async context
"value": variant.attributes.color,
}] |
fd328af
to
2856cb8
Compare
Force-pushed to fix postgres tests failing. Think I got it. |
Postgres tests still failing. Will try to do more local tests before pushing again. |
2856cb8
to
3fd9aed
Compare
So I figured out the failing test, and it has to do with how the different database engines handle With both the newer postgres function and oracle, you can actually switch between either behavior by providing I went ahead and included Unfortunately, there's not really a way to opt into the It might be desirable to have something like JsonArray(
...,
exclude_nulls=True
) or some other such construction, to use the other behavior. However, we'd have to trigger some kind of warning or wrap the functions somehow with SQLite and older postgres. I also check to see if there's at least one expression, because on postgres, providing the |
3fd9aed
to
729747a
Compare
I don't understand why the tests failed. I checked out the from test_sqlite import * # NOQA
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"USER": "user",
"NAME": "django",
"PASSWORD": "postgres",
"HOST": "localhost",
"PORT": 5436,
"OPTIONS": {
"server_side_binding": True,
},
},
} Using I still got errors
Output of
Here's my docker-compoose.yml
Postgres 15 worked. |
Looks like there's an issue I reported here: https://code.djangoproject.com/ticket/35734#comment:4 I am going to hold off on working on this feature until that's resolved. |
729747a
to
2a8f2c6
Compare
Turns out that psycopg3 is really good at sending the correct type information for server side binding when using the As per my tradition, I included way-too-verbose comments in the code that will need to be pared down. |
Marked ready for review. It still has the error with Postgres 16+ and server-side bindings, but that's not specific to the code here. |
2a8f2c6
to
2a9a12b
Compare
Rebased, force-pushed. All checks are passing now (perhaps some issue with |
2a9a12b
to
42116c8
Compare
Rebase / force-pushed. I believe this is ready for review. We might want to review #18622 first. It might inform the general approach or "vibes" of this pull request. |
1814f69
to
93f9847
Compare
I reworked the flow-control/logic of the new JsonArray class to match the recent changes to JsonObject (for consistency.) In particular:
Some questions I have: What if a user has already explicitly casted an element? Does that result in a double cast? That can't be true of the JSONObject class because the keys are passed as |
Yes, I actually confirmed that explicitly passing an expression which was already wrapped in a Cast would result in a double Cast. I added logic skip wrapping terms in Cast that were already wrapped. I also added a test. Curious if anyone has a better way of implementing |
@sarahboyce Could we get someone to look at this? It's starting to get pretty good, I think. |
Just updated docs to include a note about SQL In the future, it would be nice to add a EDIT Also rebase/force pushed. |
15d115e
to
96ff0ed
Compare
You need to update the flags on the ticket like "Needs tests" so it goes in the review queue |
Great, thanks. Done. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like jsonb_array_elements
was also discussed in the ticket - was it decided not to add this?
So I left it out for for a few reasons:
|
117cd80
to
de10500
Compare
We don't have to include it in the PR but it changes this from |
de10500
to
6dc6906
Compare
Changed. It's good practice for me. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for implementing the function (on all databases, wow!) and doing all the research 😄
For an initial pass I think it looks good, but there's a potential simplification we can do in as_postgresql
. Also, we might want to have a dedicated json
module for all the JSON-related functions (see comment for details).
Since the ticket has been updated to only be about adding JSONArray
, we can probably change the title/commit message back to Fixed
instead of Refs
.
I might try to think of other test cases later, but so far, great work @john-parton! 👍
0ba972c
to
40b3259
Compare
Just adding a root comment here as well. I've rebased against main:
|
Unchecked the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the updates - can you also move the test files to tests/db_functions/json
? 🙏
688ce5f
to
3489767
Compare
Done. I ran a quick |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a couple of small comments before I go away till the new year 🎄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@john-parton Thanks 👍 I left small comments.
9f05015
to
d3fc786
Compare
I applied most of the suggestions using Github, pulled it all down, cleaned up things (mostly black) and made some minor required tweaks. Rebased, ran tests locally, force pushed. |
d3fc786
to
1a50133
Compare
buildbot, test on oracle. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @john-parton
I pushed minor tweaks, will wait for the tests to run and hopefully land today/Monday 👍
1a50133
to
e1dae39
Compare
Co-authored-by: Sarah Boyce <[email protected]> Co-authored-by: Mariusz Felisiak <[email protected]>
e1dae39
to
4b83fba
Compare
Trac ticket number
Ticket: https://code.djangoproject.com/ticket/35718
Branch description
This branch adds a
JSONArray
function so that you can construct json arrays. It works extremely similarly to the existingJSONObject
function.Checklist
main
branch.