graphene-django-extras icon indicating copy to clipboard operation
graphene-django-extras copied to clipboard

Using "__in" filter lookup expression

Open sbernier1 opened this issue 4 years ago • 6 comments

Hello,

I would like to use "__in" as a filter lookup expression but I am getting errors. I have a manyToMany relation on a model:

class A(models.Model):
  b_list = models.ManyToManyList(B)

class AObject(DjangoObjectType):
  class Meta:
    model = A
    filter_fields = {
      "b_list": ("in",)
    }

class Query(graphene.ObjectType):
    as = DjangoFilterPaginateListField(AObject, pagination=LimitOffsetGraphqlPagination())

if I try to query with b_list_in I get the following error:  'list' object has no attribute 'split' 

Thank you!

sbernier1 avatar Mar 10 '20 20:03 sbernier1

here is the complete error log:

Traceback (most recent call last):
  File "G:\dev\intellibeer\backend\env\lib\site-packages\promise\promise.py", line 489, in _resolve_from_executor
    executor(resolve, reject)
  File "G:\dev\intellibeer\backend\env\lib\site-packages\promise\promise.py", line 756, in executor
    return resolve(f(*args, **kwargs))
  File "G:\dev\intellibeer\backend\env\lib\site-packages\graphql\execution\middleware.py", line 75, in make_it_promise
    return next(*args, **kwargs)
  File "G:\dev\intellibeer\backend\env\lib\site-packages\graphene_django_extras\fields.py", line 239, in list_resolver
    qs = filterset_class(data=filter_kwargs, queryset=qs, request=info.context).qs
  File "G:\dev\intellibeer\backend\env\lib\site-packages\django_filters\filterset.py", line 236, in qs
    self.errors
  File "G:\dev\intellibeer\backend\env\lib\site-packages\django_filters\filterset.py", line 213, in errors
    return self.form.errors
  File "G:\dev\intellibeer\backend\env\lib\site-packages\django\forms\forms.py", line 175, in errors
    self.full_clean()
  File "G:\dev\intellibeer\backend\env\lib\site-packages\django\forms\forms.py", line 376, in full_clean
    self._clean_fields()
  File "G:\dev\intellibeer\backend\env\lib\site-packages\django\forms\forms.py", line 388, in _clean_fields
    value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
  File "G:\dev\intellibeer\backend\env\lib\site-packages\django_filters\widgets.py", line 201, in value_from_datadict
    return value.split(',')
graphql.error.located_error.GraphQLLocatedError: 'list' object has no attribute 'split'

I looked through the code a bit and it appears that graphene-django-extras always sends a list to django-filters. If I send [1,2,3,4], django-filters receives a list of size 4 containing the 4 numbers. If I send "1,2,3,4", django-filters receives a list of size 1 containing the string.

sbernier1 avatar Mar 11 '20 14:03 sbernier1

ok so I randomly found a solution. using filter_fields doesn't work, but I can achieve the same result with a custom filterSet:

class AFilter(django_filters.FilterSet):
    class Meta:
      model = A
      fields = {
            "b_list": ["in"],
        }

class A(models.Model):
  b_list = models.ManyToManyList(B)

class AObject(DjangoObjectType):
  class Meta:
    model = A
    filterset_class = AFilter

class Query(graphene.ObjectType):
    as = DjangoFilterPaginateListField(AObject, pagination=LimitOffsetGraphqlPagination())

using the argument b_List_In:"1,2" then works.

sbernier1 avatar Mar 11 '20 21:03 sbernier1

nevermind. It works on one-to-many relationships, but not many-to-many, which is my main use-case.

sbernier1 avatar Mar 11 '20 22:03 sbernier1

For now I use __in on the ids and I had to change the code (default behavior is that graphene throws an error because [1,2] or "1,2" aren't Floats.):

class InFilter(django_filters.BaseInFilter):
    pass

# Add this in the filter class:
class AFilter(django_filters.FilterSet):
  bList_Id__in = InFilter(field_name='b_list__id', lookup_expr='in')
    class Meta:
      model = A
      fields = {
           ...
        }

sbernier1 avatar Mar 11 '20 23:03 sbernier1

Hi @sbernier1 , have you found a final solution for this? How do you make a query where you provide a list and query runs returns all results matching that list; something like id_list = [1, 2, 3, 4] and the query returns all objects where their id's are 1, 2, 3, and 4.

IdemenB avatar Nov 11 '20 05:11 IdemenB

Hi, I used the solution I posted for a while, but I encountered other problems with graphene-django-extras (I don't remember which ones) so I ended up using the relay-style graphql instead, because it has tons more functionalities and it works with Apollo.

sbernier1 avatar Nov 11 '20 12:11 sbernier1