Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django filter JSONField list of dicts

I run Django 1.9 with the new JSONField and have the following Test model :

class Test(TimeStampedModel):
    actions = JSONField()

Let's say the action JSONField looks like this :

[
  {
    "fixed_key_1": "foo1",
    "fixed_key_2": {
      "random_key_1": "bar1",
      "random_key_2": "bar2",
    }
  },
  {
    "fixed_key_1": "foo2",
    "fixed_key_2": {
      "random_key_3": "bar2",
      "random_key_4": "bar3",
    }
  }
]

I want to be able to filter the foo1 and foo2 keys for every item of the list. When I do :

>>> Test.objects.filter(actions__1__fixed_key_1="foo2")

The Test is in the queryset. But when I do :

>>> Test.objects.filter(actions__0__fixed_key_1="foo2")

It isn't, which makes sense. I want to do something like :

>>> Test.objects.filter(actions__values__fixed_key_1="foo2")

Or

>>> Test.objects.filter(actions__values__fixed_key_2__values__contains="bar3")

And have the Test in the queryset.

Any idea if this can be done and how ?

like image 232
Scentle5S Avatar asked Dec 18 '15 14:12

Scentle5S


3 Answers

If you wan't to filter your data by one of fields in your array of dicts, you can try this query:

Test.objects.filter(actions__contains=[{'fixed_key_1': 'foo2'}])

It will list all Test objects that have at least one object in actions field that contains key fixed_key_1 of value foo2.

Also it should work for nested lookup, even if you don't know actual indexes:

Test(actions=[
    {'fixed_key_1': 'foo4', 'fixed_key_3': [
        {'key1': 'foo2'},
    ]}
}).save()

Test.objects.filter(actions__contains=[{'fixed_key_3': [{'key1': 'foo2'}]}])

In simple words, contains will ignore everything else.

Unfortunately, if nested element is an object, you must know key name. Lookup by value won't work in that case.

like image 200
GwynBleidD Avatar answered Oct 20 '22 06:10

GwynBleidD


You should be able to use a __contains lookup for this and pass queried values as list as documented here. The lookup would behave exactly like ArrayField. So, something like this should work:

Test.objects.filter(actions__contains=[{'fixed_key_1': 'foo2'}])
like image 29
Joey Wilhelm Avatar answered Oct 20 '22 07:10

Joey Wilhelm


You can use the django-jsonfield package, I guess it's already the one you are using.

from jsonfield import JSONField
class Test(TimeStampedModel):
    actions = JSONField()

So to search to make a search with a specific property, you can just do this:

def test_filter(**kwargs):
    result = Test.objects.filter(actions__contains=kwargs)
    return result

If you are using PostgreSQL, maybe you can take advantage of PostgreSQL specific model fields.

PS: If you are dealing with a lot of JSON structure you have maybe to consider using NoSQL Database.

like image 3
Dhia Avatar answered Oct 20 '22 07:10

Dhia