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)

Вернуться на верх