1. 程式人生 > 實用技巧 >Book系列的群CURD操作

Book系列的群CURD操作

Book系列群操作

表設計與關聯models.py

from django.db import models

# 一、基表
# Model類的內部配置Meta類要設定abstract=True,這樣的Model類就是用來作為基表
# 基表不會在資料庫內創建出來
# 二、表斷關聯
# 1、表之間沒有外來鍵關聯,但是有外來鍵邏輯關聯(有充當外來鍵的欄位)
# 2、斷關聯後不會影響資料庫查詢效率,但是會極大提高資料庫增刪改效率(不影響增刪改查操作)
# 3、斷關聯一定要通過邏輯保證表之間資料的安全,不要出現髒資料,程式碼控制
# 4、斷關聯
# 5、級聯關係
#       作者沒了,詳情也沒:on_delete=models.CASCADE
#       出版社沒了,書還是那個出版社出版:on_delete=models.DO_NOTHING
#       部門沒了,員工沒有部門(空不能):null=True, on_delete=models.SET_NULL
#       部門沒了,員工進入預設部門(預設值):default=0, on_delete=models.SET_DEFAULT
# 三、ORM外來鍵設計
# 1、一對多:外來鍵放在多的一方
# 2、多對多:外來鍵放在常用的一方
# 3、一對一:外來鍵放在不常用的一方
# 4、外來鍵欄位為正向查詢欄位,related_name是反向查詢欄位

# 一張表有且只有一個自增欄位,有且只有一個主鍵
class BaseModel(models.Model):
    is_delete = models.BooleanField(default=False)
    # auto_now_add=True 只要記錄建立,不需要手動插入時間,自動把當前時間插入
    create_time = models.DateTimeField(auto_new_add=True)
    # auto_now=True,只要更新,就會把當前時間插入
    last_update_time = models.DateTimeField(auto_new=True)
    # import datetime
    # create_time=models.DateTimeField(default=datetime.datetime.now)
    class Meta:
        # 單個欄位,有索引,有唯一
        # 多個欄位,有聯合索引,聯合唯一
        abstract=True # 抽象表

class Book(BaseModel):
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    #to_field 預設不寫,關聯到Publish主鍵
    #db_constraint=False  邏輯上的關聯,實質上沒有外來鍵練習,增刪不會受外來鍵影響,但是orm查詢不影響
    # 出版社被刪,圖書不受影響
    publish = models.ForeignKey(to='Publish', on_delete=models.DO_NOTHING, db_constraint=False)
    # 多對多關係不能設定on_delete,如果想設定關係表級聯,只能手動定義關係表(純手動或半自動)
    # 什麼時候用自動,什麼時候用手動?第三張表只有關聯欄位,用自動    第三張表有擴充套件欄位,需要手動寫
    authors = models.ManyToManyField(to='Author', db_constraint=False)
    
    @property # 將方法包成資料屬性,不參與反序列化操作
    def publish_name(self):
        # 返回出版社名字
        return self.publish.name
    
    @property
    def author_list(self):
        # 使用列表生成式生成一個個的作者名字,返回列表
        return [instance.name for instance in self.authors.all()]
    
class Author(BaseModel):
    name = models.CharField(max_length=32)
    gender = models.IntegerField(choices=((1, '男'), (2, '女')), default=1)
    # 作者被刪,詳情一定被刪
    detail = models.OneToOneField(to='Detail', on_delete=models.CASCADE, db_constraint=False)
    
class Detail(BaseModel):
    phton = models.CharField(max_length=11)
    
class Publish(BaseModel):
    name = models.CharField(max_length=32)
    address = models.CharField(max_length=32)

序列化器編寫

# ser.py
from rest_framework import serializers
from api import models
# 通過檢視原始碼可知,群增時父類ListSerializer已經幫我寫了create方法,但是群改的update方法需要我們自己重寫
# 我們也可以在繼承父類ListSerializer的BookListSerializer中重寫create方法實現群增,但是沒必要
class BookListSerializer(serializers.ListSerializer):
    def update(self, instance, validated_data):
        # self.child就是BookModelSerializer
        return [self.child.update(instance[i], item) for i, item in enumerate(validated_data)]


