如下是ApiView所有的類屬性,我們抽取一部分進行介紹:
可見這些類屬性,都是使用drf組態檔的預設設定。
下列策略可以在全域性設定 或者 在每一個檢視類中設定。
允許依賴注入其他的設定檔案, ApiView原始碼的settings
類屬性使測試更容易 (也就是不使用drf的api_settings)
在status內將所有狀態碼都寫了一遍:
from .models import Book
from .serializer import BookSerializer
class BookView(APIView):
def get(self, request):
books = Book.objects.all()
ser = BookSerializer(instance=books, many=True)
return Response(ser.data)
def post(self, request):
ser = BookSerializer(data=request.data)
if ser.is_valid():
ser.save()
# 咱們現在只有ser序列化類的物件,但是咱們想要,新增的物件---》序列化成字典---》大前提,序列化類中的create方法一定要返回新增的物件
return Response({'code': 100, 'msg': '新增成功', 'result': ser.data})
else:
return Response({'code': 101, 'msg': ser.errors})
class BookDetailView(APIView):
def get(self, request, pk):
books = Book.objects.filter(pk=pk).first()
ser = BookSerializer(instance=books)
return Response(ser.data)
def put(self, request, pk):
books = Book.objects.filter(pk=pk).first()
ser = BookSerializer(instance=books, data=request.data)
if ser.is_valid():
ser.save()
return Response({'code': 100, 'msg': '修改成功', 'result': ser.data})
else:
return Response({'code': 101, 'msg': ser.errors})
def delete(self, request, pk):
Book.objects.filter(pk=pk).delete()
return Response({'code': 100, 'msg': '刪除成功'})
### ModelSerializer的使用
class BookSerializer(serializers.ModelSerializer):
# 跟表有關聯
class Meta:
model = Book
fields = ['name', 'price', 'publish_detail', 'author_list', 'publish', 'authors']
extra_kwargs = {'name': {'max_length': 8},
'publish_detail': {'read_only': True},
'author_list': {'read_only': True},
'publish': {'write_only': True},
'authors': {'write_only': True},
}
urlpatterns = [
path('admin/', admin.site.urls),
path('books/', views.BookView.as_view()),
path('books/<int:pk>/', views.BookDetailView.as_view()),
]
如果需要再新寫關於作者的五個介面 ----> 又需要寫一個CBV
但是這兩個CBV的區別僅僅在於使用的 表模型 和 序列化類 不同,其他都是重複的程式碼。
這豈不是很麻煩?能不能通過繼承的方式,優化程式碼?
於是就寫了一個 GenericAPIview --繼承於--> APIView ,以後可以基於這個類來寫5個介面。
如果你想使用GenericAPIview,你需要從如下二種選擇其一:
在檢視類中設定如下屬性 (常用)
queryset
、serializer_class
重寫GenericAPIview類的get_queryset()
/get_serializer_class()
方法
如果你重寫了一個檢視方法,那麼重要的是 你應該呼叫get_queryset() 而不是直接的存取queryset
屬性。
因為queryset
將只被設定一次,並且為了後續到來的所有請求,這個結果會被快取。
總而言之,不要直接存取queryset
、serializer_class
屬性,而是使用GenericAPIview提供的各種方法獲取。
# 首先指定模型物件 和 序列化類
class BookView(GenericAPIView):
queryset = Book.objects.all()
# queryset = Book.objects 這樣也是可行的
serializer_class = BookSerializer
# 以下程式碼都是等效的
objs = Book.objects.all()
objs = self.get_queryset()
# 以下程式碼都是等效的
ser = self.get_serializer(instance=objs, many=True)
BookSerializer(instance=objs, many=True)
get_queryset方法得到檢視中的列表物件!
這個列表物件必須是一個可迭代的,也可以是一個queryset物件。
預設使用self.queryset
來獲取(檢視中的列表物件):
應該總是使用這個方法來獲取,而不是直接呼叫self.queryset
。
get_queryset原始碼做了些什麼事?
使用get_serializer()
方法可以返回序列化器的範例,此序列化器,被應用於校驗、反序列化前端輸入和序列化後端輸出。
get_serializer原始碼做了些什麼事?
通過get_serializer_class方法獲取了我們在檢視類中指定的序列化類
新增了一個'context'引數傳入我們的序列化類。
相當於BookSerializer(instance=objs, many=True, context={一些資料...})
get_serializer_class方法基本上什麼事情都沒有做,直接將序列化器返回,有需求可以重寫get_serializer_class。
可以實現:不同的介面使用的序列化類不一樣。序列化使用某一個序列化類,反序列化用另一個序列化類。
重寫:
這裡會根據傳入的pk引數查詢出對應的模型物件,
正常情況下寫查詢一個的介面我們需要手動寫orm( 比如Book.objects.filter(pk=pk)
),根據主鍵將物件查出來。
這裡因為使用了GenericAPIview,他會自動幫我們查。
就是通過pk引數和get_object方法將模型物件查詢出來的。
返回應用於詳細檢視的物件範例。預設使用 lookup_field
引數過濾基本的查詢集。
該方法可以被重寫以提供更復雜的行為,例如基於多個 URL 引數的物件查詢。
如果你想使用pk之外的物件查詢方式,可以設定lookup_field。如果有更復雜的查詢需求,可以重寫get_object()
。
以下給出一個範例:
修改查詢條件為書籍的名字:
可見在get_object方法中,呼叫了get_queryset()
獲取了我們放在檢視中的queryset,然後使用了filter_queryset()
對我們從資料庫中獲取的queryset進行了過濾操作。
關於filter_queryset的解釋是:
給他一個queryset,他會使用任何一個你正在使用的後端過濾器,進行過濾。
self.filter_backends
:由於我們類中沒有設定,所以會指向GenericAPIview類中的filter_backends。
在GenericAPIview類中預設使用的是drf組態檔中指定的預設過濾器。
然後drf組態檔中,預設是不過濾:
所有總而言之,預設就是不過濾,但是我們可以通過在自己的檢視類中寫filter_backends
屬性,來指定過濾器。
def post(self, request):
ser = self.get_serializer(data=request.data)
if ser.is_valid():
ser.save()
return Response({'code': 100, 'msg': '新增成功', 'result': ser.data})
else:
return Response({'code': 101, 'msg': ser.errors})
def put(self, request, pk):
obj = self.get_object()
ser = self.get_serializer(instance=obj, data=request.data)
if ser.is_valid():
ser.save()
return Response({'code': 100, 'msg': '修改成功', 'result': ser.data})
else:
return Response({'code': 101, 'msg': ser.errors})
def delete(self, request, pk):
obj = self.get_object()
obj.delete()
return Response({'code': 100, 'msg': '刪除成功'})
queryset
- 用於從檢視返回物件的查詢結果集。通常,你必須設定此屬性或者重寫 get_queryset()
方法。如果你重寫了一個檢視的方法,重要的是你應該呼叫 get_queryset()
方法而不是直接存取該屬性,因為 queryset
將被計算一次,這些結果將為後續請求快取起來。serializer_class
- 用於驗證和反序列化輸入以及用於序列化輸出的Serializer類。 通常,你必須設定此屬性或者重寫get_serializer_class()
方法。lookup_field
- 用於執行各個model範例的物件查詢的model欄位。預設為 'pk'
。 請注意,在使用超連結API時,如果需要使用自定義的值,你需要確保在API檢視和序列化類都設定查詢欄位。lookup_url_kwarg
- 應用於物件查詢的URL關鍵字引數。它的 URL conf 應該包括一個與這個值相對應的關鍵字引數。如果取消設定,預設情況下使用與 lookup_field
相同的值。以下屬性用於在與列表檢視一起使用時控制分頁。
pagination_class
- 當分頁列出結果時應使用的分頁類。預設值與 DEFAULT_PAGINATION_CLASS
設定的值相同,即 'rest_framework.pagination.PageNumberPagination'
。
filter_backends
- 用於過濾查詢集的過濾器後端類的列表。預設值與DEFAULT_FILTER_BACKENDS
設定的值相同。
雖然使用了GenericAPIview類寫五個介面,但是寫的程式碼還是太多了,並沒有減少程式碼呀!
CBV類中的方法 get
、post
、put
、delete
程式碼都是重複的,是不是可以再進行優化?
drf的作者自然想到了這一點,他提供了5個檢視擴充套件類,幫我們寫了這一部分程式碼!
先匯入五個檢視擴充套件類:
from rest_framework.mixins import ListModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin, RetrieveModelMixin
class BookView(GenericAPIView, ListModelMixin):
queryset = Book.objects
serializer_class = BookSerializer
def get(self, request):
return self.list(request)
現在就只需要使用self.list
呼叫ListModeMixin類中寫的程式碼:
注意:沒有DestroyUpdateAPIView
直接上程式碼:
from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView
class BookView(ListCreateAPIView): # 查詢所有 新增一個
queryset = Book.objects
serializer_class = BookSerializer
class BookDetailView(RetrieveUpdateDestroyAPIView): # 查詢一個 修改一個 刪除一個
queryset = Book.objects
serializer_class = BookSerializer
之前我們使用兩個CBV寫五個介面的原因是:
查詢一個和查詢所有都是使用get請求,為了解耦合,避免在類中的get方法中寫太多程式碼,所以將其拆成兩個CBV。
而使用ModelViewSet可以實現,一個檢視類寫5個介面。
from rest_framework.viewsets import ModelViewSet
class BookView(ModelViewSet):
queryset = Book.objects
serializer_class = BookSerializer