drf序列化高級、自定義只讀只寫、序列化覆蓋字段、二次封裝Response、數據庫查詢優化(斷關聯)、十大接口、視圖家族、自動補全圖片鏈接


自定義只讀

自定義只讀:Model類中
@property
def 字段(self): return "字段值"

models.py

class Book(BaseModel):
    name = models.CharField(max_length=64)
    price = models.DecimalField(max_digits=5, decimal_places=2, null=True)
    image = models.ImageField(upload_to='img', default='img/default.png')

    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  # @property字段默認就是read_only,且不允許修改
    def publish_name(self):
        return self.publish.name

自定義只寫

自定義只寫:Serializer類中
字段 = serializers.字段類型(write_only=True)

serializers.py

class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        list_serializer_class = BookListSerializer
        model = models.Book
        fields = ['name', 'price', 'image', 'publish', 'authors', 'publish_name', 'author_list']
        extra_kwargs = {
            'publish': {
                'write_only': True
            }

序列化覆蓋字段

覆蓋字段:Serializer類中,規則在()中規定,可以是只讀、只寫、可讀可寫任何形式
系統同名字段 = serializers.字段類型()

# 如何覆蓋外鍵字段
    publish = serializers.PrimaryKeyRelatedField(read_only=True)  # 只讀
    # publish = serializers.PrimaryKeyRelatedField(queryset=models.Publish.objects.all(), write_only=True)  # 只寫
    # publish = serializers.PrimaryKeyRelatedField(queryset=models.Publish.objects.all())  # 可讀可寫

    # publish = serializers.PrimaryKeyRelatedField(queryset=models.Publish.objects.all())
    # authors = serializers.PrimaryKeyRelatedField(queryset=models.Author.objects.all(), many=True)
    
class Meta:
        model = models.Book
        fields = ('name',  'publish', )
        extra_kwargs = {
            'publish': {  # 系統原有的外鍵字段,要留給反序列化過程使用,序列化外鍵內容,用@property自定義
                'write_only': True,
            },

二次封裝Response

from rest_framework.response import Response


class APIResponse(Response):
    def __init__(self,  status=0, msg='ok', http_status=None, headers=None, exception=False, **kwargs):
        data = {
            'status': status,
            'msg': msg
        }
        # 在外界可以存值
        if kwargs:
            data.update(kwargs)
        super().__init__(data=data, status=http_status, headers=headers, exception=exception)

數據庫關系分析

"""
1)之間有關系的兩個表,增刪改操作會相互影響(效率低),查詢操作就是正常的連表操作
2)之間有關系的兩個表,斷開關聯,但所有數據保持與原來一致
	每個表都可以單獨操作,增刪改操作效率極高,但是容易出現臟數據(開發中完全可以避免)
	由於數據沒有任何變化,所以查詢的連表操作不會受到任何影響
	
3)Django的ORM支持斷關聯操作關系表,且所有的操作方式和沒有斷關聯操作一致
"""

斷外鍵關聯關系

有關聯的表,如果直接存在關聯關系(外鍵),增刪改操作效率低(但是安全);刪除外鍵(斷關聯),可以提高增刪改操作效率,但是要手動處理數據安全問題(避免臟數據的參數)
db_constraint=False

eg:
# 關系字段
    brand = models.ForeignKey(to='Brand', related_name='Cars', db_constraint=False)
    sponsors = models.ManyToManyField(to="Sponsor", related_name="Cars", db_constraint=False)

ORM操作外鍵關系

外鍵位置

"""
外鍵位置:
1)一對多:ForeignKey必須放在多的一方,書與出版社,外鍵應該放在書表
2)多對多:ManyToManyField放在任何一方都可以,因為會創建關系表,在關系表中用兩個外鍵分別關聯兩個表
3)一對一:OneToOneField放在依賴的表,作者與作者詳情,放在詳情表,OneToOneField會被轉換為 外鍵 + 唯一約束
"""

外鍵操作

ForeignKey可以設置:related_name, db_constraint, on_delete
OneToOneField可以設置:related_name, db_constraint, on_delete
ManyToManyField只能設置:related_name, db_constraint
		不能設置on_delete的原因:不管是關聯的A表,還是B表,數據修改,都會影響到關系表(默認級聯),
    如果想控制,只能自定義關系表,在關系表的兩個外鍵分別設置on_delete

參數含義:

related_name:表之間反向訪問的名字,默認是 表名小寫|表名小寫_set
db_constraint:表之間的關聯關系,默認為True,代表關聯,設置False,可以提高增刪改的效率,且不影響查等
on_delete:表之間級聯更新,在django 1.x下默認是CASCADE(級聯更新),在django 2.x下必須手動明確
										DO_NOTHING(級聯不更新)

案例:

"""
表關系:
作者沒,作者詳情一定沒:CASCADE  *****
作者沒,書還是該作者出的:DO_NOTHING
部門們,部門內的員工全部進入未分組部門:SET_DEFAULT (需要配合default屬性使用)  
部門們,部門內的員工部門外鍵字段設置為空:SET_NULL (需要配合null=True屬性使用)  *****
"""

class Author(models.Model):
    name = models.CharField(max_length=64)


class AuthorDetail(models.Model):
    phone = models.CharField(max_length=11)
    author = models.OneToOneField(
        to=Author,
        related_name='detail',
        db_constraint=False,
        on_delete=models.SET_NULL,
        null=True
    )

ORM四種關聯關系

級聯、不處理、置為默認值(配合默認值)、置空(配合可以為空)

基表

# 基類:是抽象的(不會完成數據庫遷移),目的是提供共有字段的
# 抽出來的基類
class BaseModel(models.Model):
    name = models.CharField(max_length=64)
    is_delete = models.BooleanField(default=0)
    updated_time = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True  # 必須完成該配置
        
class Car(BaseModel):
    pass

# 其他的表繼承了基類就有基類的屬性了

序列化類其他配置(了解)

class AuthorModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Author
        # 不常用,將全部字段提供給外界
        fields = '__all__' 
        
# ------------------------------------------------------------------

class AuthorModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Author
        # 不常用,非,排除指定字段的其他所有字段,不能自動包含 外鍵反向 字段
        exclude = ['is_delete', 'updated_time']  
        
# ------------------------------------------------------------------

class AuthorModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Author
        # 'detail', 'books' 是 外鍵(正向|反向) 字段
        fields = ['name', 'detail', 'books']
        # 不常用,自動深度,自動深度會顯示外鍵關聯表的所有字段
        depth = 2  
# 正向外鍵字段:就是外鍵的屬性名
# 反向外鍵字段:就是外鍵屬性設置的related_name

十大接口

BaseSerializer初始化方法

from rest_framework.serializers import BaseSerializer
    def __init__(self, instance=None, data=empty, **kwargs):
        self.instance = instance
        if data is not empty:
            self.initial_data = data
        self.partial = kwargs.pop('partial', False)
        self._context = kwargs.pop('context', {})
        kwargs.pop('many', None)
        super().__init__(**kwargs)

'''
instance:賦值對象的
data:賦值數據的
many:數據是單個還是多個
partial:在修改需求時使用,可以將所有校驗字段required校驗規則設置為False
context:用於視圖類和序列化類直接傳參使用
'''

十大接口序列化總結

六個必備:單查、群查、單增、單刪、單整體改(了解),單局部該
四個額外接口:群增、群刪、群整體改、群局部改

# 常見使用
# 單查接口  get請求
UserModelSerializer(instance=user_obj)

# 群查接口  get請求
UserModelSerializer(instance=user_query, many=True)

# 單增接口,request.data是字典   post請求
UserModelSerializer(data=request.data) 

# 群增接口,request.data是列表   post請求
UserModelSerializer(data=request.data, many=True)

# 單整體改接口,request.data是字典	put請求
UserModelSerializer(instance=user_obj, data=request.data)

# 群整體改接口,request.data是列表,且可以分離出pks,轉換成user_queryset	put請求
UserModelSerializer(instance=user_queryset, data=request.data, many=True)

# 單局部改接口,request.data是字典	patch請求
UserModelSerializer(instance=user_obj, data=request.data, partial=True)

# 群局部改接口,request.data是列表,且可以分離出pks,轉換成user_queryset	patch請求
UserModelSerializer(instance=user_queryset, data=request.data, partial=True, many=True)

# 刪接口,用不到序列化類  delete請求
models.Car.objects.filter(is_delete=False, pk__in=pks).update(is_delete=True)

單查群查

判斷有無pk

class CarAPIView(APIView):
    def get(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        if pk:  # 單查
            try:
                obj = models.Car.objects.get(pk=pk)
                serializer = serializers.CarModelSerializer(obj)
                return APIResponse(result=serializer.data)
            except:
                return APIResponse(status=1, msg='pk error', http_status=status.HTTP_400_BAD_REQUEST, exception=True)

        else:  # 群查
            queryset = models.Car.objects.all()
            serializer = serializers.CarModelSerializer(queryset, many=True)
            return APIResponse(results=serializer.data)

單增群增

判斷數據類型是否為列表

    def post(self, request, *args, **kwargs):
        # 單增
        if not isinstance(request.data, list):
            # 單增
            serializer = serializers.CarModelSerializer(data=request.data)
            serializer.is_valid(raise_exception=True)  # 如果校驗失敗,直接拋異常,返回給前台
            car_obj = serializer.save()
            return APIResponse(
                result=serializers.CarModelSerializer(car_obj).data,
                status=status.HTTP_201_CREATED
            )
        else:
            # 群增
            serializer = serializers.CarModelSerializer(data=request.data,many=True)
            serializer.is_valid(raise_exception=True)  # 如果校驗失敗,直接拋異常,返回給前台
            objs = serializer.save()

            return APIResponse(result=serializers.CarModelSerializer(objs, many=True).data, http_status=201)

單刪群刪

判斷前端傳過來的數據:是url參數就是單刪,是data數據就是群刪

只是更改了表示是否刪除的字段

# 單刪群刪
"""
單刪:接口:/books/(pk)/
群刪:接口:/books/   數據:[pk1, ..., pkn]
"""
    def delete(self,request, *args,**kwargs):
        pk = kwargs.get('pk')
        if pk:
            pks = [pk]  # [1]  單刪
        else:
            pks = request.data  # [2,3,4] 群刪
        try:
            rows = models.Car.objects.filter(is_delete=False, pk__in=pks).update(is_delete=True)
        except:
            return APIResponse(1, '數據有誤')
        if rows:
            return APIResponse(0, '刪除成功')
        return APIResponse(2, '刪除失敗')

單整體改/群整體改

# 單整體改群整體改
"""
單整體改:接口:/books/(pk)/ 數據:dict
群整體改:接口:/books/   數據:[{pk1, ...}, ..., {pkn, ...}] | {pks: [pk1, ..., pkn], data: [{}, ..., {}]}
"""
    def put(self,request, *args, **kwargs):
        pk = kwargs.get('pk')
        if pk:  # 單
            try:
                instance = models.Car.objects.get(is_delete=False, pk=pk)
            except:
                return APIResponse(1,'pk error', http_status=400)
            serializer = serializers.CarModelSerializer(instance=instance, data=request.data)
            serializer.is_valid(raise_exception=True)
            obj = serializer.save()  # 返回的是受影響的對象
            return APIResponse(result=serializers.CarModelSerializer(obj).data)
        else:  # 群
            pks = []
            try:
                for dic in request.data:
                    pks.append(dic.pop('pk'))
                objs = models.Car.objects.filter(is_delete=False, pk__in=pks) # 篩選  __in(或),雙下線方法
                assert len(objs) == len(request.data)  # 兩個列表長度必須一致  斷言方法
            except:
                return APIResponse(1, '數據有誤', http_status=400)

            serializer = serializers.CarModelSerializer(instance=objs, data=request.data, many=True)
            serializer.is_valid(raise_exception=True)
            objs = serializer.save()
            return APIResponse(result=serializers.CarModelSerializer(objs, many=True).data)

單局部改/群局部改

# 單局部改群局部改
"""
單局部改:接口:/books/(pk)/ 數據:dict
群局部改:接口:/books/   數據:[{pk1, ...}, ..., {pkn, ...}] | {pks: [pk1, ..., pkn], data: [{}, ..., {}]}
"""
    def patch(self,request, *args, **kwargs):
        pk = kwargs.get('pk')
        if pk:  # 單
            try:
                instance = models.Car.objects.get(is_delete=False, pk=pk)
            except:
                return APIResponse(1, 'pk error', http_status=400)
            # partial=True就是將所有反序列化字段的 required 設置為 False(提供就校驗,不提供不校驗)
            serializer = serializers.CarModelSerializer(
                instance=instance,
                data=request.data,
                partial=True
            )
            serializer.is_valid(raise_exception=True)
            obj = serializer.save()
            return APIResponse(result=serializers.CarModelSerializer(obj).data)
        else:
            pks = []
            try:
                for dic in request.data:
                    pks.append(dic.get('pk'))
                objs = models.Car.objects.filter(is_delete=False, pk__in=pks)
                assert len(objs) == len(request.data)
            except:
                return APIResponse(1, '數據有誤', http_status=400)
            serializer = serializers.CarModelSerializer(
                instance=objs,
                data=request.data,
                many=True,
                partial=True,
            )
            serializer.is_valid(raise_exception=True)
            objs = serializer.save()
            return APIResponse(result=serializers.CarModelSerializer(objs, many=True).data)

群增群改配置

serializers.py:

# 群增群改輔助類(了解)
class BookListSerializer(serializers.ListSerializer):
    """
    1)create群增方法不需要重新
    2)update群改方法需要重寫,且需要和views中處理request.data的邏輯配套使用
    3)self.child就代表該ListSerializer類綁定的ModelSerializer類
        BookListSerializer的self.child就是BookModelSerializer
    """
    # 重寫update方法
    def update(self, queryset, validated_data_list):
        return [
            self.child.update(queryset[index], validated_data) for index, validated_data in enumerate(validated_data_list)
        ]
    
class CarModelSerializer(serializers.ModelSerializer):
        class Meta:
        # 配置自定義群增群改序列化類
        list_serializer_class = BookListSerializer

十大接口小結

"""
1)初始化序列化類,設置partial=True可以將所有反序列化字段的 required 設置為 False(提供就校驗,不提供不校驗),可以運用在局部修改接口

2)初始化序列化類,設置context={...},在序列化類操作self.context,實現視圖類和序列化類數據互通

3)只有要完成資源的群改這種特殊需求時,才需要自定義ListSerializer綁定給自定義的ModelSerializer,重寫update方法,來完成需求
"""

字段提供就校驗,不提供拉到

實現單局部該/群局部改

'required':False
# partial=True就是將所有反序列化字段的 required 設置為 False(提供就校驗,不提供不校驗)

DjangoORM內置優化機制:一次最多查21條數據

queryset = models.Book.objects.filter(is_delete=False).all()

models.py

from django.db import models


# 抽出來的基類
class BaseModel(models.Model):
    name = models.CharField(max_length=64)
    is_delete = models.BooleanField(default=0)
    updated_time = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True


class Car(BaseModel):
    price = models.DecimalField(max_digits=9, decimal_places=2)

    # 關系字段
    brand = models.ForeignKey(to='Brand', related_name='Cars', db_constraint=False)
    sponsors = models.ManyToManyField(to="Sponsor", related_name="Cars", db_constraint=False)
    class Meta:
        verbose_name_plural = '汽車表'

    def __str__(self):
        return self.name

    def brand_(self):
        return self.brand.name

    # 多對多跨表查詢
    def sponsors_(self):
        temp_sponsor_list = []
        for sponsor in self.sponsors.all():
            sponsor_dic = {"name": sponsor.name}
            try:
                sponsor_dic['電話'] = sponsor.detail.phone
            except:
                sponsor_dic['電話'] = ''
            temp_sponsor_list.append(sponsor_dic)
        return temp_sponsor_list



class Brand(BaseModel):

    class Meta:
        verbose_name_plural = '品牌表'
    def __str__(self):
        return self.name

    # 一對多跨表查詢
    def car_list(self):
        temp_car_list = []
        for car_obj in self.Cars.all():
            car_dic = {
                "name": car_obj.name
            }
            try:
                car_dic['price'] = car_obj.price
            except:
                car_dic['price'] = ''
            temp_car_list.append(car_dic)
        return temp_car_list


class Sponsor(BaseModel):

    class Meta:
        verbose_name_plural = '贊助商表'
    def __str__(self):
        return self.name



class SponsorDetail(models.Model):

    class Meta:
        verbose_name_plural = '贊助商詳情表'

    phone = models.CharField(max_length=11)
    is_delete = models.BooleanField(default=1)
    updated_time = models.DateTimeField(auto_now=True)
    sponsor = models.OneToOneField(to="Sponsor", related_name="detail", db_constraint=False)

response.py

from rest_framework.response import Response


class APIResponse(Response):
    def __init__(self,  status=0, msg='ok', http_status=None, headers=None, exception=False, **kwargs):
        data = {
            'status': status,
            'msg': msg
        }
        # 在外界可以存值
        if kwargs:
            data.update(kwargs)
        super().__init__(data=data, status=http_status, headers=headers, exception=exception)


serializers.py

from rest_framework import serializers
from rest_framework import exceptions


from . import models

# 群增群改輔助類(了解)
class BookListSerializer(serializers.ListSerializer):
    """
    1)create群增方法不需要重新
    2)update群改方法需要重寫,且需要和views中處理request.data的邏輯配套使用
    3)self.child就代表該ListSerializer類綁定的ModelSerializer類
        BookListSerializer的self.child就是BookModelSerializer
    """
    # 重寫update方法
    def update(self, queryset, validated_data_list):
        return [
            self.child.update(queryset[index], validated_data) for index, validated_data in enumerate(validated_data_list)
        ]

class CarModelSerializer(serializers.ModelSerializer):

    def validate(self, attrs):
        print(self.context)  # 可以獲得視圖類在初始化序列化對象時傳入的context
        # self.context.update({'a': 10})  # 序列化類內部更新context,傳遞給視圖類
        return attrs

    class Meta:
        # 配置自定義群增群改序列化類
        list_serializer_class = BookListSerializer

        model = models.Car
        fields = ['name', 'is_delete', 'price', 'brand', 'brand_', 'updated_time', 'sponsors_']

        extra_kwargs = {
            'brand': {
                'write_only': True,
            }
            # 'name':{
            #     'required':False
            # },
            # 'price': {
            #     'required': False
            # },
        }


class BrandModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Brand
        fields = ['name', 'is_delete', 'updated_time', 'car_list']


urls.py

from django.conf.urls import url

from . import views
from django.views.static import serve
from d_car import settings
urlpatterns = [
    url(r'v1/car/$', views.CarAPIView.as_view()),
    url(r'v1/car/(?P<pk>\d+)/$', views.CarAPIView.as_view()),

    url(r'brand/$', views.BrandAPIView.as_view()),
    url(r'brand/(?P<pk>\d+)/$', views.BrandAPIView.as_view()),

    url(r'v2/car/$', views.CarV2APIView.as_view()),
    url(r'v2/car/(?P<pk>\d+)/$', views.CarV2APIView.as_view()),

    # url(r'v3/car/$', views.CarV3APIView.as_view()),
    # url(r'v3/car/(?P<pk>\d+)/$', views.CarV3APIView.as_view()),

    # 暴露文件夾
    url(r'^media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}),

]

視圖家族

"""
視圖基類:APIView、GenericAPIView
視圖工具類:mixins包下的五個類(六個方法)
工具視圖類:generics包下的所有GenericAPIView的子類
視圖集:viewsets包下的類
"""
""" GenericAPIView基類(基本不會單獨使用,了解即可,但是是高級視圖類的依賴基礎)
1)GenericAPIView繼承APIView,所有APIView子類寫法在繼承GenericAPIView時可以保持一致
2)GenericAPIView給我們提供了三個屬性 queryset、serializer_class、lookup_field
3)GenericAPIView給我們提供了三個方法 get_queryset、get_serializer、get_obj
"""
""" mixins包存放了視圖工具類(不能單獨使用,必須配合GenericAPIView使用)
CreateModelMixin:單增工具類
	create方法
	
ListModelMixin:群查工具類
	list方法

RetrieveModelMixin:單查工具類
	retrieve方法

UpdateModelMixin:單整體局部改工具類
	update方法

DestroyModelMixin:單刪工具類
	destory方法
"""
""" generics包下的所有GenericAPIView的子類(就是繼承GenericAPIView和不同mixins下的工具類的組合)
1)定義的視圖類,繼承generics包下已有的特點的GenericAPIView子類,可以在只初始化queryset和serializer_class兩個類屬性后,就獲得特定的功能

2)定義的視圖類,自己手動繼承GenericAPIView基類,再任意組合mixins包下的一個或多個工具類,可以實現自定義的工具視圖類,獲得特定的功能或功能們

注:
i)在這些模式下,不能實現單查群查共存(可以加邏輯區分,也可以用視圖集知識)
ii)DestroyModelMixin工具類提供的destory方法默認是從數據庫中刪除數據,所以一般刪除數據的需求需要自定義邏輯
"""

視圖基類 APIView、GenericAPIView

視圖基類:APIView、GenericAPIView

i)繼承APIView的,所以擁有APIView的全部
ii)三個類屬性:queryset、serializer_class、lookup_field
iii)三個方法:get_queryset、get_serializer、get_object
過渡:單獨繼承GenericAPIView類的視圖類,需要自己定義請求方法如get,還需要屬性方法體完成請求

定義的視圖類,自己手動繼承GenericAPIView基類,再任意組合mixins包下的一個或多個工具類,可以實現自定義的工具視圖類,獲得特定的功能或功能們

# ----------------------------- 過渡寫法:了解 -----------------------------

from rest_framework.generics import GenericAPIView
class BookV1APIView(GenericAPIView):
    # 將數據和序列化提示為類屬性,所有的請求方法都可以復用
    queryset = models.Book.objects.filter(is_delete=False).all()
    serializer_class = serializers.BookModelSerializer
    lookup_field = 'pk'  # 可以省略,默認是pk,與url有名分組對應的

    # 群查
    def get(self, request, *args, **kwargs):
        # queryset = models.Book.objects.filter(is_delete=False).all()  # => 方法+屬性兩行代碼
        queryset = self.get_queryset()
        # serializer = serializers.BookModelSerializer(instance=queryset, many=True)  # => 方法+屬性兩行代碼
        serializer = self.get_serializer(instance=queryset, many=True)
        return APIResponse(results=serializer.data)

    # 單查
    # def get(self, request, *args, **kwargs):
    #     obj = self.get_object()
    #     serializer = self.get_serializer(obj)
    #     return APIResponse(results=serializer.data)

    # 單增
    def post(self, request, *args, **kwargs):
        # serializer = serializers.BookModelSerializer(data=request.data)
        serializer = self.get_serializer(data=request.data)  # 同樣的步驟多了,好處就來了
        serializer.is_valid(raise_exception=True)
        obj = serializer.save()
        return APIResponse(result=self.get_serializer(obj).data, http_status=201)

基於GenericAPIView的十大接口

views.py:

# 十大接口:
# 1)單查、群查、單增、單整體改、單局部改都可以直接使用
# 2)單刪不能直接使用,因為默認提供的功能是刪除數據庫數據,不是我們自定義is_delete字段值修改
# 3)除了群查以為的接口,都要自己來實現

# 注:給序列化類context賦值{'request': request},序列化類就可以自動補全后台圖片鏈接

from rest_framework.generics import GenericAPIView
from rest_framework import mixins
from . import models, serializers
from rest_framework.response import Response

class BookV1APIView(GenericAPIView,
                    mixins.RetrieveModelMixin,
                    mixins.ListModelMixin,
                    mixins.CreateModelMixin,
                    mixins.UpdateModelMixin):

    queryset = models.Book.objects.filter(is_delete=False).all()
    serializer_class = serializers.BookModelSerializer

    def get(self, request, *args, **kwargs):
        if 'pk' in kwargs:
            return self.retrieve(request, *args, **kwargs)  # 單查
		
        # queryset = models.Book.objects.filter(is_delete=False).all()
        # 注:給序列化類context賦值{'request': request},序列化類就可以自動補全后台圖片鏈接
        # serializer = serializers.BookModelSerializer(queryset, many=True, context={'request': request})
        # return Response(serializer.data)
        return self.list(request, *args, **kwargs)  # 群查

    def post(self, request, *args, **kwargs):
        if not isinstance(request.data, list):
            return self.create(request, *args, **kwargs)

        serializer = self.get_serializer(data=request.data, many=True)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=201, headers=headers)

    def delete(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        if pk:
            pks = [pk]
        else:
            pks = request.data
        try:
            rows = models.Book.objects.filter(is_delete=False, pk__in=pks).update(is_delete=True)
        except:
            return Response(status=400)
        if rows:
            return Response(status=204)
        return Response(status=400)

    def put(self, request, *args, **kwargs):
        if 'pk' in kwargs:
            return self.update(request, *args, **kwargs)

        pks = []
        try:
            for dic in request.data:
                pks.append(dic.pop('pk'))
            objs = models.Book.objects.filter(is_delete=False, pk__in=pks)
            assert len(objs) == len(request.data)
        except:
            return Response(status=400)
        serializer = serializers.BookModelSerializer(instance=objs, data=request.data, many=True)
        serializer.is_valid(raise_exception=True)
        objs = serializer.save()
        return Response(serializers.BookModelSerializer(objs, many=True).data)

    def patch(self, request, *args, **kwargs):
        if 'pk' in kwargs:
            return self.partial_update(request, *args, **kwargs)

        pks = []
        try:
            for dic in request.data:
                pks.append(dic.pop('pk'))
            objs = models.Book.objects.filter(is_delete=False, pk__in=pks)
            assert len(objs) == len(request.data)
        except:
            return Response(status=400)
        serializer = serializers.BookModelSerializer(instance=objs, data=request.data, many=True, partial=True)
        serializer.is_valid(raise_exception=True)
        objs = serializer.save()
        return Response(serializers.BookModelSerializer(objs, many=True).data)

視圖工具類:mixins包

from rest_framework import mixins

""" mixins包存放了視圖工具類(不能單獨使用,必須配合GenericAPIView使用)
CreateModelMixin:單增工具類
	create方法
	
ListModelMixin:群查工具類
	list方法

RetrieveModelMixin:單查工具類
	retrieve方法

UpdateModelMixin:單整體局部改工具類
	update方法

DestroyModelMixin:單刪工具類
	destory方法
"""

# ----------------------------- 過渡寫法:了解 -----------------------------

from rest_framework.generics import GenericAPIView
from rest_framework import mixins
class BookV2APIView(GenericAPIView, mixins.RetrieveModelMixin, mixins.CreateModelMixin):
    queryset = models.Book.objects.filter(is_delete=False).all()
    serializer_class = serializers.BookModelSerializer

    # 單查
    def get(self, request, *args, **kwargs):
        # obj = self.get_object()
        # serializer = self.get_serializer(obj)
        # return APIResponse(results=serializer.data)

        # return self.retrieve(request, *args, **kwargs)

        response = self.retrieve(request, *args, **kwargs)
        return APIResponse(result=response.data)

    # 單增
    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

    
# ----------------------------- 開發寫法:常用 -----------------------------

from rest_framework.generics import RetrieveAPIView
class BookV3APIView(RetrieveAPIView):
    queryset = models.Book.objects.filter(is_delete=False).all()
    serializer_class = serializers.BookModelSerializer

    # 單查
    pass

工具視圖類 generics工具包:繼承指定類

generics包:工具視圖類
i)系統完成的GenericAPIView與mixins包下工具類的不同組合
只需要配置三個類屬性

