Reputation: 201
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?
Upvotes: 1
Views: 2758
Reputation: 5492
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.
Upvotes: 1