Create method for foreign key relationships with Django Rest Framework serializers

Viewed 6349

My models are like this:

class FirewallPolicy(models.Model):
    name = models.CharField(max_length=100, unique=True)
    team = models.ForeignKey(Team)
    source_ip = models.ForeignKey(IP)
    destination_ip = models.ForeignKey(IP)

Now, in order to create a new Firewall Policy, there should already be an existing team, source_ip and destination_ip. My payload to create a new Firewall Policy is as follows:

{"name": "test-create-policy-911",
 "team": "avengers",
 "source_ip": "1.1.1.1",
 "destination_ip": "2.2.2.2",
}

My serializer to create a new Firewall Policy is as follows:

class FirewallPolicyCreateSerializer(serializers.ModelSerializer):
    name = serializers.CharField(max_length=100)
    team = serializers.CharField(max_length=100)
    source_ip = serializers.CharField(max_length=100)
    destination_ip = serializers.CharField(max_length=100)

    class Meta:
        model = Policy
        fields = ['id', 'name', 'team', 'source_ip', 'destination_ip']

    def validate(self, data):
        try:
            Team.objects.get(name=data['team'])
            IP.objects.get(name=data['source_ip'])
            IP.objects.get(name=data['destination_ip'])
        except ObjectDoesNotExist:
            raise serializers.ValidationError("Entities must exist before you can associate it with a Firewall Policy")

    def create(self, validated_data):
        team = Team.objects.get(name=validated_data['team'])
        source_ip = IP.objects.get(name=validated_data['source_ip'])
        destination_ip = IP.objects.get(name=validated_data['destination_ip'])
        policy  = Policy.objects.create(name=validated_data['name'], 
                                        team_id=team.id, 
                                        source_ip_id = source_ip.id, 
                                        destination_ip_id = destination_ip.id )
        return policy 

I am not sure if this is the right way of adding foreign keys to a model as it seems too much work. Is there something I am missing where the serializer can automatically check all this and add the foreign keys ?

1 Answers

Yes, you are doing much more work than you need to.

You should define your fields using SlugRelatedField to allow DRF to automatically populate them from a field on the related model. So:

class FirewallPolicyCreateSerializer(serializers.ModelSerializer):
    team = serializers.SlugRelatedField(queryset=Team.objects.all(), slug_field='name')
    source_ip = serializers.SlugRelatedField(queryset=IP.objects.all(), slug_field='source_ip')
    destination_ip = serializers.SlugRelatedField(queryset=IP.objects.all(), slug_field='destination_ip')

    class Meta:
        model = Policy
        fields = ['id', 'name', 'team', 'source_ip', 'destination_ip']

Now you shouldn't need to define validate or create at all, as DRF will do all the relevant validation and assignment.

(Note, you didn't need to redefine the name field either, as you're not changing anything from the underlying model field.)

Related