drf框架 5 十大接口:序列化(partial, context, ListSerializer), 視圖家族(view,mixins,generics,viewsets), 序列化類外鍵字段的覆蓋(PrimaryKeyRelatedField)


序列化類外鍵字段的覆蓋

"""
1)在序列化類中自定義字段,名字與model類中屬性名一致,就稱之為覆蓋操作
    (覆蓋的是屬性的所有規則:extra_kwargs中指定的簡易規則、model字段提供的默認規則、數據庫唯一約束等哪些規則)

2)外鍵覆蓋字段用PrimaryKeyRelatedField來實現,可以做到只讀、只寫、可讀可寫三種形式
    只讀:read_only=True
    只寫:queryset=關聯表的queryset, write_only=True
    可讀可寫:queryset=關聯表的queryset
    
3)當外界關聯的數據是多個時,需標識many=True條件
"""
class BookModelSerializer(serializers.ModelSerializer):
    # 如何覆蓋外鍵字段  publish是別的表的主鍵
    # 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())  # queryset后面的就是綁定的外鍵對象
    authors = serializers.PrimaryKeyRelatedField(queryset=models.Author.objects.all(), many=True)

    class Meta:
        model = models.Book
        fields = ('name', 'price', 'image', 'publish', 'authors')

 

十大接口序列化總結

"""
def __init__(self, instance=None, data=empty, **kwargs):
    pass

instance:是要被賦值對象的 - 對象類型數據賦值給instance
data:是要被賦值數據的 - 請求來的數據賦值給data
kwargs:內部有三個屬性:many、partial、context
    many:操作的對象或數據,是單個的還是多個的
    partial:在修改需求時使用,可以將所有校驗字段required校驗規則設置為False
    context:用於視圖類和序列化類直接傳參使用
"""

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

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

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

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

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

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

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

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

# 刪接口,用不到序列化類

 

十大接口核心知識小結

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

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

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

案例

models.py
# 基類:是抽象的(不會完成數據庫遷移),目的是提供共有字段的
class BaseModel(models.Model):
    is_delete = models.BooleanField(default=False)
    updated_time = models.DateTimeField(auto_now_add=True)

    class Meta:
        abstract = True  # 必須完成該配置

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

    @property  # 自定義序列化過程
    def author_list(self):
        temp_author_list = []
        for author in self.authors.all():
            author_dic = {
                "name": author.name
            }
            try:
                author_dic['phone'] = author.detail.phone
            except:
                author_dic['phone'] = ''
            temp_author_list.append(author_dic)
        return temp_author_list
    

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


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


class AuthorDetail(BaseModel):
    phone = models.CharField(max_length=11)
    author = models.OneToOneField(to=Author, related_name='detail', db_constraint=False, on_delete=models.CASCADE)
urls.py
url(r'^books/$', views.BookAPIView.as_view()),
url(r'^books/(?P<pk>\d+)/$', views.BookAPIView.as_view()),
serializers.py
from rest_framework import serializers
from . import models

# 群增群改輔助類(了解)
class BookListSerializer(serializers.ListSerializer):  # 群增是通過ListSserializer完成的,但是源碼只寫了create方法,update群改方法需要自己寫 """
    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 自定義的屬性(源碼內部定義ModelSerializer中的ListSerializer類有child屬性,對應外部的ModelSerializer,此處也就是BookModelSerilizer對象,調用其內部的update方法)
            self.child.update(queryset[index], validated_data) for index, validated_data in enumerate(validated_data_list)
        ]

# 主序列化類
class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        # 配置自定義群增群改序列化類
        list_serializer_class = BookListSerializer  # 改完繼承ListSerializer的類后,配置。源碼就是先查詢list_serializer_class,沒有再用自己的。就能用自己寫的群改方法

        model = models.Book
        fields = ('name', 'price', 'image', 'publish', 'authors', 'publish_name', 'author_list')
        # fields = ('name', 'price', 'image', 'publish', 'authors', 'abc')
        extra_kwargs = {
            'image': {
                'read_only': True,
            },
            'publish': {  # 系統原有的外鍵字段,要留給反序列化過程使用,序列化外鍵內容,用@property自定義
                'write_only': True,
            },
            'authors': {
                'write_only': True,
            },
        }

    # 需求:內外傳參
    # 1)在鈎子函數中,獲得請求請求對象request 視圖類 => 序列化類
    # 2)序列化鈎子校驗過程中,也會產生一些數據,這些數據能不能傳遞給外界使用:序列化類 => 視圖類
    # 序列化類的context屬性,被視圖類與序列化類共享
    def validate(self, attrs):
        print(self.context)  # 可以獲得視圖類在初始化序列化對象時傳入的context
        # self.context.update({'a': 10})  # 序列化類內部更新context,傳遞給視圖類
        return attrs
