Search code examples
djangodjango-jsonfield

Django JsonField filter by two fields


class Blog:
   values = JSONField(blank=True, default=list)
[
  {
    "id": 1,
    "value": "31"
  },
  {
    "id": 2,
    "value": "Hello"
  },
  ...
]

I need to get all objects where the id is 1 and value of that field is greater than 31. I have tried q = queryset.filter(values__0__id=1, values__0__value_gte=31) but it works only for objects if an object that I need located only in first element.


Solution

  • Apparently, nowadays Django has not built-in support for array elements comparison for JSONField. Fortunately, Django allows to make a lot of custom staff. For example, Django's raw SQL feature.

    And if you use PostgreSQL as your main DB, you can use JSON Processing Functions. The jsonb_array_elements() from JSON processing functions is pretty nice choice.

    Combining features above, we can make a little workaround for your case:

    # Create method that uses raw SQL within your `objects` Model Manager.
    # Create your own filters as you want. This example should be improved
    # and handle exception cases of course.
    def filter_blogs_json(json_field, sql_operator, value):
        return Blog.objects.raw(f"SELECT id, data FROM yourappname_blog CROSS JOIN jsonb_array_elements(values) AS data WHERE (data->'{json_field}')::numeric {sql_operator} {value};")
    
    # You can get raw objects queryset 
    raw_blogs_qs = filter_blogs_json('value', '>=', 31)
    
    # Then you can process it anyway you want
    filtered_blog_ids = [b.id for b in raw_blogs_qs]
    queryset = Blog.objects.filter(...).filter(id__in=filtered_blog_ids)
    

    Pretty easy, isn't it? :)

    Moreover, I believe it is possible to make your own queryset Lookup's for JSONField, extend queries as you want and etc.