ivelum / djangoql

Advanced search language for Django
MIT License
983 stars 88 forks source link

Dealing with JSONFields #94

Open twang817 opened 2 years ago

twang817 commented 2 years ago

Given a JSONField in the model.

class MyModel(models.Model):
  data = models.JSONField()

And a custom lookup:

FooField(StrField):
  name = "foo"
  suggest_options = True
  def get_options(self, search):
    name = self.get_lookup_name()
    lookup = {}
    if search:
      lookup[f"{name}__icontains"] = search
    return self.model.objects.filter(**lookup).order_by(name).values_list(name, flat=True).distinct()
  def get_lookup_name(self):
    return "data__foo"

class MyModelQLSchema(DjangoQLSchema):
  def get_fields(self, model):
    fields = super().get_field(model)
    if model == MyModel:
      fields += [FooField(model=MyModel)]
    return fields

On Sqlite, I am able now to search for something like:

foo = "asdf"

But, on Postgres, I get an error:

Traceback (most recent call last):
  File "...", line 89, in _execute
    return self.cursor.execute(sql, params)
psycopg2.errors.UndefinedFunction: operator does not exist: json = unknown
LINE 1: ...("myapp_mymodel"."data" -> 'foo') = '"asdf"' ...

I am able to run the query:

select * from myapp_mymodel where (data->>'foo')::text = 'asdf'

This query also works (closer to the query that django is using):

select * from myapp_mymodel where (data->'foo')::text = '"asdf"'

The question is how do I implement a custom lookup that will make these casts? It appears that get_lookup wants Q objects. Is there a way I can work with annotations? Or directly manipulate the queryset?