DRF-编写双嵌套序列化器的更好方法

时间:2019-07-06 07:19:46

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

我正在构建一个待办事项应用程序,可以在待办事项上发布评论。 为此,我制作了一个可写的双嵌套序列化器。它可以工作,但是我编写的更新方法很难理解,因此我试图使其更具可读性。是否有更好(或标准的方式)为双嵌套序列化器编写更新方法?

我已经阅读了官方文档中有关嵌套序列化程序的文档。 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

serializers.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"
                }
            ]
        }
    ]
}

1 个答案:

答案 0 :(得分:0)

创建模型应该由相应的序列化程序负责。因此,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()