ii)自己完成GenericAPIView與mixins包下工具類的不同組合
需要配置三個類屬性,需要書寫請求方法,如post,內部直接調用self.create方法

iii)自己繼承一堆generics包下的工具視圖,完成組合
只需要配置三個類屬性,但是單查、群查不能共存

工具

繼承指定的類,完成相應的功能

from rest_framework import generics  點進去看看又那些類

RetrieveAPIView	  : 單查  		   內部:get	方法寫好了
ListAPIView		  :群查		       內部:get  方法寫好了
CreateAPIView     :單增			   內部:post	方法
DestroyAPIView    : 單刪			   內部:delete	方法寫好了
UpdateAPIView	 : 單整體改,單局部改   內部:put、patch	方法寫好了

ListCreateAPIView   : 群查,和單增  內部: get、post 方法寫好了
ListCreateAPIView   : 群增			內部:post方法

。。。

單查:

from rest_framework.generics import RetrieveAPIView
class BookV3APIView(RetrieveAPIView):
    queryset = models.Book.objects.filter(is_delete=False).all()
    serializer_class = serializers.BookModelSerializer

    # 單查  RetrieveAPIView內部已經寫好了get方法
    pass

基於generics包下工具視圖類的六大基礎接口

六個必備:單查、群查、單增、單刪、單整體改(了解),單局部該
四個額外接口:群增、群刪、群整體改、群局部改

