先看一張圖,對DRF的各個APIView,Mixin,Viewset等有個基本印象:
具體使用方法:
1、APIView:
DRF 的API視圖 有兩種實現方式:
一種是基於函數的:@api_view
一種是基於類的:APIView,APIView是Restframework提供的所有視圖的基類,繼承自Django的View父類
1)@api_view
使用@api_view裝飾器,使得我們此處的Request不在是Django標准的HttpRequest,而是restframework的Request。
默認情況下,只有GET請求會被接收,它也允許我們自己配置函數允許接收的請求類型
@api_view():默認只接收GET請求
@api_view(http_method_names=['GET']):指明接收的請求類型
@api_view(['GET', 'POST']):接收get post請求
@api_view(['GET','POST'])
def Snippet_list(request):
if request.method=="GET":
pass
elif request.method == "POST":
pass
2)APIView
APIView是基於類的裝飾器,顯然,類視圖更符合面向對象的原則,它支持GET POST PUT DELETE等請求類型,且各種類型的請求之間,有了更好的分離
在進行dispatch分發之前,會對請求進行身份認證,權限檢查,流量控制。
支持定義的屬性:
authentication_classes=():列表或元組,身份認證類
permission_classes=():列表或元組,權限檢查類
throttle_classes=():列表或元組,流量控制類
class Snippet_list(APIView):
def dispatch(self, request, *args, **kwargs):
# 請求到來之后,都需要執行dispatch方法,dispatch根據請求方式的不同,分別觸發對應的get post, put等方法
return super().dispatch(request, *args, **kwargs)
def get(self, request, pk, format=None):
snippet = Snippets.objects.get(pk=pk)
serializer = SnippetSerializer(snippet, many=True)
return Response(serialzer.data)
def post(self, request):
serializer = SnippetSerializer(data = request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk, format=None):
snippet = Snippets.object.get(pk=pk)
snippet.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
配置url:
urlpatterns = [
url(r"^snippets/(?P<pk>[0-9]+)$", Snippet_list.as_view()),
]
2、mixins
使用基於類的視圖的一個最大的好處就是我們可以靈活的選擇各種View,使我們的開發更加的簡潔
mixins里面對應了ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
這些Mixin方法,對我們上面APIView中重寫的GET POST PUT DELETE進行了封裝,使得我們的代碼更加簡潔
下面是使用mixin的例子:
GenericAPIView繼承了APIView,通過GenericViewSet來構建我們的視圖,然后組合ListModelMixin和CreateModelMixin來實現我們上面的代碼功能
基類提供了核心的功能,ListModelMixin和CreateModelMixin提供了.list()和.create()函數,下面我們將get和post與.list和.create綁定起來
class snippet_list(mixins.ListModelMixin, generic.GenericAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
3、使用generic
我們上面使用mixin的代碼,已經非常簡潔了,但是restframework給我們提供了一組混合的generic視圖,可以使我們的代碼 大大簡化
GenericAPIView:繼承自APIView,增加了對列表視圖或者詳情視圖可能用到的通用支持方法。通常使用時,可搭配一個或者多個Mixin擴展類
支持定義的屬性:
列表視圖與詳情視圖通用:queryset(視圖的查詢集), serializer_class(視圖使用的序列化器)
列表視圖專用:pagination_classes(分頁控制類),filter_backends(過濾控制)
詳情頁視圖專用:lookup_field(查詢單一數據庫對象時使用的條件字段,默認為pk)
提供的方法:
通用:
1)get_queryset(self):返回視圖使用的查詢集,是獲取數據的基礎,默認返回queryset,可以重寫:
def get_queryset(self):
user = self.request.user
return user.accounts.all()
2)get_serializer_class(self):返回序列化器,默認返回serializer_class,可以重寫,如:
def get_serializer_class(self):
if self.request.user.is_staff:
return AllAccountSerializer
return BasicAccountSerializer
3)get_serializer(self, *args, **kwargs):返回序列化器對象
詳情視圖使用:
get_object(self):返回詳情視圖所需的模型類數據對象,默認使用lookup_field參數來過濾queryset
該方法默認使用APIView提供的check_object_permissions方法檢查當前對象是否有權限被訪問
class SnippetDetail(GenericAPIView):
queryset = Snippets.objects.all()
seriazlier_class = SnippetSerializer
def get(self, request, pk):
snippet = self.get_object()
serializer = self.get_serializer(snippet)
return Response(serialzier.data)
url:url(r"^snippet/?P<pk>[0-9]+/$", SnippetDetail.as_view())
五個擴展類:generics.ListAPIView, generics.CreateAPIView, generic.RetrieveAPIView, generic.UpdateAPIView, generic.DestroyAPIView,它們會繼承GenericAPIView和mixin對應的視圖
上面可以看到generics.ListAPIView里面做的事情,它替我們繼承了mixins.ListModelMixin和GenericAPIView,還重寫了get方法,所以我們直接繼承generic.ListAPIView,代碼就會更簡潔,
只需要設置一下queryset和serializer_class即可,如下:
class snippet_list(generics.ListAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
4、視圖集ViewSet
使用視圖集ViewSet,可以將一系列邏輯相關的動作放到一個類中。
list():提供一組數據
retrieve():提供單個數據
create():創建數據
destroy():刪除數據
update():修改數據
ViewSet主要是和路由搭配使用,將我們通常使用的get, post等和方法和ViewSet實現的action如list(), create()等對應上。
action屬性:
在視圖集中,我們可以通過action對象屬性來確定當前請求視圖集時的action動作是哪個
def get_serializer_class(self):
if self.action == "create":
return SnippetCreateSerializer
else:
return SnippetListSerializer
url配置:
urlpatterns = [
url(r"^snippet/$", SnippetViewSet.as_view({"get":"list", "post":"create"})),
url(r"^snippet/?P<pk>[0-9]+/$", SnippetViewSet.as_view({"get":"retrive"})),
]
上面url配置,是我們手動的將請求方式和action動作對應起來,其實restframework也已經幫我對應好了,它是通過Router模塊來完成的,我們只需要使用router將對應的視圖注冊即可。
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r"snippet", SnippetViewSet)
urlpatterns = [
url(r"^", include(router.urls))
]
5、Django restframework 過濾器
如過濾文章閱讀量小於100的:
class ArticleViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
def get_queryset(self):
read_nums = self.query_params.get("readNums", 0)
if read_nums:
return Article.objects.filter(read_nums__lt=int(read_nums))
return Article.objects.all()
上面這種方式,如果過濾字段少的話,可以方便使用,但是如果過濾字段多,那代碼量就會很繁復了。如果還想要用簡潔的代碼實現大量的過濾邏輯,就需要用到其他方法,如django-filter
pip install django-filter,並將django-filter加入到INSTALLED_APPS中。
如:
from django_filters.rest_framework import DjangoFilterBackend
class ArticleViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerialzier
filter_backends = (DjangoFilterBackend, )
filter_fields = ("name", "title")
這種寫法相對於上面第一種寫法就簡單很多了,但是如果我們想要查詢閱讀數量大於10,小於100的文章要怎么辦呢?所以這種寫法還是有他的局限性,要實現這種情況的過濾,需要我們自定義過濾器:
新建filters.py
import django_filters
class ArticleFilter(django_filters.rest_framework.FilterSet):
title= django_filters.CharFilter(lookup_expr='icontains')
readNums = django_filters.NumberFilter(lookup_expr="gte")
class Meta:
model = Article
fields = ["min_readers","readNums", "clickNums"]
說明:
上面:model:Article---指出該類是為Article 這個model定義的過濾類
lookup_expr------指出過濾條件,是包含,還是大於或是其他,后面會想起說明
fields----------------指定了支持過濾的字段,如果fields中指定了字段,但是沒有指定過濾方法,則表示精確查詢
view中使用:
class SnippetViewset(mixins.ListModelMixin, viewsets.GenericViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
filter_backends = (DjangoFilterBackends, )
filter_class = ArticleFilter
多表過濾:
class Book(model.Model):
name = model.CharField(max_length = 50)
class Author(model.Model):
name = model.CharField(max_length=20)
book = model.ForeignKey(Book)
例如上面的作者和書是一對多的關系,一個人可以寫多本書。那么我們想要通過書本的名字去查詢它的作者,要怎么做呢?
class AuthorFilter(django_filters.rest_framework.FilterSet):
book__name = django_filters.CharFilter(lookup_expr='icontains')
class Meta:
model = Author
fields = ["book__name"]
在View中使用:
按照上面的單表查詢的View寫法也是可以的,這里我們通過另一種重寫list的方式來完成,旨在說明自定義分頁的寫法。
class AuthorViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
queryset = Author.objects.all()
serializer = AuthorSerializer
def list(self, request, *args, **kwargs):
object = AuthorFilter(request.query_params, queryset=self.queryset)
# 過濾后查詢出的數據
filterRecords = object.qs.count()
# 前端傳過來的參數:當前第幾頁
curP = int(request.query_params.get("p", 0))
# 前端傳過來的參數:每頁有幾條數據
size = int(request.query_params.get("s", 0))
if curP and size:
start = (curP-1)*size
length = size
else:
start = 0
length = filterRecords
# 通過調用過濾器的返回值的qs來處理自定義分頁,有興趣的話可以去django.db.model.query.py中去看看
object = object.qs[start:(start+length)]
serializer = self.get_serializer(object, many = True)
return Response(serializer.data)