DRF
0x01 概述
-
DRF 是 Django REST Framework 的缩写,有利于实现前后端分离项目(Django 基础)
- DRF 官网链接
- DRF 相关包
- coreapi:自动生成 API 文档
- Markdown:解析 Markdown 语法
- Pygments:语法高亮
- django-filter:支持 Django 过滤器
- django-guardian:实现 DRF 对象级权限控制
-
前后端分离
-
交互形式
graph LR A(前端/客户端)--HTTP Method-->B(RESTful API) B-->C(后端/服务器) C--JSON or XML-->B B-->A -
开发模式
graph LR 提出需求-->约定接口规范和数据格式 -->前后端并行开发 -->前后端对接 -->前端调试效果 -->集成 -->交付 -
数据接口规范
graph LR A(定制接口<br/>确定规范)-->B(前端开发<br/>模拟数据) -->C(连调<br/>校验格式) -->D(提测<br/>自动化测试) A-->E(后端开发<br/>数据自测) -->C
-
-
Restful API 最佳实践详解
协议、域名、版本、路径、HTTP 动词、过滤信息、状态码、错误处理、返回结果、Hypermedia API
0x02 创建项目
-
创建 Django 项目
-
修改 settings.py
ALLOWED_HOSTS = ['*'] # ... STATIC_ROOT = os.path.join(BASE_DIR, "static") STATICFILES_DIRS = [ os.path.join(BASE_DIR, "staticfiles") ]
-
安装 DRF
pip install djangorestframework
-
在 settings.py 中配置 DRF
INSTALLED_APPS = [ # ... 'rest_framework', 'rest_framework.authtoken', ] # ... REST_FRAMEWORK = { # 分页器 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 50, # 时间字段 'DATETIME_FORMAT': '%Y-%m-%d %H:%M:%S', # Response 对象参数 'DEFAULT_RENDER_CLASSES': [ 'rest_framework.renders.JSONRenderer', 'rest_framework.renders.BrowsableRenderer', ], # Request 解析器 'DEFAULT_PARSER_CLASSES': [ 'rest_framework.parsers.JSONParser', 'rest_framework.parsers.FormParser', 'rest_framework.parsers.MultiPartParser', ], # 权限 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAuthenticated', ], # 认证 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.TokenAuthentication', ] }
-
在 ./urls.py 配置根路由
from django.urls import path, include urlpatterns = [ path('api/', include('rest_framework.urls')), ]
0x03 添加模型
-
在 models.py 中创建新模型
from django.db import models class User(models.Model): username = models.CharField(max_length=30, unique=True) password = models.CharField(max_length=255) age = models.IntegerField(default=0) class Meta: verbose_name = '用户表' verbose_name_plural = verbose_name ordering = ('age',) def __str__(self): return self.username
-
在 admin.py 中配置模型
from django.contrib import admin from App.models import * @admin.register(User) class UserAdmin(admin.ModelAdmin): # 需要显示的字段 list_display = ('username', 'password', 'age') # 可以搜索的字段 search_fields = list_display # 需要过滤的字段 list_filter = list_display
0x04 序列化
-
序列化:将 Django 的
queryset
数据或instance
数据转换为 JSON 数据- 反序列化:将 JSON 数据转换为
queryset
数据或instance
数据
- 反序列化:将 JSON 数据转换为
-
序列化作用:
- 验证处理 request.data
- 验证器的参数
- 同时序列化多个对象
- 序列化过程中添加上下文
- 对无效的数据进行异常处理
-
继承序列化模型类 ModelSerializer
-
在 App 目录下新建 serializers.py
from rest_framework import serializers from App.models import * class UserSerializer(serializers.ModelSerializer): # 外键字段 foreignKeyName = serializers.ReadOnlyField(source='Table.Key') class Meta: model = User # 排除指定字段 # exclude = ('id',) # 设置需要显示的字段 fields = '__all__' # 指定遍历深度 depth = 2
-
-
实现携带 URL 的 HyperlinkedModelSerializer
-
修改 serializers.py
class UserSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = User # url 参数名是默认值,可在 settings.py 的 URL_FIELD_NAME 中修改 fields = ('url',)
-
0x05 API 接口开发
(1)RESTful API 接口
-
在 App/views.py 中,RESTful API开发方法
-
Function Based View,FBV 函数式编程,Django 原生方法
import json from django.http import HttpResponse, JsonResponse from django.views.decorators.csrf import csrf_exempt @csrf_exempt def user_list(request): datas = { 'username': '111', 'age': '20' } if request.method == 'GET': return JsonResponse(datas) if request.method == 'POST': user = json.loads(request.body.decode('utf-8')) return HttpResponse(json.dumps(user), content_type='application/json')
- 装饰器
csrf_exempt
用于对 POST 请求取消 CSRF 的限制
- 装饰器
-
Classed Based View,CBV 类视图
import json from django.http import HttpResponse, JsonResponse from django.views.decorators.csrf import csrf_exempt from django.views import View class UserList(View): def get(self, request): datas = { 'username': '111', 'age': '20' } return JsonResponse(datas) @csrf_exempt def post(self, request): user = json.loads(request.body.decode('utf-8')) return HttpResponse(json.dumps(user), content_type='application/json')
-
思路:对不同的请求方法,用对于的函数处理
-
对于装饰器的使用可以放在类外:
from django.utils.decorators import method_decorator @method_decorator(csrf_exempt, name='dispatch') class UserList(View): def get(self, request): # ... def post(self, request): # ...
-
-
Generic Classed Based View,GCBV 通用类视图
-
viewsets,DRF 视图集
-
(2)FBV
装饰器 api_view
-
views.py
from rest_framework.decorators import api_view from rest_framework.response import Response from rest_framework import status from App.models import * from App.serializers import * @api_view(["GET", "POST"]) def user_list(request): if request.method == "GET": userSerializer = UserSerializer(instance=User.objects.all(), many=True) return Response(data=userSerializer.data, status=status.HTTP_200_OK) elif request.method == "POST": userSerializer = UserSerializer(data=request.data, partial=True) if userSerializer.is_valid(): userSerializer.save() return Response(data=userSerializer.data, status=status.HTTP_201_CREATED) return Response(userSerializer.errors, status=status.HTTP_400_BAD_REQUEST)
-
对信息进行获取、更新、删除
@api_view(["GET", "PUT", "DELETE"]) def user_detail(request, pk): try: user = User.objects.get(pk=pk) except User.DoesNotExist: return Response(data={"msg": "Not Exist"}, status=status.HTTP_404_NOT_FOUND) else: if request.method == "GET": serializer = UserSerializer(instance=user) return Response(data=serializer.data, status=status.HTTP_200_OK) elif request.method == "PUT": serializer = UserSerializer(instance=user, data=request.data) if serializer.is_valid: serializer.save() return Response(data=serializer.data, status=status.HTTP_200_OK) return Response(data=serializer.data, status=status.HTTP_400_BAD_REQUEST) elif request.method == "DELETE": user.delete() return Response(status=status.HTTP_204_NO_CONTENT)
-
-
子路由 App/urls.py
from django.urls import path, include from App.views import * urlpatterns = [ path('fbv/list/', user_list, name='userList'), path('fbv/detail/<int:pk>/', user_detail, name='userDetail'), ]
-
根路由 ./urls.py
from django.urls import path, include urlpatterns = [ path('user/', include('App.urls')) ]
(3)CBV
需要导入 APIView
-
views.py
from rest_framework.views import APIView class UserList(APIView): def get(self, request): queryset = User.objects.all() serializer = UserSerializer(instance=queryset, many=True) return Response(serializer.data, status=status.HTTP_200_OK) def post(self, request): serializer = UserSerializer(data=request.data) if serializer.is_valid(): serializer.save(user = self.request.user) return Response(data=serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
-
对信息进行获取、更新、删除
class UserDetail(APIView): @staticmethod def get_object(pk): try: return User.objects.all(pk=pk) except User.DoesNotExist: return def get(self, request, pk): obj = self.get_object(pk) if not obj: return Response(data={"msg": "Not Exist"}, status=status.HTTP_404_NOT_FOUND) serializer = UserSerializer(instance=obj) def put(self, request, pk): obj = self.get_object(pk) if not obj: return Response(data={"msg": "Not Exist"}, status=status.HTTP_404_NOT_FOUND) serializer = UserSerializer(instance=obj) if serializer.is_valid(): serializer.save() return Response(data=serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) def delete(self, request, pk): obj = self.get_object(pk) if not obj: return Response(data={"msg": "Not Exist"}, status=status.HTTP_404_NOT_FOUND) obj.delete() return Response(status=status.HTTP_204_NO_CONTENT)
-
-
子路由 App/urls.py
from django.urls import path from App import views urlpatterns = [ path('cbv/list/', views.UserList.as_view(), name='userList'), path('cbv/detail/<int:pk>', views.UserDetail.as_view(), name='userDetail'), ]
(4)Generic Classed Based View
需要导入 generics
-
views.py
from rest_framework import generics class GUserList(generics.ListCreateAPIView): # 变量名继承于 generics,名称固定 queryset = User.objects.all() serializer_class = UserSerializer # 重写方法 def perform_create(self, serializer): serializer.save(user=self.request.user)
-
子路由 App/urls.py
from App import views urlpatterns = [ path('gcbv/list/', views.GUserList.as_view(), name='userList'), path('gcbv/detail/<int:pk>', views.GUserDetail.as_view(), name='userDetail'), ]
(5)viewsets
-
views.py
from rest_framework import viewsets class UserViewSet(viewsets.ModelViewSet): # 忽略权限、认证、限流等操作 queryset = User.objects.all() serializer_class = UserSerializer def perform_create(self, serializer): serializer.save(user=self.request.user)
-
子路由 App/urls.py
-
方法一
from App import views urlpatterns = [ path('viewsets/', views.UserViewSet.as_view( # 传入字典,key 为 HTTP 方法,value 为视图集的 Mixin 中的方法 {"get": "list", "post": "create"} ), name='userViewSetList'), path('viewsets/<int:pk>/', views.UserViewSet.as_view( # put:全部更新 # patch:部分更新 {"get": "retrieve", "put": "update", "patch": "partial_update", "delete": "destory"} ), name='userViewSetDetail'), ]
-
方法二
from django.urls import path, include from App import views from rest_framework.routers import DefaultRouter router = DefaultRouter() router.register(prefix="viewsets", viewset=views.UserViewSet) urlpatterns = [ path("", include(router.urls)) ]
-
0x06 认证与权限
认证:用户登录的身份信息校验
权限:已登录用户访问接口的校验
(1)认证方式
- BasicAuthentication
- 基本账号密码验证,适用于测试开发环境,不建议用于生产环境
- SessionAuthentication
- 使用基本 Django 后端会话验证,需要 CSRF_Token
- TokenAuthentication
(2)Token 生成
-
在 settings.py 中进行配置
INSTALLED_APPS = [ 'rest_framework.authtoken', ]
-
在 views.py 中
-
生成 Token
# 信号 from django.db.models.signals import post_save # 接收 from django.dispatch import receiver # 模型 from django.conf import settings from rest_framework.authtoken.models import Token @receiver(post_save, sender=settings.AUTH_USER_MODEL) def generate_token(sender, instance=None, created=False, **kwargs): if created: Token.objects.create(user=instance)
-
绑定认证方式
from rest_framework.decorators import api_view, authentication_classes from rest_framework.authentication import BasicAuthentication @authentication_classes((BasicAuthentication, )) def user_list(request): # ...
-
-
在根路由 urls.py 中配置路由
from rest_framework.authtoken import views urlpatterns = [ # 获取 Token 的接口 path('api-token-auth/', views.obtain_auth_token),
-
使用 POST 方法获取 Token
(3)权限控制
a. 常用的权限类与使用
-
常见权限类
IsAuthenticatedOrReadOnly
:已登录可以增删改查,未登录仅可查询IsAuthenticated
:已登录可以增删改查,未登录不允许任何操作IsAdminUser
:仅管理员请求AllowAny
:允许任何请求
-
使用
-
views.py
from rest_framework.decorators import permission_classes from rest_framework.permissions import IsAuthenticated # FBV @permission_classes((IsAuthenticated, )) def user_list(request): # ... # GCBV class GUserList(generics.ListCreateAPIView): permission_classes = IsAuthenticated
-
b. 自定义对象级别权限
-
新建文件 App/permission.py
from rest_framework import permissions class IsOwnerReadOnly(permissions.BasePermission): # 仅允许对象的所有者进行编辑,其他人只读 def has_object_permission(self, request, view, obj): if request.method in permissions.SAFE_METHODS: return True return request.user == obj.user
-
导入 views.py
from App.permission import IsOwnerReadOnly class GUserDetail(generics.RetrieveUpdateDestroyAPIView): permission_classes = (IsAuthenticated, IsOwnerReadOnly)
0x07 API 接口文档
(1)生成
-
安装 pyyaml、uritemplate
-
settings.py
REST_FRAMEWORK = { 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema', }
-
根路由 urls.py
from rest_framework.schemas import get_schema_view # 视图概要 schema_view = get_schema_view(title="API DOCUMENTATION", description="DRF") urlpatterns = [ path('schema/', schema_view), ]
(2)配置 coreapi
-
安装 coreapi
-
修改 settings.py
REST_FRAMEWORK = { 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema', }
-
修改根路由 urls.py
from rest_framework.documentation import include_docs_urls urlpatterns = [ path('docs/', include_docs_urls(title="API DOCUMENTATION", description="DRF")), ]
-End-