【问题标题】:DRF - a better way to write double nested serializersDRF - 编写双嵌套序列化程序的更好方法
【发布时间】:2019-11-16 14:51:03
【问题描述】:

我正在构建一个可以将 cmets 发布到 todo 的 todo 应用程序。 为此,我制作了一个可写的双嵌套序列化程序。它可以工作,但是我写的更新方法很难理解,所以我试图让它更具可读性。是否有更好的(或标准的方法)为双嵌套序列化程序编写更新方法?

我已阅读官方文档中有关嵌套序列化程序的文档。 https://www.django-rest-framework.org/api-guide/serializers/

models.py

class CustomUser(AbstractUser):

    def __str__(self):
        return self.email


class Todo(models.Model):
    publisher = models.ForeignKey(
        CustomUser,
        on_delete=models.CASCADE,
        related_name="todos",
    )
    title = models.CharField(max_length=50)
    pub_date = models.DateTimeField('date published')
    description = models.CharField(max_length=800)
    is_done = models.BooleanField(default=False)

    def __str__(self):
        return self.title


class Comment(models.Model):
    subject = models.ForeignKey(
        Todo,
        on_delete=models.CASCADE,
        related_name="comments",
    )
    publisher = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
    title = models.CharField(max_length=50, default=None)
    pub_date = models.DateTimeField('date published')
    description = models.CharField(max_length=800)

    def __str__(self):
        return self.description

序列化器.py

class CommentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Comment
        fields = (
            'url',
            'title',
            'pub_date',
            'description',
        )
        read_only_fields = (
            'url',
            'pub_date',
        )


class TodoSerializer(serializers.ModelSerializer):
    comments = CommentSerializer(required=False, many=True)

    class Meta:
        model = Todo
        fields = (
            'url',
            'title',
            'pub_date',
            'description',
            'is_done',
            'comments'
        )
        read_only_fields = (
            'url',
            'pub_date',
        )


class UserSerializer(serializers.ModelSerializer):
    todos = TodoSerializer(required=False, many=True)

    class Meta:
        model = CustomUser
        fields = (
            'url',
            'email',
            'username',
            'todos',
        )
        read_only_fields = (
            'url',
            'email',
        )

    def create(self, validated_data):
        todos = validated_data.pop('todos', None)
        user = CustomUser.objects.create(**validated_data)

        if todos is not None:
            for todo in todos:
                comments = todo.pop('comments', None)
                Todo.objects.create(user=user, **todo)
                if comments is not None:
                    for comment in comments:
                        Comment.objects.create(todo=todo, **comment)
        return user

    def update(self, instance, validated_data):
        todos_data = validated_data.pop('todos', None)
        todos = (instance.todos).all()
        todos = list(todos)
        instance.username = validated_data.get('username', instance.username)
        instance.save()
        if todos_data is not None:
            for todo_data in todos_data:
                comments_data = todo_data.pop('comments')
                todo = todos.pop(0)
                comments = (todo.comments).all()
                comments = list(comments)

                todo.title = todo_data.get('title', todo.title)
                todo.description = todo_data.get('description', todo.description)
                todo.is_done = todo_data.get('is_done', todo.is_done)
                todo.save()
                if comments_data is not None:
                    for comment_data in comments_data:
                        comment = comments.pop(0)
                        comment.title = comment_data.get('title', comment.title)
                        comment.description = comment_data.get('description', comment.description)
                        comment.save()
        return instance

预期的 JSON

{
    "url": "http://127.0.0.1:8000/api/users/4/",
    "email": "api01@example.com",
    "username": "api01",
    "todos": [
        {
            "url": "http://127.0.0.1:8000/api/todo/1/",
            "title": "first todo1.1",
            "pub_date": "2019-07-04T12:40:56.799308+09:00",
            "description": "description for first todo1.1",
            "is_done": true,
            "comments": [
                {
                    "url": "http://127.0.0.1:8000/api/comment/1/",
                    "title": "first comment-1.1",
                    "pub_date": "2019-07-03T12:32:26.604598+09:00",
                    "description": "aaaaaaaaa-1.1"
                },
                {
                    "url": "http://127.0.0.1:8000/api/comment/2/",
                    "title": "second comment-1.1",
                    "pub_date": "2019-07-03T12:56:22.906482+09:00",
                    "description": "bbbbbbbbbbb-1.1"
                }
            ]
        }
    ]
}

【问题讨论】:

  • 好吧,我不确定您希望如何使用它。所以你期望用户会同时创建他所有的 todo 并且每个 todo 在创建时已经有一堆 cmets?这与现实相去甚远。实际上,您今天分别创建每个,并且 cmets 逐渐添加到每个待办事项中。所以你的序列化器是不必要的复杂
  • 哦.. 我想我误解了使用序列化程序的方式。我认为我必须将所有这些组合起来才能在一个页面中使用它们,例如当我想将来自特定用户的待办事项加载到页面上时,以及发布到待办事项上的 cmets。谢谢!
  • 您始终可以发出多个请求来获取页面的数据。此外,您可以决定在同一请求中返回所有附加到它们的 todos 和 cmets,但单独创建它们。您可以阅读有关 read_only 和 write_only 字段

标签: python django django-rest-framework nested serialization


【解决方案1】:

创建模型应该是各个序列化程序的责任。所以TodoSerializer 应该只创建 Todo 对象,CommentSerializer 应该创建 Comment 对象。更新也一样

你也不需要打电话给Model.objects.create(**data)。序列化程序可以自己完成。您只需致电super().create(validated_data)super().update(instance, validated_data)

class TodoSerializer(serializers.ModelSerializer):

    comments = serializers.ListField(child=serializers.DictField, required=True)

    class Meta:
        model = CustomUser
        fields = (
            'title',
            'pub_date',
            'description',
            'is_done',
            'comment',
        )

    def create(self, validated_data)
        todo = super().create(validated_data)
        comments = validated_data.pop('comments', [])
        for comment in comments:
            comment['todo'] = todo.id
            serializer = CommentSerializer(data=comment)
            serializer.is_valid(raise_exception=True)
            serializer.save()


class AddBulkTodos(serializers.ModelSerializer):
    todos = serializers.ListField(child=serializers.DictField, required=True)

    class Meta:
        model = CustomUser
        fields = (
            'url',
            'email',
            'username',
            'todos',
        )
        read_only_fields = (
            'url',
        )

    def create(self, validated_data)
        todos = validated_data.pop('todos', [])
        user = super().create(validated_data)
        for todo in todos:
            todo['user'] = user.id
            serializer = TodoSerializer(data=todo)
            serializer.is_valid(raise_exception=True)
            serializer.save()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-31
    • 1970-01-01
    • 1970-01-01
    • 2021-05-23
    • 1970-01-01
    相关资源
    最近更新 更多