Django REST Framework: The powers of a Serializer (1)

Django REST Framework: The powers of a Serializer (1)

Water has been taken for granted. - Jan Eliasson

DRF's Serializer has been proved to be a killing feature.

Here I am going to share details of updating a Foreign key object data while updating a reverse relation object using serializer.

The point here is that, while we usually use foreign key fields in the serializer for reading operation, we can also use them to write operations.

Update FK object data:

Let's consider a scenario.

Where we have an account, to which loan applications can be submitted. While approving an application, we need to collect the approved_amount and reason.

class Account(models.Model):
    account_number = models.CharField(max_length=255)
    approved_amount = models.PositiveIntegerField(default=0)
    reason = models.CharField(max_length=255, null=True, blank=True)

    def __str__(self):
        return self.account_number


class LoanApplication(models.Model):

    class Status(models.TextChoices):
        SUBMITTED = 'SUBMITTED', 'Submitted',
        REJECTED = 'REJECTED', 'Rejected'
        APPROVED = 'APPROVED', 'Approved'

    account = models.ForeignKey(Account, on_delete=models.CASCADE)
    status = models.CharField(
        max_length=255, choices=Status.choices, default=Status.SUBMITTED)

    def __str__(self):
        return str(self.account)

The above requirement shall be done using only one API call.

As shown in the above code shots, we have approved_amount and reason in the Account model, whereas approval shall be done to the LoanApplication object.

Let's see how the serializer tackles the above problem.

As in the below shots, while approval, we collect the amount and reason in the same API. In serializer, just before updating LoanApplication, we separate Account data and update to the appropriate account, then proceed to update the loan application.

class AccountSerializer(serializers.ModelSerializer):

    class Meta:
        model = Account
        fields = '__all__'


class LoanApplicationSerializer(serializers.ModelSerializer):
    approved_amount = serializers.IntegerField(
        source='account.approved_amount')
    reason = serializers.CharField(source='account.reason')

    class Meta:
        model = LoanApplication
        exclude = ('account',)

    def update(self, instance, validated_data):
        account_data = validated_data.pop('account', None)
        if account_data:
            account_serializer = AccountSerializer(
                instance.account, data=account_data, partial=True)
            account_serializer.is_valid(raise_exception=True)
            account_serializer.save()

        return super().update(instance, validated_data)
  1. Github code