zoukankan      html  css  js  c++  java
  • day74

    基表的概念

    abstract = True

    # 基表
    class BaseModel(models.Model):
        is_delete = models.BooleanField(default=False)
        create_time = models.DateTimeField(auto_now_add=True)
        class Meta:
            # 基表,抽象类
            abstract = True
    

    class Book(BaseModel):
    pass

    class Publish(BaseModel):
    pass

    表关系的建立

    通常我们在实际开发项目中我们的数据库各种表是不建立外键关联表的,是为什么呢?

    因为我们在建立了外键关系之后,随着业务发展,表越来越多,外间关系越来越复杂,假如有一天表字段可能有问题,需要重构什么的,需要删除掉,那会发现删不掉了,因为和其他很多表都建立了外键关系,这时候我们的数据库断开关联表关系的优点就体现出来了:

    • 断关联表不会影响连表的查询操作
    • 还有助于提升连表增删改操作的效率
    • 最重要的一点是方便后期的扩展与重构
    • 那么他的缺点就是数据库本身没有连表的检测,容易出现脏数据,需要通过我们严格的代码逻辑来避免脏数据的产生。

    我们来举个例子,比如说图书管理系统:

    • Book 和 Publish 一对多,外键在多的一方
    • Book 和 Author 多对多,外键在查询频率高的一方
    • Author 和 AuthorDetail,一对一,外键根据实际情况建在合理的情况,一般在查询频率低的地方
    class Book(BaseModel):
        name = models.CharField(max_length=16)
        price = models.DecimalField(max_digits=5, decimal_places=2)
        publish = models.ForeignKey(to='Publish',
                                    related_name='books', db_constraint=False,
                                    on_delete=models.DO_NOTHING,null=True)
        authors = models.ManyToManyField(to='Author', related_name='books', db_constraint=False)
    

    class Publish(BaseModel):
    name = models.CharField(max_length=16)
    address = models.CharField(max_length=64)

    class Author(BaseModel):
    name = models.CharField(max_length=16)

    class AuthorDetail(BaseModel):
    mobile = models.CharField(max_length=11)
    author = models.OneToOneField(to='Author', related_name='detail',
    db_constraint=False, on_delete=models.CASCADE)

    外键字段属性解释

    • related_name:可以在外键中设置外键反向查询的字段,正向查询按字段,反向查询按related_name值

    • db_constraint:在外键字段中控制表是否关联,默认为True表示关联,设置False表示断开关联。

    • on_delete:在外键中必须设置,表示级联关系

      在Django1.X版本下,系统默认提供的是models.CASCADE

      在Django2.X版本下,必须手动明确on_delete参数(有如下可选的参数)

      注:在多对多关系字段中不能设置on_delete级联关系,默认为级联,如要处理级联关系,需手动明确关系表,处理关系表中的多个外键。

      • CASCADE:默认值,表示级联,比如作者和作者详情关联,作者删除,详情也删除
      • DO_NOTHING:外键不会被级联,什么也不做,比如作者与书关联,作者删除,书还在
      • SET_DEFAULT:外键设置级联后,删除后外键字段设置为默认值,必须配合default参数使用,比如部门与员工关联,部门解散后,员工进入待定默认部门
      • SET_NULL:外键设置级联后,删除后外键字段设置为Null,必须配合Null=True参数使用,比如部门与员工关联,部门解散后,员工无所属部门为Null

    DRF中Response类二次封装

    from rest_framework.response import Response
    

    class APIResponse(Response):
    def init(self,status=0,msg='ok',results=None,http_status=None,
    headers=None,exception=False,content_type=None,kwargs)
    :

    # 组织data响应体字典
    data = {
    'status':status,
    'msg':msg,
    }
    # results只要不是空都是数据,包括0,false,条件不能写if results
    if results is not None:
    data['results'] = results
    # 将kwargs中额外的k-v数据添加到data中
    data.update(
    kwargs)

        <span class="hljs-comment"># data=None, status=None,template_name=None, headers=None,exception=False, content_type=None</span>
        super().__init__(data=data,status=http_status,headers=headers,exception=exception,content_type=content_type)</code></pre>
    

    连表查询

    depth配置查询深度

    • 自动查询深度
    • 会把需要序列化出来的外键字段关联的其他表中所有信息查询出来,前提是外键字段参与序列化
    • 可设置查询几层的深度

    depth = 5

    class PublishModelSerializer(serializers.ModelSerializer):
        #
        # books = BookModelSerializer(many=True)
        class Meta:
            model = models.Publish
            fields = ['name','address','books_list']
            # fields = ['name','books']
            # 设置自动查询深度,会把需要序列化出来的外键字段关联的其他表中所有信息查询出来
            # 可设置几层的深度
            depth = 5

    插拔式自定义序列化字段连表查询

    • 设置fields参与序列化的字段
    • 在models模型类中配置方法

    ModelSerializer序列化类

    class PublishModelSerializer(serializers.ModelSerializer):
        #
        # books = BookModelSerializer(many=True)
        class Meta:
            model = models.Publish
            fields = ['name','address','books_list']

    models模型类中配置

    class Publish(BaseModel):
        name = models.CharField(max_length=16)
        address = models.CharField(max_length=64)
    

    @property
    def books_list(self):
    book_list = []
    books = self.books.all()
    for book in books:
    book_dict = {
    'name':book.name,
    'price':book.price
    }
    book_list.append(book_dict)
    return book_list

    子序列化

    • 只能在序列化类中使用
    • 字段名必须是外键名
    • 在外键关联数据是多条数据的时候,需要明确many=True
    • 单向操作,作为子序列化的类必须写在序列化类的上方

    路由配置:

    url(r'^v1/publishes/$',views.PublishView.as_view()),

    视图配置:

    
    # 出版社群查
    class PublishView(APIView):
        def get(self,request,*args,**kwargs):
            pk = kwargs.get('pk')
            if pk:
                publish_obj = models.Publish.objects.filter(pk=pk,is_delete=False).first()
                if not publish_obj:
                    return Response({'status': 1, 'msg': 'geterror'}, status=400)
                publish_dict = serializers.PublishModelSerializer(publish_obj, many=False).data
                return Response({'status': 0, 'msg': 'ok', 'results': publish_dict})
            else:
                publish_query = models.Publish.objects.all()
                publish_obj = serializers.PublishModelSerializer(publish_query,many=True)
                return Response({'status':0,'msg':'ok','results':publish_obj.data})
    

    序列化类配置:

    # 子序列化类
    class BookModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            # fields = '__all__'
            fields = ['name','price']
    

    class PublishModelSerializer(serializers.ModelSerializer):
    #
    books = BookModelSerializer(many=True)
    class Meta:
    model = models.Publish
    fields = ['name','address','books']

        <span class="hljs-comment"># 了解配置</span>
        <span class="hljs-comment"># fields = '__all__'</span>
        <span class="hljs-comment"># exclude = ['name']</span>
        <span class="hljs-comment"># depth = 2  # 自动深度,值代表深度次数,但是被深度的外键采用__all__,显示所有字段</span></code></pre>
    

    实现十大接口

    url配置

    from django.conf.urls import url, include
    from django.conf import settings
    from . import views
    from . import models
    from django.views.static import serve
    

    urlpatterns = [
    url(r'^v1/books/$',views.BookAPIView.as_view()),
    url(r'^v1/books/(?P<pk>d+)/$',views.BookAPIView.as_view()),
    ]

    models模型类配置

    class Book(BaseModel):
        name = models.CharField(max_length=16)
        price = models.DecimalField(max_digits=5, decimal_places=2)
        publish = models.ForeignKey(to='Publish',
                                    related_name='books', db_constraint=False,
                                    on_delete=models.DO_NOTHING)
        authors = models.ManyToManyField(to='Author', related_name='books', db_constraint=False)
    

    @property
    def publish_info(self):
    return {
    'name':self.publish.name,
    'address':self.publish.address
    }

    @property
    def author_list(self):
    author_list_temp = []
    authors = self.authors.all()
    for author in authors:
    author_dict = {
    'name':author.name
    }
    # 有详情才处理详情信息
    try:
    author_dict['mobile'] = author.detail.mobile
    except:
    author_dict['mobile'] = '无'

            author_list_temp.append(author_dict)
        <span class="hljs-keyword">return</span> author_list_temp
    

    class Publish(BaseModel):
    name = models.CharField(max_length=16)
    address = models.CharField(max_length=64)

    @property
    def books_list(self):
    book_list = []
    books = self.books.all()
    for book in books:
    book_dict = {
    'name':book.name,
    'price':book.price
    }
    book_list.append(book_dict)
    return book_list

    class Author(BaseModel):
    name = models.CharField(max_length=16)

    class AuthorDetail(BaseModel):
    mobile = models.CharField(max_length=11)
    author = models.OneToOneField(to='Author', related_name='detail',db_constraint=False, on_delete=models.CASCADE)

    单查群查

    接口使用说明

    • 单查:获取pk序列化
    • 群查:获取所有对象序列化,使用many=True参数
    请求方式 请求参数 API链接
    单查 GET http://127.0.0.1:8000/api/v1/books/n/
    群查 GET http://127.0.0.1:8000/api/v1/books/

    视图代码:

        def get(self,response,*args,**kwargs):
            pk = kwargs.get('pk')
            if pk:
                # 单查
                book_obj = models.Book.objects.filter(is_delete=False,pk=pk).first()
                if not book_obj:
                    return APIResponse(1,'error',http_status=400)
                book_ser = serializers.BookModelSerializer(book_obj)
            else:
                # 群查
                book_query = models.Book.objects.filter(is_delete=False).all()
                book_ser = serializers.BookModelSerializer(book_query,many=True)
            return APIResponse(results=book_ser.data)

    序列化类代码

    class BookModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            # 配置fields 为__all__ 表示序列化所有的字段
            # fields = '__all__'
    
        fields = [<span class="hljs-string">'name'</span>,<span class="hljs-string">'price'</span>,<span class="hljs-string">'publish'</span>,<span class="hljs-string">'authors'</span>,<span class="hljs-string">'publish_info'</span>,<span class="hljs-string">'author_list'</span>]
        extra_kwargs = {
            <span class="hljs-string">'publish'</span>:{
                <span class="hljs-string">'write_only'</span>:<span class="hljs-keyword">True</span>
            },
            <span class="hljs-string">'authors'</span>:{
                <span class="hljs-string">'write_only'</span>:<span class="hljs-keyword">True</span>
            }
        }</code></pre>
    

    单删群删

    • 单删:提供pk删除
    • 群删:提供列表([pk,,,,,,pk])

    接口使用说明

    请求方式 请求参数 API链接
    单删 DELETE http://127.0.0.1:8000/api/v1/books/n/
    群删 DELETE [pk,,,,,pkn] http://127.0.0.1:8000/api/v1/books/

    视图代码

        def delete(self, request, *args, **kwargs):
            '''
            删除逻辑:修改is_delete字段,修改成功代表删除成功,修改失败代表删除失败
            单删接口:/books/(pk)
            群删接口:/books/
            '''
            pk = kwargs.get('pk')
            if pk:
                # 将单删格式化成群删一条,列表类型
                pks = [pk]
            else:
                # 群删,提供列表中套pk的类型数据
                pks = request.data
            print(pks)
            try:
                rows = models.Book.objects.filter(is_delete=False,pk__in=pks).update(is_delete=True)
            except:
                return APIResponse(1,'数据有误')
    
        <span class="hljs-keyword">if</span> rows:
            <span class="hljs-keyword">return</span> APIResponse(<span class="hljs-number">0</span>,<span class="hljs-string">'删除成功'</span>)
        <span class="hljs-keyword">return</span> APIResponse(<span class="hljs-number">1</span>,<span class="hljs-string">'删除失败'</span>)</code></pre>
    

    单增群增

    • 单增:提供pk和字典数据,many=False
    • 群增:提供列表套字段数据,many=True

    接口使用说明

    请求方式 请求参数 API链接
    单删 POST {"k":"v"} http://127.0.0.1:8000/api/v1/books/
    群删 POST [{"k":"v"},{"k":"v"}] http://127.0.0.1:8000/api/v1/books/

    视图代码

        def post(self,request,*args,**kwargs):
            '''
            单增接口:/books/,数据:{...}
            群增接口:/books/,数据:[{...},{...}]
            处理逻辑:将数据交给反序列化类处理,数据的类型关系到many属性是否为True
            '''
            if isinstance(request.data,dict):
                many = False
            elif isinstance(request.data,list):
                many = True
            else:
                return APIResponse(1,'数据有误',http_status=400)
            book_ser = serializers.BookModelSerializer(data=request.data,many=many)
            book_ser.is_valid(raise_exception=True)
            book_obj = book_ser.save()
            return APIResponse(results=serializers.BookModelSerializer(book_obj,many=many).data)

    反序列化类代码

    class BookModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            # 配置fields 为__all__ 表示序列化所有的字段
            # fields = '__all__'
    
        fields = [<span class="hljs-string">'name'</span>,<span class="hljs-string">'price'</span>,<span class="hljs-string">'publish'</span>,<span class="hljs-string">'authors'</span>,<span class="hljs-string">'publish_info'</span>,<span class="hljs-string">'author_list'</span>]
        extra_kwargs = {
            <span class="hljs-string">'publish'</span>:{
                <span class="hljs-string">'write_only'</span>:<span class="hljs-keyword">True</span>
            },
            <span class="hljs-string">'authors'</span>:{
                <span class="hljs-string">'write_only'</span>:<span class="hljs-keyword">True</span>
            }
        }</code></pre>
    

    整体单改群改

    接口使用说明

    • 整体单改要提供主键值和字典数据(需提供全部字段,不需要提供主键)
    • 整体群改要提供一个大列表包含数据字典(需提供全部字段和主键)
    • 整体群改需要重写ListSerializer的update方法
    请求方式 请求参数 API链接
    整体单改 PUT {"k":"v"} http://127.0.0.1:8000/api/v1/books/n/
    整体群改 PUT [{"k":"v"},{"k":"v"}] http://127.0.0.1:8000/api/v1/books/

    视图代码

        def put(self, request, *args, **kwargs):
            '''
            单改接口:/books/(pk),数据:{"k":"v"}
            群改接口:/books/,数据:[{"k":"v"},{"k":"v"}]
            逻辑:将数据交给反序列化类处理,数据的类型关系到many属性是否为True
            '''
            pk = kwargs.get('pk')
            if pk:
                # 单改
                try:
                    book_obj = models.Book.objects.get(is_delete=False,pk=pk)
                except:
                    return APIResponse(1,'pk error',http_status=400)
                book_ser = serializers.BookModelSerializer(instance=book_obj,data=request.data)
                book_ser.is_valid(raise_exception=True)
                book_obj = book_ser.save()
                return APIResponse(results=serializers.BookModelSerializer(book_obj).data)
            else:
                # 群改
                # 数据时列表套字典,每个字典中必须带pk,如果有一条没有带pk,返回数据有误
                # 如果pk对应的对象已被删除或不存在,返回数据有误
                try:
                    pks = []
                    for dic in request.data:
                        pks.append(dic.pop('pk'))
                    book_query = models.Book.objects.filter(is_delete=False,pk__in=pks).all()
                    if len(pks) != len(book_query):
                        raise Exception('pk对应的数据不存在')
                except Exception as e:
                    return Response({'detail':'%s'%e},status=400)
    
            book_ser = serializers.BookModelSerializer(instance=book_query,data=request.data,many=<span class="hljs-keyword">True</span>)
            <span class="hljs-comment"># print(book_ser)</span>
            book_ser.is_valid(raise_exception=<span class="hljs-keyword">True</span>)
            book_obj = book_ser.save()
            <span class="hljs-keyword">return</span> APIResponse(results=serializers.BookModelSerializer(book_obj,many=<span class="hljs-keyword">True</span>).data)</code></pre>
    

    序列化类

    # 多表操作群改
    class BookListSerializer(serializers.ListSerializer):
        def create(self, validated_data):
            return super().create(validated_data)
    
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update</span><span class="hljs-params">(self, instance_list, validated_data_list)</span>:</span>
        <span class="hljs-keyword">return</span> [
            self.child.update(instance_list[index],attrs)  <span class="hljs-keyword">for</span> index,attrs <span class="hljs-keyword">in</span> enumerate(validated_data_list)
        ]
    

    # 子序列化类
    class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
    # ModelSerializer 配置了ListSerializer辅助类,帮助完成群增和群改
    # 如果只有群增,不需要自定义配置,
    # 如果要完成群改,必须自定义配置
    list_serializer_class = BookListSerializer
    model = models.Book
    # 配置fields 为__all__ 表示序列化所有的字段
    # fields = 'all'

        fields = [<span class="hljs-string">'name'</span>,<span class="hljs-string">'price'</span>,<span class="hljs-string">'publish'</span>,<span class="hljs-string">'authors'</span>,<span class="hljs-string">'publish_info'</span>,<span class="hljs-string">'author_list'</span>]
        extra_kwargs = {
            <span class="hljs-string">'publish'</span>:{
                <span class="hljs-string">'write_only'</span>:<span class="hljs-keyword">True</span>,
                <span class="hljs-comment"># 'required':True</span>
            },
            <span class="hljs-string">'authors'</span>:{
                <span class="hljs-string">'write_only'</span>:<span class="hljs-keyword">True</span>
            }
        }
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">validate</span><span class="hljs-params">(self, attrs)</span>:</span>
        print(self)
        print(<span class="hljs-string">'.................'</span>)
        print(<span class="hljs-string">'传入的request对象:%s'</span>% self.context.get(<span class="hljs-string">'request'</span>))
        <span class="hljs-keyword">return</span> attrs
    

    局部单改群改

    接口使用说明

    • 局部单改提供主键值和数据字典(字段选填)
    • 局部群改群改要提供一个大列表包含数据字典(字段选填)
    • 设置参数将所有字段变成可选参数partial=True
    请求方式 请求参数 API链接
    局部单改 PATHC {"k":"v"} http://127.0.0.1:8000/api/v1/books/n/
    局部群改 PATHC [{"k":"v"},{"k":"v"}] http://127.0.0.1:8000/api/v1/books/

    视图代码

        def patch(self, request, *args, **kwargs):
            '''
            单改接口:/books/(pk),数据:{"k":"v"}
            群改接口:/books/,数据:[{"k":"v"},{"k":"v"}]
            逻辑:将数据交给反序列化类处理,数据的类型关系到many属性是否为True
            '''
            pk = kwargs.get('pk')
            if pk:
                # 单改
                try:
                    book_obj = models.Book.objects.get(is_delete=False,pk=pk)
                except:
                    return APIResponse(1,'pk error',http_status=400)
                book_ser = serializers.BookModelSerializer(instance=book_obj,data=request.data,partial=True)
                book_ser.is_valid(raise_exception=True)
                book_obj = book_ser.save()
                return APIResponse(results=serializers.BookModelSerializer(book_obj).data)
            else:
                # 群改
                # 数据时列表套字典,每个字典中必须带pk,如果有一条没有带pk,返回数据有误
                # 如果pk对应的对象已被删除或不存在,返回数据有误
                try:
                    pks = []
                    for dic in request.data:
                        pks.append(dic.pop('pk'))
                    book_query = models.Book.objects.filter(is_delete=False,pk__in=pks).all()
                    if len(pks) != len(book_query):
                        raise Exception('pk对应的数据不存在')
                except Exception as e:
                    return Response({'detail':'%s'%e},status=400)
    
            book_ser = serializers.BookModelSerializer(instance=book_query,data=request.data,many=<span class="hljs-keyword">True</span>,partial=<span class="hljs-keyword">True</span>, context={<span class="hljs-string">'request'</span>: request})
            <span class="hljs-comment"># print(book_ser)</span>
            book_ser.is_valid(raise_exception=<span class="hljs-keyword">True</span>)
            book_obj = book_ser.save()
            <span class="hljs-keyword">return</span> APIResponse(results=serializers.BookModelSerializer(book_obj,many=<span class="hljs-keyword">True</span>).data)</code></pre>
    

    序列化类

    # 多表操作群改
    class BookListSerializer(serializers.ListSerializer):
        def create(self, validated_data):
            return super().create(validated_data)
    
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update</span><span class="hljs-params">(self, instance_list, validated_data_list)</span>:</span>
        <span class="hljs-keyword">return</span> [
            self.child.update(instance_list[index],attrs)  <span class="hljs-keyword">for</span> index,attrs <span class="hljs-keyword">in</span> enumerate(validated_data_list)
        ]
    

    # 子序列化类
    class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
    # ModelSerializer 配置了ListSerializer辅助类,帮助完成群增和群改
    # 如果只有群增,不需要自定义配置,
    # 如果要完成群改,必须自定义配置
    list_serializer_class = BookListSerializer
    model = models.Book
    # 配置fields 为__all__ 表示序列化所有的字段
    # fields = 'all'

        fields = [<span class="hljs-string">'name'</span>,<span class="hljs-string">'price'</span>,<span class="hljs-string">'publish'</span>,<span class="hljs-string">'authors'</span>,<span class="hljs-string">'publish_info'</span>,<span class="hljs-string">'author_list'</span>]
        extra_kwargs = {
            <span class="hljs-string">'publish'</span>:{
                <span class="hljs-string">'write_only'</span>:<span class="hljs-keyword">True</span>,
                <span class="hljs-comment"># 'required':True</span>
            },
            <span class="hljs-string">'authors'</span>:{
                <span class="hljs-string">'write_only'</span>:<span class="hljs-keyword">True</span>
            }
        }
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">validate</span><span class="hljs-params">(self, attrs)</span>:</span>
        print(self)
        print(<span class="hljs-string">'.................'</span>)
        print(<span class="hljs-string">'传入的request对象:%s'</span>% self.context.get(<span class="hljs-string">'request'</span>))
        <span class="hljs-keyword">return</span> attrs
    

  • 相关阅读:
    设计模式实战应用之五:工厂方法模式
    Codeforces445A_DZY Loves Chessboard(预处理)
    void f(int(&amp;p)[3]){} 和void f(int(*p)[3]){}的差别
    《linux 内核全然剖析》 mktime.c
    Java中对象、对象引用、堆、栈、值传递以及引用传递的详解
    android 仿ios开关控件
    ViewDragHelper实战 自己打造Drawerlayout
    [javase学习笔记]-8.5 statickeyword的使用场景
    玩转图片Base64编码
    Android stuido viewpagerindicator的使用
  • 原文地址:https://www.cnblogs.com/gfhh/p/12115510.html
Copyright © 2011-2022 走看看