简体   繁体   中英

Avoid validation of foreign key in django rest framework serializer

I write an API for the following models:

class TemplateProjectGroup(models.Model):
    pass


class TemplateProject(models.Model):
    name = models.CharField(max_length=255, unique=True)
    description = models.CharField(max_length=1024, blank=True)
    group = models.ForeignKey(TemplateProjectGroup, on_delete=models.CASCADE)
    project = models.ForeignKey(Project, on_delete=models.CASCADE)
    avatar_url = models.URLField(max_length=1024, blank=True)

The logic is following: User can create an instance of TemplateProject with non existed group field. So if group is not existed, it should be created with a specific ID. So, I have this serializer:

class TemplateProjectSerializer(serializers.ModelSerializer):

    def create(self, validated_data):
        template_project_group_id = validated_data.pop('group')
        project = validated_data.pop('project')
        group, _ = models.TemplateProjectGroup.objects.get_or_create(id=template_project_group_id)
        template_project = models.TemplateProject.objects.create(**validated_data, group_id=group.id, project_id=project.id)
        return template_project

    def update(self, instance, validated_data):
        template_project_group_id = validated_data.pop('group')
        group, _ = models.TemplateProjectGroup.objects.get_or_create(id=template_project_group_id)
        instance.save()
        instance.update(**validated_data, group=group)
        return instance

    class Meta:
        model = models.TemplateProject
        fields = ('name', 'description', 'group', 'project', 'avatar_url')

and the view:

class TemplateProjectsView(generics.ListCreateAPIView):
    pagination_class = None

    serializer_class = serializers.TemplateProjectSerializer

    def get_queryset(self):
        return models.TemplateProject.objects.all() 

It works well, when I try to retrieve list of objects, but I cannot create an object using this API, because I get following error:

Invalid pk "1" - object does not exist.

So, before creating an object, a validation is applied for all fields, and serializer cannot serialize this integer into an object because this object, which is referenced by foreign key, does not exist. I wrote a method validate_group(self, value) , but exception raises before the execution point arrives this method. The more close point I could put a break in a debugger is method is_valid(self, raise_exception=False) . I could create missing objects there, but I think, that would be a bad practice because this method actually doesn't has an aim for validating or preparing data.

How to properly create an object before it passes all validations?

One possible options is, define group explicitly as an integer field. This way, group field will not be tried to be validated as a TemplateProjectGroup instance.

class TemplateProjectSerializer(serializers.ModelSerializer):
    group = serializers.IntegerField(source='group.id')
    ...

With this setup, you can get group id like this in create or update method of the serializer:

template_project_group_id = validated_data.pop('group').get('id')

Another option is, you could get or create a group instance in the view, by getting group id from the request, and then always pass an existing group id to the serializer, and expect an existing group id in the serializer. This would mean moving some of the validation logic to the view (you'd need to check at least if an integer is supplied for group field), but you wouldn't need to tweak your serializer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM