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. |