Using the Django REST Framework, how can I set a field to be excluded from one view, but included in an other, without much bloat code?
For example, if I had these models, how can I make sure that only the name of the house is displayed in one view, and all of it's properties are displayed in an other. Also, how can I display the realtor's name, and not only it's ID when I'm querying for a house?
class Realtor(models.Model):
name = models.CharField(max_length=100)
class House(models.Model):
name = models.CharField(max_length=100)
address = models.TextField()
price = models.DecimalField(max_digits=10, decimal_places=2)
realtor = models.ForeignKey(Realtor, related_name='houses', on_delete=models.CASCADE)
You will need to define a ModelSerializer
for each case :
# serializers.py
from rest_framework import serializers
from models import House, Realtor
class BaseHouseSerializer(serializers.ModelSerializer):
class Meta:
model = House
fields = ["name"]
class RealtorSerializer(serializers.ModelSerializer):
class Meta:
model = Realtor
fields = ["name"]
class DetailedHouseSerializer(serializers.ModelSerializer):
realtor = RealtorSerializer() # Using nested serializer
class Meta:
model = House
fields = "__all__"
Then use these serializers in you views :
from rest_framework import generics
from serializers import BaseHouseSerializer, DetailedHouseSerializer
from models import House, Realtor
class HouseList(generics.ListAPIView):
queryset = House.objects.all()
serializer_class = BaseHouseSerializer
class DetailedHouseList(generics.ListAPIView):
queryset = House.objects.all()
serializer_class = DetailedHouseSerializer
It depends on how configurable you want you code to be.
The answer of Pierre couy simple and good, however, if you want more control, you can create a dynamic serializer class, and use it like this:
from rest_framework import serializers
from .models import Realtor, House
class DynamicSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
fields = kwargs.pop('fields', None)
super().__init__(*args, **kwargs)
if fields is not None:
allowed = set(fields)
for field_name in list(self.fields):
if field_name not in allowed:
self.fields.pop(field_name)
class HouseSerializer(DynamicSerializer):
realtor = serializers.SerializerMethodField(read_only=True)
price = serializers.FloatField()
class Meta:
model = House
fields = ['id', 'name', 'address', 'price', 'realtor']
def get_realtor(self, obj):
realtor_fields = self.context.get("realtor_fields", ['id', 'name']) #or whatever you want here
return RealtorSerializer(obj.realtor, context=self.context, fields=realtor_fields).data
class RealtorSerializer(DynamicSerializer):
houses = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Realtor
fields = ['id', 'name', 'houses']
def get_houses(self, obj):
house_fields = self.context.get('house_fields', ['name'])
return HouseSerializer(obj.houses.all(), many=True, context=self.context, fields=house_fields).data
This is an example where you can get all the realtors, but only the houses' name and price will be seen. If you get all the houses, you'll only see the realtors' name.
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Realtor, House
from .serializers import RealtorSerializer, HouseSerializer
class RealtorsGetCreate(APIView):
def get(self, request):
realtors = Realtor.objects.all()
serializer = RealtorSerializer(realtors, many=True, context={"house_fields":["name", "price"]})
return Response(serializer.data)
def post(self, request):
new_realtor = Realtor.objects.create(name=request.data.get("name"))
new_realtor.save()
return Response(201)
class HousesGetCreate(APIView):
def get(self, request):
houses = House.objects.all()
serializer = HouseSerializer(houses, many=True context={"realtor_fields":["name"]},)
return Response(serializer.data)
def post(self, request):
realtor = Realtor.objects.get(id=request.data.get("realtor_id"))
new_house = House.objects.create(
address = request.data.get("address"),
name = request.data.get("name"),
price = request.data.get("price"),
realtor = realtor
)
new_house.save()
return Response(201)
This code provides more flexibility in case you need it. (And you will need it, most likely)