Serializing 3 Interconnected Django relational models

Viewed 51

I am trying to write a set of serializers for my models. I want the serializer for the deck to spit out the cards that match the decks ID in the CardToDeck Model and then fetch the card matching the card_id in the Card Model and Ideally be able to write with the given solution.

models.py

from django.db import models

# Create your models here.

class CardToDeck(models.Model):
    deck_id = models.ForeignKey("Deck", related_name="card_to_deck", on_delete=models.CASCADE)
    card_id = models.ForeignKey("Card", related_name="card_to_deck", on_delete=models.CASCADE)

class Card(models.Model):
    id = models.AutoField(primary_key=True)
    multiverseid = models.IntegerField(unique=True) # Used to fetch the card from the API
    name = models.CharField(max_length=145)


class Deck(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=145)

serializers.py:

from .models import *
from rest_framework import serializers


class CardSerializer(serializers.ModelSerializer):
    class Meta:
        model = Card
        fields = ('id', 'name')

class CardToDeckSerializer(serializers.ModelSerializer):
    class Meta:
        model = CardToDeck
        fields = ('deck_id', 'card_id')

# Serializer for Deck that grabs the nested model references
class DeckSerializer(serializers.ModelSerializer):
    cards = serializers.PrimaryKeyRelatedField(queryset=CardToDeck.objects.all(), many=True)
    class Meta:
        model = Deck
        depth = 2
        fields = ('name', 'cards')

What I would like the JSON to look like:

{
    "name": "deckNameGoHere",
    "cards": [{"id":1, "name":"cardNameGoHere", "quantity":3},{"id":2, "name":"cardNameGoHere", "quantity":2}]
}

I just spent the last 4 hours trying to figure this out and feel quite dumb, any help would be appreciated. I looked into the docks but couldn't find the resources I needed.

2 Answers

Assuming quantity is in CardToDeck, you can do something like this:

class CardToDeckSerializer(serializers.ModelSerializer):
    id = serializers.CharField(source='card_id.id')
    name = serializers.CharField(source='card_id.name')

    class Meta:
        model = CardToDeck
        fields = ('id', 'name', 'quantity')


class DeckSerializer(serializers.ModelSerializer):
    cards = CardToDeckSerializer(source='card_to_deck' many=True)
    class Meta:
        model = Deck
        fields = ('name', 'cards')

According to DRF documents:

PrimaryKeyRelatedField may be used to represent the target of the relationship using its primary key.

You can use CardToDeckSerializer as DeckSerializer's field without PrimeryKeyRelated and as a serializer object if you want to read the data and bring it back:

from .models import *
from rest_framework import serializers


class CardSerializer(serializers.ModelSerializer):
    class Meta:
        model = Card
        fields = ('id', 'name')

class CardToDeckSerializer(serializers.ModelSerializer):
    class Meta:
        model = CardToDeck
        fields = ('deck_id', 'card_id')

# Serializer for Deck that grabs the nested model references
class DeckSerializer(serializers.ModelSerializer):
    # if not intended to use bulk action it's better to keep the "many" fields as read only
    cards = CardToDeckSerializer(many=True, read_only=True)
    class Meta:
        model = Deck
        depth = 2
        fields = ('name', 'cards')

If you wanted to show a list of related linkes, you could use the following:

HyperlinkedRelatedField

I highly recommend reading DRF's document on nested Serializers

Related