【3.0】DRF之初识
【一】序列化与反序列化
- api接口开发,最核心最常见的一个过程就是序列化
【1】序列化
-
把我们识别的数据转换成指定的格式提供给别人。
-
例如:
- 我们在django中获取到的数据默认是模型对象(queryset)
- 但是模型对象数据无法直接提供给前端或别的平台使用
- 所以我们需要把数据进行序列化,变成字符串或者json数据,提供给别人。
【2】反序列化
- 把别人提供的数据转换/还原成我们需要的格式。
- 例如:
- 前端js提供过来的json数据
- 对于python而言就是字符串
- 我们需要进行反序列化换成模型类对象
- 这样我们才能把数据保存到数据库中
【3】小结
-
序列化:
- drf称为 read(读取数据)
- 序列化
- queryset --- > json
- 返给前端
-
反序列化:
- drf称为 write(写入数据)
- 反序列化
- 字符串 --- > json
- 接收前端的数据
【二】接口引入
BOOK表为例
【1】准备数据
- models.py
from django.db import models
# Create your models here.
class Books(models.Model):
name = models.CharField(max_length=32)
price = models.IntegerField(null=True, blank=True)
- 数据库迁移
python mange.py makemigrations
python manage.py migrate
- 手动录入两条数据
西游记 888
水浒传 999
【2】创建接口
(1)查询所有图书
-
接口设计
path("books/all/", BookView.as_view()),
-
功能实现
# 查询所有数据 class BookView(View): def get(self, request): back_dict = {"code": "", "msg": "", "result": []} book_list = models.Books.objects.all() for book in book_list: books = {"id": book.pk, "name": book.name, "price": book.price} back_dict["result"].append(books) back_dict["code"] = 1000 back_dict["msg"] = "请求成功!" return JsonResponse(back_dict)
-
响应数据
-
postman
get 请求 携带主键值
http://127.0.0.1:8000/books/all/
{ "code": 1000, "msg": "请求成功!", "result": [ { "id": 1, "name": "西游记", "price": 888 }, { "id": 2, "name": "水浒传", "price": 999 } ] }
-
(2)新增一本图书
-
接口设计
# 增加单本图书 path("books/add/", BookAddView.as_view()),
-
功能实现
# 增加单条数据 class BookAddView(View): def post(self, request): back_dict = {"code": "", "msg": "", "result": []} data = json.loads(request.body) name = data.get('name') price = data.get('price') # print(name,price) models.Books.objects.create(name=name, price=price) back_dict["code"] = 1000 back_dict["msg"] = "添加成功!" return JsonResponse(back_dict)
-
响应数据
-
postman
post 请求 必须带参数 { "name":"小红帽", "price":66 }
http://127.0.0.1:8000/books/add/
{ "code": 1000, "msg": "添加成功!", "result": [] }
-
(3)修改一本图书
-
接口设计
# 修改单本图书 path("books/change/", BookChangeView.as_view()),
-
功能实现
# 修改单本书籍 class BookChangeView(View): def post(self, request, pk): data = json.loads(request.body) name = data.get('name') price = data.get('price') book_obj = models.Books.objects.filter(pk=pk).first() back_dict = {"code": "", "msg": "", "result": []} if book_obj: if name: book_obj.name = name back_dict["msg"] = "书籍名字成功!" if price: book_obj.price = price back_dict["msg"] = "书籍名字和价格成功!" else: book_obj.price = price back_dict["msg"] = "书籍价格成功!" back_dict["code"] = 1000 book_obj.save() else: back_dict["code"] = 1001 back_dict["msg"] = "书籍不存在!" return JsonResponse(back_dict)
-
响应数据
- postman
post 请求 三个参数都可选加 { "pk":"2", "name":"小叮当", "price":"88888" }
http://127.0.0.1:8000/books/change/
{ "code": 1000, "msg": "书籍名字成功!", "result": [] } { "code": 1000, "msg": "书籍名字和价格成功!", "result": [] }
(4)查询一本图书
-
接口设计
path("books/detail/<int:pk>/", BookDetailView.as_view()),
-
功能实现
# 查询单条 数据 class BookDetailView(View): def get(self, request, pk): back_dict = {"code": "", "msg": "", "result": []} book_detail = models.Books.objects.filter(pk=pk).first() books = {"id": book_detail.pk, "name": book_detail.name, "price": book_detail.price} back_dict["code"] = 1000 back_dict["msg"] = "请求成功!" back_dict["result"].append(books) return JsonResponse(back_dict)
-
响应数据
-
postman
- 携带参数:书籍主键值ID
get 请求 携带主键值 必须携带
http://127.0.0.1:8000/books/detail/1/
{ "code": 1000, "msg": "请求成功!", "result": [ { "id": 1, "name": "西游记", "price": 888 } ] }
-
(5)删除一本图书
-
接口设计
# 查询单本图书 path("books/detail/<int:pk>/", BookDetailView.as_view()),
-
功能实现
# 删除单挑数据 class BookDeleteView(View): def get(self, request, pk): back_dict = {"code": "", "msg": "", "result": []} book_obj = models.Books.objects.filter(pk=pk).first() if book_obj: book_obj.delete() back_dict["code"] = 1000 back_dict["msg"] = "删除成功!" else: back_dict["code"] = 1001 back_dict["msg"] = "书籍不存在!" return JsonResponse(back_dict)
-
响应数据
-
postman
get 请求 携带主键值
http://127.0.0.1:8000/books/delete/1/
{ "code": 1000, "msg": "请求成功!", "result": [ { "id": 1, "name": "西游记", "price": 888 }, { "id": 2, "name": "水浒传", "price": 999 } ] }
-
【3】小结
- 模型层
from django.db import models
# Create your models here.
class Books(models.Model):
name = models.CharField(max_length=32)
price = models.IntegerField(null=True, blank=True)
- 路由层
"""day02 URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
from app01.views import BookView, BookDetailView, BookChangeView, BookAddView, BookDeleteView
urlpatterns = [
path('admin/', admin.site.urls),
# 查询全部图书
path("books/all/", BookView.as_view()),
# 查询单本图书
path("books/detail/<int:pk>/", BookDetailView.as_view()),
# 删除单本图书
path("books/delete/<int:pk>/", BookDeleteView.as_view()),
# 增加单本图书
path("books/add/", BookAddView.as_view()),
# 修改单本图书
path("books/change/", BookChangeView.as_view()),
]
- 视图层
import json
from django.http import JsonResponse
from django.shortcuts import render, HttpResponse
from django.views import View
from . import models
from .models import Books
# Create your views here.
# 查询所有数据
class BookView(View):
def get(self, request):
back_dict = {"code": "", "msg": "", "result": []}
book_list = models.Books.objects.all()
for book in book_list:
books = {"id": book.pk, "name": book.name, "price": book.price}
back_dict["result"].append(books)
back_dict["code"] = 1000
back_dict["msg"] = "请求成功!"
return JsonResponse(back_dict)
# 查询单条 数据
class BookDetailView(View):
def get(self, request, pk):
back_dict = {"code": "", "msg": "", "result": []}
book_detail = models.Books.objects.filter(pk=pk).first()
books = {"id": book_detail.pk, "name": book_detail.name, "price": book_detail.price}
back_dict["code"] = 1000
back_dict["msg"] = "请求成功!"
back_dict["result"].append(books)
return JsonResponse(back_dict)
# 删除单挑数据
class BookDeleteView(View):
def get(self, request, pk):
back_dict = {"code": "", "msg": "", "result": []}
book_obj = models.Books.objects.filter(pk=pk).first()
if book_obj:
book_obj.delete()
back_dict["code"] = 1000
back_dict["msg"] = "删除成功!"
else:
back_dict["code"] = 1001
back_dict["msg"] = "书籍不存在!"
return JsonResponse(back_dict)
# 增加单条数据
class BookAddView(View):
def post(self, request):
back_dict = {"code": "", "msg": "", "result": []}
data = json.loads(request.body)
name = data.get('name')
price = data.get('price')
# print(name,price)
models.Books.objects.create(name=name, price=price)
back_dict["code"] = 1000
back_dict["msg"] = "添加成功!"
return JsonResponse(back_dict)
# 修改单本书籍
class BookChangeView(View):
def post(self, request, pk):
data = json.loads(request.body)
pk = data.get('pk')
name = data.get('name')
price = data.get('price')
book_obj = models.Books.objects.filter(pk=pk).first()
back_dict = {"code": "", "msg": "", "result": []}
if book_obj:
if name:
book_obj.name = name
back_dict["msg"] = "书籍名字成功!"
if price:
book_obj.price = price
back_dict["msg"] = "书籍名字和价格成功!"
else:
book_obj.price = price
back_dict["msg"] = "书籍价格成功!"
back_dict["code"] = 1000
book_obj.save()
else:
back_dict["code"] = 1001
back_dict["msg"] = "书籍不存在!"
return JsonResponse(back_dict)
【三】DRF介绍与快速使用
- DRF(Django REST Framework)是一个强大且灵活的开发工具包,用于构建基于Django的Web API。
- 它提供了许多内置的功能和工具,使得编写高质量的API变得更加容易和高效。
【1】使用DRF进行快速开发的简要步骤:
(1)安装DRF:
- 使用pip包管理器,在终端中运行以下命令来安装DRF:
pip install djangorestframework
(2)配置DRF:
- 在你的Django项目的
settings.py
文件中,确保将DRF添加到INSTALLED_APPS
列表中:
INSTALLED_APPS = [
...
'rest_framework',
...
]
(3)创建序列化器(Serializer):
- 序列化器是DRF中一个重要的概念,它将Python对象转换为JSON等可被传输的格式,并可以反序列化接收到的数据。
- 在你的应用程序中创建一个名为
serializers.py
的文件,并定义你的序列化器类。 - 一个示例:
from rest_framework import serializers
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = '__all__'
- 在这个示例中,我们使用
ModelSerializer
来自动创建序列化器类。
(4)创建视图(View):
- 在你的应用程序中创建一个名为
views.py
的文件,并定义视图类。 - 一个示例:
from rest_framework import generics
from .serializers import MyModelSerializer
from .models import MyModel
class MyModelListView(generics.ListCreateAPIView):
queryset = MyModel.objects.all()
serializer_class = MyModelSerializer
- 在这个示例中,我们使用
ListCreateAPIView
来创建一个支持列表和创建操作的通用视图。
(5)配置URL路由:
- 在你的应用程序的
urls.py
文件中,定义DRF的URL路由。 - 一个示例:
from django.urls import path
from .views import MyModelListView
urlpatterns = [
path('mymodels/', MyModelListView.as_view(), name='mymodel-list'),
]
- 通过上述配置,当访问
/mymodels/
时,将会调用MyModelListView
视图。
- 以上是使用DRF进行快速开发的简要步骤。
- 当然,DRF还提供了许多其他功能,如认证、权限控制、过滤器、分页等,你可以根据自己的需求进一步学习和定制。
- 你可以参考官方文档以获得更详细的信息:
【2】快速使用
(1)路由
from app01.views import BookView
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
router.register('books', BookView, 'books')
urlpatterns = [
]
urlpatterns += router.urls
(2)视图
from .serializer import BookSerializer
from rest_framework.viewsets import ModelViewSet
class BookView(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
(3)序列化类
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
【补充】小问题
- django 中有个app
- djangorestframework:drf
- 帮助我们,快速实现符合resful规范的接口
【补充】下载兼容性问题
- 安装命令
pip3.8 install djangorestframework==稍微将版本
-
如果你是django2
- 直接这样装,装最新drf,他们不匹配
- ---》pip会自动把django卸载,安装最新django,安装最新drf
-
django3 ,这样没有任何问题
强制更新
pip3.8 install djangorestframework --upgrade
【补充】包扩展
- 如果写了一个包,或app,想给别人用
- ---》把你写的包,放到pypi上别人pip install
- 安装---》使用
【四】DRF之APIView源码分析
【1】基于APIView的5个接口
(1)视图类
from rest_framework.views import APIView # APIView继承了djagno原来的View
from .serializer import BookSerializer
from rest_framework.response import Response
class BookView(APIView):
# 查询所有
def get(self, request):
book_list = Book.objects.all()
# drf提供了序列化类(先别关注)
ser = BookSerializer(instance=book_list, many=True) # 序列化
return Response({'code': 100, 'msg': '成功', 'result': ser.data})
def post(self, request):
ser = BookSerializer(data=request.data) # 反序列化
if ser.is_valid(): # 数据校验---》有些不合法的禁止
ser.save() # 保存到数据库中
return Response({'code': 100, 'msg': '成功'})
class BookDetailView(APIView):
# 查询单条
def get(self, request, pk):
book = Book.objects.filter(pk=pk).first()
ser = BookSerializer(instance=book, many=False) # 序列化
return Response({'code': 100, 'msg': '成功', 'result': ser.data})
# 修改一条
def put(self, request, pk):
book = Book.objects.filter(pk=pk).first()
ser = BookSerializer(instance=book, data=request.data) # 反序列化
if ser.is_valid(): # 数据校验---》有些不合法的禁止
ser.save() # 保存到数据库中
return Response({'code': 100, 'msg': '成功'})
def delete(self, request, pk):
Book.objects.filter(pk=pk).delete()
return Response({'code': 100, 'msg': '删除成功'})
(2)序列化类
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
(3)路由
urlpatterns = [
path('books/', BookView.as_view()),
path('books/<int:pk>/', BookDetailView.as_view()),
]
【2】CBV源码分析
(1)cbv写法:
- 视图中写视图类,继承View,写跟请求方式同名的方法
class BookView(View):
def get(self,request):
return 四件套
- 在路径用写
path('books/', BookView.as_view())
- 如上写法,为什么能够执行
(2)前置条件
-
前端请求,一旦路径匹配成功,就会执行
- BookView.as_view()(request传入,)
-
入口在
- BookView.as_view()--->执行结果---》View中有个as_view类的绑定方法
@classmethod
def as_view(cls, **initkwargs):
def view(request, *args, **kwargs):
self = cls(**initkwargs)
res=self.dispatch(request, *args, **kwargs)
return res
return view
- 执行结果是view 的内存地址: 请求来了,执行view(request)
path('books/', view)
- 执行 View类中的as_view方法中的内层的view函数,路由匹配成功,本质是在执行
self.dispatch(request, *args, **kwargs)
-
self是谁的对象?
- BookView的对象
-
去BookView中dispatch,找不到
- 去父类,View中找到了
-
View这个类的dispatch
def dispatch(self, request, *args, **kwargs):
# request.method.lower() 如果是get请求, ‘get’ 在这个列表里面
if request.method.lower() in self.http_method_names:
# handler=getattr(BookView的对象,'get')
# handler就是BookView类中的get方法
handler = getattr(self, request.method.lower())
else:
handler = self.http_method_not_allowed
# 执行 BookView类中的get方法 (request)
return handler(request, *args, **kwargs)
- 最终本质跟写fbv的执行流程一样
(3)最终结论
- 什么请求方式,就会执行视图类中的什么方法
【3】APIView执行流程分析
- 有了drf,后期都写CBV,都是继承APIView及其子类
-
执行流程:
-
入口
path('books/', BookView.as_view())
# ---》请求来了,执行BookView.as_view()(request)
- as_view 是谁的?
- APIView的as_view
@classmethod
def as_view(cls, **initkwargs):
# super()代指的是:父类对象 View类的对象
# View的as_view(**initkwargs)----》执行结果是view,是View类的as_view方法中的view
view = super().as_view(**initkwargs)
view=csrf_exempt(view) # 局部禁用csrf,
return view
-
path('books/', View类的as_view中的view,只是去掉了csrf的认证)
-
请求来了
- 执行
- 【View类的as_view中的view,只是去掉了csrf的认证(request)】
-
执行:
- self.dispatch(request, *args, **kwargs)
- self要从根上找
-
self.dispatch
- 是APIView的dispatch,源码如下
def dispatch(self, request, *args, **kwargs):
# request 是新的request, request是老的request
request = self.initialize_request(request, *args, **kwargs)
self.request = request
try:
# 执行了认证,权限和频率
self.initial(request, *args, **kwargs)
# 在执行视图类方法之前,去掉了csrf认证,包装了新的request,执行了认证频率和权限
#### 执行请求方式字符串对应的方法
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
【4】总结
-
以后只要继承APIView的所有视图类的方法,都没有csrf的校验了
-
以后只要继承APIView的所有视图类的方法 中的request是新的request了
-
在执行视图类的方法之前,执行了三大认证(认证,权限,频率)
-
期间除了各种错误,都会被异常捕获,统一处理
【补充】装饰器语法糖
fbv,局部禁用csrf,如何写?
@csrf_exempt
def index(request):
pass
- 本质原理是(装饰器本质):
- index=csrf_exempt(index)
【补充】FBV装饰器
- 写一个装饰器,装饰在fbv上
- 这个fbv可以接受前端的编码格式可以是urlencoded,form-data,json
- 取数据,都是从request.data中取
- 您可以使用Python编写一个装饰器函数,将其应用于使用函数基础视图(FBV)的端点。
- 这个装饰器可以处理不同的编码格式(urlencoded、form-data、json),并从
request.data
中提取数据。
下面是一个示例实现:
from functools import wraps
from django.http import QueryDict
import json
def parse_request_data(view_func):
@wraps(view_func)
def wrapper(request, *args, **kwargs):
if request.content_type == 'application/x-www-form-urlencoded':
# 处理urlencoded编码格式
data = request.POST
elif request.content_type.startswith('multipart/form-data'):
# 处理form-data编码格式
data = request.FILES
elif request.content_type == 'application/json':
# 处理json编码格式
try:
data = json.loads(request.body)
except json.JSONDecodeError:
data = {}
else:
# 其他编码格式处理(可根据实际情况进行扩展)
data = {}
request.data = data
return view_func(request, *args, **kwargs)
return wrapper
- 使用这个装饰器,您可以将其应用于需要处理不同编码格式数据的FBV上,例如:
@parse_request_data
def your_view(request):
# 在这里使用request.data来访问解析后的数据
# 例如,如果是urlencoded编码格式,可以通过request.data['key']获取值
return HttpResponse("response")
- 在上述示例中
parse_request_data
装饰器会根据请求的content_type
来解析不同的编码格式- 并将解析后的数据存储在
request.data
中,以供视图函数使用。
本文来自博客园,作者:Chimengmeng,转载请注明原文链接:https://www.cnblogs.com/dream-ze/p/17593136.html