前置基础知识
web开发的两种模式
1. 前后端不分离
完整的html页面是在后端生成的,后端给前端返回完整的页面,前端只是进行展示,前端与后端的耦合度很高。
缺点:只适用于纯网页的应用。
优点:有利于网站的SEO优化
2. 前后端分离
完整的html页面是在前端生成的,后端只给前端返回所需的数据,前端将数据填充在页面上,前端与后端的耦合度很低。
优点:可以对接不同类型的客户端。
缺点:不利于SEO优化
3.API: 在前后端分离开发模式中,我们通常将后端开发的每个视图都称为一个接口或者API。
4.RESTful设计风格
统一的接口设计方式就是普遍采用的RESTful API设计风格
Restful风格设计-关键点
- URL地址尽量使用名词复数,不要使用动词。
- 访问同一个URL地址,采用不同的请求方式,代表要执行不同的操作。(GET)获取 POST(新增) PUT(修改)DELETE(删除) 不常用:PATCH(修改) HEAD(只返回请求头没有请求体) OPTIONS(获取信息)
- 过滤参数可以放在查询字符串中。常见的参数:
?limit=10:指定返回记录的数量?offset=10:指定返回记录的开始位置。?page=2&pagesize=100:指定第几页,以及每页的记录数。?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
- 针对不同操作,服务器向用户返回不同的响应数据。一般遵循以下规范:
1. 获取一组数据,返回一组数据2. 获取指定数据,返回指定数据3. 新增数据,返回新增的数据4. 修改数据,返回修改的数据5. 删除数据,返回空
- 服务器返回的响应数据格式,应该尽量使用JSON。
- 响应状态码,服务器向客户端返回的状态码和提示信息,常见的状态码如下:
200 OK - [GET/PUT]:服务器成功返回用户请求的数据201 CREATED - [POST]:用户新建数据成功。204 NO CONTENT - [DELETE]:用户删除数据成功。400 INVALID request - [POST/PUT]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。。500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
了解:
- 域名:使用专有域名
- 版本:将版本信息放在url地址
- 错误:将错误信息返回
- 在访问api接口时,将和接口相关的其他API接口的地址也在响应数据中返回
使用Django开发REST API
需求: 设计一套符合RestAPI风格的接口,提供以下5个接口: 1. 获取所有直播间数据:GET /lives/ 2. 新增一本直播间数据:POST /lives/ 3. 获取指定的直播间数据(根据id):GET /lives/(?P<pk>d )/ 4. 修改指定的直播间数据(根据id):PUT /lives/(?P<pk>d )/ 5. 删除指定的直播间数据(根据id):DELETE /lives/(?P<pk>d )/
模型类定义
# models.pyclass LiveInfo(models.Model): live_id = models.IntegerField(verbose_name='直播间号') live_streamer = models.CharField(max_length=20, verbose_name='主播名字') live_title = models.CharField(max_length=30, verbose_name='直播间标题') live_pop = models.IntegerField(default=0, verbose_name='人气') live_content = models.CharField(default='未设定', max_length=20, verbose_name='直播类型') is_delete = models.BooleanField(default=False, verbose_name='删除标记') class Meta: db_table = 'tb_lives' verbose_name = '直播间' verbose_name_plural = verbose_name def __str__(self): return self.live_streamer
views.py文件中定义视图实现API接口:
class LiveListView(View): def get(self, request): """获取所有直播间""" lives = LiveInfo.objects.all() lives_list = [] for live in lives: lives_list.append({ 'live_id': live.live_id, 'live_streamer': live.live_streamer, 'live_title': live.live_title, 'live_pop': live.live_pop, 'live_content': live.live_content }) return JsonResponse(lives_list, safe=False) def post(self, request): """新增直播间""" passclass LiveDetailView(View): def get(self, request, pk): """查询一间直播间信息""" pass def put(self, request, pk): """修改直播间信息""" pass def delete(self, request, pk): """删除直播间""" pass
RestAPI开发核心工作
分析上节直播间管理的5个API接口,可以发现,在开发REST API接口时,视图中做的最主要有三件事:
- 将请求的数据(如JSON格式)转换为模型类对象
- 操作数据库
- 将模型类对象转换为响应的数据(如JSON格式)
在以上操作中,涉及到两个概念:序列化和反序列化。
序列化Serialization
将程序中的一个数据结构类型转换为其他格式(字典、JSON、XML等),例如将Django中的模型类对象转换为字典或JSON字符串,这个转换过程我们称为序列化。
反序列化
将其他格式(字典、JSON、XML等)转换为程序中的数据,例如将JSON字符串或字典转换保存为Django中的模型类对象,这个过程我们称为反序列化。
在开发REST API接口时,我们在视图中在做的最核心的事是:
- 将数据库数据序列化为前端所需要的格式,并返回。
- 将前端发送的数据反序列化保存到模型类对象,并保存到数据库中。
Django REST framework 简介
Django REST framework 框架是一个用于构建Web API 的强大而又灵活的工具。通常简称为DRF框架 或 REST framework。
作用:帮助我们大大提高REST API的开发速度
核心功能:
- 序列化器:序列化和反序列化
- 类视图,MiXin扩展类:简化视图代码的编写
环境安装与使用:
pip install djangorestframework# 注:DRF框架依赖于Django,需要先安装Django环境,再安装djangorestframework。
在Django项目中使用DRF框架进行开发时,需要将rest_framework在INSTALLED_APPS中进行注册:
INSTALLED_APPS = [ ... 'rest_framework',]
序列化器
序列化器类定义
from rest_framework import serializersclass LiveInfoSerializer(serializers.Serializer): id = serializers.IntegerField(label='ID', read_only=True) live_id = serializers.IntegerField(label='直播间号') live_streamer = serializers.CharField(max_length=20, label='主播名字', required=False) live_title = serializers.CharField(max_length=30, label='直播间标题') live_pop = serializers.IntegerField(default=0, label='人气', required=False) live_content = serializers.CharField(default='未设定', max_length=20, label='直播类型', required=False) is_delete = serializers.BooleanField(default=False, label='删除标记', required=False)
选项参数:
- write_only:为True,字段只在反序列化时使用
- read_only:为True,字段只在序列化时使用
- required:为True,如果字段在反序列化时使用,该字段必传传入
- default:设置序列化和反序列化操作时的默认值
- max_length和min_length:设置字符串的最大长度和最小长度
- max_value和min_value:设置数字的最大值和最小值
序列化操作
- 序列化单个对象:序列化单个对象obj时,在创建序列化器对象时,将obj传递给instance即可
from livetest.models import LiveInfofrom livetest.serializers import LiveInfoSerializerclass LiveDetailView(View): def get(self, request, pk): """查询一间直播间信息""" try: live = LiveInfo.objects.get(pk=pk) except LiveInfo.DoesNotExist: return HttpResponse(status=404) # 创建序列化器对象 serializer = LiveInfoSerializer(live) # 返回序列化之后的数据 return JsonResponse(serializer.data)
- 序列化多个对象:如果要被序列化的是包含多个对象的查询集QuerySet或list,在创建序列化器对象时,需要添加many=True参数
class LiveListView(View): def get(self, request): """获取所有直播间""" lives = LiveInfo.objects.all() # 创建序列化器对象 serialize# 获取序列化之后的数据r = LiveInfoSerializer(lives, many=True) return JsonResponse(serializer.data, safe=False)
- 关联对象嵌套序列化
如果在序列化对象数据时,需要将其关联的对象一并序列化,则定义序列化器类的字段时,需要在定义对应的关联对象嵌套序列化字段,比如和直播分类关联的直播间。对于嵌套关联字段,可以采用以下3种方式进行定义:
1.PrimaryKeyRelatedField :将关联对象序列化为关联对象的主键。
# 在LiveInfoSerializer中添加此字段Ltype = serializers.PrimaryKeyRelatedField(label='直播类型', read_only=True)或Ltype = serializers.PrimaryKeyRelatedField(label='直播类型', queryset=LiveInfo.objects.all())
2.使用关联对象的序列化器
Ltype = LiveInfoSerializer()
3.StringRelatedField:将关联对象序列化为关联对象模型类__str__方法的返回值。
Ltype = serializers.StringRelatedField(label='直播类型')
如果和一个对象关联的对象有多个,在序列化器类中定义嵌套序列化字段时,需要多添加一个many=True参数。
反序列化操作
# 1. 创建序列化器对象serializer = 序列化器类(data=<待校验字典数据>)# 2. 数据校验:成功返回True,失败返回Falseserializer.is_valid()# 3. 获取校验成功之后的数据serializer.validated_data# 4. 如果校验失败,获取失败的错误提示信息serializer.errors
调用is_valid时,会根据对应序列化类字段是否需要传递、字段类型以及一些选项参数对data中的数据进行校验。
在调用is_valid进行数据校验时,除了一些基本的默认验证行为,可能还需要补充一些验证行为,可以使用以下三种方法:
数据校验通过之后,可以调用序列化对象的save方法进行数据保存,save方法内部会调用对应序列化器类中的create或update方法,可以在create中实现数据新增,在update中实现数据更新。
ModelSerializer使用
如果序列化器类对应的是Django的某个模型类,则定义序列化器类时,可以直接继承于ModelSerializer。
ModelSerializer是Serializer类的子类,相对于Serializer,提供了以下功能:
- 基于模型类字段自动生成序列化器类的字段
- 包含默认的create()和update()方法的实现
class LiveInfoSerializer(serializers.ModelSerializer): class Meta: model = LiveInfo # 使用fields来指明依据模型类的哪些字段生成序列化器类的字段,__all__表明包含所有字段,也可以指明具体哪些字段 # fields = '__all__' # fields = ('id', 'title', ...) # 使用exclude可以指明排除哪些字段 exclude = ['is_delete'] extra_kwargs = { # 'live_title': {'validators': [live_name]}, 'live_streamer': {'required': False}, 'live_pop': {'min_value': 0, 'required': False}, }
案例-序列化器改写后的视图代码:
import jsonfrom django.http import JsonResponse, HttpResponsefrom django.views import Viewfrom livetest.models import LiveInfofrom livetest.serializers import LiveInfoSerializerclass LiveListView(View): def get(self, request): """获取所有直播间""" lives = LiveInfo.objects.all() serializer = LiveInfoSerializer(lives, many=True) return JsonResponse(serializer.data, safe=False) def post(self, request): """新增直播间""" json_dict = json.dumps(request.body.decode()) serializer = LiveInfoSerializer(data=json_dict) serializer.is_valid(raise_exception=True) serializer.save() # 返回新增直播间 return JsonResponse(serializer.data, status=201)class LiveDetailView(View): def get(self, request, pk): """查询一间直播间信息""" try: live = LiveInfo.objects.get(pk=pk) except LiveInfo.DoesNotExist: return HttpResponse(status=404) serializer = LiveInfoSerializer(live) return JsonResponse(serializer.data) def put(self, request, pk): """修改直播间信息""" try: live = LiveInfo.objects.get(pk=pk) except LiveInfo.DoesNotExist: return HttpResponse(status=404) json_dict = json.dumps(request.body.decode()) serializer = LiveInfoSerializer(live, data=json_dict) serializer.is_valid(raise_exception=True) serializer.save() return JsonResponse(serializer.data) def delete(self, request, pk): """删除直播间""" try: live = LiveInfo.objects.get(pk=pk) except LiveInfo.DoesNotExist: return HttpResponse(status=404) # 参数校验 live.delete() return HttpResponse(status=204)
视图部分
APIView
APIView是REST framework提供的所有视图的基类,继承自Django的View类。
APIView和View的区别:
- 请求对象:Request类的对象request.data:解析之后的请求体数据 ,request.query_params:解析之后的查询字符串数据
- 响应对象:Response类对象统一返回Response对象,原始响应数据根据客户端请求头Accept转换为对应的格式进行返回
- 异常处理机制默认异常处理机制,会将视图中出现的异常处理成合适的响应返回给客户端
- 其他:认证&权限&限流
案例-使用APIView改写RestAPI:
GenericAPIView
继承自APIView,在APIView功能基础上,主要增加了操作序列化器和数据库查询的属性和方法。
GenericAPIView和APIView的区别:
- 继承自APIView
- 封装了操作序列化器的属性和方法属性:serializer_class方法:get_serializer_class和get_serializer
- 封装了数据库操作的属性和方法属性:queryset方法:get_queryset和get_object
- 其他:过滤和分页
案例-使用GenericAPIView改写RestAPI:
from rest_framework import statusfrom rest_framework.generics import GenericAPIViewfrom rest_framework.response import Responsefrom livetest.models import LiveInfofrom livetest.serializers import LiveInfoSerializerclass LiveListView(GenericAPIView): serializer_class = LiveInfoSerializer queryset = LiveInfo.objects.all() def get(self, request): """获取所有直播间""" queryset = self.get_queryset() serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) def post(self, request): """新增直播间""" serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) serializer.save() # 返回新增直播间 return Response(serializer.data, status=status.HTTP_201_CREATED)class LiveDetailView(GenericAPIView): serializer_class = LiveInfoSerializer queryset = LiveInfo.objects.all() def get(self, request, pk): """查询一间直播间信息""" instance = self.get_object() serializer = self.get_serializer(instance) return Response(serializer.data) def put(self, request, pk): """修改直播间信息""" instance = self.get_object() serializer = self.get_serializer(instance, data=request.data) serializer.is_valid(raise_exception=True) serializer.save() return Response(serializer.data) def delete(self, request, pk): """删除直播间""" instance = self.get_object() # 参数校验 instance.delete() return Response(status=status.HTTP_204_NO_CONTENT)
Mixin扩展类
使用GenericAPIView改写之后的RestAPI接口中,图书管理的各个代码已经变成了通用的代码,这些代码和视图所使用的序列化器类和查询集已经没有直接的关系,DRF框架已经将这些代码做了封装,就是5个Mixin扩展类。
案例-Mixin改写RestAPI接口:
from rest_framework import mixinsfrom rest_framework.generics import GenericAPIViewfrom livetest.models import LiveInfofrom livetest.serializers import LiveInfoSerializerclass LiveListView(mixins.ListModelMixin, mixins.CreateModelMixin,GenericAPIView): serializer_class = LiveInfoSerializer queryset = LiveInfo.objects.all() def get(self, request): """获取所有直播间""" return self.list(request) def post(self, request): """新增直播间""" return self.create(request)class LiveDetailView(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, GenericAPIView): serializer_class = LiveInfoSerializer queryset = LiveInfo.objects.all() def get(self, request, pk): """查询一间直播间信息""" return self.retrieve(request, pk) def put(self, request, pk): """修改直播间信息""" return self.update(request, pk) def delete(self, request, pk): """删除直播间""" return self.destroy(request, pk)
子类视图:Django框架为了方便视图的编写,还提供了9个子类视图类。
子类视图一定同时继承了GenericAPIView和对应的Mixin扩展类,同时类中提供了对应的请求处理方法,并且请求处理方法中调用的就是Mixin扩展类中封装的通用方法。
案例-子类视图改写RestAPI接口:
from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIViewfrom livetest.models import LiveInfofrom livetest.serializers import LiveInfoSerializerclass LiveListView(ListCreateAPIView): serializer_class = LiveInfoSerializer queryset = LiveInfo.objects.all()class LiveDetailView(RetrieveUpdateDestroyAPIView): serializer_class = LiveInfoSerializer queryset = LiveInfo.objects.all()
视图集ViewSet
将操作同一组资源相关的处理方法放在同一个类中,这个类叫做视图集。
基本使用:
常用视图集父类:
- ViewSet:继承自APIView与ViewSetMixin,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。
- GenericViewSet:继承自GenericAPIView与ViewSetMixin,可以直接搭配Mixin扩展类使用。
- ModelViewSet:继承自GenericViewSet和5个Mixin扩展类。
- ReadOnlyModelViewSet:继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin。
案例-ViewSet改写:
from django.http import Http404from rest_framework import statusfrom rest_framework.response import Responsefrom rest_framework.viewsets import ViewSetfrom livetest.models import LiveInfofrom livetest.serializers import LiveInfoSerializerclass LiveInfoViewSet(ViewSet): def list(self, request): """获取所有直播间""" lives = LiveInfo.objects.all() serializer = LiveInfoSerializer(lives, many=True) return Response(serializer.data) def create(self, request): """新增直播间""" serializer = LiveInfoSerializer(data=request.data) serializer.is_valid(raise_exception=True) serializer.save() # 返回新增直播间 return Response(serializer.data, status=status.HTTP_201_CREATED) def retrieve(self, request, pk): """查询一间直播间信息""" try: live = LiveInfo.objects.get(pk=pk) except LiveInfo.DoesNotExist: raise Http404 serializer = LiveInfoSerializer(live) return Response(serializer.data) def update(self, request, pk): """修改直播间信息""" try: live = LiveInfo.objects.get(pk=pk) except LiveInfo.DoesNotExist: raise Http404 serializer = LiveInfoSerializer(live, data=request.data) serializer.is_valid(raise_exception=True) serializer.save() return Response(serializer.data) def destroy(self, request, pk): """删除直播间""" try: live = LiveInfo.objects.get(pk=pk) except LiveInfo.DoesNotExist: raise Http404 # 参数校验 live.delete() return Response(status=status.HTTP_204_NO_CONTENT)# urls.pyfrom django.conf.urls import urlfrom booktest import viewsurlpatterns = [ url(r'^lives/$', views.LiveInfoViewSet.as_view({ 'get': 'list', 'post': 'create' })), url(r'^lives/(?P<pk>d )/$', views.LiveInfoViewSet.as_view({ 'get': 'retrieve', 'put': 'update', 'delete': 'destroy' }))]
案例-GenericViewSet改写:
案例-ModelViewSet并添加了一些额外的方法改写:
from rest_framework.decorators import actionfrom rest_framework.response import Responsefrom rest_framework.viewsets import ModelViewSetfrom livetest.models import LiveInfofrom livetest.serializers import LiveInfoSerializerclass LiveInfoView(ModelViewSet): serializer_class = LiveInfoSerializer queryset = LiveInfo.objects.all() lookup_value_regex = 'd ' @action(methods='get', detail=False) def latest(self, request): """查询最新的直播间""" live = LiveInfo.objects.latest('pk') serializer = self.get_serializer(live) return Response(serializer.data) @action(methods='put', detail=True) def change_pop(self, request, pk): """修改指定直播间的人气""" live = self.get_object() live.bread = request.data.get('live_pop') live.save() serializer = self.get_serializer(live) return Response(serializer.data)# urls.pyfrom django.conf.urls import urlfrom . import viewsurlpatterns = [ url(r'^lives/$', views.LiveInfoViewSet.as_view({ 'get': 'list', 'post': 'create' })), url(r'^lives/(?P<pk>d )/$', views.LiveInfoViewSet.as_view({ 'get': 'retrieve', 'put': 'update', 'delete': 'destroy' })), url(r'^lives/latest/$', views.LiveInfoViewSet.as_view({ 'get': 'latest' })), url(r'^lives/(?P<pk>d )/change_pop/$', views.LiveInfoViewSet.as_view({ 'put': 'change_pop' }))]
路由Router:动态生成视图集中处理方法的url配置项。
对于视图集ViewSet,除了可以自己手动进行URL配置指明请求方式与action处理函数之间的对应关系外,还可以使用路由Router来自动生成路由信息。
REST framework提供了两个Router类:
- SimpleRouter
- DefaultRouter
案例-router改写:
# urls.pyfrom rest_framework.routers import DefaultRouterfrom livetest import viewsurlpatterns = []# 创建Router对象router = DefaultRouter()# 注册routerrouter.register('lives', views.LiveInfoView, base_name='live')# 添加配置项urlpatterns = router.urls
DRF框架到此常用的功能都已经讲解完毕了,当然DRF框架还有其他功能:认证、权限、限流、过滤、排序、分页和异常处理机制。将在后面讲述,敬请关注。
作者简介:Python菜鸟工程师,将在接下来的一段时间内与大家分享一些与Python相关的知识点。如若文中出现问题,各位大佬多多指点,互相学习。喜欢的关注一个吧!谢谢!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。