views.py
from rest_framework.views import APIView
from . import models, serializers
from .response import APIResponse

# 六個必備接口:單查、群查、單增、單刪、單整體改(了解)、單局部改
# 四個額外接口:群增、群刪、群整體改、群局部改
class BookAPIView(APIView):
    # 單查群查
    """
    單查:接口:/books/(pk)/
    群查:接口:/books/
    """
    def get(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        if pk:
            obj = models.Book.objects.filter(is_delete=False, pk=pk).first()
            serializer = serializers.BookModelSerializer(instance=obj)
            return APIResponse(result=serializer.data)
        else:
            queryset = models.Book.objects.filter(is_delete=False).all()
            serializer = serializers.BookModelSerializer(instance=queryset, many=True)
            return APIResponse(results=serializer.data)

    # 單增群增
    """
    單增:接口:/books/   數據:dict
    群增:接口:/books/   數據:list
    """
    def post(self, request, *args, **kwargs):
        # 如何區別單增群增:request.data是{}還是[]
        if not isinstance(request.data, list):
            # 單增
            serializer = serializers.BookModelSerializer(data=request.data)
            serializer.is_valid(raise_exception=True)  # 如果校驗失敗,會直接拋異常,返回給前台
            obj = serializer.save()
            # 為什么要將新增的對象重新序列化給前台:序列化與反序列化數據不對等
            return APIResponse(result=serializers.BookModelSerializer(obj).data, http_status=201)
        else:
            # 群增
            serializer = serializers.BookModelSerializer(data=request.data, many=True)
            serializer.is_valid(raise_exception=True)  # 如果校驗失敗,會直接拋異常,返回給前台
            objs = serializer.save()
            # 為什么要將新增的對象重新序列化給前台:序列化與反序列化數據不對等
            return APIResponse(result=serializers.BookModelSerializer(objs, many=True).data, http_status=201)

    # 單刪群刪
    """
    單刪:接口:/books/(pk)/
    群刪:接口:/books/   數據:[pk1, ..., pkn]
    """
    def delete(self, request, *args, **kwargs):
        pk = kwargs.get('pk')
        if pk:
            pks = [pk]  # 將單刪偽裝成群刪一條
        else:
            pks = request.data  # 群刪的數據就是群刪的主鍵們

        try:  # request.data可能提交的是亂七八糟的數據,所以orm操作可能會異常
            rows = models.Book.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.Book.objects.get(is_delete=False, pk=pk)
            except:
                return APIResponse(1, 'pk error', http_status=400)
            # 序列化類同時賦值instance和data,代表用data重新更新instance => 修改
            serializer = serializers.BookModelSerializer(instance=instance, data=request.data)
            serializer.is_valid(raise_exception=True)
            obj = serializer.save()
            return APIResponse(result=serializers.BookModelSerializer(obj).data)
        else:  #
            """ 分析request.data數據 [{'pk':1, 'name': '', 'publish': 1, 'authors': [1, 2]}, ...]
            1)從 request.data 中分離出 pks 列表
            2)pks中存放的pk在數據庫中沒有對應數據,或者對應的數據已經被刪除了,這些不合理的pk要被剔除
            3)pks最終轉換得到的 objs 列表長度與 request.data 列表長度不一致,就是數據有誤
            """
            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 APIResponse(1, '數據有誤', http_status=400)

            serializer = serializers.BookModelSerializer(instance=objs, data=request.data, many=True)
            serializer.is_valid(raise_exception=True)
            objs = serializer.save()
            return APIResponse(result=serializers.BookModelSerializer(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.Book.objects.get(is_delete=False, pk=pk)
            except:
                return APIResponse(1, 'pk error', http_status=400)
            # partial=True就是將所有反序列化字段的 required 設置為 False(提供就校驗,不提供不校驗)
            serializer = serializers.BookModelSerializer(instance=instance, data=request.data, partial=True)
            serializer.is_valid(raise_exception=True)
            obj = serializer.save()
            return APIResponse(result=serializers.BookModelSerializer(obj).data)
        else:  #
            pks = []
            try:  # 只要不是要求的標准數據,一定會在下方三行代碼某一行拋出異常
                for dic in request.data:
                    pks.append(dic.get('pk'))
                objs = models.Book.objects.filter(is_delete=False, pk__in=pks)
                assert len(objs) == len(request.data)  # 兩個列表長度必須一致
            except:
                return APIResponse(1, '數據有誤', http_status=400)

            serializer = serializers.BookModelSerializer(
                instance=objs,
                data=request.data,
                many=True,
                partial=True,
                context={'request': request}  # 初始化時,對context賦值,將視圖類中數據傳遞給序列化類
            )

            serializer.is_valid(raise_exception=True)
            objs = serializer.save()

            print(serializer.context)  # 在完成序列化類校驗后,可以重新拿到序列化類內部對context做的值更新
            return APIResponse(result=serializers.BookModelSerializer(objs, many=True).data)

 

視圖家族

"""
視圖基類: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方法默認是從數據庫中刪除數據,所以一般刪除數據的需求需要自定義邏輯
"""
urls.py
from django.conf.urls import url
from . import views

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

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

    url(r'^v3/books/$', views.BookV3APIView.as_view()),
    url(r'^v3/books/(?P<pk>\d+)/$', views.BookV3APIView.as_view()),
]
views.py
# ----------------------------- 過渡寫法:了解 -----------------------------

from rest_framework.generics import GenericAPIView
class BookV1APIView(GenericAPIView):
    # 將數據和序列化提示為類屬性,所有的請求方法都可以復用
    queryset = models.Book.objects.filter(is_delete=False).all()  # orm有優化機制(懶加載),一次只查21條數據,不夠了,再往后查21條
    serializer_class = serializers.BookModelSerializer
    lookup_field = 'pk'  # 可以省略,源碼默認是pk,與url有名分組對應的。如果url有名分組是name等,此處就要改成'name'

    # 群查
    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()  # 通過lookup_field設定的字段內容,和get_queryset,去庫中拿對應的queryset對象,源碼已經寫好了
    #     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)
    
# ----------------------------- 過渡寫法:了解 -----------------------------

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)  # 獲取返回的結果,通過獲得里面的data,通過二次封裝的response進行返回 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

 總結

"""
1)序列化類初始化各參數含義:
    instance:被賦值對象
    data:被賦值數據
    many:表示操作的對象或數據是否是多個
    partial:修改校驗字段均為選填
    context:視圖類與序列化類間傳參
    
    查:instance | instance, many
    增:data
    改:instance, data | instance, data, partial
    刪:orm操作
    
2)覆蓋外鍵字段(認證規則)
    字段 = serializers.PrimaryKeyRelatedField(queryset)
    字段 = serializers.PrimaryKeyRelatedField(read_only)
    字段 = serializers.PrimaryKeyRelatedField(queryset, write_only)
    
3)十大接口:
    群增群改需要ListSerializer類輔助,在ModelSerializer的Meta list_serializer_class配置中建立關聯,create方法可以繼承,update方法需要重寫
    
4)視圖家族:
    APIView:基類
    
    GenericAPIView:基類
        i)繼承APIView的,所以擁有APIView的全部
        ii)三個類屬性:queryset、serializer_class、lookup_field
        iii)三個方法:get_queryset、get_serializer、get_object
        過渡:單獨繼承GenericAPIView類的視圖類,需要自己定義請求方法如get,還需要屬性方法體完成請求
        
    mixins包:工具類 - eg:RetrieveModelMixin:retrieve
        retrieve、list、create、update、partial_update、destory
        過渡:因為上方六個方法體都是依賴與GenericAPIView類的,所以必須配合GenericAPIView類使用
        
    generics包:工具視圖類
        i)系統完成的GenericAPIView與mixins包下工具類的不同組合
            只需要配置三個類屬性
            
        ii)自己完成GenericAPIView與mixins包下工具類的不同組合
            需要配置三個類屬性,需要書寫請求方法,如post,內部直接調用self.create方法
            
        iii)自己繼承一堆generics包下的工具視圖,完成組合
            只需要配置三個類屬性,但是單查、群查不能共存
"""

 


免責聲明!

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



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