6

Models

class Task(Model):
    employee_owner = ForeignKey(Employee, on_delete=CASCADE)
    employee_doer = ForeignKey(Employee, on_delete=CASCADE)

Views

class TaskViewSet(ModelViewSet):
    serializer_class = TaskSerializer
    queryset = Task.objects.all()
    filter_class = TaskFilter

Filter

class TaskFilter(FilterSet):
    owner_id = NumberFilter(name='employee_owner__id')
    doer_id = NumberFilter(name='employee_doer__id')

    class Meta:
        model = Task
        fields = {
            'owner_id',
            'doer_id'
        }

Endpoints

http://localhost:8000/api/tasks?owner_id=1&doer_id=1

(Gives only those tasks where owner and doer are the same employee)

http://localhost:8000/api/tasks?owner_id=1

(Gives only those tasks where owner is specific employee and doer is anyone)

http://localhost:8000/api/tasks?doer_id=1

(Gives only those tasks where doer is specific employee and owner is anyone)

What I want

I want an endpoint like:

http://localhost:8000/api/tasks?both_id=1

(Which would give me all results from above 3 endpoints)

I want django-filter to do filtering exactly like:

Task.objects.filter(
    Q(employee_owner__id=1) | Q(employee_doer__id=1)
)

How can I achieve that ? Thank you.

2 Answers 2

17

You can customize filter using method argument like this:

class TaskFilter(FilterSet):
    owner_id = NumberFilter(name='employee_owner__id')
    doer_id = NumberFilter(name='employee_doer__id')
    both_id = NumberFilter(method='filter_both')

    class Meta:
        model = Task
        fields = {
            'owner_id',
            'doer_id',
            'both_id' 
        }

    def filter_both(self, queryset, name, value):
        return queryset.filter(
            Q(employee_owner__id=value) | Q(employee_doer__id=value)
        )
Sign up to request clarification or add additional context in comments.

4 Comments

Perfect answer from neverwalkaloner as usual :)
You have a Q() in there, but neither your answer or the docs linked seem to explain where it is imported from or what it represents.
Ah, found it finally with the right search against model.objects.filter - it's from django.db.models import Q
I have a similar case and for me this doesn't work as both_id is not a field within the Task model (if I understood the question correctly). The provided solution returns 'Meta.fields' must not contain non-model field names: both_id
5

Personally I would like to suggest use DjangoFilterBackend for filtration

from django_filters.rest_framework import DjangoFilterBackend

After that

class TaskViewSet(ModelViewSet):
    serializer_class = TaskSerializer
    queryset = Task.objects.all()
    filter_backends = (DjangoFilterBackend,)
    filterset_fields = ['owner_id', 'doer_id']# pass query through params

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.