| 1 | | When using F objects for key lookups on HStoreFields it never digs down to get the keys but unexpectedly returns the entire object. |
| 2 | | |
| 3 | | Lets say we have this model: |
| 4 | | {{{ |
| 5 | | from django.contrib.postgres.fields import HStoreField |
| 6 | | from django.db import models |
| 7 | | |
| 8 | | class Person(models.Model): |
| 9 | | name = models.CharField(max_length=256) |
| 10 | | attributes = HStoreField() |
| 11 | | }}} |
| 12 | | |
| 13 | | We then populate it. |
| 14 | | {{{ |
| 15 | | Person.objects.create(name="James", attributes={"age": "30", "height": "190"}) |
| 16 | | }}} |
| 17 | | |
| 18 | | Here comes the thing that bothers me. If we try to access either of these keys the entire attributes cell will be returned. |
| 19 | | {{{ |
| 20 | | p = Person.objects.annotate(length=F("attributes__height")) |
| 21 | | p[0].height |
| 22 | | >>> {"age": "30", "height": "190"} |
| 23 | | }}} |
| 24 | | |
| 25 | | While the expected result would be an error or the value of the height field, it's not. |
| 26 | | |
| 27 | | 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. |
| 28 | | |
| 29 | | {{{ |
| 30 | | from django.contrib.postgres.fields.jsonb import KeyTransformFactory |
| 31 | | |
| 32 | | class HStoreF(F): |
| 33 | | def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False): |
| 34 | | rhs = super().resolve_expression(query, allow_joins, reuse, summarize, for_save) |
| 35 | | field_list = self.name.split("__") |
| 36 | | if field_list[-1] == rhs.target.name: |
| 37 | | raise LookupError( |
| 38 | | "HStoreF requires a key lookup in order to avoid unexpected behavior. " |
| 39 | | "Please append '__somekey' to '{}'." |
| 40 | | .format("__".join(field_list))) |
| 41 | | return KeyTransformFactory(field_list[-1])(rhs) |
| 42 | | |
| 43 | | }}} |
| 44 | | |
| 45 | | 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. |
| 46 | | |
| 47 | | Similar issue with JSONField: https://code.djangoproject.com/ticket/29769 |
| | 1 | Document `django.contrib.postgres.fields.hstore.KeyTransform` in the [https://docs.djangoproject.com/en/dev/ref/contrib/postgres/fields/#hstorefield HStoreField documentation] that can be used on the right hand side of a filter or an annotation. |