zoukankan      html  css  js  c++  java
  • drf框架3-ModelSerializer操作序列化与反序列化

    项目准备:dg_proj

    配置:settings.py
    INSTALLED_APPS = [
        # ...
        'rest_framework',
    ]
    
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'dg_proj',
            'USER': 'root',
            'PASSWORD': 'root',
        }
    }
    """
    任何__init__文件
    import pymysql
    pymysql.install_as_MySQLdb()
    """
    
    LANGUAGE_CODE = 'zh-hans'
    TIME_ZONE = 'Asia/Shanghai'
    USE_I18N = True
    USE_L10N = True
    USE_TZ = False
    
    MEDIA_URL = '/media/'
    MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
    
    路由
    # 主
    from django.conf.urls import url, include
    from django.contrib import admin
    from django.views.static import serve
    from django.conf import settings
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^api/', include('api.urls')),
        url(r'^media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}),
    ]
    
    # 子
    from django.conf.urls import url
    from . import views
    urlpatterns = [
        
    ]
    

    函数复习

    # * 前都是位置参数:无值位置必须赋值,有值位置可以不要赋值,必须在无值位置之后
    # * 后都是关键字参数:无值关键字必须赋值,有值关键字可以不要赋值,都是指名道姓传参,所以顺序任意
    # * 可以紧跟一个变量,用来接收所有未接收完的位置参数
    def fn(a, b, c=0, *, d=0, x):
        print(a)
        print(b)
        print(c)
        print(d)
        print(x)
    
    fn(10, 20, 30, x=30, d=100)
    

    特殊注释

    # TODO 今天做到这了,结合TODO控制台可以完成快速定位
    
    # 假设a就是str类型,实际操作值不是0,而是不能明确标识的字符串
    a = 0  # type: str
    # 再书写 a. 就可以提示 str 方法
    
    x, y = 0, 0  # type: str, dict
    
    def z(a:str, b:dict):
        pass
    
    def fn(a, b):
        """
        :param str a:
        :param dict b:
        :return:
        """
    

    多表设计

    """
    Book表
    	name、price、img、authors、publish、is_delete、create_time
    
    Publish表
    	name、address、is_delete、create_time
    	
    Author表:
    	name、age、is_delete、create_time
    
    AuthorDetail表
    	mobile, author、is_delete、create_time
    	
    	
    BaseModel基表
    	is_delete、create_time
    上面四表继承基表,可以继承两个字段
    """
    

    基表

    utils/model.py
    from django.db import models
    
    class BaseModel(models.Model):
        create_time = models.DateTimeField(auto_now_add=True, null=True)
        is_delete = models.BooleanField(default=False)
        class Meta:
            # 抽象表,不会完成数据库迁移
            abstract = True
    

    多表关系

    模型:api/models.py
    from django.db import models
    
    # 多表
    # 书表 出版社表 作者表
    """
    Book表: name、price、img、authors、publish、is_delete、create_time
    Publish表: name、address、is_delete、create_time
    Author表: name、age、is_delete、create_time
    AuthorDetail表: mobile, author、is_delete、create_time
    """
    
    """
    多表关系总结
    一对一:Author、AuthorDetail两表
    1)关系字段放在AuthorDetail表中:作者删除详情删除,详情删除作者保留
    2)作者找详情用 外键related_name(detail),详情找作者用 外键字段(author)
    3)db_constraint断开表关联,on_delete规定逻辑关联删除动作,models.CASCADE级联删除
    author = models.OneToOneField(to='Author', null=True,
            related_name='detail',
            db_constraint=False,
            on_delete=models.CASCADE
        )
    一对多:Book、Publish两表
    1)关系字段放在Book表中(多的一方):出版社删除书外键不动,书删除没有任何影响
    2)出版社找书用 外键related_name(books),书找出版社 外键字段(publish)
    3)db_constraint断开表关联,on_delete规定逻辑关联删除动作,models.DO_NOTHING关联无动作
    publish = models.ForeignKey(to='Publish', null=True,
            related_name='books',
            db_constraint=False,
            on_delete=models.DO_NOTHING,
            
        )
        
    外键字段的其它关联方式
    1)断关联,删除关联表记录,外键值置空
    db_constraint=False, on_delete=models.SET_NULL, null=True,
    2)断关联,删除关联表记录,外键值置默认值
    db_constraint=False, on_delete=models.SET_DEFAULT, default=1,
    
    多对多:Book、Author两表
    1)关系字段放在任意一方都可以:出版社删除或书删除彼此不影响,但关系表一定级联删除
    2)正向找 外键字段,反向找 外键字段related_name
    3)db_constraint断开表关联,on_delete不存在(不设置,本质在第三张表中设置,且一定是级联)
    authors = models.ManyToManyField(to='Author', null=True,
            related_name='books',
            db_constraint=False,
        )
    """
    
    from utils.model import BaseModel
    class Book(BaseModel):
        name = models.CharField(max_length=64)
        price = models.DecimalField(max_digits=5, decimal_places=2)
        img = models.ImageField(upload_to='img', default='img/default.png')
        publish = models.ForeignKey(to='Publish', null=True,
            related_name='books',
            db_constraint=False,
            on_delete=models.DO_NOTHING,
            # on_delete=models.SET_NULL, null=True,
            # on_delete=models.SET_DEFAULT, default=1,
        )
        authors = models.ManyToManyField(to='Author', null=True,
            related_name='books',
            db_constraint=False,
        )
        class Meta:
            db_table = 'old_boy_book'
            verbose_name = '书籍'
            verbose_name_plural = verbose_name
        def __str__(self):
            return self.name
    
    class Publish(BaseModel):
        name = models.CharField(max_length=64)
        address = models.CharField(max_length=64)
        class Meta:
            db_table = 'old_boy_publish'
            verbose_name = '出版社'
            verbose_name_plural = verbose_name
        def __str__(self):
            return self.name
    
    class Author(BaseModel):
        name = models.CharField(max_length=64)
        age = models.IntegerField()
        class Meta:
            db_table = 'old_boy_author'
            verbose_name = '作者'
            verbose_name_plural = verbose_name
        def __str__(self):
            return self.name
    
    class AuthorDetail(BaseModel):
        mobile = models.CharField(max_length=11)
        # related_name 反向查询的 字段  eg:author_obj.detail.mobile
        # 一对一外键:放在拆出来的表,断关联,设置级联
        author = models.OneToOneField(to='Author', null=True,
            related_name='detail',
            db_constraint=False,
            on_delete=models.CASCADE
        )
        class Meta:
            db_table = 'old_boy_author_detail'
            verbose_name = '作者详情'
            verbose_name_plural = verbose_name
        def __str__(self):
            return '%s的详情' % self.author.name
    
    测试代码:script/t_dg.py
    import os
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dg_proj.settings")
    import django
    django.setup()
    
    from api import models
    
    # 一对一
    # author = models.Author.objects.first()  # type: models.Author
    # print(author.name)
    # print(author.detail.mobile)
    # author.delete()
    
    # author_detail = models.AuthorDetail.objects.first()  # type: models.AuthorDetail
    # print(author_detail.mobile)
    # print(author_detail.author.name)
    # author_detail.delete()
    
    
    # 一对多
    # publish = models.Publish.objects.filter(pk=2).first()  # type: models.Publish
    # print(publish.name)
    # print(publish.books.first().name)
    # publish.delete()
    
    # publish = models.Publish.objects.filter(is_delete=False).first()  # type: models.Publish
    # if publish:
    #     print(publish.name)
    #     print(publish.books.first().name)
    #     publish.is_delete = True
    #     publish.save()
    
    # book = models.Book.objects.first()  # type: models.Book
    # print(book.name)
    # print(book.publish.name)
    # book.delete()
    
    
    # 多对多
    # book = models.Book.objects.first()  # type: models.Book
    # print(book.name)
    # print(book.authors.first().name)
    # book.delete()
    
    author = models.Author.objects.first()  # type: models.Author
    print(author.name)
    print(author.books.first().name)
    author.delete()
    

    简单认识序列化

    序列化类:api/serializers.py
    from rest_framework import serializers
    from . import models
    
    """
    序列化总结:
    1)在自定义的ModelSerializer类中设置class Meta
        model 绑定序列化相关的模型类
        fields 插拔方式指定序列化字段
    2)在模型类中通过 方法属性 自定义连表查询的字段,在fields中插拔
    3)如果就使用外键字段完成连表深度查询,用 序列化深度
        外键字段 = 外键序列化类(many=True|False)
    """
    class PublishModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Publish
            fields = ('name', 'address')
    class AuthorModelSerializer(serializers.ModelSerializer):
    
        class Meta:
            model = models.Author
            fields = ('name', 'age', 'mobile')
    class BookModelSerializer(serializers.ModelSerializer):
        # 正向序列化深度
        # (不建议:必须用外键名才需要序列化深度,
        # 建议:自定义外键序列化名,在model类中插拔字段(属性方法))
        publish = PublishModelSerializer()
        authors = AuthorModelSerializer(many=True)
    
        # 了解:在ModelSerializer中不建议使用,如何书写了必须在fields中声明使用
        # p_n = serializers.SerializerMethodField()
        # def get_p_n(self, obj: models.Book):
        #     return obj.publish.name
    
        class Meta:
            model = models.Book
            fields = ('name', 'price', 'img', 'publish_name', 'authors_info', 'publish', 'authors')
    
            # 了解
            # fields = '__all__'  # 所有字段
            # # exclude = ('id', 'is_delete')  # 刨除某些字段
            # depth = 1  # 跨表自动深度(展示外键表的所有字段)
    
    
    序列化视图类:api/views.py
    from rest_framework.views import APIView
    from utils.response import APIResponse
    from . import models, serializers
    class BookAPIView(APIView):
        def get(self, request, *args, **kwargs):
            book_query = models.Book.objects.all()
            book_ser = serializers.BookModelSerializer(book_query, many=True)
            return APIResponse(0, 'ok', results=book_ser.data)
    
    

    简单认识反序列化

    反序列化模型类:api/models.py
    # 自定义model类的方法属性,完成插拔式跨表查询
    class Book(BaseModel):
        # ...
        @property
        def publish_name(self):
            return self.publish.name
        @property
        def authors_info(self):
            author_list = []
            for author in self.authors.all():
                author_list.append({
                    'name': author.name,
                    'age': author.age,
                    'mobile': author.detail.mobile
                })
            return author_list
        
    class Author(BaseModel):
        # ...
        @property
        def mobile(self):
            return self.detail.mobile
    
    
    
    反序列化类:api/serializers.py
    """
    反序列化总结:
    1)在自定义的ModelSerializer类中设置class Meta
        model 绑定反序列化相关的模型类
        fields 插拔方式指定反序列化字段
        extra_kwargs 定义系统校验字段的规则
    2)可以自定义局部钩子和全局钩子完成字段的复杂校验规则
    3)不需要重写create和update完成增加修改,ModelSerializer类已经帮我们实现了
    """
    class BookModelDeserializer(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            fields = ('name', 'price', 'publish', 'authors')
            extra_kwargs = {
                'name': {
                    'min_length': 3,
                    'error_messages': {
                        'min_length': '太短'
                    }
                },
                'publish': {
                    'required': True
                },
                'authors': {
                    'required': True
                },
            }
    
        # 自定义校验
        def validate_name(self, value):
            if 'sb' in value:
                raise serializers.ValidationError('书名有敏感词汇')
            return value
    
        def validate(self, attrs):
            name = attrs.get('name')
            publish = attrs.get('publish')
            if models.Book.objects.filter(name=name, publish=publish):
                raise serializers.ValidationError({'book': '书籍以存在'})
            return attrs
    
    
    
    序列化视图类:api/views.py
    from rest_framework.views import APIView
    from utils.response import APIResponse
    
    from . import models, serializers
    class BookAPIView(APIView):
        def get(self, request, *args, **kwargs):
            book_query = models.Book.objects.all()
            book_ser = serializers.BookModelSerializer(book_query, many=True)
            return APIResponse(0, 'ok', results=book_ser.data)
    
        def post(self, request, *args, **kwargs):
            book_ser = serializers.BookModelDeserializer(data=request.data)
            if book_ser.is_valid():
                book_ser.save()
                return APIResponse(0, 'ok')
            else:
                return APIResponse(1, '添加失败', results=book_ser.errors)
    
    

    序列化与反序列化共存(重点)

    路由:api/urls.py
    from django.conf.urls import url
    from . import views
    urlpatterns = [
        # ...
        url(r'^v2/books/$', views.BookV2APIView.as_view()),
        url(r'^v2/books/(?P<pk>.*)/$', views.BookV2APIView.as_view()),
    ]
    
    
    模型层修订:api/models.py
    class Book(BaseModel):
        # ...
        class Meta:
            # ...
            # 联合唯一 => patch结果修改部分数据就会收到'name'、'publish'联合限制
            unique_together = ('name', 'publish')
    
    
    序列化层:api/serializers.py
    class BookV2ModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            fields = ('name', 'price', 'publish', 'authors', 'img', 'publish_name', 'authors_info')
            extra_kwargs = {
                'publish': {
                    'required': True,
                    'write_only': True
                },
                'authors': {
                    'required': True,
                    'write_only': True
                },
                'img': {
                    'read_only': True
                }
            }
        def validate_name(self, value):
            if 'sb' in value:
                raise serializers.ValidationError('书名有敏感词汇')
            return value
        def validate(self, attrs):
            name = attrs.get('name')
            publish = attrs.get('publish')
            if models.Book.objects.filter(name=name, publish=publish):
                raise serializers.ValidationError({'book': '书籍以存在'})
            return attrs
    
    
    视图层:api/views.py
    class BookV2APIView(APIView):
        def get(self, request, *args, **kwargs):
            book_query = models.Book.objects.filter(is_delete=False).all()
            book_ser = serializers.BookV2ModelSerializer(book_query, many=True)
            return APIResponse(0, 'ok', results=book_ser.data)
    
        def post(self, request, *args, **kwargs):
            book_ser = serializers.BookV2ModelSerializer(data=request.data)
            if book_ser.is_valid():
                book_obj = book_ser.save()
                return APIResponse(0, 'ok',
                    results=serializers.BookV2ModelSerializer(book_obj).data
                )
            else:
                return APIResponse(1, '添加失败', results=book_ser.errors)
    
        def patch(self, request, *args, **kwargs):
            pk = kwargs.get('pk')
            if not pk:
                return APIResponse(1, 'pk error')
            try:
                book_obj = models.Book.objects.get(is_delete=False, pk=pk)
            except:
                return APIResponse(1, 'pk no book')
            book_ser = serializers.BookV2ModelSerializer(partial=True, instance=book_obj, data=request.data)
            if book_ser.is_valid():
                book_obj = book_ser.save()
                return APIResponse(1, 'ok',
                    results=serializers.BookV2ModelSerializer(book_obj).data
                )
            else:
                return APIResponse(1, '更新失败', results=book_ser.errors)
    
        def delete(self, request, *args, **kwargs):
            # 单删 /books/(pk)/
            # 群删 /books/  数据包携带 pks => request.data
            pk = kwargs.get('pk')
            if pk:
                pks = [pk]
            else:
                pks = request.data.get('pks')
            if not pks:
                return APIResponse(1, '删除失败')
            book_query = models.Book.objects.filter(is_delete=False, pk__in=pks)
            print(book_query)
            if not book_query.update(is_delete=True):  # 受影响的行得大于0
                return APIResponse(1, '删除失败')
            return APIResponse(0, '删除成功')
    
    
    
  • 相关阅读:
    c# 对文件操作
    c# 获取当前绝对路径
    c# 过滤特殊字符
    c# txt代码转换成HTML格式
    c# 对象集合转Json
    c# DataReader转换为Json
    c# DataSet转换为Json
    ABAP-SAP的LUW和DB的LUW的区别
    ABAP-关于隐式与显式的DB Commit
    ABAP-Keyword Documentation
  • 原文地址:https://www.cnblogs.com/gaohuayan/p/11462684.html
Copyright © 2011-2022 走看看