Opened 5 years ago
Last modified 2 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