ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Django笔记(六):DRF框架

Django笔记(六):DRF框架

前后端分离是互联网应用开发的标准使用方式,让前后端通过接口实现解耦,能够更好的进行开发和维护。

RESTful接口常见规范

在接口设计中,大家遵循一定的规范可以减少很多不必要的麻烦,例如url应有一定辨识度,可以加入api等关键词,路径中尽量不要含有动词,根据请求方式对业务逻辑进行划分等等,如:

请求方式数据库操作描述
GETSELECT获取数据
POSTCREATE添加数据
PUTUPDATE更新数据
DELETEDELETE删除数据

DRF安装

安装命令:

pip install djangorestframework

settings.py注册:

INSTALLED_APPS = ["rest_framework",...
]

视图编写

app/views.py

from rest_framework.views import APIView
from rest_framework.response import Responseclass IndexView(APIView):def get(self, request):res = dict()res['mes'] = "success"res['data'] = 123return Response(res)

视图函数变成了视图类,需要继承APIView。可在类内部分别定义get,post等请求函数,视图会根据请求方式映射不同处理函数。

路由配置

每条路由对应一个视图函数,故需将视图类转为视图函数

urls.py

from django.contrib import admin
from django.urls import path
from app1 import viewsurlpatterns = [path('admin/', admin.site.urls),path('index/', views.IndexView.as_view()),
]

访问指定路由后:

 DRF框架自带的接口界面很好看,也便于调试。

序列化操作

将数据库数据整理为接口返回数据的过程很繁琐,DRF简化了序列化操作。

定义model,/app/models.py

from django.db import models# Create your models here.
class Player(models.Model):id = models.AutoField(primary_key=True)name = models.CharField(max_length=20)

定义了player模型,两个字段id和name。为了方便后台操作,我们将其进行admin注册(app/admin.py)

from django.contrib import admin
from app.models import Player
# Register your models here.admin.site.register(Player)

记得进行数据迁移!!!之后便可在admin管理界面看到Player表格,登录admin管理系统需要创建用户,创建命令:

python manage.py createsuperuser

按提示注册后,登录admin管理界面:

可以看到创建的表格,手动添加两条数据。如果我们希望返回所有player信息,视图应该这么写(app/views.py):

from rest_framework.views import APIView
from rest_framework.response import Response
from app1.models import Playerclass PlayersView(APIView):def get(self, request):players = Player.objects.all()res = list()for p in players:t = dict()t["id"] = p.idt["name"] = p.nameres.append(t)return Response(res)

 字段较少的时候无妨,字段太多的时候,这是个让人抓狂的操作。

可以先编写一个序列化类(app/serializer.py):

from rest_framework import serializers
from app1.models import Playerclass PlayersModelSerializer(serializers.ModelSerializer):class Meta:model=Player# fields="__all__"fields = ("id", "name")

此类用于对Player进行序列化,Meta类中只需指明指定模型,以及想要序列化的字段即可,fields的all参数指所有字段。

视图修改(app/views.py):

from rest_framework.views import APIView
from rest_framework.response import Response
from app1.models import Player
from app1.serializers import PlayersModelSerializerclass PlayersView(APIView):def get(self, request):players = Player.objects.all()# 创建序列化对象,many指多条数据players_json = PlayersModelSerializer(players, many=True)print(players_json.data)# data返回序列化后的数据return Response(players_json.data)

设置路由后,访问结果:

ModelSerializer只是对模型进行序列化,如果对其以外复杂结构进行序列化,可以继承Serializer类,逐个字段进行手动编写,以及序列化嵌套等等,不在赘述。

Mixins类改进

Django的mixins实现了各种功能让其他函数继承,能够让用户用更少的代码操作模型,一般会配合GenericAPIView使用,Mixin有五类:

描述请求方法
ListModelMixin返回查询集列表,提供list方法GET
CreateModelMixin创建实例,提供create()方法POST
RetrieveModelMixin返回一个具体实例,提供retrieve()方法GET
UpdateModelMixin更新实例,提供update()方法PUT、PATCH
DestoryModelMixin删除实例,提供delete()方法DELETE

同样实现上面返回player列表的功能,视图可以这样写(app/views.py):