# 六大基礎接口  
# 1)直接繼承generics包下的工具視圖類,可以完成六大基礎接口
# 2)單查群查不能共存,因為都是get方法
# 3)單刪一般會重寫



views.py:

from . import models, serializers
from rest_framework.response import Response
from rest_framework import generics
class BookV2APIView(generics.ListAPIView,
                    generics.RetrieveAPIView,
                    generics.CreateAPIView,
                    generics.UpdateAPIView,
                    generics.DestroyAPIView):
    queryset = models.Book.objects.filter(is_delete=False).all()
    serializer_class = serializers.BookModelSerializer

    def get(self, request, *args, **kwargs):  # 重寫單查群查的get
        if 'pk' in kwargs:
            return self.retrieve(request, *args, **kwargs)
        return self.list(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):  # 重寫刪除(不是真的刪除數據,是修改字段)
        pk = kwargs.get('pk')
        models.Book.objects.filter(is_delete=False, pk=pk).update(is_delete=True)
        return Response(status=204)

視圖集(開發常用,最高級)

路由組件(繁瑣,可以自定義映射關系)

urls.py

url(r'^v3/books/$', views.BookV3APIView.as_view(
    {'get': 'list', 'post': 'create', 'delete': 'multiple_destroy'}
	)),

url(r'^v3/books/(?P<pk>\d+)/$', views.BookV3APIView.as_view(
        {'get': 'my_list'}
    )),

