前后端分离是互联网应用开发的标准使用方式,让前后端通过接口实现解耦,能够更好的进行开发和维护。
在接口设计中,大家遵循一定的规范可以减少很多不必要的麻烦,例如url应有一定辨识度,可以加入api等关键词,路径中尽量不要含有动词,根据请求方式对业务逻辑进行划分等等,如:
请求方式 | 数据库操作 | 描述 |
GET | SELECT | 获取数据 |
POST | CREATE | 添加数据 |
PUT | UPDATE | 更新数据 |
DELETE | DELETE | 删除数据 |
安装命令:
pip install djangorestframework
settings.py注册:
INSTALLED_APPS = [
"rest_framework",
...
]
app/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
class IndexView(APIView):
def get(self, request):
res = dict()
res['mes'] = "success"
res['data'] = 123
return Response(res)
视图函数变成了视图类,需要继承APIView。可在类内部分别定义get,post等请求函数,视图会根据请求方式映射不同处理函数。
每条路由对应一个视图函数,故需将视图类转为视图函数
urls.py
from django.contrib import admin
from django.urls import path
from app1 import views
urlpatterns = [
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 Player
class PlayersView(APIView):
def get(self, request):
players = Player.objects.all()
res = list()
for p in players:
t = dict()
t["id"] = p.id
t["name"] = p.name
res.append(t)
return Response(res)
字段较少的时候无妨,字段太多的时候,这是个让人抓狂的操作。
可以先编写一个序列化类(app/serializer.py):
from rest_framework import serializers
from app1.models import Player
class 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 PlayersModelSerializer
class 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类,逐个字段进行手动编写,以及序列化嵌套等等,不在赘述。
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 PlayersModelSerializer
class PlayersView(mixins.ListModelMixin, generics.GenericAPIView):
queryset = Player.objects.all()
serializer_class = PlayersModelSerializer
def 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 PlayersModelSerializer
class PlayersView(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):
queryset = Player.objects.all()
serializer_class = PlayersModelSerializer
def 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 = PlayersModelSerializer
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)
路由需要提供pk参数:(urls.py)
urlpatterns = [
path("players//", views.PlayerDetailView.as_view()),
...
]
如果想要通过其他字段查找数据,需要提供lookup_field参数,指明要查找的字段(查询结果多于一条时会报错)
class PlayerDetailView(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin,
generics.GenericAPIView):
queryset = Player.objects.all()
serializer_class = PlayersModelSerializer
lookup_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//", views.PlayerDetailView.as_view()),
...
]
GenericAPIView还有许多子类,直接将Mixins和GenericAPIView进行了组合,有这么多:
类 | 提供方法 |
CreateAPIView |
post |
ListAPIView | get |
RetrieveAPIView | get |
DestroyAPIView | delete |
UpdateAPIView | put, patch |
ListCreateAPIView | get, post |
RetrieveUpdateAPIView | get, put, patch |
RetrieveDestroyAPIView | get, delete, patch |
RetrieveUpdateDestroyAPIView | get, put, delete, patch |
上面的试图类可以这么写:
class PlayerDetailView(generics.RetrieveUpdateDestroyAPIView):
queryset = Player.objects.all()
serializer_class = PlayersModelSerializer
lookup_field = "name"
言简意赅。
DRF提供了自定义返回类,可以自己编写,也可以硬往里赛东西,比如:
def get(self, request, *args, **kwargs):
res = dict()
res["mes"] = "success"
res["data"] = self.list(request, *args, **kwargs).data
return Response(res)
把原来Response中的data取出来,重新塞点东西再返回。
DRF有三个分页方式,这里说一个PageNumberPagination,创建一个分页类(app/paginations.py):
from rest_framework.pagination import PageNumberPagination
class 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 PlayerPagination
class PlayersView(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):
queryset = Player.objects.all()
serializer_class = PlayersModelSerializer
pagination_class = PlayerPagination
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
一般APIView配合序列化就能很好开发接口,Mixins和GenericAPIView少不了几行代码,且queryset和response部分限制较多,自定义覆盖原方法的代码也就差不多把少的几行代码补回了,如果需要分页功能,可以用GenericAPIView编写。