from rest_framework import mixins, generics
from app1.models import Player
from app1.serializers import PlayersModelSerializerclass PlayersView(mixins.ListModelMixin, generics.GenericAPIView):queryset = Player.objects.all()serializer_class = PlayersModelSerializerdef get(self, request):return self.list(request)

queryset和serializer_class是要操作的数据集合序列化类,GenericAPIView需要的参数。因为ListModelMixin提供了list函数,get请求视图的返回结果可以直接调用。这里的执行结果与上面相同。(如果queryset需要条件查询,需要重写get_queryset函数。

如果是添加数据,则:

from rest_framework import mixins, generics
from app1.models import Player
from app1.serializers import PlayersModelSerializerclass PlayersView(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):queryset = Player.objects.all()serializer_class = PlayersModelSerializerdef get(self, request):return self.list(request)def post(self, request):return self.create(request)

提供name参数即可添加成功。

另外三个Mixin类使用相同,因为要对指定数据进行修改,故需提供词条数据的pk(主键)来找到此条数据。(app/views.py)

class PlayerDetailView(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin,generics.GenericAPIView):queryset = Player.objects.all()serializer_class = PlayersModelSerializerdef get(self, request, *args, **kwargs):return self.retrieve(request, *args, **kwargs)def put(self, request, *args, **kwargs):return self.update(request, *args, **kwargs)def delete(self, request, *args, **kwargs):return self.destroy(request, *args, **kwargs)

路由需要提供pk参数:(urls.py)

urlpatterns = [path("players/<int:pk>/", views.PlayerDetailView.as_view()),...
]

如果想要通过其他字段查找数据,需要提供lookup_field参数,指明要查找的字段(查询结果多于一条时会报错)

class PlayerDetailView(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin,generics.GenericAPIView):queryset = Player.objects.all()serializer_class = PlayersModelSerializerlookup_field = "name"def get(self, request, *args, **kwargs):return self.retrieve(request, *args, **kwargs)def put(self, request, *args, **kwargs):return self.update(request, *args, **kwargs)def delete(self, request, *args, **kwargs):return self.destroy(request, *args, **kwargs)
urlpatterns = [path("players/<name>/", views.PlayerDetailView.as_view()),...
]

 GenericAPIView类

GenericAPIView还有许多子类,直接将Mixins和GenericAPIView进行了组合,有这么多:

提供方法

CreateAPIView

post
ListAPIViewget
RetrieveAPIViewget
DestroyAPIViewdelete
UpdateAPIViewput, patch
ListCreateAPIViewget, post
RetrieveUpdateAPIViewget, put, patch
RetrieveDestroyAPIViewget, delete, patch
RetrieveUpdateDestroyAPIViewget, put, delete, patch

 上面的试图类可以这么写:

class PlayerDetailView(generics.RetrieveUpdateDestroyAPIView):queryset = Player.objects.all()serializer_class = PlayersModelSerializerlookup_field = "name"

言简意赅。

自定义返回数据

DRF提供了自定义返回类,可以自己编写,也可以硬往里赛东西,比如:

def get(self, request, *args, **kwargs):res = dict()res["mes"] = "success"res["data"] = self.list(request, *args, **kwargs).datareturn Response(res)

把原来Response中的data取出来,重新塞点东西再返回。

分页

 DRF有三个分页方式,这里说一个PageNumberPagination,创建一个分页类(app/paginations.py):

from rest_framework.pagination import PageNumberPaginationclass PlayerPagination(PageNumberPagination):page_size = 2 # 每页显示的数据数量max_page_size = 4 # 每页最多显示的数据数量page_size_query_param = "size" # 显示数量的变量名page_query_param = "page" # 页数的变量名"""如果访问localhost/player/?page=2&size=3,则会返回以三条数据分页的第二页内容"""

用户视图(app/views.py):

from rest_framework import mixins, generics
from app1.models import Player
from app1.serializers import PlayersModelSerializer
from app1.paginations import PlayerPaginationclass PlayersView(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):queryset = Player.objects.all()serializer_class = PlayersModelSerializerpagination_class = PlayerPaginationdef get(self, request, *args, **kwargs):return self.list(request, *args, **kwargs)

 尾

一般APIView配合序列化就能很好开发接口,Mixins和GenericAPIView少不了几行代码,且queryset和response部分限制较多,自定义覆盖原方法的代码也就差不多把少的几行代码补回了,如果需要分页功能,可以用GenericAPIView编写。

返回列表