views.py

from rest_framework.viewsets import ModelViewSet
from rest_framework.response import Response
class BookV3APIView(ModelViewSet):
    queryset = models.Book.objects.filter(is_delete=False).all()
    serializer_class = serializers.BookModelSerializer

    # 可以在urls.py中as_view({'get': 'my_list'})自定義請求映射
    def my_list(self, request, *args, **kwargs):
        return Response('ok')

路由組件(簡化)

urls.py

from django.conf.urls import url, include
from . import views
# 路由組件,必須配合視圖集使用
from rest_framework.routers import SimpleRouter
router = SimpleRouter()

# 以后就寫視圖集的注冊即可:BookV3APIView和BookV4APIView都是視圖集
router.register('v3/books', views.BookV3APIView, 'book')
router.register('v4/books', views.BookV4APIView, 'book')

urlpatterns = [
    url('', include(router.urls))
]

視圖組件 views.py

from rest_framework.viewsets import ReadOnlyModelViewSet
class BookV4APIView(ReadOnlyModelViewSet):
    queryset = models.Book.objects.filter(is_delete=False).all()
    serializer_class = serializers.BookModelSerializer

重寫刪除方法:destroy

因為繼承的刪除方法:是真的刪除了數據

我們修改的:修改is_delete字段

# 需要完成字段刪除,不是重寫delete方法,而是重寫destroy方法
    def destroy(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        models.Book.objects.filter(is_delete=False, pk=pk).update(is_delete=True)
        return Response(status=204)

群刪接口

# 群刪接口
    def multiple_destroy(self, request, *args, **kwargs):
        try:
            models.Book.objects.filter(is_delete=False, pk__in=request.data).update(is_delete=True)
        except:
            return Response(status=400)
        return Response(status=204)

視圖級使用總結

六個必備:單查、群查、單增、單刪、單整體改(了解),單局部該
四個額外接口:群增、群刪、群整體改、群局部改
# 注:給序列化類context賦值{'request': request},序列化類就可以自動補全后台圖片鏈接
"""
視圖集的使用總結
1)可以直接繼承ModelViewSet,實現六大繼承接口(是否重寫destroy方法,或其他方法,根據需求決定)
2)可以直接繼承ReadOnlyModelViewSet,實現只讀需求(只有單查群查)
3)繼承ViewSet類,與Model類關系不是很密切的接口:登錄的post請求,是查詢操作;短信驗證碼發生接口,借助第三方平台
4)繼承GenericViewSet類,就代表要配合mixins包,自己完成任意組合
5)繼承以上4個視圖集任何一個,都可以與路由as_view({映射})配合,完成自定義請求響應方法
"""

自定義路由組件(了解)

from rest_framework.routers import SimpleRouter as DrfSimpleRouter
from rest_framework.routers import Route, DynamicRoute

class SimpleRouter(DrfSimpleRouter):
    routes = [
        # List route.
        Route(
            url=r'^{prefix}{trailing_slash}$',
            mapping={
                'get': 'list',
                'post': 'create',  # 注:群增只能自己在視圖類中重寫create方法,完成區分
                'delete': 'multiple_destroy',  # 新增:群刪
                'put': 'multiple_update',  # 新增:群整體改
                'patch': 'multiple_partial_update'  # 新增:群局部改
            },
            name='{basename}-list',
            detail=False,
            initkwargs={'suffix': 'List'}
        ),
        # Dynamically generated list routes. Generated using
        # @action(detail=False) decorator on methods of the viewset.
        DynamicRoute(
            url=r'^{prefix}/{url_path}{trailing_slash}$',
            name='{basename}-{url_name}',
            detail=False,
            initkwargs={}
        ),
        # Detail route.
        Route(
            url=r'^{prefix}/{lookup}{trailing_slash}$',
            mapping={
                'get': 'retrieve',
                'put': 'update',
                'patch': 'partial_update',
                'delete': 'destroy'
            },
            name='{basename}-detail',
            detail=True,
            initkwargs={'suffix': 'Instance'}
        ),
        # Dynamically generated detail routes. Generated using
        # @action(detail=True) decorator on methods of the viewset.
        DynamicRoute(
            url=r'^{prefix}/{lookup}/{url_path}{trailing_slash}$',
            name='{basename}-{url_name}',
            detail=True,
            initkwargs={}
        ),
    ]

上傳文件接口

urls.py

from django.conf.urls import url, include
from . import views
# 路由組件,必須配合視圖集使用
from rest_framework.routers import SimpleRouter
router = SimpleRouter()

# /books/image/(pk) 提交 form-data:用image攜帶圖片
router.register('books/image', views.BookUpdateImageAPIView, 'book')

urlpatterns = [
    url('', include(router.urls))
]

serializers.py

class BookUpdateImageModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields = ['image']

views.py

# 上次文件 - 修改頭像 - 修改海報
from rest_framework.viewsets import GenericViewSet
from rest_framework import mixins
class BookUpdateImageAPIView(GenericViewSet, mixins.UpdateModelMixin):
    queryset = models.Book.objects.filter(is_delete=False).all()
    serializer_class = serializers.BookUpdateImageModelSerializer

圖片自動補全后台圖片鏈接

queryset = models.Book.objects.filter(is_delete=False).all()
        # 注:給序列化類context賦值{'request': request},序列化類就可以自動補全后台圖片鏈接
serializer = serializers.BookModelSerializer(queryset, many=True, context={'request': request})
return Response(serializer.data)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM