1. Web应用开发模式
1.1 前后端分离
通俗地讲, 其实判定很简单:如果前端和后端这两个角色, 只通过API 文档就能进行数据交流,就说明他们的逻辑是分离的。我们可以称之为 “前后端代码分离”。
如果除了 API 文档之外还需要各种其他的数据交流方式,比如后端把数据藏在一个 div 的属性里,那么就不是前后端分离的。像各种框架里的模板引擎和渲染功能。
至于所谓的 “最佳实践”并不存在, 但通用的, 类似的方案如下:
- 前端服务器
- 将前端的所有代码使用javascript+node.js的方式部署在node.js服务器上,作为前端稳定的服务,提供页面使用。
- 后端服务器
- 后端的服务器写各种业务逻辑代码, 如业务逻辑, 鉴权逻辑.
- 前后端交互
- 后端服务为前端提供一个统一的访问入口(一般是单独部署一个服务),使用restful等风格提供http服务供前端调用,实现前后端数据传输(通常以JSON来交换数据)。
这样一来, 现在 前端服务器不再处理任何业务,它接收到请求后,经过转换,发送给各个相关后端服务器,将各个后端服务器返回的,处理过的业务数据填入 HTML 模板,最后发送给浏览器。前端服务器和后端服务器间,可以选用任何你觉得合适的通信手段,可以是 REST,可以是 RPC,选用什么样的通信手段,这是另一个议题了。
1.2
1.3 前后端分离, 是浏览器和后端服务分离吗?
不是,前后端分离里的前端不是浏览器,指的是生成 HTML 的那个服务,它可以是一个仅仅生成 HTML 的 Web 服务器,也可以是在浏览器中通过 JS 动态生成 HTML 的 单页应用。
实践中,有实力的团队往往在实现前后端分离里时,前端选用 node 服务器,后端选用 C#、Java 等。
2. API接口规范
Restful
- 基于http协议
- 传统的路由设计
- 增: localhost.com/user/add
- 删: localhost.com/user/delete/1
- 改: localhost.com/user/edit/1
- 查: localhost.com/user/query/1
- Restful的设计规范, 基于资源, 资源操作由请求方式决定
- 比如 api.localhost/users/
- 增: post
- 删: delete
- 删除单个资源: api.localhost.com/users/1
- 删除所有资源: localhost/users/
- 改:
- put: 更新单个资源
- patch: 更新局部/部分资源
- 查: get
- 查单个资源: api.localhost.com/users/1
- 查所有资源: api.localhost.com/users/
- 过滤,通过在url上传参的形式传递搜索条件
- https://api.localhost.com/v1/zoos?limit=10:指定返回记录的数
- https://api.localhost.com/v1/zoos?offset=10:指定返回记录的开始位置
- https://api.localhost.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数
- https://api.localhost.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
- https://api.localhost.com/v1/zoos?animal_type_id=1:指定筛选条件
RPC
- 性能更高
- 基于TCP/IP协议(传输层)
3. 序列化
3.1 什么是序列化?
序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。
- 序列化:把对象转化为可传输的字节序列过程称为序列化。
- 反序列化:把字节序列还原为对象的过程称为反序列化。
3.2 为什么要序列化?
如果光看定义我想你很难一下子理解序列化的意义,那么我们可以从另一个角度来推导出什么是序列化, 那么究竟序列化的目的是什么?
其实序列化最终的目的是为了对象可以跨平台存储,和进行网络传输。而我们进行跨平台存储和网络传输的方式就是IO,而我们的IO支持的数据格式就是字节数组
在大多数开发者, 所谓序列化是指数据在 对象和json之前的相互转换.
4. Django Rest Framework
4.1 DRF的安装
创建新环境
conda create –prefix=C:/ProgramData/Anaconda3/envs/ENV_DRF_01 python=3.9
– 激活环境
- conda activate C:\ProgramData\Anaconda3\envs\ENV_DRF_01
安装django
- conda install django=3.2.*
创建django项目
- django-admin startproject myshop
安装drf, drf是基于django的一个包
- pip install djangorestframework
- pip install pymysql
创建django项目内的APP
- python manage.py startapp LearnDRF
进行django配置
- 数据库
- INSTALLED_APPS
4.2 DRF快速体验
4.2.1 创建并配置数据库
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'learndrf', # 数据库名字
'USER': 'root',
'PASSWORD': 'zxcvbnm',
'HOST': '127.0.0.1', # 那台机器安装了MySQL
'PORT': 3306,
}
}
4.2.2 模型类
class Student(models.Model):
# 模型字段
name = models.CharField(max_length=100,verbose_name="姓名")
sex = models.BooleanField(default=1,verbose_name="性别")
age = models.IntegerField(verbose_name="年龄")
class_null = models.CharField(max_length=5,verbose_name="班级编号")
description = models.TextField(max_length=1000,verbose_name="个性签名")
class Meta:
db_table="tb_student"
verbose_name = "学生"
verbose_name_plural = verbose_name
4.2.3 执行迁移
python manage.py makemigrations
python manage.py migrate
可以添加一些数据, 方便测试.
4.2.4 FBV 和CBV
4.2.4.1 FBV
fbv是指function based view, 望文生义, 义如其名.
fbv 模式的路由
path(‘login_fbv/’, login)
- login是view.py的login函数.
4.2.4.2 CBV
path(‘student/’, StudentView.as_view())
- StudentView是view.py的类
- as_view()是drf包里面视图方法
5. DRF的使用
5.1 DRF的优势?
谈到DRF的使用, 很容易会产生一堆问题? DRF有什么作用? 通常用于哪些场景? 它的优势是什么? 为什么用DRF, 而不是django?
一句话回答: DRF可以更快, 更高效的完成REST API开发.
5.2 DRF如何实现这些优势的?
DRF包含一系列高度抽象和功能丰富的组件
5.2.0.1 序列化组件
1.作用: 把python中的对象,转成json格式字符串
使用步骤1: 写一个类继承Serializer或者ModelSerializer
举例(类中选取字段进行序列化):
class BookSerializer(serializers.Serializer):
id = serializers.CharField()
title = serializers.CharField()
price = serializers.CharField()
举例(把类中字段全部进行序列化):
class TestSer(serializers.ModelSerializer):
class Meta:
model = models.Takes
fields = ‘__all__’
总结:
变量名和source指定的值不能一样
source=’publish.name’还支持继续 .
source 还支持方法(没用)
支持写方法,如下
方法一定传一个参数,是当前book对象
publish_dic=serializers.SerializerMethodField()
def get_publish_dic(self,obj):
return
结果:{‘id’:obj.publish.pk,’name’:obj.publish.name}
5.2.0.2 认证组件
1.认证组件的好处:
比如要访问books/路径,必须登录之后才能访问。一旦登录成功,在响应结果中写一个随机字符串
举例: {status:100
msg:登录成功
token:sdafsdfasd
}
2.使用步骤1:写一个类,继承BaseAuthentication
3.使用步骤2:def authenticate(self,request) ,记住传request对象
4.在视图类中使用:(不要加括号):
1.局部使用:authentication_classes=[AuthLogin](写在views中)
2.全局使用:-REST_FRAMEWORK=
{“DEFAULT_AUTHENTICATION_CLASSES”:[“app01.auth.AuthLogin”,](在setting中配置)
3.全局使用的局部禁用:authentication_classes = [](写在views中)
5.2.0.3 权限组件
1.使用步骤1:写一个类,继承BasePermission
2.使用步骤2:def has_permission(self, request, view): ,记住传
request对象和view
3.在视图类中使用:(不要加括号):
1.局部使用:permission_classes=[MyPer](写在views中)
2.全局使用:-REST_FRAMEWORK={“DEFAULT_PERMISSION_CLASSES”:[‘app01.auth.MyPer’]}(在setting中配置)
3.全局使用的局部禁用:permission_classes = [](写在views中)
5.2.0.4 节流组件
1.使用步骤1:写一个类,继承SimpleRateThrottle
2.使用步骤2:def get_cache_key(self, request, view):,记住传request对象和view
3.使用步骤3
:’DEFAULT_THROTTLE_RATES’: {
这个key跟scope对应,value值3/m 3/h 4/d
‘xx’: ‘3/m’
}(在setting中配置)
节流作用:节流(Throttling)类似于权限,因为它决定了是否应该对请求进行授权。节流表示一个临时状态,并用于控制客户端对API的请求率。
5.2.0.5 视图组件
使用:
1.局部使用:throttle_classes = [VisitThrottle](写在views中)
2.全局使用:REST_FRAMEWORK={“DEFAULT_THROTTLE_CLASSES”:[“app01.auth.VisitThrottle”]}(在setting中配置)
3.全局使用的局部禁用:throttle_classes = [](写在views中)
视图作用: 数据库查询, 2. 构建序列化器, 进行序列化操作, 返回数据
5.2.0.6 解析器
1.局部使用:parser_classes=[JSONParser,](写在views中)
2.全局使用:’DEFAULT_PARSER_CLASSES’:[‘rest_framework.parsers.JSONParser’](在setting中配置)
解析器作用:解析器的作用就是服务端接收客户端传过来的数据,把数据解析成自己可以处理的数据。本质就是对请求体中的数据进行解析。
5.2.0.7 渲染器
DRF提供的渲染器有很多,默认是
‘DEFAULT_RENDERER_CLASSES’: (
‘rest_framework.renderers.JSONRenderer’,
‘rest_framework.renderers.BrowsableAPIRenderer’,
),
渲染器的作用:渲染器同解析器相反,它定义了框架按照content_type来返回不同的响应。
5.2.0.8 分页组件
1.路由
urlpatterns = [
re_path(‘(?P<version>[v1|v2]+)/page1/’, Pager1View.as_view(),) #分页1
]
序列化
from rest_framework import serializers
from api import models
class PagerSerialiser(serializers.ModelSerializer):
class Meta:
model = models.Role
fields = “__all__”
视图
from api.utils.serializsers.pager import PagerSerialiser
from rest_framework.response import Response
from rest_framework.pagination import PageNumberPagination
class Pager1View(APIView):
def get(self,request,*args,**kwargs):
#获取所有数据
roles = models.Role.objects.all()
#创建分页对象
pg = PageNumberPagination()
#获取分页的数据
page_roles = pg.paginate_queryset(queryset=roles,request=request,view=self)
#对数据进行序列化
ser = PagerSerialiser(instance=page_roles,many=True)
return Response(ser.data)
settings配置
REST_FRAMEWORK = {
#分页
“PAGE_SIZE”:2 #每页显示多少个
}
5.2.0.9 版本组件
自定义版本处理
class ParamVersion(object):
def determine_version(self, request, *args, **kwargs):
# version = request._request.GET.get(“version”)
# version = request.query_params.get(‘version’)
version = request.GET.get(“version”)
return version
class UsersView(APIView):
versioning_class = ParamVersion
def get(self, request, *args, **kwargs):
version = request.version
return JsonResponse({‘version’: version})
5.2.0.10 路由组件
使用方法
# format参数是给渲染器用的
url(r’^(?P<version>[v1|v2]+)/myview\.(?P<format>\w+)$’, MyView.as_view({“get”: “list”, “post”: “create”})),
创建router对象,并注册视图集。
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r’myview’, MyView)
最原始的 Serializer + APIView
2. ModelSerializer + APIView
6. 序列化和视图类的使用
6.1 前置了解
视图类
- APIView
- 继承APIView父类(Django中View的子类)
- 具备View的所有特性
- 提供了认证、授权、限流等功能
- GenericAPIView
- GenericAPIView为APIView的子类,它拓展了过滤、查询、分页的功能
序列化器
- Serializer
- ModelSerializer
这里将会通过以下三种组合来熟悉DRF的写法.
DRF的 Serializer + DRF的 APIView
DRF的 ModelSerializer + DRF的 APIView
DRF的 ModelSerializer + DRF的 GenericAPIView
6.2 Serializer + APIView
path('student/', StudentView.as_view()), #drf的as_view方法
re_path('student/(\d+)/', StudentDetailView.as_view()), #drf的as_view方法
class StudentView(APIView):
def get(self, request):
print(request.GET)
print(request.query_params)
students = Student.objects.all()
print(type(students))
# students 是一个queryset 数据集. 所谓的序列化就是对该数据再进行封装, 这里其实是for循环, 将里面的数据都拿到,
serializer = StudentsSerializer(instance=students, many=True)
return Response(serializer.data)
def post(self, request):
print(request)
print(request.data)
print(type(request.data))
serializer = StudentsSerializer(data=request.data)
if serializer.is_valid():
stu = Student.objects.create(**serializer.validated_data)
ser = StudentsSerializer(instance=stu, many=False)
return Response(ser.data)
else:
return Response(serializer.errors)
class StudentDetailView(APIView):
print('StudentDetailView')
def get(self, request, id):
students = Student.objects.get(pk=id)
serializer = StudentsSerializer(instance=students, many=False)
return Response(serializer.data)
def delete(self, request, id):
Student.objects.get(pk=id).delete()
return Response("已删除")
def put(self, request, id):
print(request)
print(request.data)
print(type(request.data))
serializer = StudentsSerializer(data=request.data)
if serializer.is_valid():
print(serializer.validated_data)
# 成功修改数据的条数, 为1
# n = Student.objects.filter(pk=id).update(**serializer.validated_data)
print(n)
#要修改的数据对象
stu = Student.objects.get(id=id)
print(id)
ser = StudentsSerializer(instance=stu, many=False)
print(ser.data)
return Response(ser.data)
else:
return Response(serializer.errors)
get 查询所有数据
- http://127.0.0.1:8000/student/
post 添加数据
- http://127.0.0.1:8000/student/
delete 删除单条数据
- http://127.0.0.1:8000/student/1/
put 修改单条数据
- http://127.0.0.1:8000/student/1/
get 查询单条数据
- http://127.0.0.1:8000/student/1/
6.3 ModelSerializer + APIView
path('publish/', PublishView.as_view()),
re_path('publish/(\d+)/', PublishDetailView.as_view()),
class PublishSerializer(serializers.ModelSerializer):
class Meta:
model = Publish
# fiedls = "__all__"
fields = ["name", "email"]
class PublishView(APIView):
def get(self, reqeust):
publishes = Publish.objects.all()
ps = PublishSerializer(instance=publishes, many=True)
return Response(ps.data)
def post(self, request):
print(request.data, type(request.data))
serializer = PublishSerializer(data=request.data)
if serializer.is_valid():
serializer.save() # create方法 将符合条件的数据插入到数据库
return Response(serializer.data)
else:
return Response(serializer.errors)
class PublishDetailView(APIView):
def get(self, reqeust, id):
publish = Publish.objects.get(pk=id)
ps = PublishSerializer(instance=publish, many=False)
return Response(ps.data)
def delete(self, request, id):
Publish.objects.get(pk=id).delete()
return Response()
def put(self, request, id):
# post请求
instance = Publish.objects.get(pk=id)
serializer = PublishSerializer(data=request.data, instance=instance)
if serializer.is_valid():
serializer.save() # instance的 update
return Response(serializer.data)
else:
return Response(serializer.errors)
6.4 ModelSerializer + GenericAPIView
path('author/', AuthorView.as_view()),
re_path('author/(?P<pk>\d+)/', AuthorDetailView.as_view()),
from rest_framework.generics import GenericAPIView
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = ["name", "age"]
class AuthorView(GenericAPIView):
queryset = Author.objects.all()
serializer_class = AuthorSerializer
def get(self, reqeust):
# serializer = self.serializer_class(instance=self.queryset,many=True)
serializer = self.get_serializer(instance=self.get_queryset(), many=True)
return Response(serializer.data)
def post(self, request):
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
serializer.save() # create方法 将符合条件的数据插入到数据库
return Response(serializer.data)
else:
return Response(serializer.errors)
class AuthorDetailView(GenericAPIView):
queryset = Author.objects.all()
serializer_class = AuthorSerializer
def get(self, request, pk): # url的pk名
serializer = self.get_serializer(instance=self.get_object(), many=False)
return Response(serializer.data)
def delete(self, request, pk):
self.get_object().delete()
return Response()
def put(self, request, id):
# post请求
instance = self.get_object()
serializer = self.get_serializer(data=request.data, instance=instance)
if serializer.is_valid():
serializer.save() # instance的 update
return Response(serializer.data)
else:
return Response(serializer.errors)
6.5 ModelSerializer + Mixin
from rest_framework.mixins import ListModelMixin, CreateModelMixin, RetrieveModelMixin, DestroyModelMixin, \
UpdateModelMixin
from rest_framework.generics import GenericAPIView
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = ["name", "age"]
class AuthorView(GenericAPIView, ListModelMixin, CreateModelMixin):
queryset = Author.objects.all()
serializer_class = AuthorSerializer
def get(self, reqeust):
return self.list(reqeust)
def post(self, request):
return self.create(request)
class AuthorDetailView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
queryset = Author.objects.all()
serializer_class = AuthorSerializer
def get(self, request, pk): # url的pk名
return self.retrieve(request)
def delete(self, request, pk):
return self.destroy(request)
def put(self, request, pk):
return self.update(request)
6.6 基于Mixin的进一步封装
ListModelMixin, CreateModelMixin, RetrieveModelMixin, DestroyModelMixin, \ UpdateModelMixin
from rest_framework.generics import ListCreateAPIView,RetrieveUpdateDestroyAPIView
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = ["id","name", "age"]
class AuthorView(ListCreateAPIView):
queryset = Author.objects.all()
serializer_class = AuthorSerializer
class AuthorDetailView(RetrieveUpdateDestroyAPIView):
queryset = Author.objects.all()
serializer_class = AuthorSerializer
本站文章除单独注明外均为原创,本文链接https://bowmanjin.com/670,未经允许请勿转载。
请先
!