DRF - лучший способ написания двойных вложенных сериализаторов - PullRequest
2 голосов
/ 06 июля 2019

Я создаю приложение 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

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 голосов
/ 09 июля 2019

Ответственность за создание модели должен нести соответствующий сериализатор.Поэтому 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()
...