美文网首页
Django REST framework 模型中 Many-t

Django REST framework 模型中 Many-t

作者: rollingstarky | 来源:发表于2020-08-19 19:30 被阅读0次

    一、模型

    # blogs/models.py
    from django.db import models
    
    
    class TagModel(models.Model):
        tag = models.CharField(max_length=20, unique=True)
        description = models.CharField(max_length=100)
    
        class Meta:
            db_table = 'blog_tags'
    
    
    class PostModel(models.Model):
        title = models.CharField(max_length=50)
        update_date = models.DateTimeField(auto_now=True)
        tags = models.ManyToManyField(TagModel)
    
        class Meta:
            db_table = 'blog_posts'
    

    二、Serializers

    # blogs/serializers.py
    from rest_framework import serializers
    from blogs.models import TagModel, PostModel
    
    
    class TagSerializer(serializers.ModelSerializer):
        class Meta:
            model = TagModel
            fields = '__all__'
    
    
    class PostSerializer(serializers.ModelSerializer):
        class Meta:
            model = PostModel
            fields = '__all__'
    

    三、视图

    # blogs/views.py
    from rest_framework.viewsets import ModelViewSet
    from blogs.models import TagModel, PostModel
    from blogs.serializers import TagSerializer, PostSerializer
    
    
    class TagViewSet(ModelViewSet):
        queryset = TagModel.objects.all()
        serializer_class = TagSerializer
    
    
    class PostViewSet(ModelViewSet):
        queryset = PostModel.objects.all()
        serializer_class = PostSerializer
    

    四、URLs

    # blogs/urls.py
    from django.urls import include, path
    from rest_framework import routers
    from blogs.views import TagViewSet, PostViewSet
    
    router = routers.DefaultRouter()
    router.register(r'tags', TagViewSet)
    router.register(r'posts', PostViewSet)
    
    urlpatterns = [
        path('', include(router.urls))
    ]
    

    修改项目总体的 URL 配置 urls.py

    from django.contrib import admin
    from django.urls import path, include
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('blog/', include('blogs.urls'))
    ]
    

    五、效果

    # GET http://127.0.0.1:8002/blog/tags/
    [
        {
            "description": "Technique for python programming",
            "id": 1,
            "tag": "Python"
        },
        {
            "description": "Shell programming tricks",
            "id": 2,
            "tag": "Shell"
        },
        {
            "description": "Linux system administration",
            "id": 3,
            "tag": "Linux"
        }
    ]
    
    # GET http://127.0.0.1:8002/blog/posts/
    [
        {
            "id": 1,
            "tags": [
                1,
                3
            ],
            "title": "Python for linux administration",
            "update_date": "2020-08-11T07:01:50.893976Z"
        }
    ]
    

    六、自定义序列化方式

    为了在获取 posts 列表时,每条 post 中的 tags 不仅仅显示 id,还显示对应的标签名称。修改代码如下:

    # blogs/serializers.py
    from rest_framework import serializers
    from blogs.models import TagModel, PostModel
    
    
    class TagSerializer(serializers.ModelSerializer):
        class Meta:
            model = TagModel
            fields = '__all__'
    
    
    class TagsReadOnly(serializers.ModelSerializer):
        class Meta:
            model = TagModel
            fields = ['id', 'tag']
    
    
    class PostSerializer(serializers.ModelSerializer):
        tags = TagsReadOnly(many=True)
    
        class Meta:
            model = PostModel
            fields = '__all__'
    

    效果:

    # GET http://127.0.0.1:8002/blog/posts/
    [
        {
            "id": 1,
            "tags": [
                {
                    "id": 1,
                    "tag": "Python"
                },
                {
                    "id": 3,
                    "tag": "Linux"
                }
            ],
            "title": "Python for linux administration",
            "update_date": "2020-08-11T07:01:50.893976Z"
        }
    ]
    

    但此时若使用 POST 或 PUT 方法新增或更新数据,会报出如下错误(即在新增或修改 post 的同时,会尝试创建关联的 tags,但这些 tags 本就已经存在,从而导致冲突):

    {
        "tags": [
            {
                "tag": [
                    "tag model with this tag already exists."
                ]
            }
        ]
    }
    

    测试 POST 和 PUT 动作时使用的 JSON 数据格式如下:

    {
        "tags": [
            {
                "id": 1,
                "tag": "Python"
            }
        ],
        "title": "An interesting post"
    }
    

    为了使 posts 接口在接收数据时支持列表类型的 tags(类似 "tags": [1, 2, 3] 这种)且能够成功更新,可以选择覆盖 PostSerializerto_internal_valuecreate 方法:

    # blogs/serializers.py
    from rest_framework import serializers
    from blogs.models import TagModel, PostModel
    
    
    class TagSerializer(serializers.ModelSerializer):
        class Meta:
            model = TagModel
            fields = '__all__'
    
    
    class TagsReadOnly(serializers.ModelSerializer):
        class Meta:
            model = TagModel
            fields = ['id', 'tag']
    
    
    class PostSerializer(serializers.ModelSerializer):
        tags = TagsReadOnly(many=True)
    
        class Meta:
            model = PostModel
            fields = '__all__'
    
        def to_internal_value(self, data):
            return data
    
        def create(self, validated_data):
            tags_data = validated_data.pop('tags')
            post = PostModel.objects.create(**validated_data)
            tags = [TagModel.objects.get(
                pk=id) for id in tags_data]
            post.tags.set(tags)
            post.save()
            return post
    

    其中 to_internal_value 方法用于验证前端传入的数据(上述代码中不做任何验证);create 方法用于新增或更新后台数据。

    此时再向 posts 接口 POST 或 PUT 数据时,就可以使用如下格式:

    {
        "tags": [1, 2, 3],
        "title": "An interesting post"
    }
    

    相关文章

      网友评论

          本文标题:Django REST framework 模型中 Many-t

          本文链接:https://www.haomeiwen.com/subject/spzedktx.html