层层递进:深度理解Django DRF RESTful API接口实现
视频参考:https://www.bilibili.com/video/BV1Dm4y1c7QQ?spm_id_from=333.788.videopod.episodes&vd_source=8b9f3b9649de70beedaad88d5f8d3d0d&p=10
层层递进:四种方式
方式1:函数式编程 Function Based View
方式2:类视图 Classed Based View
方式3:通用类视图 Generic Classed Based View
方式4:DRF的视图集 viewsets
Views:
from django.shortcuts import render # Create your views here. import json from django.http import HttpResponse, JsonResponse from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_exempt from django.views import View from rest_framework import status from rest_framework.decorators import api_view from rest_framework import generics from rest_framework.response import Response from rest_framework.views import APIView from rest_framework import viewsets from course.models import Course from course.serializers import CourseSerializer course_dict = { "name": "课程名称", "introduction": "课程介绍", "price": 999 } @csrf_exempt def course_list(request): """ 方式1:函数式变成 Django 原生的方式编写接口 对于post请求 需要使用 csrf_exempt 装饰器来取消 csrf问题 """ if request.method == 'GET': # 等同于 return JsonResponse(course_dict) return HttpResponse(json.dumps(course_dict), content_type='application/json') elif request.method == 'POST': crs = json.loads(request.body.decode('utf-8')) # 等同于 return JsonResponse(crs, safe=False) # return HttpResponse(json.dumps(crs), content_type='application/json') @method_decorator(csrf_exempt, name='dispatch') class CourseView(View): """ 方式2:类视图 Classed Based View 原生Django的view 对于post请求,可以通过 @method_decorator(csrf_exempt, name='dispatch') 或 在 def post 方法上添加@csrf_exempt 解决csrf问题 """ def get(self, request): return JsonResponse(course_dict) # @csrf_exempt def post(self, request): crs = json.loads(request.body.decode('utf-8')) return HttpResponse(json.dumps(course_dict), content_type='application/json') """ 可以看出:通过 JsonResponse HttpResponse 也能实现API接口,但是很多东西都需要自己去实现 比如说:数据分页,数据排序,接口认证,接口权限,接口限流等 """ """ 此时 我们引入 DRF """ @api_view(['GET', 'POST']) def drf_course_list(request): """ 方式1:函数式编程 Function Based View 查询、新增 课程信息 """ if request.method == 'GET': # 序列化 s = CourseSerializer(instance=Course.objects.all(), many=True) # DRF已经封装好了一整套状态码 return Response(data=s.data, status=status.HTTP_200_OK) elif request.method == 'POST': # 反序列化 ---- 反序列化时就需要进行数据校验 # partial=True 表示部分更新 也就是说前端可以只传递部分字段 比模型字段少 只对非必填的字段生效,必填字段不传递也会走到400 s = CourseSerializer(data=request.data, partial=True) if s.is_valid(): s.save(teacher=request.user) # 新增成功的状态码是 201 return Response(data=s.data, status=status.HTTP_201_CREATED) # 校验不通过 返回400 return Response(data=s.errors, status=status.HTTP_400_BAD_REQUEST) @api_view(['GET', 'PUT', "DELETE"]) def drf_course_detail(request, pk): """ 方式1:函数式编程 Function Based View 获取、更新、删除一个课程 DRF已经封装了状态码,严格遵循 """ try: course = Course.objects.get(pk=pk) except Course.DoesNotExist: return Response(data={"msg": "没有此课程信息"}, status=status.HTTP_404_NOT_FOUND) if request.method == 'GET': s = CourseSerializer(instance=course) # 查询单条数据 用 200 return Response(data=s.data, status=status.HTTP_200_OK) elif request.method == 'PUT': # 更新操作时 不需要设置 teacher=user s = CourseSerializer(instance=course, data=request.data, partial=True) if s.is_valid(): s.save() # 更新成功用 200 return Response(data=s.data, status=status.HTTP_200_OK) # 校验不通过 返回400 return Response(data=s.errors, status=status.HTTP_400_BAD_REQUEST) elif request.method == 'DELETE': course.delete() # 删除成功用 204 return Response(status=status.HTTP_204_NO_CONTENT) class DrfCourseView(APIView): """ 方式2:类视图 Classed Based View 查询、新增课程信息 """ def get(self, request): """ 查看课程信息 """ queryset = Course.objects.all() # instance 是 后端model查询的 数据集 s = CourseSerializer(instance=queryset, many=True) return Response(s.data, status=status.HTTP_200_OK) def post(self, request): """ 新增课程信息 """ # data 是前端传递的数据, return前 先调用 is_valid 方法 s = CourseSerializer(data=request.data, partial=True) if s.is_valid(): s.save(teacher=self.request.user) return Response(data=s.data, status=status.HTTP_201_CREATED) return Response(data=s.errors, status=status.HTTP_400_BAD_REQUEST) class DrfCourseDetailView(APIView): """ 方式2:类视图 Classed Based View 查询、更新、删除 一条课程信息 """ def get_object(self, pk): try: return Course.objects.get(pk=pk) except Course.DoesNotExist: return def get(self, request, pk): """ 查询 一条课程信息 """ obj = self.get_object(pk) if not obj: return Response(data={"msg": "课程信息不存在"}, status=status.HTTP_404_NOT_FOUND) s = CourseSerializer(instance=obj) return Response(data=s.data, status=status.HTTP_200_OK) def put(self, request, pk): """ 更新 一条课程信息:严格按照model字段进行全量更新 """ obj = self.get_object(pk) if not obj: return Response(data={"msg": "课程信息不存在"}, status=status.HTTP_404_NOT_FOUND) # 因为没有实现patch方法,所以此处暂时允许partial=True 实现部分更新 s = CourseSerializer(instance=obj, data=request.data, partial=True) if s.is_valid(): s.save() return Response(data=s.data, status=status.HTTP_200_OK) return Response(data=s.errors, status=status.HTTP_400_BAD_REQUEST) # def patch(self, request, pk): # """ # 部分 更新 一条课程信息 # """ # obj = self.get_object(pk) # if not obj: # return Response(data={"msg": "课程信息不存在"}, status=status.HTTP_404_NOT_FOUND) # s = CourseSerializer(instance=obj, data=request.data, partial=True) # if s.is_valid(): # s.save() # return Response(data=s.data, status=status.HTTP_200_OK) # return Response(data=s.errors, status=status.HTTP_400_BAD_REQUEST) def delete(self, request, pk): """ 删除 一条课程信息 """ obj = self.get_object(pk) if not obj: return Response(data={"msg": "课程信息不存在"}, status=status.HTTP_404_NOT_FOUND) obj.delete() return Response(status=status.HTTP_204_NO_CONTENT) class GDrfCourseView(generics.ListCreateAPIView): """ 方式3:通用类视图 Generic Classed Based View 继承于 generics.ListCreateAPIView 实现 查询、新增 课程信息 """ queryset = Course.objects.all() serializer_class = CourseSerializer def perform_create(self, serializer): # 由于用户是我们自己需要加的外键,所以需要重写create方法,把teacher加进去 serializer.save(teacher=self.request.user) class GDrfCourseDetailView(generics.RetrieveUpdateDestroyAPIView): """ 方式3:通用类视图 Generic Classed Based View 继承于 generics.RetrieveUpdateDestroyAPIView 实现 查询、新增、删除 一条课程信息 """ queryset = Course.objects.all() serializer_class = CourseSerializer class CourseViewSet(viewsets.ModelViewSet): """ 方式4:DRF的视图集 viewsets 实现 增 删 改 查 所有功能 从 方式 1 2 3 一路实现过来,我们发现 我们始终需要将 请求分成几个方法 多个类。而ViewSets就是把增删改查融合到一个类中 FastAPI 也有类似的实现 """ queryset = Course.objects.all() serializer_class = CourseSerializer def perform_create(self, serializer): serializer.save(teacher=self.request.user)
models:
from django.db import models from django.conf import settings # Create your models here. class Course(models.Model): # 不需要添加id字段,django migrate时会自动添加 # help_text form表单的提示等可以使用 name = models.CharField(max_length=255, unique=True, verbose_name="课程名称", help_text="课程名称") introduction = models.TextField(help_text="课程介绍", verbose_name="课程介绍") price = models.DecimalField(max_digits=6, decimal_places=2, help_text="课程价格", verbose_name="课程价格") # 讲师用户直接关联django的用户 teacher = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, help_text="课程讲师", verbose_name="课程讲师") created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") updated_at = models.DateTimeField(auto_now=True, verbose_name="更新时间") # 添加元数据 class Meta: verbose_name = "课程信息" verbose_name_plural = verbose_name # 增加排序 ordering = ['price', "-created_at"] def __str__(self): return self.name
urls:
from django.urls import path, include from rest_framework.routers import DefaultRouter from course import views # 方式4:DRF的视图集 viewsets router = DefaultRouter() router.register(prefix='viewsets', viewset=views.CourseViewSet) urlpatterns = [ # 方式1:函数式编程 Function Based View path('fbv/list/', views.drf_course_list, name='fbv_course_list'), path('fbv/detail/<int:pk>/', views.drf_course_detail, name='fbv_course_detail'), # 方式2:类视图 Classed Based View path('cbv/list/', views.DrfCourseView.as_view(), name='cbv_course_list'), path('cbv/detail/<int:pk>/', views.DrfCourseDetailView.as_view(), name='cbv_course_detail'), # 方式3:通用类视图 Generic Classed Based View path('gbv/list/', views.GDrfCourseView.as_view(), name='gbv_course_list'), path('gbv/detail/<int:pk>/', views.GDrfCourseDetailView.as_view(), name='gbv_course_detail'), # 方式4:DRF的视图集 viewsets # # 通过如下方式配置路由时,因为 viewsets/ 和 viewsets/<int:pk>/ 共用一个view,所以必须指明路由哪些ViewSets的哪些方法 # # {'get': 'list'} 含义:HTTP - get请求访问 viewsets.ModelViewSet.ListModelMixin.list 方法 # path("viewsets/", views.CourseViewSet.as_view({'get': 'list', "post": "create"}), name='viewsets_list'), # path("viewsets/<int:pk>/", views.CourseViewSet.as_view( # {'get': 'retrieve', "put": "update", "patch": "partial_update", "delete": "destroy"}), name='viewsets_detail'), path("", include(router.urls)), ]
序列化类:
""" 序列化能帮我们做什么? 1. 验证处理 request.data 前端提交个过来的数据,要能进行验证 2. 验证器的参数 3. 同时序列化多个对象 4. 序列化的过程中添加上下文 比如 模型中不涉及的字段 5. 无效的数据异常处理 """ from django import forms from .models import Course from rest_framework import serializers from django.contrib.auth.models import User class CourseForm(forms.ModelForm): class Meta: model = Course fields = '__all__' class UserSerializer(serializers.ModelSerializer): class Meta: model = User fields = '__all__' """ 可以看到:DRF的序列化和 from django import forms 非常相似 DRF 可以实现 返回字段并带url,每个url都能访问到字段的详情页 """ class CourseSerializer2(serializers.HyperlinkedModelSerializer): # 直接序列化的话,teacher字段是获取的user.id ,如果期望 获取到用户名称 则可以使用readonlyfield实现 teacher = serializers.ReadOnlyField(source='teacher.username') # 外键字段 只读 class Meta: model = Course # url 是默认值,可在settings.py中设置 URL_FIELD_NAME 使全局生效 fields = ('id', 'url', 'name', 'introduction', 'teacher', 'price') class CourseSerializer(serializers.ModelSerializer): # 直接序列化的话,teacher字段是获取的user.id ,如果期望 获取到用户名称 则可以使用readonlyfield实现 teacher = serializers.ReadOnlyField(source='teacher.username') # 外键字段 只读 class Meta: model = Course # exclude = ('name',) # 排除在外的字段 fields = ('id', 'name', 'introduction', 'teacher', 'price') # 序列化的字段 # fields = '__all__' depth = 2 # 设置外键关联 深度
settings:
# DRF 全局 配置 REST_FRAMEWORK = { 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema', 'DEFAULT_PERMISSION_CLASSES': ( # 权限 'rest_framework.permissions.IsAuthenticated', # 要求登录 ), 'DEFAULT_AUTHENTICATION_CLASSES': ( # 认证 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.TokenAuthentication', ), 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 5, 'DATETIME_FORMAT': '%Y-%m-%d %H:%M:%S', # 'DEFAULT_RENDERER_CLASSES': ( # 默认如此 可以不配置 # 'rest_framework.renderers.JSONRenderer', # 'rest_framework.renderers.BrowsableAPIRenderer', # ), 'DEFAULT_PARSER_CLASSES': ( # 解析 request.data 'rest_framework.parsers.JSONParser', 'rest_framework.parsers.FormParser', 'rest_framework.parsers.MultiPartParser', ), # 'URL_FIELD_NAME': 'link', # serializers.HyperlinkedModelSerializer序列化 把 默认的url 改为 link }
通过层层递进的实现,DRF将增删改查 简化到仅有几行。
其他细节请通过视频学习。

浙公网安备 33010602011771号