Сериализаторы против представлений для извлечения объекта по полю внешнего ключа - PullRequest
1 голос
/ 05 октября 2019

Я бы хотел, чтобы пользователи могли выполнять запрос POST для создания поста в блоге и использовать topic name вместо topic id в качестве опции.

Минимальная модель может выглядеть следующим образом.

models.py

class Topic(models.Model):
    name = models.CharField(max_length=30, unique=True)
    description = models.CharField(max_length=100)

class Post(models.Model):
    name = models.CharField(max_length=30)
    topic = models.ForeignKey(Topic, on_delete=models.PROTECT)
    created_on = models.DateTimeField(blank=True, auto_now_add=True, editable=False)

Теперь есть 2 возможных подхода, которые я рассмотрел:

1) Обеспечьте простоту просмотра.

views.py

class PostList(ListCreateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

serializers.py

class PostSerializer(serializers.ModelSerializer):
    topic_name  = serializers.CharField()

    class Meta:
        model = Topic
        fields = ('name', 'topic_name', 'created_on')
        read_only_fields = ('created_on',)

    def validate_topic_name(self, value):
        """Verify that the topic exists."""
        if not Topic.objects.filter(name=value).exists():
            raise serializers.ValidationError("Specified Topic Name does not exist!")
        else:
            return value

    def create(self, validated_data):
        """Create a Post."""
        topic_name = validated_data.pop('topic_name', None)
        topic = Topic.objects.get(name=topic_name)  
        return Post.objects.create(topic=topic, **validated_data)

2) Держите сериализаторы простыми.

views.py

class PostList(ListCreateAPIView):
    queryset = Post.objects.all()

    def create(self, request, *args, **kwargs):

        serializer = self.get_serializer(data=request.data)

        if not serializer.is_valid():
            return Response(
                serializer.errors, status=status.HTTP_400_BAD_REQUEST)

        topic = get_object_or_404(Topic, name=serializer.data['topic_name'])  

        post = Post.objects.create(
                 name=serializer.data['name'],
                 topic=topic)

        return Response({'name': serializer.data['name'], 'description': serializer.data['topic_name']},
                    status=status.HTTP_201_CREATED)

serializers.py

class PostSerializer(serializers.ModelSerializer):
    topic_name  = serializers.CharField()

    class Meta:
        model = Topic
        fields = ('name', 'topic_name', 'created_on')
        read_only_fields = ('created_on',)

Мой вопрос:

  • Куда мне поставить сложность? В сериализаторе в просмотрах?
  • Есть ли лучший способ продолжить?

1 Ответ

1 голос
/ 05 октября 2019

Нет необходимости добавлять эту сложность в ваш код вообще, потому что эта функция уже предоставлена ​​serializers.SlugRelatedField, которая позволяет вам ссылаться на связанное поле, используя строку вместо идентификатора. Вот как это выглядит с вашей моделью:

views.py

class PostList(ListCreateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

serializers.py

class PostSerializer(serializers.ModelSerializer):
    topic = serializers.SlugRelatedField(slug_field='name',
                                         queryset=Topic.objects.all())

    class Meta:
        model = Post
        fields = '__all__'
...