2

I have a class with a json field in it

class A(models.Model)
    brand = JSONField()

If I post an array of JSON like [{'brand_id:1', 'name':'b1'}, {'brand_id:2', 'name':'b2'}] it is stored as an array of JSON. This works fine.

How should I query so as to check if '1' is present in the brand_id of any dictionary in that array?

Arjunsingh
  • 703
  • 9
  • 22
  • Possible duplicate of [Django filter JSONField list of dicts](https://stackoverflow.com/questions/34358278/django-filter-jsonfield-list-of-dicts) – Brown Bear May 09 '18 at 11:13

2 Answers2

5

Well first of all your JSON here is malformed. I presume it is meant to be:

[{'brand_id': 1, 'name': 'b1'}, {'brand_id': 2, 'name': 'b2'}] 

If that's the case, to test for 1 in such a blob, something like this will tell you if 1 is to be found anywhere in the JSON as a value:

def check_for_one(json_data):
    return any([1 in data.values() for data in json_data])

But you want to know specifically if 1 is a value owned by a key brand_id anywhere in the JSON so you can also use a loop to add in some extra conditions:

def check_for_one(json_data):
    match = []
    for data in json_data:
        for key, value in data.items():
            if key == 'brand_id' and value == 1:
                match.append(True)
    return any(match)

You can incorporate such logic as methods on your model class like this:

class A(models.Model):
    brand = JSONField()

    def check_for_one_comprehension(self):
        return any([1 in data.values() for data in self.brand])

    def check_for_one_loop(self):
        match = []
        for data in self.brand:
            for key, value in data.items():
                if key == 'brand_id' and value == 1:
                    match.append(True)
        return any(match)

But, if you actually want to filter instances from the database where the JSON data is an array at the top level and brand_id == 1, that needs a different approach, and this should do it:

A.objects.filter(brand__contains=[{'brand_id': 1}])

Note the additional [{}] braces! If you just call contains=['brand_id': 1] it will throw a syntax error and if you call contains={'brand_id': 1} it will not match.

jhrr
  • 1,624
  • 14
  • 22
  • Can you explain why brand__contains=[{'brand_id': 1}] works but brand__contains={'brand_id': 1} does not? – rtindru May 09 '18 at 13:00
  • 1
    Because `contains={'brand_id': 1}` is not taking account of the fact it needs to filter through the top-level *list* we have with the json shaped like: `brand = [{'brand_id': 1, 'name': 'b1'}, {'brand_id': 2, 'name': 'b2'}] `. If the json was shaped like `brand = {'brand_id': 1, 'name': 'b1'}`, then it would match. – jhrr May 09 '18 at 13:50
  • Very helpful. Note that if the JSON structure is not a list (e.g., `{'brand_id: 1}`, no `[]`s), then you don't need the `[]`s in python. – dfrankow Sep 24 '21 at 20:02
3

This worked:

A.objects.filter(brands__contains=[{'brand_id':1}])

I didnt check it first by using the array. Link pointed put by @Bear Brown gives sufficient info. Its easy in django, but finding it out took time :P.

Arjunsingh
  • 703
  • 9
  • 22
  • This does not work if the JSON Field stores an array. – rtindru May 09 '18 at 11:28
  • 1
    @rtindru not true, you can filter through a json array but you would need the `{}` in this case to cast the key and value correctly. `['brand_id':1]` will throw a syntax error. – jhrr May 09 '18 at 11:43
  • @jhrr True; the code above raises a `SyntaxError`. Can you describe how the filter would work inside the JSON field if the field stores a JSON array? – rtindru May 09 '18 at 12:32
  • @rtindru I have put the correct version in my main answer above. See the very end of that. – jhrr May 09 '18 at 12:34
  • 1
    Works like a charm; my mistake. I was missing the array notation. Can you explain why `brand__contains=[{'brand_id': 1}]` works but `brand__contains={'brand_id': 1}` does not? – rtindru May 09 '18 at 13:00