#30566: Unable to do key lookup when a key is a number on PostgreSQL JSONB field
-------------------------------------+-------------------------------------
Reporter: | Owner: (none)
NyanKiyoshi |
Type: Bug | Status: new
Component: | Version: 2.2
contrib.postgres | Keywords: json, jsonb,
Severity: Normal | postgres
Triage Stage: | Has patch: 0
Unreviewed |
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
-------------------------------------+-------------------------------------
Hi,
Currently, if we try working for example with the following model using a
JSONField (jsonb):
{{{#!python
from django.contrib.postgres.fields import JSONField
from django.db import models
class MyProduct(models.Model):
attributes = JSONField(default=dict, blank=True)
}}}
With some dummy JSON data and a lookup the keys:
{{{#!python
json_data = {"123": ["82"], "hello": ["82"]}
MyProduct.objects.create(attributes=json_data)
MyProduct.objects.filter(attributes__123__has_key="82").first() # We get
None, but we expect to match the product
MyProduct.objects.filter(attributes__hello__has_key="82").first() # We
get a match to the product
}}}
The two queries generated:
{{{#!sql
SELECT * FROM "product_myproduct" WHERE ("product_myproduct"."attributes"
-> 123)
SELECT * FROM "product_myproduct" WHERE ("product_myproduct"."attributes"
-> 'hello')
}}}
So in the SQL queries we notice django passed the `123` lookup value as an
integer instead of a string. Which, in PostgreSQL means that we want the
JSONB value of the 123rd key–which is not what we want.
I would expect that the lookup actually sends everything as a string, no
matter what. And instead, having to use another lookup to get a key by
index, e.g. `Q(field__key_at__1="something")`.
The same thing happens for the `->>` operator (which inherits the issue
from `->`).
This issue happens at `django.contrib.postgres.fields.jsonb.KeyTransform`:
{{{#!python
class KeyTransform(Transform):
operator = '->'
nested_operator = '#>'
def __init__(self, key_name, *args, **kwargs):
super().__init__(*args, **kwargs)
self.key_name = key_name
def as_sql(self, compiler, connection):
key_transforms = [self.key_name]
previous = self.lhs
while isinstance(previous, KeyTransform):
key_transforms.insert(0, previous.key_name)
previous = previous.lhs
lhs, params = compiler.compile(previous)
if len(key_transforms) > 1:
return "(%s %s %%s)" % (lhs, self.nested_operator),
[key_transforms] + params
try:
int(self.key_name)
except ValueError:
lookup = "'%s'" % self.key_name
else:
lookup = "%s" % self.key_name
return "(%s %s %s)" % (lhs, self.operator, lookup), params
}}}
The snippet in question is the following, where we do the integer check:
{{{#!python
try:
int(self.key_name)
except ValueError:
lookup = "'%s'" % self.key_name
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/30566>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/054.42dba494b8af1a2ec206ef0af3cb7216%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.