Shift N' Tab Shift N' Tab - 16 days ago 6
Python Question

Reverse Filter Django Model with DRF

I have problem filtering in

django-models
and i am using
django-rest-framework
to work this serialized data.

What i want here is to get all herds record both with animal(s) in which the animal may had a
species_type='Cow'
or an empty herd(s).

This is my models.

models.py

class Herd(models.Model):
name = models.CharField(max_length=25)
description = models.TextField(max_length=250, null=True)

created_at = models.DateTimeField(auto_now_add=True, editable=False)
updated_at = models.DateTimeField(auto_now=True, editable=False)


class Animal(models.Model):
name = models.CharField(max_length=25)
species_type = models.CharField(max_length=25)
breed = models.CharField(max_length=25)

herd = models.ForeignKey(Herd, related_name='animals', on_delete=models.CASCADE)

created_at = models.DateTimeField(auto_now_add=True, editable=False)
updated_at = models.DateTimeField(auto_now=True, editable=False)


serializers.py

class AnimalSerializer(serializers.ModelSerializer):

class Meta:
model = Animal
fields = [
'name',
'species_type',
'breed'
]
read_only_fields = ['id', 'created_at', 'updated_at']

class HerdSerializer(serializers.ModelSerializer):
animals = AnimalSerializer(many=True, read_only=True)

class Meta:
model = Herd
fields = [
'id',
'name',
'description',
'animals'
]
read_only_fields = ['created_at', 'updated_at']


This is my viewset which handle all crud operations.

views.py

class HerdViewset(viewsets.ModelViewSet):
"""
This viewset automatically provides `list`, `create`, `retrieve`,
`update` and `destroy` actions.
"""
queryset = Herd.objects.all()
serializer_class = HerdSerializer


Now when i browse the
HerdViewSet
endpoint
/api/herd/
i got the results of all herds with animals or empty herd(s). But the animals in some herd does not filter the
species_type='Cow'
it still returns all animal which belong to that herd whether it
species_type
is goat, sheep, etc..

Answer

You can filter by species_type from Herd. Because you have defined a related_name in the foreign key.

try this

from django.db.models import Count, Q

Herd.objects.annotate(animalCount=Count('animals')).filter(Q(animals__species_type='Cow')|Q(animalCount=0))
  1. annotate is used to add an extra field to the results, so a new field animalCount is used to hold how many animals for that herd.
  2. Q is used to build complicated query condition, so for this case, Q(animals__species_type='Cow')|Q(animalCount=0) means, filter by the animals in the herd with species type 'Cow', or there is no animals in that herd.