Opened 6 years ago
Last modified 3 years ago
#30711 closed Cleanup/optimization
Add HStoreF for F object like querying on HStoreField. — at Initial Version
| Reported by: | Gustav Eiman | Owned by: | |
|---|---|---|---|
| Component: | Documentation | Version: | dev |
| Severity: | Normal | Keywords: | HStoreField F |
| Cc: | Triage Stage: | Unreviewed | |
| Has patch: | yes | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
When using F objects for key lookups on HStoreFields it never digs down to get the keys but unexpectedly returns the entire object.
Lets say we have this model:
from django.contrib.postgres.fields import HStoreField
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=256)
attributes = HStoreField()
We then populate it.
Person.objects.create(name="James", attributes={"age": "30", "height": "190"})
Here comes the thing that bothers me. If we try to access either of these keys the entire attributes cell will be returned.
p = Person.objects.annotate(length=F("attributes__height"))
p[0].height
>>> {"age": "30", "height": "190"}
While the expected result would be an error or the value of the height field, it's not.
I propose either making F objects support these fields or creating a new type to handle this, a workaround that I'm playing around with myself.
from django.contrib.postgres.fields.jsonb import KeyTransformFactory
class HStoreF(F):
def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
rhs = super().resolve_expression(query, allow_joins, reuse, summarize, for_save)
field_list = self.name.split("__")
if field_list[-1] == rhs.target.name:
raise LookupError(
"HStoreF requires a key lookup in order to avoid unexpected behavior. "
"Please append '__somekey' to '{}'."
.format("__".join(field_list)))
return KeyTransformFactory(field_list[-1])(rhs)
An issue with this is that updating models with it still wont work. As an error is raised in Query.resolve_ref() due to the fact that it interprets the "__" as an attempted join. This could be solved by using a different lookup separator for keys (maybe relevant for JSONField to?), but I was unable to successfully implement this.
Similar issue with JSONField: https://code.djangoproject.com/ticket/29769