How to filter one-to-many relationship with django rest api?
I have a one-to-many relationship like:
class Category(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField(max_length=100)
images = models.ImageField(upload_to="photos/categories")
category = models.ForeignKey("Category", on_delete=models.CASCADE, related_name='part_of', blank=True, null=True)
date_create = models.DateTimeField(auto_now_add=True)
date_update = models.DateTimeField(auto_now=True)
description = models.TextField(max_length=1000, blank=True)
legislation = models.TextField(max_length=1000, blank=True)
review = models.TextField(max_length= 000, blank=True)
eaza = models.TextField(max_length=1000, blank=True)
class Meta:
verbose_name = "category"
verbose_name_plural = "categories"
def __str__(self):
return self.name
class Animal(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField(max_length=100)
images = models.ImageField(upload_to="photos/categories")
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='animals')
date_create = models.DateTimeField(auto_now_add=True)
date_update = models.DateTimeField(auto_now=True)
description = models.TextField(max_length=1000, blank=True)
legislation = models.TextField(max_length=1000, blank=True)
review = models.TextField(max_length=1000, blank=True)
eaza = models.TextField(max_length=1000, blank=True)
class Meta:
verbose_name = "animal"
verbose_name_plural = "animals"
def __str__(self):
return self.name
and serializer:
class AnimalSerializer(serializers.ModelSerializer):
class Meta:
model = Animal
fields = ['id','category_id','name', 'description']
class CategorySerializer(serializers.ModelSerializer):
animals = AnimalSerializer(many=True)
class Meta:
model = Category
fields = ['id','category_id','name', 'description', 'animals']
and urls:
from . import views
urlpatterns = [
path('', CategoryViewSet.ApiOverview, name='home'),
path('all/', views.view_items, name='view_items'),
path('all/<int:id>/', views.detail_item ),
]
views.py:
@api_view(['GET'])
def view_items(request):
queryset = Category.objects.all()
serializer = CategorySerializer(queryset, many=True)
# checking for the parameters from the URL
if request.query_params:
items = Category.objects.filter(**request.query_params.dict())
serializer = CategorySerializer(items , many=True)
else:
items= Category.objects.all()
serializer = CategorySerializer(items , many=True)
# if there is something in items else raise error
if items:
return Response(serializer.data)
else:
return Response(status=status.HTTP_404_NOT_FOUND)
So for example if I do this: http://127.0.0.1:8000/djangoadmin/all/?name=cobra
[
{
"id": 28,
"category_id": 22,
"name": "cobra",
"description": "cobra",
"animals": [
{
"id": 4,
"category_id": 28,
"name": "indian",
"description": "cobra"
},
{
"id": 5,
"category_id": 28,
"name": "cape cobra",
"description": "cape cobra"
},
{
"id": 6,
"category_id": 28,
"name": "Chinese cobra",
"description": "Chinese cobra"
}
]
}
]
But of course I only want the array in animals:
{
"id": 4,
"category_id": 28,
"name": "indian",
"description": "cobra"
},
{
"id": 5,
"category_id": 28,
"name": "cape cobra",
"description": "cape cobra"
},
{
"id": 6,
"category_id": 28,
"name": "Chinese cobra",
"description": "Chinese cobra"
}
But for example if I do this: http://127.0.0.1:8000/djangoadmin/all/?category_id=28
I get an:
HTTP 404 Not Found
Question: how to return only the child items?
The data child items and parents items is comeing because of your serializer
@api_view(['GET'])
def view_items(request):
queryset = Category.objects.all()
serializer = CategorySerializer(queryset, many=True)
# checking for the parameters from the URL
if request.query_params:
items = Animal.objects.filter(**request.query_params.dict())
serializer = AnimalSerializer(items , many=True)# note here i'm using AnimalSerializer to see only child items
else:
items= Category.objects.all()
serializer = CategorySerializer(items , many=True)
# if there is something in items else raise error
if items:
return Response(serializer.data)
else:
return Response(status=status.HTTP_404_NOT_FOUND)
You can also do - filters the Category queryset based on the query parameters, then filters the Animal queryset based on the category_id of the first item in the filtered Category queryset. Finally, it serializes the filtered Animal queryset and returns it in the response.
@api_view(['GET'])
def view_items(request):
queryset = Category.objects.all()
serializer = CategorySerializer(queryset, many=True)
if request.query_params:
items = Category.objects.filter(**request.query_params.dict())
serializer = CategorySerializer(items , many=True)
else:
items = Category.objects.all()
serializer = CategorySerializer(items , many=True)
if items:
# Filter the animals based on the category_id of the parent category
animal_queryset = Animal.objects.filter(category_id=items[0].id)
animal_serializer = AnimalSerializer(animal_queryset, many=True)
return Response(animal_serializer.data)
else:
return Response(status=status.HTTP_404_NOT_FOUND)