层层递进:深度理解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
方式4DRF的视图集 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将增删改查 简化到仅有几行。

其他细节请通过视频学习。

posted @ 2026-01-11 19:21  丶会飞的羊  阅读(3)  评论(0)    收藏  举报