Django Many-To-One Relationship add data to Many side

Viewed 119

I have two models like below. I'm using Django restframework.

class Family(models.Model):
    name= models.CharField(max_length=100)

class Parishioner(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    family = models.ForeignKey(Family, on_delete=models.CASCADE)

There are my ViewSets

class FamilyViewSet(viewsets.ModelViewSet):
    queryset = Family.objects.all()
    serializer_class = FamilySerializer
    authentication_classes = (TokenAuthentication,)
    permission_classes = (IsAuthenticated,)

class ParishionerViewSet(viewsets.ModelViewSet):
    queryset = Parishioner.objects.all()
    serializer_class = serializers.ParishionerSerializer
    authentication_classes = (TokenAuthentication,)
    permission_classes = (IsAuthenticated,)

These are my Serializers

class FamilySerializer(serializers.ModelSerializer):

    class Meta:
        model = Family
        fields = ('id', 'name')
        read_only_fields = ('id',)


class ParishionerSerializer(serializers.ModelSerializer):
    class Meta:
        model = Parishioner
        fields = ('id', 'first_name', 'last_name', 'family')
        read_only_fields = ('id',)
        depth = 1

So basically Parishioner can have one Family. Family has multiple members(Parishioners)

Currently I have to go to all Parishioners and select the Family.

is it possible to add a field called members to Family model, where members would be an Array of Existing Parishioners ? or is there any other way to handle this ?

enter image description here

2 Answers

It would be a bad idea to do this at the database side, since that would mean the database does no longer check this.

But you can easily do this in the serializer. We do this by adding an instance of the ParishionerSerializer to the FamilySerializer. We need to define the ParishionerSerializer before the FamilySerializer, since otherwise, that identifier is not yet assigned:

class ParishionerSerializer(serializers.ModelSerializer):
    class Meta:
        model = Parishioner
        fields = ('id', 'first_name', 'last_name', 'family')
        read_only_fields = ('id',)
        depth = 1


class FamilySerializer(serializers.ModelSerializer):
    parishioner_set = ParishionerSerializer(many=True, read_only=True)

    class Meta:
        model = Family
        fields = ('id', 'name')
        read_only_fields = ('id',)

You can rename it to members at the model layer and then update the serializer accordingly, or only in the subserializer.

Option 1: use related_name=…

We can rename the relation in reverse to memebers by specifying a value for the related_name=… [Django-doc]:

class Parishioner(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    family = models.ForeignKey(
        Family,
        on_delete=models.CASCADE,
        related_name='members'
    )

then the serializer thus uses member instead:

class FamilySerializer(serializers.ModelSerializer):
    members = ParishionerSerializer(many=True, read_only=True)

    class Meta:
        model = Family
        fields = ('id', 'name')
        read_only_fields = ('id',)

Option 2: rename the serializer with source=…

We can also only specify this at the serializer level, by specifying a source=… parameter [drf-doc]:

class FamilySerializer(serializers.ModelSerializer):
    members = ParishionerSerializer(
        source='parishioner_set',
        many=True,
        read_only=True
    )

    class Meta:
        model = Family
        fields = ('id', 'name')
        read_only_fields = ('id',)

Fam = Family.objects.get(id=1) Fam.parishioner_set.all()

This should do

Related