組件化10大接口

組件化10大接口

表設計

基表

Model類的內部配置Meta類要設置abstract=True,這樣的Model類就是用來做爲基表git

多表:Book,Publish,Author,AuthorDetail數據庫

  1. 基表必須設置abstract,基表就是給普通Model類繼承使用的,
  2. 設置了abstract就不會完成數據庫遷移完成建表
  3. abstract 目的就是隻爲了繼承設置,而且告訴django,不要進行創建這個表格

表斷關聯

db_constraint=Falsedjango

  1. 表之間沒有外鍵關聯,可是有外鍵邏輯關聯(有充當外鍵的字段)
  2. 斷關聯後不會影響數據庫查詢效率,可是會極大提升數據庫增刪改效率(不影響增刪改查操做)
  3. 斷關聯必定要經過邏輯保證表之間數據的安全
  4. 斷關聯 db_constraint=False,將表之間的關係斷掉,能夠在邏輯上依舊能夠連表
  5. 級聯關係
    • 做者沒了,詳情也沒:on_delete=models.CASCADE,刪除做者,詳情也會一併刪除,級聯刪除
    • 出版社沒了,書仍是那個出版社出版:on_delete=models.DO_NOTHING,刪除出版社,書的出版社不變,不刪除級聯
    • 部門沒了,員工沒有部門(空不能):null=True, on_delete=models.SET_NULL,刪除我的的部門,設置外鍵字段爲空
    • 部門沒了,員工進入默認部門(默認值):default=0,on_delete=models.SET_DEFAULT。刪除我的的部門,設置全部的外鍵字段爲0
    • 對對錶不能設計on_delete

ORM外鍵設計

一、一對多:外鍵放在多的一方
二、多對多:外鍵放在經常使用的一方
三、一對一:外鍵放在不經常使用的一方
四、外鍵字段爲正向查詢字段,related_name是反向查詢字段json

author = models.OneToOneField(to='Author', related_name='detail', db_constraint=False, on_delete=models.CASCADE)

有做者能夠沒有詳情,刪除做者,詳情必定會被級聯刪除api

外鍵字段爲正向查詢字段,related_name是反向查詢字段安全

related_name='books',反向查詢能夠直接點books,反向查詢的字段
db_constraint=False,去掉外鍵創建的關聯
on_delete=models.DO_NOTHING,不執行級聯刪除app

# ManyToManyField字段不提供設置on_delete,若是想設置關係表級聯,只能手動定義關係表
    authors = models.ManyToManyField(to='Author', related_name='books', db_constraint=False)

表設計

from django.db import models
from django.conf import settings
# 1、基表
# Model類的內部配置Meta類要設置abstract=True,這樣的Model類就是用來做爲基表

# 多表:Book,Publish,Author,AuthorDetail
class BaseModel(models.Model):
    is_delete = models.BooleanField(default=False)
    create_time = models.DateTimeField(auto_now_add=True)

    class Meta:
        # 基表必須設置abstract,基表就是給普通Model類繼承使用的,
        # 設置了abstract就不會完成數據庫遷移完成建表
        # abstract 目的就是隻爲了繼承設置,而且告訴django,不要進行創建這個表格

        abstract = True  # 抽象的表


class Book(BaseModel):# 繼承表
    name = models.CharField(max_length=16)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    # related_name='books',反向查詢能夠直接點books,反向查詢的字段
    # db_constraint=False,去掉外鍵創建的關聯
    # on_delete=models.DO_NOTHING,不執行級聯刪除
    publish = models.ForeignKey(to='Publish', related_name='books', db_constraint=False, on_delete=models.DO_NOTHING)
    # 重點:多對多外鍵實際在關係表中,ORM默認關係表中兩個外鍵都是級聯
    # db_constraint=False
    # ManyToManyField字段不提供設置on_delete,若是想設置關係表級聯,只能手動定義關係表
    authors = models.ManyToManyField(to='Author', related_name='books', db_constraint=False)

    # 自定義連表深度,不須要反序列化,由於自定義插拔屬性不參與反序列化
    @property
    def publish_name(self):
        return self.publish.name

    @property
    def author_list(self):
        temp_author_list = []
        for author in self.authors.all():
            temp_author_list.append({
                'name': author.name,
                'sex': author.get_sex_display(),
                'mobile': author.detail.mobile
            })
        return temp_author_list


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


class Author(BaseModel):
    name = models.CharField(max_length=16)
    sex = models.IntegerField(choices=[(0, '男'), (1, '女')], default=0)


