Search code examples
pythondjangodjango-rest-frameworkdjango-serializer

How to set nested related fields serializers in Djando Rest Framework?


I'm using Django Rest Framework to build an API where I have the following models of Users making, confirming and showing interest on Events:

models.py

class User(AbstractBaseUser, PermissionsMixin):
    user_name = models.CharField(_("user name"), max_length=150, unique=True)
    slug = AutoSlugField(populate_from='user_name', unique=True)
    
class Event(models.Model):
    name = models.CharField(max_length=100, blank=False, null=False)
    owner = models.ForeignKey(User, related_name="owned_events", on_delete=models.SET_NULL, blank=True, null=True)
    confirmed = models.ManyToManyField(User, related_name="confirmed_events", blank=True)
    interested = models.ManyToManyField(User, related_name="interested_events", blank=True)

to serialize it I used the following code as I found here and at the DRF docs:

serializers.py

class UserSerializer(serializers.HyperlinkedModelSerializer):

    class Meta:
        model = User
        fields = [
            "url",
            "user_name",
            "password",
        ]
        extra_kwargs = { "password": {"write_only": True} }


class EventSerializer(serializers.HyperlinkedModelSerializer):
    owner = UserSerializer(required=False)
    confirmed = UserSerializer(required=False, many=True)
    interested = UserSerializer(required=False, many=True)

    class Meta:
        model = Event
        lookup_field = 'slug'
        extra_kwargs = { 'url': {'lookup_field': 'slug'} }
        fields = [
            "url",
            "owner",
            "name", 
            "confirmed",
            "interested",
        ]

It works just fine like that, but I wanted the UserSerializer to show confirmed and interested events of each user just like each event shows the users confirmed and interested. I changed serializers and got the url of each event, like that:

serializers.py with HyperlinkedRelatedField on UserSerializer

class UserSerializer(serializers.HyperlinkedModelSerializer):
    confirmed_events = serializers.HyperlinkedRelatedField(
        queryset=Event.objects.all(),
        view_name='event-detail', 
        lookup_field='slug', 
        many=True, 
        required=False
    )
    interested_events = serializers.HyperlinkedRelatedField(
        queryset=Event.objects.all(),
        view_name='event-detail', 
        lookup_field='slug', 
        many=True, 
        required=False
    )

    class Meta:
        model = User
        fields = [
            "url",
            "user_name",
            "password",
            "confirmed_events",
            "interested_events",
        ]
        extra_kwargs = { "password": {"write_only": True} }

This got me the following JSON from the User model:

 {
        "user_name": "d",
        "confirmed_events": [],
        "interested_events": [
            "http://localhost:8000/events/eqwer-2/",
            "http://localhost:8000/events/test/",
            "http://localhost:8000/events/test-2/",
            "http://localhost:8000/events/test-3/",
        ]
 }

And from the Event model:

{
    "url": "http://localhost:8000/events/eqwer-2/",
    "owner": null,
    "name": "eqwer",
    "slug": "eqwer-2",
    "confirmed": [],
    "interested": [
        {
            "user_name": "d",
            "confirmed_events": [],
            "interested_events": [
                "http://localhost:8000/events/eqwer-2/",
                "http://localhost:8000/events/test/",
                "http://localhost:8000/events/test-2/",
                "http://localhost:8000/events/test-3/",

            ]
        }
    ]
},

The Event model JSON is fine because it shows each user's data, but I wanted the User JSON to show each event data instead of just the event URL, it'd be something like:

 {
        "user_name": "d",
        "confirmed_events": [],
        "interested_events": [
            {
                "url": "http://localhost:8000/events/eqwer-2/",
                "owner": null,
                "name": "eqwer",
                "slug": "eqwer-2",             
            },
        ]
 }

Solution

  • Create a separate serializer for interested_events like so:

    class InterestedEventsSerializer(serializers.HyperlinkedModelSerializer):
        class Meta:
            fields = ('url', 'owner', 'name', 'slug')
    

    And in your UserSerializer declare the interested_events using the serializer above:

    class UserSerializer(serializers.HyperlinkedModelSerializer):
        confirmed_events = ... #
        interested_events = InterestedEventsSerializer(many=True)
    
        class Meta:
            model = User
            fields = [
                "url",
                "user_name",
                "password",
                "confirmed_events",
                "interested_events",
            ]
            extra_kwargs = { "password": {"write_only": True} }