Избегайте проверки внешнего ключа в сериализаторе django rest framework

#python #django #django-rest-framework

#python #django #django-rest-framework

Вопрос:

Я пишу API для следующих моделей:

 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)
  

Логика следующая: пользователь может создать экземпляр TemplateProject с несуществующим group полем. Поэтому, если группа не существует, она должна быть создана с определенным идентификатором. Итак, у меня есть этот сериализатор:

 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')
  

и представление:

 class TemplateProjectsView(generics.ListCreateAPIView):
    pagination_class = None

    serializer_class = serializers.TemplateProjectSerializer

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

Это работает хорошо, когда я пытаюсь получить список объектов, но я не могу создать объект с помощью этого API, потому что я получаю следующую ошибку:

 Invalid pk "1" - object does not exist.
  

Итак, перед созданием объекта проверка применяется ко всем полям, и сериализатор не может сериализовать это целое число в объект, потому что этот объект, на который ссылается внешний ключ, не существует. Я написал метод validate_group(self, value) , но возникает исключение до того, как точка выполнения достигает этого метода. Более близкая точка, в которой я мог бы сделать перерыв в отладчике, — это метод is_valid(self, raise_exception=False) . Я мог бы создать там недостающие объекты, но я думаю, что это было бы плохой практикой, потому что этот метод на самом деле не нацелен на проверку или подготовку данных.

Как правильно создать объект, прежде чем он пройдет все проверки?

Ответ №1:

Одним из возможных вариантов является явное определение group как целочисленного поля. Таким образом, поле group не будет проверяться как экземпляр TemplateProjectGroup.

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

С помощью этой настройки вы можете получить идентификатор группы, подобный этому, в методе create или update сериализатора:

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

Другой вариант заключается в том, что вы можете получить или создать экземпляр группы в представлении, получив идентификатор группы из запроса, а затем всегда передавать существующий идентификатор группы сериализатору и ожидать существующий идентификатор группы в сериализаторе. Это означало бы перенос части логики проверки в представление (вам нужно будет проверить, по крайней мере, указано ли целое число для поля group), но вам не нужно будет настраивать ваш сериализатор.