DAY96-Rest Framework(一)-RESTful规范、APIView、序列化组件 一、CBV源码分析 二、resful规范 三、drf(django rest framwork)框架 四、APIView源码分析 五、序列化 六、基于drf的增删查改
1.基础配置
# 路由
from app01 import views
urlpatterns = [
url(r'^books/', views.Books.as_view()),
]
# 视图
from django.views import View
class Books(View):
def get(self,request):
return HttpResponse('ok')
2.执行顺序
# 第一步
# 请求通过url找到对应的类的as_view函数
#url(r'^books/', views.Books.as_view()),
def as_view(cls, **initkwargs):
def view(request, *args, **kwargs):
# 将类所需的参数以及类赋值给self
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
# 第三步,执行当前类的dispatch方法并返回
return self.dispatch(request, *args, **kwargs)
# 第二步,执行闭包函数view,把Books和路由的参数initkwargs传入
view.view_class = cls
view.view_initkwargs = initkwargs
# take name and docstring from class
update_wrapper(view, cls, updated=())
# and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
return view
# 最后返回view的内存地址,相当于执行过后的dispatch方法的返回值
# 也就是说,views.Books.as_view()是得到了返回的内存地址并加上()执行
dispatch
# 由第三步进入
# 由于自定义的Books没有dispatch,会去继承的父类里找
def dispatch(self, request, *args, **kwargs):
# http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
# 判断request带来的请求方式在不在列表里
if request.method.lower() in self.http_method_names:
# 如果在里面,就把当前的请求方式加上()执行并把返回值赋给handler
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
# 最后返回handler的返回值
return handler(request, *args, **kwargs)
总结
1.Books.as_view
2.as_view里的view
3.view里的dispatch
4.dispatch会判断你的请求方式并执行,最后返回结果response
5.view返回dispatch的执行结果
6.as_view返回view的内存地址
7.最后在url里加上()执行
二、resful规范
1.什么是resful
- REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
- 所有的数据,不过是通过网络获取的还是操作(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其他架构风格的最本质属性
- 对于REST这种面向资源的架构风格,有人提出一种全新的结构理念,即:面向资源架构(ROA:Resource Oriented Architecture)
2.resful的10项规范
-
总是使用HTTPS协议
-
域名
将API部署在专用域名(存在跨域问题):https://api.example.com
-
版本控制
https://www.example.com/api/v1(v2)
请求头里携带版本号
-
路径:由于是面向资源编程,后缀为名词
-
method:比如说书本,就只有books的路由,怎么区分增删查改的操作,就靠不同的请求
GET :从服务器取出资源(一项或多项)
POST :在服务器新建一个资源
PUT :在服务器更新资源(客户端提供改变后的完整资源)
PATCH :在服务器更新资源(客户端提供改变的属性)
DELETE :从服务器删除资源
-
条件过滤:可以通过url添加过滤条件
https://www.example.com/api/v1/books?page=2&per_page=100:指定第几页,以及每页的记录数
-
状态码
200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务) 204 NO CONTENT - [DELETE]:用户删除数据成功。 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。 403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。 406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。 422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。 #更多看这里:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
-
错误处理:应当返回错误信息
{ 'error':'xx错误', }
-
返回结果
GET:返回所有数据列表 POST:返回新增的数据 GET:返回单条数据 #https://www.example.com/api/v1/books/1 {id:1, 'name':'alex', 'salary':3000}, PUT:更新,返回完整的资源对象 {id:1, 'name':'alex', 'salary':3000}, PATCH:更新,返回完整的资源对象 {id:1, 'name':'alex', 'salary':3000}, DELETE:删除 返回空
-
Hypermedia API:即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
{ status:100 msg:成功 url:127.0.0.1/books/1 } #核心:返回结果中提供链接
三、drf(django rest framwork)框架
1.安装
它是一个app,要在咱的项目中用,只是快速的构建resful规范的接口
pip3 install djangorestframework
2.注册
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config',
'rest_framework'
]
3.方法
request.data:所有的Body体里的数据都以key:value放到了data里
# 前台传来不同的数据格式,类型也不同,但使用方式是一样的
# 前台传来JSON:request.data中的数据就是一个字典
# 前台传来urlencode:request.data中的数据是一个QueryDict对象
四、APIView源码分析
1.基础配置
# 路由
from app01 import views
urlpatterns = [
url(r'^books/', views.Books.as_view()),
]
# 视图
from rest_framework.views import APIView
from rest_framework.request import Request
from rest_framework.response import Response
class Books(APIView):
def get(self,request):
return HttpResponse('ok')
2.执行顺序
# 执行顺序大致与CBV的执行顺序相同,有改动
def as_view(cls, **initkwargs):
# 调用父类APIView的as_view方法,APIView是继承View
# APIView里没有as_view,也就是说,是调用View的as_view方法并把最终结果赋值给了view
view = super(APIView, cls).as_view(**initkwargs)
view.cls = cls
view.initkwargs = initkwargs
# csrf_exempt局部禁用
# 因为drf框架是不需要csrf认证的
return csrf_exempt(view)
# 第三步调用的dispatch从Books里找,没有就往上一层父类里找,APIView里有
def dispatch(self, request, *args, **kwargs):
self.args = args
self.kwargs = kwargs
# APIView的dispatch调用APIView的initialize_request对request进一步的封装
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
self.initial(request, *args, **kwargs)
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
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
def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
parser_context = self.get_parser_context(request)
# 把原来的request以及所需参数传入,返回一个已经封装过的rest_framework中的Request对象
return Request(
request,
parsers=self.get_parsers(),
# 认证
authenticators=self.get_authenticators(),
# 分页
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
总结
1.Books(APIView).as_view (调用了View的as_view方法)
2.as_view里的view
3.view里的dispatch(从下往上找,APIView的dispatch)
4.APIView的dispatch先对原先的request进行封装,然后判断请求方式并执行,最后返回结果response
5.view返回dispatch的执行结果
6.as_view返回view的内存地址
7.最后在url里加上()执行
3.补充
hasattr(self, 'get')--判断self类中是不是有该(get)方法
setattr(self,get,get_all):相当于把get函数,变成了get_all
__setattr___方法:赋予值时触发
getattr(self, 'get'):从self中拿到get函数的内存地址
__getattr__方法: .的时候触发,也就是获得值时触发
五、序列化
方式一:for循环
class Books(View):
def get(self,request):
books = models.Books.objects.all()
ll = [{'name':book.name,'price':book.price} for book in books]
return JsonResponse(ll,safe=False)
方式二:django自带的序列化
from django.core import serializers
class Books(View):
def get(self,request):
books = models.Books.objects.all()
ret = serializers.serialize('json',books)
return HttpResponse(ret)
方式三:drf提供的序列化组件
from rest_framework.serializers import Serializer
from rest_framework import serializers
class BooksSerializer(Serializer):
name = serializers.CharField(max_length=32)
price = serializers.CharField(max_length=32)
class Books(APIView):
def get(self, request):
books = models.Books.objects.all()
# instance:要序列化的对象(可能是queryset,也可能是单个对象) # many:如果是queryset---True,,如果是单个对象--False
ret = BooksSerializer(instance=books,many=True)
# 生成的是列表,需要转成JSON格式
#[OrderedDict([('name', 'ab'), ('price', '12')]), 。。。。。]
return JsonResponse(ret.data,safe=False)
六、基于drf的增删查改
class BooksSerializer(Serializer):
name = serializers.CharField(max_length=32)
price = serializers.CharField(max_length=32)
class Books(APIView):
# 获得所有获得一条
def get(self, request, *args, **kwargs):
id = kwargs.get('id')
books = models.Books.objects.all()
ret = BooksSerializer(books, many=True)
if id:
book = models.Books.objects.filter(pk=id).first()
ret = BooksSerializer(book, many=False)
return JsonResponse(ret.data, safe=False)
# 创建数据
def post(self, request, *args, **kwargs):
name = request.POST.get('name')
price = request.POST.get('price')
book = models.Books.objects.create(name=name, price=price)
ret = BooksSerializer(book, many=False)
return JsonResponse(ret.data, safe=False)
# 更新完整记录
def put(self, request, *args, **kwargs):
id = kwargs.get('id')
name = request.data.get('name')
price = request.data.get('price')
if id:
models.Books.objects.filter(pk=id).update(name=name, price=price)
book = models.Books.objects.filter(pk=id).first()
res = BooksSerializer(book, many=False)
return JsonResponse(res.data, safe=False)
# 更新一个字段
def patch(self, request, *args, **kwargs):
id = kwargs.get('id')
price = request.data.get('price')
if id:
models.Books.objects.filter(pk=id).update(price=price)
book = models.Books.objects.filter(pk=id).first()
res = BooksSerializer(book, many=False)
return JsonResponse(res.data, safe=False)
# 删除
def delete(self, request, *args, **kwargs):
id = kwargs.get('id')
if id:
models.Books.objects.filter(pk=id).delete()
book = models.Books.objects.filter(pk=id).first()
res = BooksSerializer(book, many=False)
return JsonResponse(res.data, safe=False)