class AuthorDetail(BaseModel):
    mobile = models.CharField(max_length=11)
    # 有做者能夠沒有詳情,刪除做者,詳情必定會被級聯刪除
    # 外鍵字段爲正向查詢字段,related_name是反向查詢字段
    author = models.OneToOneField(to='Author', related_name='detail', db_constraint=False, on_delete=models.CASCADE)

響應模塊格式化response(二次封裝)

返回信息組件化

格式:post

{
    'status':0,
    'msg':"",
    'results':'',
    ...
}
from rest_framework.response import Response

class APIResponse(Response):
    # 格式化data
    def __init__(self, status=0, msg='ok', results=None, http_status=None, headers=None, exception=False, **kwargs):
        data = {  # json的response基礎有數據狀態碼和數據狀態信息
            'status': status,
            'msg': msg
        }
        if results is not None:  # 後臺有數據,響應數據
            data['results'] = results
        data.update(**kwargs)  # 後臺的一切自定義響應數據直接放到響應數據data中
        super().__init__(data=data, status=http_status, headers=headers, exception=exception)

自定義異常

在使用drf的時候,驗證與is_valid(raise_exception=True)衝突,所以使用

from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework.response import Response
def exception_handler(exc, context):
    # drf_exception_handler的執行結果就是異常信息的Response對象或None
    # 是Response對象能夠直接返回
    # 是None能夠從exc中拿到異常信息,從context拿到是誰出現的異常,本身格式化成Response對象返回
    # 重點:自定義異常模塊目的是記錄異常信息到日誌文件 - 產品階段
    response = drf_exception_handler(exc, context)
    if response is None:
        response = Response({'detail': '%s' % exc}, status=500, exception=True)
    # logging.error(response.data)
    return response

全局配置

配置settings.py

REST_FRAMEWORK = {
    # 異常
    # 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',#系統異常
    'EXCEPTION_HANDLER': 'api.utils.exception_handler',#重寫異常

}

序列化

# 瞭解
fields = '__all__'#返回全部
exclude = ('id', )#除了id 其餘的返回

 depth = 1#連表深度查詢返回
    

#下面倆個不能一塊兒使用
fields = '__all__'#返回全部
exclude = ('id', )#除了id 其餘的返回
from rest_framework import serializers
from . import models
class BookListSerializer(serializers.ListSerializer):
    # 一、create方法父級ListSerializer已經提供了
    # def create(self, validated_data):
    #     # 經過self.child來訪問綁定的ModelSerializer
    #     print(self.child)
    #     raise Exception('我不提供')

    # 二、父級ListSerializer沒有經過update方法的實現體,須要本身重寫
    def update(self, instance, validated_data):
        # print(instance)
        # print(validated_data)
        return [
            self.child.update(instance[i], attrs) for i, attrs in enumerate(validated_data)
        ]



class BookModelSerializer(serializers.ModelSerializer):
    # 經過BookModelSerializer.Meta.list_serializer_class來訪問綁定的ListSerializer
    class Meta:
        # 關聯ListSerializer完成羣增羣改
        #################################################################
        list_serializer_class = BookListSerializer

        model = models.Book
        # fields = ('name', 'price', 'publish', 'authors')
        # fields = ('name', 'price', 'publish_name', 'author_list')
        # 瞭解
        # fields = '__all__'
        # exclude = ('id', )
        # depth = 1
        # 序列化與反序列化整合
        fields = ('name', 'price', 'publish_name',
                  'author_list', 'publish', 'authors')
        extra_kwargs = {
            'publish': {
                'write_only': True
            },
            'authors': {
                'write_only': True
            }
        }

子序列化

子序列化都是提供給外鍵(正向方向)完成深度查詢的,外鍵數據是惟一:many=False;不惟一:many=True

只能參與序列化,且反序列化不能寫(反序列化外鍵字段會拋異常)

# 前提:若是隻有查需求的接口,自定義深度還能夠用子序列化方式完成
class PublishModelSerializer(serializers.ModelSerializer):
    # 子序列化都是提供給外鍵(正向方向)完成深度查詢的,外鍵數據是惟一:many=False;不惟一:many=True
    # 注:只能參與序列化,且反序列化不能寫(反序列化外鍵字段會拋異常)
    books = BookModelSerializer(many=True)
    class Meta:
        model = models.Publish
        fields = ('name', 'address', 'books')

十大接口

from django.conf.urls import url
from . import views
urlpatterns = [
    url(r'^books/$', views.BookAPIView.as_view()),
    url(r'^books/(?P<pk>\d+)/$', views.BookAPIView.as_view()),
]
from rest_framework.views import APIView
from rest_framework.response import Response
from . import models, serializers
from .response import APIResponse
class BookAPIView(APIView):

