drf入门规范
web应用模式
在开发web应用时有两种应用模式:前后端分离和前后端不分离
前后端不分离
在该模式下前端浏览器看到的所有页面都是由后端控制,前后端的耦合度非常高,具体流程见图一
前后端代码都在一起,只需要一个服务器就好了
前后端分离
在该模式下后端不再控制前端页面,仅仅返回前端所需要的数据,至于前端页面所展示的东西或者说效果都是由前端自己来控制,具体流程见图二
前后端代码分离,需要两个服务器
图一
图二
API接口
API接口即应用程序接口,是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节
为了在团队内部形成共识、防止个人习惯差异引起的混乱,我们需要找到一种大家都觉得很好的接口实现规范,而且这种规范能够让后端写的接口,用途一目了然,减少双方之间的合作成本
通过网络我们规定了前后台信息交互规则的url链接,即前后台信息交互的媒介
web的API接口与正常的url接口不同,它可以简单的概括为四点
url地址:长得像返回数据的url链接
请求方式:get,post,delete,put
请求参数:json格式或xml格式的key-value形式的数据
响应结果:json格式或xml格式的数据,请求参数中的output参数值决定了响应结果用什么样的格式
API接口案例
-https://api.map.baidu.com/place/v2/search?ak=6E823f587c95f0148c19993539b99295®ion=上海&query=肯德基&output=xml
-https://api.map.baidu.com/place/v2/search?ak=6E823f587c95f0148c19993539b99295®ion=上海&query=肯德基&output=json
上面两个都是获取上海地区的肯德基门店地址,第一个获取的是xml格式的数据,第二个获取的是json格式的数据
接口测试工具:Postman
1.下载
官网下载:https://www.postman.com/downloads/
2.安装
双击即可
3.使用
restful规范
RESTful是一种定义web API接口的设计风格,尤其适用于前后端分离的应用模式中
10个规范
1.数据的安全保障,通常使用https协议
url链接一般都采用https协议进行传输
采用https协议可以提高数据交互过程中的安全性
2.接口中带api标识
-https://api.lqz.com/books
-https://www.lqz.com/api/books
3.多版本共存,路径中携带版本信息
-https://api.lqz.com/v1/login
-https://www.lqz.com/api/v2/login
4.数据即是资源,均使用名词,尽量不要出现动词
接口一般都是用于完成前后台数据的交互,交互的数据我们称之为资源
接口的形式:
https://api.baidu.com/users
https://api.baidu.com/books
特殊的接口可以出现动词,因为这些接口一般没有一个明确的资源,或者动词就是接口的核心含义
https://api.baidu.com/login
5.资源操作由请求方式决定
操作资源一般都会涉及到增删改查,我们提供请求方式来标识这些动作
https://api.baidu.com/books - get请求:获取所有书
https://api.baidu.com/books/1 - get请求:获取主键为1的书
https://api.baidu.com/books - post请求:新增一本书书
https://api.baidu.com/books/1 - put请求:修改主键为1的书
https://api.baidu.com/books/1 - delete请求:删除主键为1的书
6.在请求地址中带过滤条件
https://api.baidu.com/books?name=红&price=99
7.响应状态码: 两套
http响应状态码:
1xx:请求正在处理 2xx:响应成功 3xx:重定向 4xx:客户端错误 5xx:服务端错误
公司内部规定的响应状态码,放在响应体中
{code:0}
8.返回数据中带错误信息
{
code:0
msg:"登录成功/用户名密码错误"
}
9.返回的结果应该符合以下规范
GET 获取所有数据:返回资源对象的列表 [{},{},{}]
GET 单个对象:返回单个资源对象 {}
POST 新增对象:返回新生成的资源对象 {}
PUT 修改对象:返回完整的资源对象 {}
DELETE 删除: 返回一个空文档
10.相应数据中带链接
序列化与反序列化
api接口开发最核心最常见的一个过程就是序列化,所谓序列化就是将数据转换格式,序列化可以分为两个阶段:
序列化:将我们识别的数据转换成制定的格式提供给别人
反序列化:将别人提供的数据转换成我们需要的格式
基于diango原生编写5个接口
我们以后写的接口基本上都是这5个接口及其变形
查询所有
查询单个
新增一个
修改一个
删除一个
基于books单表为例写5个接口
创建表
表迁移
录入数据:直接录或者后台管理录
写查询所有接口:遵循restful规范,使用CBV
新增一个数据
查询一个数据
修改一个数据:put提交的数据不能从request.POST中提取
删除一个数据
drf介绍和快速使用
# djangorestframework: drf 帮助我们快速的实现符合restful规范的接口
# django 最新 4.x ,一般都会用最新版的上一版3.x
# drf最新支持到djagno 3.x ,最新不支持2.x
# 安装drf
pip3 install djangorestframework -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com/simple/
# 由于你是django2.x 它发现它不支持,它会自动写在dajgno,安装最新的django 4.x
# 使用drf编写5个接口(听个响)
# views中
from .serializer import BookSerializer
from rest_framework.viewsets import ModelViewSet
class BookView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
# serializer
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
# urls中
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
router.register('books', views.BookView, 'books')
urlpatterns = [
path('admin/', admin.site.urls),
]
# 两个列表相加
urlpatterns += router.urls
CBV源码分析
路由中写的:path('api/v1/books/', views.BookView.as_view())
当请求来了以后,匹配成功执行,views.BookView.as_view()会自动加括号执行并将request传进去,执行结果是View里面绑定给类的方法as_view,返回的结果是内层函数view
@classonlymethod
def as_view(cls, **initkwargs):
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.setup(request, *args, **kwargs)
if not hasattr(self, 'request'):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
return self.dispatch(request, *args, **kwargs)
return view
所以当请求来了以后执行的其实就是内层函数view(request),返回的结果则是self.dispatch
def dispatch(self, request, *args, **kwargs):
# 将equest.method请求方式转小写再比较
if request.method.lower() in self.http_method_names:
# 通过获取的字符串反射出属性或方法
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
# BookView中的方法加括号传入request
return handler(request, *args, **kwargs)
上面就是dispatch的源码,
APIView执行流程
基于APIView+JsonResponse编写接口
class BookView(APIView):
def get(self, request):
books = models.Books.objects.all()
book_list = []
for book in books:
book_list.append({'name': book.name, 'price': book.price})
return JsonResponse(book_list, safe=False)
基于APIView+Response编写接口
class BookView(APIView):
def get(self, request):
books = models.Books.objects.all()
book_list = []
for book in books:
book_list.append({'name': book.name, 'price': book.price})
return Response(book_list) # 这种的无论是列表还是字典都可以被序列化
APIView的执行流程
1.路由中写的是:path('books/', views.BookView.as_view()),请求来了以后执行的还是views.BookView.as_view(),但是此时的as_view已经不是原来的as_view了,而是APIView中的as_view
2.as_view的执行
源码:
class APIView(View):
@classmethod
def as_view(cls, **initkwargs):
# 调用父类的as_view方法,即调用django原生的View方法中的as_view方法
# 此时的view就是django原生as_view方法中的闭包函数view
view = super().as_view(**initkwargs)
# csrf_exempt是一个装饰器,它加在所有方法之上,排除了所有的csrf认证
return csrf_exempt(view)
csrf_exempt的源码:是一个装饰器
def csrf_exempt(view_func):
def wrapped_view(*args, **kwargs):
return view_func(*args, **kwargs)
wrapped_view.csrf_exempt = True
return wraps(view_func)(wrapped_view)
3.csrf_exempt(view)执行之后结果是self.dispatch,但此时的dispatch要先从APIView中找,找不到再去View中找,结果是找到了
源码:
def dispatch(self, request, *args, **kwargs):
# 括号中的request是django原生的request,这一行代码是将原来的request包装成新的request,这个是drf提供的Resquest类的对象
request = self.initialize_request(request, *args, **kwargs)
#上面一行代码执行完成以后request就变成新的了,原来的request在request._request
# 将新的request放到self对象中(Book_view的对象)
self.request = request
try:
# 执行了三大认证(认证,频率,权限),使用新的request
self.initial(request, *args, **kwargs)
# 和原来的dispatch执行程序一样
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
# 将新的request传入,视图类方法中的request也是新的
response = handler(request, *args, **kwargs)
except Exception as exc:
# 全局异常捕获,在执行三大认证和视图类方法的过程中,如果出了异常,都能捕获到
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
总的来说就是:
1.取出所有的csrf
2.包装了新的request,之后在视图类中使用的都是新的request,原本的在request._request
3.执行三大认证
4.异常捕获
Request对象源码分析
request来源
原来的:django.core.handlers.wsgi.WSGIRequest
新的:from rest_framework.request import Request
Request源码:
正常情况下我们获取前台提交的方法使用request.method方法,但是在新的request中没有method方法,只有原来的request有,在这种情况下新的request中就出现了一个__getattr__方法用于获取原来的request中的method方法
def __getattr__(self, attr):
try:
# 通过反射从原来的request中取得对应的属性或方法
return getattr(self._request, attr)
except AttributeError:
return self.__getattribute__(attr)
在新的request中有:
request.data方法,它包装成了属性,这样之后无论是post还是put请求放在body提交的数据(无论是什么编码格式)都可以从request.data中拿取,取出来就是字典
同样的还有request.query_params,可以从这里面拿取get请求携带的参数
request.FILES是可以拿取前端提交过来的文件