源碼分析
GenericAPIView
繼承自APIView
,也就是在APIView
基礎上再做了一層封裝,源碼如下:
class GenericAPIView(views.APIView):
queryset = None
serializer_class = None
lookup_field = 'pk'
lookup_url_kwarg = None
filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
def get_queryset(self):
assert self.queryset is not None, (
"'%s' should either include a `queryset` attribute, "
"or override the `get_queryset()` method."
% self.__class__.__name__
)
queryset = self.queryset
if isinstance(queryset, QuerySet):
queryset = queryset.all()
return queryset
def get_object(self):
queryset = self.filter_queryset(self.get_queryset())
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
assert lookup_url_kwarg in self.kwargs, (
'Expected view %s to be called with a URL keyword argument '
'named "%s". Fix your URL conf, or set the `.lookup_field` '
'attribute on the view correctly.' %
(self.__class__.__name__, lookup_url_kwarg)
)
filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
obj = get_object_or_404(queryset, **filter_kwargs)
self.check_object_permissions(self.request, obj)
return obj
def get_serializer(self, *args, **kwargs):
serializer_class = self.get_serializer_class()
kwargs.setdefault('context', self.get_serializer_context())
return serializer_class(*args, **kwargs)
def get_serializer_class(self):
assert self.serializer_class is not None, (
"'%s' should either include a `serializer_class` attribute, "
"or override the `get_serializer_class()` method."
% self.__class__.__name__
)
return self.serializer_class
def get_serializer_context(self):
return {
'request': self.request,
'format': self.format_kwarg,
'view': self
}
def filter_queryset(self, queryset):
for backend in list(self.filter_backends):
queryset = backend().filter_queryset(self.request, queryset, self)
return queryset
@property
def paginator(self):
if not hasattr(self, '_paginator'):
if self.pagination_class is None:
self._paginator = None
else:
self._paginator = self.pagination_class()
return self._paginator
def paginate_queryset(self, queryset):
if self.paginator is None:
return None
return self.paginator.paginate_queryset(queryset, self.request, view=self)
def get_paginated_response(self, data):
assert self.paginator is not None
return self.paginator.get_paginated_response(data)
我們可以看到GenericAPIView
中定義了6個類屬性和8個方法,接下來一個個分析
類屬性
- queryset = None
- serializer_class = None
- lookup_field = 'pk'
- lookup_url_kwarg = None
- filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
- pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
queryset
queryset
是用來控制視圖返回給前端的數據。如果沒什么邏輯,可以直接寫在視圖的類屬性中,如果邏輯比較復雜,也可以重寫get_queryset
方法用來返回一個queryset
對象。如果重寫了get_queryset
,那么以后獲取queryset
的時候就需要通過調用get_queryset
方法。因為queryset
這個屬性只會調用一次,以后所有的請求都是使用他的緩存。
serializer_class
serializer_class
用來驗證和序列化數據的。也是可以通過直接設置這個屬性,也可以通過重寫get_serializer_class
來實現。
lookup_field
在檢索的時候,根據什么參數進行檢索。默認是pk
,也就是主鍵。
lookup_url_kwarg
在檢索的url
中的參數名稱。默認沒有設置,跟lookup_field
保持一致。
filter_backends
用於過濾查詢集的過濾器后端類的列表。默認值與DEFAULT_FILTER_BACKENDS
設置的值相同。
pagination_class
當分頁列出結果時應使用的分頁類。默認值與 DEFAULT_PAGINATION_CLASS
設置的值相同,即 'rest_framework.pagination.PageNumberPagination'
。
方法
- get_queryset
- get_object
- get_serializer
- get_serializer_class
- get_serializer_context
- filter_queryset
get_queryset
def get_queryset(self):
# 斷言queryset是否不為None
assert self.queryset is not None, (
"'%s' should either include a `queryset` attribute, "
"or override the `get_queryset()` method."
% self.__class__.__name__
)
# 定義queryset屬性,獲取父類的queryset,如果父類沒有定義類屬性`queryset`,那么默認值就是None,就會報上面斷言的錯誤
queryset = self.queryset
# 如果queryset是QuerySet對象,那么返回全部內容
if isinstance(queryset, QuerySet):
queryset = queryset.all()
# 如果不是queryset,那么直接返回
return queryset
get_queryset
默認是返回數據庫全部數據,如果想返回其他數據,需要自定義
get_object
def get_object(self):
queryset = self.filter_queryset(self.get_queryset())
# 查找過濾的條件,默認是pk
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
assert lookup_url_kwarg in self.kwargs, (
'Expected view %s to be called with a URL keyword argument '
'named "%s". Fix your URL conf, or set the `.lookup_field` '
'attribute on the view correctly.' %
(self.__class__.__name__, lookup_url_kwarg)
)
filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
obj = get_object_or_404(queryset, **filter_kwargs)
# 可能會引發權限被拒絕
self.check_object_permissions(self.request, obj)
return obj
該方法是用於在數據檢索(通過pk查找)的時候,返回一條數據的。
get_serializer
def get_serializer(self, *args, **kwargs):
serializer_class = self.get_serializer_class()
kwargs.setdefault('context', self.get_serializer_context())
return serializer_class(*args, **kwargs)
返回應該用於驗證和反序列化輸入以及序列化輸出的序列化器實例
get_serializer_class
def get_serializer_class(self):
assert self.serializer_class is not None, (
"'%s' should either include a `serializer_class` attribute, "
"or override the `get_serializer_class()` method."
% self.__class__.__name__
)
return self.serializer_class
返回用於序列化的類。默認使用self.serializer_class
。如果您需要根據傳入請求提供不同的序列化,您可能需要重寫它。
get_serializer_context
def get_serializer_context(self):
return {
'request': self.request,
'format': self.format_kwarg,
'view': self
}
提供給序列化的額外上下文。
filter_queryset
def filter_queryset(self, queryset):
for backend in list(self.filter_backends):
queryset = backend().filter_queryset(self.request, queryset, self)
return queryset
給定一個查詢集,使用正在使用的過濾器對其進行過濾。您不太可能想要覆蓋此方法,但如果您想將配置的過濾后端應用到默認查詢集,您可能需要從列表視圖或自定義get_object
方法中調用它。
實戰案例
class StudentsGenericView(GenericAPIView):
queryset = Student.objects.all() # 定義了類屬性queryset,告訴視圖要針對哪個模型做處理
serializer_class = StudentModelSerializer # 定義類屬性serlializer_class,告訴視圖你的序列化的類是什么
def get(self, request, *args, **kwargs):
pk = kwargs.get("pk")
if pk:
many = False
query = self.get_object() # 通過pk檢索數據,返回一條數據
else:
many = True
query = self.get_queryset() # 返回動態的數據集,默認返回全部
serializer = self.get_serializer(query, many=many) # 如果數據對象是queryset對象,many需要為True
return APIResponse(results=serializer.data)