class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        # 通過list_serializer_class關聯BookListSerializer,從而訪問繫結的ListSerializer,實現群增群改
        list_serializer_class = BookListSerializer
        model = models.Book
        # 插拔式屬性,進行序列化操作,資料屬性(publish_name,author_list)不參與反序列化操作
        fields = ('pk', 'name', 'price', 'author_list', 'publish_name', 'publish', 'authors')
        extra_kwargs = {
            'publish': {'write_only': True},# 提交資料時填寫,不會展示給使用者看
            'publish_name': {'read_only': True}, # 只展示,不會提交
            'authors': {'write_only': True},
            'author_list': {'read_only': True}
        }

使用GenericAPIView實現5個介面

# views.py
from rest_framework.generics import GenericAPIView
from rest_framework import status
from api import models
from api import ser
from utils.response import APIResponse # 封裝的response物件

class BooksGenericAPIView(GenericAPIView):
    queryset = models.Book.objects.all().filter(is_delete=False) # 篩選出所有沒被刪除的資料
    serializer_class = ser.BookModelSerializer # 寫入好的序列化器

    # 單查、群查
    def get(self, request, *args, **kwargs):
        if kwargs:# 通過kwargs是否有值來判斷是查詢單個數據還是查詢所有資料
            instance = self.get_object()
            serializer = self.get_serializer(instance=instance)
        else:
            book_list = self.get_queryset()
            serializer = self.get_serializer(instance=book_list, many=True)
        return APIResponse(message='查詢成功', result=serializer.data) # 返回自定義格式的json資料

    # 單增、群增
    def post(self, request, *args, **kwargs):
        if isinstance(request.data, list): # 規定群增,前端傳過來的必須是個列表
            # save()呼叫繼承的父類LiseSerializer中的create方法
            serializer = self.get_serializer(data=request.data, many=True) 
        else:
            # save()呼叫BookModelSerializer的create方法
            serializer = self.get_serializer(data=request.data)
        # 使用全域性異常處理進行異常捕獲,以json格式返回異常
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return APIResponse(message='建立成功', result=serializer.data, status=status.HTTP_201_CREATED)

    # 單改、群改
    def put(self, request, *args, **kwargs):
        if kwargs:
            instance = self.get_object()
            # save()呼叫BookModelSerializer的update方法
            serializer = self.get_serializer(instance=instance, data=request.data)
            # 區域性修改
            # 區域性修改就是在整體修改基礎上設定partial=True,或者將所有參與反序列化欄位設定為required=False
            # serializer = self.get_serializer(instance=instance, data=request.data, partial=True)
        else:
            book_list = []# 用來儲存書物件
            modify_list = []# 用來儲存修改的資料
            for data in request.data:
                pk = data.pop('pk')
                book_list.append(models.Book.objects.get(pk=pk))# 將獲取到的書物件追加到列表
                modify_list.append(data)# 將需要修改的資料追加到列表
            # save()會呼叫我們自己重寫的update方法(ListSerializer沒有幫我們寫群改的update)
            # BookModelSerializer通過list_serializer_class來關聯BookListSerializer,從而訪問繫結的ListSerializer從而實現群改,其中的self.child就是指BookModelSerializer
            serializer = self.get_serializer(instance=book_list, data=modify_list, many=True)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return APIResponse(message='修改成功', result=serializer.data)

    # 單刪、群刪
    def delete(self, request, *args, **kwargs):
        # 不管是單刪還是群刪都存入列表pks中,只需要通過__in來篩選即可
        pks = []
        if kwargs:
            pk = kwargs.get('pk')
            pks.append(pk)
        else:
            pks = request.data.get('pks')
        # 規定群刪除傳入格式{"pks":[]}
        res = models.Book.objects.filter(pk__in=pks, is_delete=False).update(is_delete=True)
        if not res:
            return APIResponse(code=101, message='要刪除的資料不存在', status=status.HTTP_404_NOT_FOUND)
        return APIResponse(message='成功刪除%s條資料' % res)

路由設定

# urls.py
# 總路由:
    path('api/', include('api.urls'))
    
# 子路由:
	path('books/', views.BooksGenericAPIView.as_view()),
    re_path(r'books/(?P<pk>\d+)', views.BooksGenericAPIView.as_view()),