單查、羣查

# 單查、羣查
    def get(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        if pk:
            book_obj = models.Book.objects.filter(pk=pk, is_delete=False).first()
            if not book_obj:
                return APIResponse(1, 'pk error', http_status=400)
            book_data = serializers.BookModelSerializer(book_obj).data
            return APIResponse(results=book_data)

        book_query = models.Book.objects.filter(is_delete=False).all()
        return APIResponse(0, 'ok', data=serializers.BookModelSerializer(book_query, many=True).data)

單刪、羣刪

# 單刪、羣刪
    def delete(self, request, *args, **kwargs):
        """
        單刪:前臺數據爲pk,接口爲 /books/(pk)/
        羣刪:前臺數據爲pks,接口爲 /books/
        """
        pk = kwargs.get('pk')
        # 將單刪羣刪邏輯整合
        if pk:  # /books/(pk)/的接口就不考慮羣刪,就固定爲單刪
            pks = [pk]
        else:
            pks = request.data.get('pks')
        # 前臺數據有誤(主要是羣刪沒有提供pks)
        if not pks:
            return APIResponse(1, 'delete error', http_status=400)
        # 只要有操做受影響行,就是刪除成功,反之失敗
        rows = models.Book.objects.filter(is_delete=False, pk__in=pks).update(is_delete=True)
        if rows:
            return APIResponse(0, 'delete ok')
        return APIResponse(1, 'delete failed')

單增、羣增

# 單增、羣增
    def post(self, request, *args, **kwargs):
        """
        單增:前臺提交字典,接口 /books/
        羣增:前臺提交列表套字典,接口 /books/
        """
        request_data = request.data
        if isinstance(request_data, dict):  # 單增
            book_ser = serializers.BookModelSerializer(data=request_data)
            if book_ser.is_valid():
                book_obj = book_ser.save()
                return APIResponse(results=serializers.BookModelSerializer(book_obj).data)
            return APIResponse(1, msg=book_ser.errors)
        elif isinstance(request_data, list) and len(request_data) != 0 :  # 羣增
            book_ser = serializers.BookModelSerializer(data=request_data, many=True)
            book_ser.is_valid(raise_exception=True)
            book_obj_list = book_ser.save()
            return APIResponse(results=serializers.BookModelSerializer(book_obj_list, many=True).data)
        else:
            return APIResponse(1, 'data error', http_status=400)

單總體改、羣總體改

重寫update(更新)方法

父級ListSerializer沒有經過update方法的實現體,須要本身重寫

class BookListSerializer(serializers.ListSerializer):
    # 一、create方法父級ListSerializer已經提供了
    # def create(self, validated_data):
    #     # 經過self.child來訪問綁定的ModelSerializer
    #     print(self.child)
    #     raise Exception('我不提供')

    # 二、父級ListSerializer沒有經過update方法的實現體,須要本身重寫
    def update(self, instance, validated_data):
        # print(instance)
        # print(validated_data)
        return [
            self.child.update(instance[i], attrs) for i, attrs in enumerate(validated_data)
        ]
class BookModelSerializer(serializers.ModelSerializer):
    # 經過BookModelSerializer.Meta.list_serializer_class來訪問綁定的ListSerializer
    class Meta:
        # 關聯ListSerializer完成羣增羣改
        list_serializer_class = BookListSerializer

單總體改、羣總體改

instance=obj

# 修改和新增,都須要經過數據,數據依舊給data,修改與新增不一樣點,instance要被賦值爲被修改對象
book_ser = serializers.BookModelSerializer(instance=book_obj, data=request_data)
# 單總體改、羣總體改
    def put(self, request, *args, **kwargs):
        """
        單總體改:前臺提交字典,接口 /books/(pk)/
        羣總體改:前臺提交列表套字典,接口 /books/,注每個字典均可以經過pk
        """
        pk = kwargs.get('pk')
        request_data = request.data
        if pk: # 單改
            try:
                book_obj = models.Book.objects.get(pk=pk)
            except:
                return APIResponse(1, 'pk error')

            # 修改和新增,都須要經過數據,數據依舊給data,修改與新增不一樣點,instance要被賦值爲被修改對象
            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:  # 羣改
            if not isinstance(request_data, list) or len(request_data) == 0:
                return APIResponse(1, 'data error', http_status=400)

            # [{pk:1,...}, {pk:3,...}, {pk:100,...}] => [obj1, obj3, obj100] + [{...}, {...}, {...}]
            # 要考慮pk對應的對象是否被刪,以及pk沒有對應的對象
            # 假設pk3被刪,pk100沒有 => [obj1] + [{...}]

            # 注:必定不要在循環體中對循環對象進行增刪(影響對象長度)的操做
            obj_list = []
            data_list = []
            for dic in request_data:
                # request_data多是list,單內部不必定是dict
                try:
                    pk = dic.pop('pk')
                    try:
                        obj = models.Book.objects.get(pk=pk, is_delete=False)
                        obj_list.append(obj)
                        data_list.append(dic)
                    except:
                        pass
                except:
                    return APIResponse(1, 'data error', http_status=400)

            book_ser = serializers.BookModelSerializer(instance=obj_list, data=data_list, many=True)
            book_ser.is_valid(raise_exception=True)
            book_obj_list = book_ser.save()
            return APIResponse(results=serializers.BookModelSerializer(book_obj_list, many=True).data)

單局部改、羣局部改

partial=True

# 局部修改就是在總體修改基礎上設置partial=True,將全部參與反序列化字段設置爲required=False
book_ser = serializers.BookModelSerializer(instance=book_obj, data=request_data, partial=True)
# 單局部改、羣局部改
    def patch(self, request, *args, **kwargs):
        """
        單總體改:前臺提交字典,接口 /books/(pk)/
        羣總體改:前臺提交列表套字典,接口 /books/,注每個字典均可以經過pk
        """
        pk = kwargs.get('pk')
        request_data = request.data
        if pk:
            try:
                book_obj = models.Book.objects.get(pk=pk)
            except:
                return APIResponse(1, 'pk error')
            # 局部修改就是在總體修改基礎上設置partial=True,將全部參與反序列化字段設置爲required=False
            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:  # 羣改
            if not isinstance(request_data, list) or len(request_data) == 0:
                return APIResponse(1, 'data error', http_status=400)

            # [{pk:1,...}, {pk:3,...}, {pk:100,...}] => [obj1, obj3, obj100] + [{...}, {...}, {...}]
            # 要考慮pk對應的對象是否被刪,以及pk沒有對應的對象
            # 假設pk3被刪,pk100沒有 => [obj1] + [{...}]

            # 注:必定不要在循環體中對循環對象進行增刪(影響對象長度)的操做
            obj_list = []
            data_list = []
            for dic in request_data:
                # request_data多是list,單內部不必定是dict
                try:
                    pk = dic.pop('pk')
                    try:
                        obj = models.Book.objects.get(pk=pk, is_delete=False)
                        obj_list.append(obj)
                        data_list.append(dic)
                    except:
                        pass
                except:
                    return APIResponse(1, 'data error', http_status=400)

            book_ser = serializers.BookModelSerializer(instance=obj_list, data=data_list, many=True, partial=True)
            book_ser.is_valid(raise_exception=True)
            book_obj_list = book_ser.save()
            return APIResponse(results=serializers.BookModelSerializer(book_obj_list, many=True).data)

升級版

單改

# 單改
    def _single_update(self, pk, data):
        try:
            instance = models.Worker.objects.get(pk=pk, is_delete=False)
            ser = serializers.WorkerModelSerializer(instance=instance, data=data, partial=True)
            ser.is_valid(raise_exception=True)
            obj = ser.save()
            return APIResponse(data=serializers.WorkerModelSerializer(obj).data)
        except:
            return APIResponse(1, 'pk error', http_status=400)

羣改

# 羣改
    def _many_updata(self, data):
        instance_list = []
        data_list = []
        for dic in data:
            try:
                # 不是字典,數據錯誤
                pk = dic.pop('pk')
                if len(dic) == 0: raise Exception('隨便')
                try:
                    # pk沒有,對應的obj沒有,對應的obj已刪除,該數據丟棄
                    obj = models.Worker.objects.get(pk=pk, is_delete=False)
                    instance_list.append(obj)
                    data_list.append(dic)
                except:
                    pass
            except:
                return APIResponse(1, 'data error', http_status=400)

        ser = serializers.WorkerModelSerializer(instance=instance_list, data=data_list, partial=True, many=True)
        ser.is_valid(raise_exception=True)
        obj_list = ser.save()
        return APIResponse(data=serializers.WorkerModelSerializer(obj_list, many=True).data)
def patch(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        request_data = request.data
        if pk and isinstance(request_data, dict) and len(request_data) != 0:
            return self._single_update(pk, request_data)
        elif not pk and isinstance(request_data, list) and len(request_data) != 0:
            return self._many_updata(request_data)
        else:
            return APIResponse(1, 'data error', http_status=400)
相關文章
相關標籤/搜索