drf02
今日内容概要
- RESTful规范
- 序列化和反序列化
- 基于django原生编写5个接口
- drf介绍和快速入门
- cbv源码分析
RESTful规范
REST全称是Representational State Transfer,中文的意思是:表征想状态转移,它首次出现在2000年Roy Fielding的博士论文中
RESTful是一种定义Web API接口的设计风格,尤其适合用于前后端分离的应用模式中
-
数据的安全保障,通常使用
https(http+ssl加密/tsl加密)
协议url链接
一般都采用https协议进行传输采用
https
协议,可以提高数据交互过程中的安全性 -
接口中带有
api
标识用第二个
-
多版本共存,路径中带版本信息
-
数据即是资源,均使用名词,尽量不出现动词(最核心的)
接口一般都是完成前后台数据的交互,交互的数据我们称之为资源
接口形式如下
特殊的接口可以出现动词,因为这些接口一般没有一个明确的资源,或者动词就是接口的核心含义
-
资源操作由请求方式决定(method)
操作资源一般都会涉及到增删改查,我们提供请求方式来标识增删改查的动作
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的书 -
在请求地址中带过滤条件
https://api.baidu.com/books?name=红&price=99
可以理解为查询图书名称带
红
且价格为99
的图书比如我们写的侧边栏。
-
响应状态码:两套
http响应状态码:
1xx : 请求正在处理,
2xx: 成功响应 见最多的就是
-
200(成功)
服务器已成功处理了请求。通常,这表示服务器提供了请求的网页。
-
201(已创建)
请求成功并且服务器创建了新的资源。3xx: 重定向 301 永久重定向 302,307 临时重定向
4xx: 客户端报错 403 没有权限 404 访问的地址不存在
5xx: 服务端报错
-
503(服务不可用)
服务器目前无法使用(由于超载或停机维护)。通常,这只是暂时状态。第二套
公司内部规定的响应状态码,放在响应体中
{"code":0} 咱们后期一般使用100 101 102 这种
-
返回数据中带错误信息
-
返回的结果应该符合以下规范(好多公司不遵循这个)
-
GET
获取所有数据:返回资源对象的列表(数组)[{name:红楼梦,price:99},{name:红楼梦,price:99},{name:红楼梦,price:99}]
-
GET
单个对象:返回单个资源对象{name:红楼梦,price:99}
-
POST
新增对象:返回新生成的资源对象{name:西游记,price:99}
-
PUT
修改对象:返回完整的资源对象{name:西游记,price:100}
-
DELETE
删除对象:返回一个空文档
-
-
响应数据中带链接
序列化与反序列化
api接口开发,最核心最常见的一个过程就是序列化,所谓序列化就是把【数据转换格式】,序列化可以分两个阶段:
-
序列化:把我们识别的数据转换成指定的格式提供给别人
字典,列表 json格式存到文件中了
例如:我们在django中获取到的数据默认是模型对象,但是模型对象数据无法直接提供给前端或者别的平台使用,所以我们需要把数据进行序列化,变成字符串或者json数据,提供给别人
当我们读数据的时候,把数据从数据库中把数据读出来
然后把数据序列化成
json格式字符串
然后发送到前端我们一般把读数据就是序列化操作
-
反序列化:把别人提供的数据转换/还原成我们需要的格式
例如:前端js提供的
json
数据,对于python
来说就是字符串,我们需要进行反序列化,才能把数据保存到数据库中当我们写数据,前端发送过来的数据,前端发送的数据就是字符串
我们需要把字符串反序列化成数据能保存的数据。
我们一般把写数据就是反序列化
drf介绍和快速使用
djangorestframework: drf 帮助我们快速的实现符合resstful规范的接口
django 最新 4.x , 一般都会用最新版的上一版的稳定版3.x
drf最新支持到django 3.x 最新的不支持django2.x
安装dirf
pip3 install djangorestframework
由于你是django2.x 它发现它不支持,它会自动卸载django,安装最新的django4.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),
]
# 两个列表相加 [1,2,4] + [6,7,8]=
urlpatterns += router.urls
在 settings中
'rest_framework' # 注册drf这个app,如果不注册用浏览器访问会报错,如果注册了,用浏览器访问能看到好看的页面,不仅仅是json格式的数据
不注册会报错
注册了
cbv源码分析
从as_view()
入手
def as_view(cls, **initkwargs):
"""Main entry point for a request-response process."""
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError("You tried to pass in the %s method name as a "
"keyword argument to %s(). Don't do that."
% (key, cls.__name__))
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r. as_view "
"only accepts arguments that are already "
"attributes of the class." % (cls.__name__, key))
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)
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
作业
新增图书,三种编码格式都能增加成功,django原生5个接口
model层
class Book(models.Model):
name = models.CharField(max_length=32, verbose_name="书名")
price = models.CharField(max_length=12, verbose_name="价格")
publish = models.CharField(max_length=12, verbose_name="出版社名称")
class Meta:
verbose_name_plural = "图书表"
def __str__(self):
return "图书%s对象" % self.name
url层
path('books/', views.BooksView.as_view()),
path('books/<int:pk>/', views.Books2View.as_view())
view层
from .models import Book
from django import views
from django.http import JsonResponse
import json
from json import JSONDecodeError
class BooksView(views.View):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
book_obj = BooK()
self.file_list = [i for i in book_obj.__dict__ if not i.startswith('_')]
def get(self, request):
book_queryset = Book.objects.all()
book_list = []
for book in book_queryset:
book_list.append({book_file: getattr(book, book_file) for book_file in self.file_list})
return JsonResponse(book_list, safe=False, json_dumps_params={'ensure_ascii': False})
def post(self, request):
bank_data = {'code': 10000, "msg": '', "error": {}}
res_dict = {}
try:
book_data = request.POST if request.POST else json.loads(request.body)
except JSONDecodeError as e:
bank_data['msg'] = '什么都没写'
else:
# 删除字段 id
self.file_list.remove('id')
# 循环出所有字段
for book_file in self.file_list:
# 判断前端传过来的字段是否完整, 不完整则提示它字段为空
if not book_data.get(book_file):
bank_data['error'][book_file] = '字段%s为空' % book_file
else:
res_dict[book_file] = book_data.get(book_file)
# 判断字段时候有为空的 如果有不为空则添加
if not bank_data.get('error'):
Book.objects.create(**res_dict)
bank_data['data'] = res_dict
bank_data['msg'] = '添加成功'
else:
bank_data['code'] = 10001
bank_data['msg'] = '添加失败'
return JsonResponse(bank_data, json_dumps_params={'ensure_ascii': False})
class Books2View(views.View):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
book_obj = Book.objects.first()
self.file_list = [i for i in book_obj.__dict__ if not i.startswith('_')]
def get(self, request, pk):
book_obj = Book.objects.filter(pk=pk).first()
data = {book_file: getattr(book_obj, book_file) for book_file in self.file_list}
return JsonResponse(data)
def put(self, request, pk):
book_obj = Book.objects.filter(pk=pk).first()
# put 请求的POST和GET都取不到, 所以只能在body里面取
book_data = json.loads(request.body)
print(book_data)
for book_file in self.file_list:
setattr(book_obj, book_file, book_data.get(book_file, getattr(book_obj, book_file)))
book_obj.save()
print(book_obj.name)
data = {book_file: getattr(book_obj, book_file) for book_file in self.file_list}
return JsonResponse(data)
def delete(self, request, pk):
Book.objects.filter(pk=pk).delete()
return JsonResponse({})
使用ajax提交到后台数据
ajax
urlencoded
form-data
json
前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
{% load static %}
<script src="{% static 'js_dir/jquery-1.12.4.js' %}"></script>
<script src="{% static 'js_dir/sweelalert.js' %}"></script>
</head>
<body>
<button id="btn1">普通数据</button>
<input type="file" id="file">
<button id="btn2">文件数据</button>
<button id="btn3">Json数据</button>
<script>
$('#btn1').click(function () {
$.ajax({
url: '/aaa/',
type: 'post',
data: {"name": "jason"},
success: function (args) {
swal(args.msg)
}
})
})
$('#btn2').click(function () {
let formData = new FormData()
formData.append("file", $('#file')[0].files[0])
$.ajax({
url: '/aaa/',
type: 'post',
data: formData,
contentType: false,
processData: false,
success: function (args) {
swal(args.msg)
}
})
})
$('#btn3').click(function () {
$.ajax({
url: '/aaa/',
type: 'put',
data: JSON.stringify({"name": "jason"}),
contentType: "application/json",
success: function (args) {
swal(args.msg)
}
})
})
</script>
</body>
</html>
view层
def aa_func(request):
bank_data = {'msg': ''}
if request.method == 'POST':
bank_data['msg'] = request.POST.get('name')
if not bank_data.get('msg'):
file = request.FILES.get('file')
res = len(file)
bank_data['msg'] = str(res)
return JsonResponse(bank_data)
elif request.method == 'PUT':
bank_data['msg'] = json.loads(request.body).get('name')
return JsonResponse(bank_data)
return render(request, 'content.html')