[学习笔记]3小时搞定DRF框架 | Django REST framework前后端分离框架实践

news2024/11/19 15:21:58

3小时搞定DRF框架 | Django REST framework前后端分离框架实践

前言(基于INSCODE AI助手)

DRF(全称Django REST framework)是一个用于构建 Web API 的强力工具集,是一个基于Django的Python Web框架,它为开发人员提供了一套快速开发 RESTful API 的工具,它能够自动化 API 可视化、文档化,实现接口的自动化测试以及自动化的API路由、序列化、视图、验证、分页、版本管理、认证等等功能。DRF简化了API的开发,并提供了一系列的工具来构建高质量的API。

1.1:课程介绍

学习目标:使用DRF开发RESTful API接口
学习内容:序列化、视图集、路由、认证、权限
课程效果:DRF的多种视图实现课程信息的增删改查

2.1: 从四个方面理解前后端分离

  • 交互形式
    在这里插入图片描述

RESTful API是i前后端约定好的接口规范

  • 代码组织方式
    前后端不分离:django模板语法
    前后端半分离
    前后端分离
  • 开发模式
    前后端不分离项目开发流程:
    在这里插入图片描述
    前后端分离项目开发流程:
    在这里插入图片描述
  • 数据接口规范流程
    在这里插入图片描述

2.2 深入理解什么是RESTful API

可以学习别人的接口规范学习。
例如:
github-API
在这里插入图片描述
在这里插入图片描述
深入理解什么是RESTful API

2.3 Pycharm搭建项目开发环境

  • Settings.py
# 因为RESTframework是有前端文件的,后面使用时会全部放到这个目录
STATIC_ROOT = os.pardir.join(BASE_DIR, "static")
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "staticfiles")
]
  • manage.py
makemigrations
migrate
createsuperuser

在这里插入图片描述

2.4 framework介绍和安装

django rest framework官网:https://www.django-rest-framework.org/

按照文档执行下面步骤:

  • 1.安装相应包
pip install djangorestframework
pip install markdown       # Markdown support for the browsable API.
pip install django-filter  # Filtering support
  • 在settings.py中修改配置
# Add 'rest_framework' to your INSTALLED_APPS setting.

INSTALLED_APPS = [
    ...
    "rest_framework", # 用于开发RESTful API
    'rest_framework.authtoken', # DRF自带的Token认证
]

对于rest framework所有相关配置都放在settings的一个字典里面

# DRF的全局配置
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',  # 自带分页类
    'PAGE_SIZE': 50,  # 每页数据量
    'DATETIME_FORMAT': '%Y-%m-%d %H:%M:%S',  # 时间显示格式
    'DEFAULT_RENDER_CLASSES': [
        'rest_framework.renders.JSONRenderer',
        'rest_framework.renders.BrowsableAPIRenderer',
    ],  # 返回response对象所用的类
    'DEFAULT_PARSER_CLASSES': [
        'rest_framework.parsers.JSONParser',
        'rest_framework.parsers.FormParser',
        'rest_framework.parsers.MultiPartParser',
    ],  # 解析器,如何解析request请求中的request.data
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],  # 权限相关配置
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
    ],  # 认证相关配置
    "URL_FIELD_NAME": 'link',
}
  • 在urls.py中修改配置
urlpatterns = [
    ...
    path('api-auth/', include('rest_framework.urls')) # DRF的登录退出
]
  • DRF的20个模块
    序列化、视图、路由、认证、权限、限流、过滤、解析器、渲染器、分页、版本、测试、概要、异常处理、配置、验证、状态码、内容协商、缓存、元数据

3.1 开发课程信息模型类

models.py

from django.db import models

# Create your models here.
from django.db import models
from django.conf import settings


class Course(models.Model):
    name = models.CharField(max_length=255, unique=True, help_text='课程名称', verbose_name='名称')
    introduction = models.TextField(help_text='课程介绍', verbose_name='介绍')
    teacher = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, help_text='课程讲师', verbose_name='讲师')
    price = models.DecimalField(max_digits=6, decimal_places=2, help_text='课程价格')
    created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
    update_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')

    class Meta:
        verbose_name = "课程信息"
        verbose_name_plural = verbose_name
        ordering = ('price',)

    def __str__(self):
        return self.name

将字段注册到后台,方便从admin后台添加数据
admin.py

from django.contrib import admin
from .models import Course


# Register your models here.

@admin.register(Course)
class CourseAdmin(admin.ModelAdmin):
    list_display = ('name', 'introduction', 'teacher', 'price')
    search_fields = list_display
    list_filter = list_display

3.2 什么是序列化

序列化也叫序列化器,是指把查询集QuerySet或者模型类实例instance这种django的数据类型转化为json或者xml
把queryset或者instance转化为方便前端可以轻松渲染的json/xml/yaml
所以序列化和反序列化是为了前后端数据交互
深度剖析 Django 的 serializer

  • 启动项目,进入admin后台,插入数据
    在这里插入图片描述
    在这里插入图片描述

  • 在python console中测试django的序列化器

from course.models import Course
from django.core import serializers

serializers.serialize('json', Course.objects.all())
# '[{"model": "course.course", "pk": 1, "fields": {"name": "Django REST framework快速入门", "introduction": "快速入门Django REST framework,学会开发一套自己的Restful API服务,并且自动生成API文档", "teacher": 1, "price": "9.99", "created_at": "2023-07-28T10:11:46.882", "update_at": "2023-07-28T10:11:46.882"}}]'

# 如果只需要序列化一部分字段
serializers.serialize('json', Course.objects.all(), fields=("name"))
# '[{"model": "course.course", "pk": 1, "fields": {"name": "Django REST framework快速入门"}}]'

但django的序列化器还有很多需要完善的地方
1.验证处理:在反序列化的过程中,当前端传递过来的数据放在request.data里时,要转化为模型类实例,然后保存。这时需要对前端提交的数据进行验证。
2.验证器的参数
3.同时序列化多个对象
4.序列化的过程中添加上下文
5.没有对无效的数据异常处理
6.关系型字段的外键字段只返回了id,需要我们自己处理关系型字段
drf的序列化器可以帮我们很好的解决这些问题。

3.3 如何继承ModelSerializer序列化模型类

Python学习————序列化器(复习
在应用下建一个serializes.py
模型类的序列化的写法和django开发时form的写法非常类似
先回顾给模型类建form表单类

from django import forms
from rest_framework import serializers  # 导入序列化器

from .models import Course


class CourseForm(forms.ModelForm):
    class Meta:
        model = Course
        fields = ('name', 'introduction', 'teacher', 'price')

模型类序列化的写法几乎一样,只是继承自serializers.ModelSerializer
teacher是外键,所以想得到讲师对应的名字,需要把用户的名称序列化出来。
teacher关联到的用户表,并不是新建课程时新建一个用户,所以该字段可以设为只读ReadOnlyField

class CourseSerializer(serializers.ModelSerializer):
    teacher = serializers.ReadOnlyField(source='teacher.username') # 外键字段、只读
    
    class Meta:
        model = Course  # 写法和上面的CourseForm类似
        # exclude = ('id', ) # 注意元组中只有1个元素时不能写成("id")
        # fields = ('name', 'introduction', 'teacher', 'price')
        fields = "__all__"

同样方法序列化用户模型类
ModelSerializer是更高一级的封装,它继承自Serializer,要实现比较灵活的自定义,可以继承自Serializer
我们这里只序列化模型类,比较简单,所以可以直接用

from django.contrib.auth.models import User

class UserSerializer(serializers.ModelSerializer):

    class Meta:
        model = User
        fields = "__all__"

对外键字段的序列化,可以指定遍历的深度
如果表结构有子表关联到父表,父表又关联到另外的父表,就可以设置深度

class CourseSerializer(serializers.ModelSerializer):
    teacher = serializers.ReadOnlyField(source='teacher.username')  # 外键字段、只读

    class Meta:
        model = Course  # 写法和上面的CourseForm类似
        # exclude = ('id', ) # 注意元组中只有1个元素时不能写成("id")
        # fields = ('name', 'introduction', 'teacher', 'price')
        fields = "__all__"
        depth = 2  # 深度

3.4 带URL的HyperlinkedModelSerializer

假设请求课程信息时,希望每条课程信息的记录,都能跳转到详情页

DRF相关的配置,都写在settings.py中的REST_FRAMEWORK的字典中
继承自HyperlinkedModelSerializer
fields里加上url
url是默认值,如果要改成其他名称,如“link”,可在settings.py中设置URL_FIELD_NAME使全局生效

settings.py

# DRF的全局配置
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',  # 自带分页类
    'PAGE_SIZE': 50,  # 每页数据量
    'DATETIME_FORMAT': '%Y-%m-%d %H:%M:%S',  # 时间显示格式
    'DEFAULT_RENDER_CLASSES': [
        'restframework.renders.JSONRenderer',
        'restframework.renders.BrowsableAPIRenderer',
    ],  # 返回response对象所用的类
    'DEFAULT_PARSER_CLASSES': [
        'rest_framework.parsers.JSONparser',
        'rest_framework.parsers.Formparser',
        'rest_framework.parsers.MultiPartparser',
    ],  # 解析器,如何解析request请求中的request.data
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],  # 权限相关配置
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
    ],  # 认证相关配置
    "URL_FIELD_NAME": 'link',
}

serializers.py

class CourseSerializer(serializers.HyperlinkedModelSerializer):
    teacher = serializers.ReadOnlyField(source='teacher.username')  # 外键字段、只读

    class Meta:
        model = Course  # 写法和上面的CourseForm类似
        # url是默认值,可在settings.py中设置URL_FIELD_NAME使全局生效
        # fields = ('id', 'url', 'name', 'introduction', 'teacher', 'price', 'created_at', 'update_at')
        fields = ('id', 'link', 'name', 'introduction', 'teacher', 'price', 'created_at', 'update_at')

4.1 Django的views开发RESTful API接口

通过DRF使用四种不同的方式来Restful API接口

  • 函数式编程
  • 类视图
  • 通用类视图
  • DRF的视图集

在views.py文件中

4.1.1 Django原生的view-函数式编程编写API接口

思路就是判断这个是什么方法,然后进行处理

@csrf_exempt
def course_list(request):
    course_dict = {
        'name': '课程名称',
        'introduction': '课程介绍',
        'price': 0.11
    }
    if request.method == "GET":
        # return HttpResponse(json.dumps(course_dict), content_type='application/json')
        return JsonResponse(course_dict) # 这两种写法是等价的
    if request.method == "POST":
        course = json.loads(request.body.decode('utf-8'))
        return JsonResponse(course, safe=False) # 如果解析的数据不是字典类型,是字符串的话,需要使用safe=false

需要注意:post请求,需要使用装饰器来解除csrf限制

4.1.2 Django原生的view-类视图编程编写API接口

CBV的编写思路是对不同的请求方法用对应的函数处理

import json

from django.http import JsonResponse, HttpResponse
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator

# Django CBV 编写API接口
@method_decorator(csrf_exempt, name='dispatch')
class CourseList(View):
    def get(self, request):
        return JsonResponse(course_dict)

    @csrf_exempt
    def post(self, request):
        course = json.loads(request.body.decode('utf-8'))
        return JsonResponse(course, safe=False)  # 如果解析的数据不是字典类型,是字符串的话,需要使用safe=false

可以导入方法装饰器,装饰的方法是是dispatch,因为根据django的请求响应原理,当request请求来的时候,它首先到达dispatch方法,通过dispatch方法处理后,再找到post方法

上面的两种方式,是直接使用原生的Django写接口,有很多东西都需要自己从零开始实现。
比如分页、排序、认证、权限、限流等等
于是就有了DRF的视图,集成了这些接口开发常用的功能

4.2 DRF的装饰器api_view - 函数式编程

4.2.1 实现“获取所有课程信息或新增一个课程”的接口

  • 新建drf的fbv视图
from rest_framework.decorators import api_view
from rest_framework.response import Response # django使用的是JsonResponse或HttpResponse,而drf直接封装好了,就是Response
from rest_framework import status
from .models import Course
from .serializers import CourseSerializer

"""一、函数式编程 Function Based View"""
@api_view(["GET", "POST"])
def course_list(request):
    """
    获取所有课程信息或新增一个课程
    :param request:
    :return:
    """
    if request.method == "GET":
        s = CourseSerializer(instance=Course.objects.all(), many=True) # 序列化多个对象,所以需要many=True
        # 序列化后的数据可以通过s.data获得
        return Response(data=s.data, status=status.HTTP_200_OK)
    elif request.method == "POST":
        s = CourseSerializer(data=request.data, partial=True) # 反序列化, partial=True表示部分更新
        if s.is_valid():
            s.save(teacher=request.user) # 讲师是只读属性
            return Response(data=s.data, status=status.HTTP_201_CREATED)
        return Response(s.erros, status=status.HTTP_400_BAD_REQUEST)

函数式编程会用到装饰器
partial=True表示部分更新

  • 新建路由

为了方便管理course这个app下的路由,在course这个应用下创建一个urls.py文件,然后通过include包含到项目的urls中
django urls include用法

course/urls.py

from django.urls import path
from course import views

urlpatterns = [
    # Function Based View
    path("fbv/list/", views.course_list, name="fbv-list")
]

drf_tutorial/urls.py

urlpatterns = [
    path('api-auth/', include('rest_framework.urls')), # DRF的登录退出
    path("admin/", admin.site.urls),
    path('course/', include('course.urls'))
]

在这里插入图片描述

先不用HyperlinkedModelSerializer的api了,因为HyperlinkedModelSerializer是高度集成的序列化类,后面使用drf的视图集来编程可以用。所以序列化类改用:

class CourseSerializer(serializers.ModelSerializer):
    teacher = serializers.ReadOnlyField(source='teacher.username')  # 外键字段、只读

    class Meta:
        model = Course  # 写法和上面的CourseForm类似
        # exclude = ('id', ) # 注意元组中只有1个元素时不能写成("id")
        # fields = ('name', 'introduction', 'teacher', 'price')
        fields = "__all__"
        depth = 2  # 深度
  • 返回结果
    在这里插入图片描述
  • 测试post请求
    在这里插入图片描述

4.2.2 实现“获取一个课程信息或更新删除某个课程信息”的接口

  • 实现视图
@api_view(["GET", "PUT", "DELETE"])
def course_detail(request, pk): # 通过url传递pk
    """
    获取、更新、删除一个课程
    :param request:
    :param pk:
    :return:
    """
    try:
        course = Course.objects.get(pk=pk)
    except Course.DoesNotExist:
        return Response(data={"msg": "没有此课程信息"}, status=status.HTTP_404_NOT_FOUND)
    else:
        if request.method == "GET":
            s = CourseSerializer(instance=course) # 序列化一个对象不需要many=True
            return Response(data=s.data, status=status.HTTP_200_OK)

        elif request.method == "PUT": # PUT方法表示更新,部分写法和POST方法类似
            s = CourseSerializer(instance=course, data=request.data) #instance是指要序列化哪个实例,data表示数据哪里来的
            # 表示把data的数据,反序列化之后,保存或者更新到cuorse对象里
            if s.is_valid():
                s.save() # 不需要teacher字段
                return Response(s.data, status=status.HTTP_200_OK)

        elif request.method == "DELETE":
            course.delete()
            return Response(status=status.HTTP_204_NO_CONTENT)
  • 实现路由
urlpatterns = [
    # Function Based View
    path("fbv/list/", views.course_list, name="fbv-list")
    path("fbv/detail/<int:pk>", views.course_detail, name="fbv-detail")
]

4.3 如何使用Postman测试API接口(我这里是apipost)

我这里以apipost为例

认证方式:

  • basic auth认证:账户名、密码认证

  • api key认证:token认证
    在这里插入图片描述

  • OAuth 2.0等

要在请求头加一些key,value,可以点header
query是url带上的参数
后端代码里解析的request.data,一般是放在raw里面

  • Post请求
    在这里插入图片描述
  • Get请求
    在这里插入图片描述

4.4 DRF中的视图APIView - 类视图编程

4.4.1 实现“获取所有课程信息或新增一个课程”的接口

  • 导入APIView
from rest_framework.views import APIView
  • 实现类视图
"""二、类视图 Class Based View"""
class CourseList(APIView):
    def get(self, request):
        """
        :param request:
        :return:
        """
        queryset = Course.objects.all()
        s = CourseSerializer(instance=queryset, many=True)
        return Response(s.data, status=status.HTTP_200_OK)

    def post(self, request):
        """
        :param request:
        :return:
        """
        s = CourseSerializer(data=request.data)
        if s.is_valid():
            s.save(teacher=self.request.user)
            print(type(request.data), type(s.data))
            # type(request.data): <class 'dict'>
            # type(s.data): <class 'rest_framework.utils.serializer_helpers.ReturnDict'>
            return Response(data=s.data, status=status.HTTP_201_CREATED)
        return Response(s.errors, status=status.HTTP_400_BAD_REQUEST)
  • 新建路由
urlpatterns = [
    # Function Based View
    path("fbv/list/", views.course_list, name="fbv-list"),
    path("fbv/detail/<int:pk>/", views.course_detail, name="fbv-detail"),

    # Class Based View
    path("cbv/list/", views.CourseList.as_view(), name='cbv-list')
]
  • 测试请求
    在这里插入图片描述
    在这里插入图片描述

4.4.2 实现“详情页”的接口

静态方法:无论是通过类对象直接调用还是实例对象进行调用都是可以的,需要注意的是在静态方法中无法使用实例属性和方法
python静态方法、实例方法、类方法使用

  • 实现接口
class CourseDetail(APIView):
    @staticmethod  # 静态方法
    def get_object(pk):
        """
        :param pk:
        :return:
        """
        try:
            return Course.objects.get(pk=pk)
        except Course.DoesNotExist:
            return

    def get(self, request, pk):
        """
        :param request:
        :param pk:
        :return:
        """
        obj = self.get_object(pk=pk)
        if not obj:
            return Response(data={"msg": "没有此课程信息"}, status=status.HTTP_404_NOT_FOUND)
        s = CourseSerializer(instance=obj)
        return Response(s.data, status=status.HTTP_200_OK)

    def put(self, request, pk):
        obj = self.get_object(pk=pk)
        if not obj:
            return Response(data={"msg": "没有此课程信息"}, status=status.HTTP_404_NOT_FOUND)
        s = CourseSerializer(instance=obj, data=request.data)
        if s.is_valid():
            s.save()
            return Response(data=s.data, status=status.HTTP_200_OK)
        return Response(s.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk):
        """
        :param request:
        :param pk:
        :return:
        """
        obj = self.get_object(pk=pk)
        if not obj:
            return Response(data={"msg": "没有此课程信息"}, status=status.HTTP_404_NOT_FOUND)
        obj.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
  • 实现路由
urlpatterns = [
    # Function Based View
    path("fbv/list/", views.course_list, name="fbv-list"),
    path("fbv/detail/<int:pk>/", views.course_detail, name="fbv-detail"),

    # Class Based View
    path("cbv/list/", views.CourseList.as_view(), name='cbv-list'),
    path("cbv/detail/<int:pk>/", views.CourseDetail.as_view(), name='cbv-detail'),
]
  • 测试请求
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

类视图编程方法和函数式编程的编写方法,代码几乎一样。下一小节讲解更加简洁的编写方法。

4.5 DRF中的通用类视图GenericAPIView

导入generics

from rest_framework import generics

4.5.1 实现查看和请求新增的接口

通用类视图,实际上是把一些常见的增删改查操作对应的类通过mixin把他混合在一起。
这里属性的名称是固定的
这里需要重载perform_create方法

4.5.2 实现课程详情的接口

  • 实现通用类视图

下面的写法等价于class GCourseDetail(generics.RetrieveUpdateDestroyAPIView):
不过对于下面的方法,请求的方法不再是GET、PUT、PATCH、DELETE,而是retrieve,update,destory等,所以还是建议用class GCourseDetail(generics.RetrieveUpdateDestroyAPIView):

from rest_framework import mixins
from rest_framework.generics import GenericAPIView


class GCourseDetail(generics.mixins.RetrieveModelMixin,
                                   mixins.UpdateModelMixin,
                                   mixins.DestroyModelMixin,
                                   GenericAPIView):
    queryset = Course.objects.all()
    serializer_class = CourseSerializer
  • 新增路由
urlpatterns = [
    # Function Based View
    path("fbv/list/", views.course_list, name="fbv-list"),
    path("fbv/detail/<int:pk>/", views.course_detail, name="fbv-detail"),

    # Class Based View
    path("cbv/list/", views.CourseList.as_view(), name='cbv-list'),
    path("cbv/detail/<int:pk>/", views.CourseDetail.as_view(), name='cbv-detail'),

    # Generic Class Based View
    path("gcbv/list/", views.GCourseList.as_view(), name='gcbv-list'),
    path("gcbv/detail/<int:pk>/", views.GCourseDetail.as_view(), name='gcbv-detail'),
]
  • 测试请求
    在这里插入图片描述

通用类视图里面还做好了其他事情:总个数(count)、下一页(next)、上一页(previous)、数据(results)
在这里插入图片描述
在这里插入图片描述

4.6 DRF的viewsets开发课程信息的增删改查接口 - 视图集

  • 导入viewsets
from rest_framework import viewsets
  • 创建视图集
"""四、DRF的视图集"""
class CourseViewSet(viewsets.ModelViewSet):
    queryset = Course.objects.all()
    serializer_class = CourseSerializer
    
    def perform_create(self, serializer):
        serializer.save(teacher=self.request.user)

不做权限认证只需要四行代码

4.7 Django的URLs与DRF的Routers

  • 新增路由

写法和前面不一样
两种视图集的路由写法

    • 1 传统写法
    # DRF viewsets
    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": "destory"}
    ), name="viewsets-detail")
    • 2.DRF的路由routers
  • 导入
from rest_framework.routers import DefaultRouter
router = DefaultRouter() # 把类实例化
router.register(prefix="viewsets", viewset=views.CourseViewSet) # 把视图集注册到这个路由实例里面

urlpatterns = [
    path("", include(router.urls))
]
  • 测试接口
    在这里插入图片描述

5.1 DRF认证方式介绍

DRF常用的三种认证方式:
1.BasicAuthentication:用户名密码认证
2.SessionAuthentication:Session认证
3.TokenAuthentication:Token认证
在这里插入图片描述

也可以实现自定义的认证方案,或者使用第三方的包,比如json webtoken(关于jwt的认证,会在接下来的drf进阶实战课程讲解)
认证和权限的区别:
认证是对用户登录的身份进行校验
权限指的是一个登录验证通过的用户,他能够访问哪些接口;或者对于某一个接口,他能够拿到什么级别的数据
根据Django restframework对http request的响应流程来看,身份认证是发生在权限检查和限流检查之前的

认证和权限,核心依赖两个数据:request.user, request.auth
认证机制实际上依赖的是Django的auth框架

'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.TokenAuthentication', ],
中的多个认证类,DRF会按顺序从上往下对每个类进行认证。并使用第一个成功通过的认证类来返回值,来设置request.user和request.auth

5.1.1 BasicAuthentication

设置basicauthentication,则headers会多一个authorization,它是用户名密码经过base64库计算出来的密文,这是postman自动完成的。base64库不具有机密性,是可以解密的。所以basicauthentication一般用于测试工作,不用于生产环境

  • 测试,通过basicauthentication认证后,request.user和request.auth返回什么
print(self.request.user, self.request.auth)
print(type(self.request.user), type(self.request.auth))
# admin None
# <class 'django.contrib.auth.models.User'> <class 'NoneType'>
  • 查看响应头有什么信息
    密码正确
    在这里插入图片描述
    密码错误

认证失败的话会在响应头里面多一个WWW_Authenticate字段
在这里插入图片描述

5.1.2 SessionAuthentication

使用的是Django默认的会话后端,通常在前端js里使用http,ajax请求时可以使用session认证,使用这个时,需要给后端提供csrf令牌的。

5.1.3 TokenAuthentication

是一种简单的,基于令牌的http认证方式

  • 在settings.py的INSTALLED_APPS加上'rest_framework.authtoken'

会生成一张数据表

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "rest_framework",  # 用于开发RESTful API
    'rest_framework.authtoken',  # DRF自带的Token认证
    'course.apps.CourseConfig',
]
  • 生成token的方式

5.1.3.1 使用Django manag.py生成Token

只能供管理员测试
手动生成token

python manage.py drf_create_token admin #给admin建一个token

在这里插入图片描述

5.1.3.2 使用Django的信号机制生成Token

目的:
自动生成token
用户可以通过接口请求到token

view.py中

from django.db.models.signals import post_save # 保存之后,即用户已经建好之后的信号
from django.conf import settings
from django.dispatch import receiver # 接收信号
from rest_framework.authtoken.models import Token

@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def generate_token():

也可以直接导入django的user模型类作为认证模型类:from django.contrib.auth.models import User
原理:当User模型类新建一个实例并把并保存的时候,即新建一个用户的时候,因为保存要调用save方法
保存之后,post_save信号会传递给receiver,receiver接受之后,就会执行函数generate_token
执行函数时,instance是指新建的用户实例,created=True,于是在authtoken_token表中生成一条记录

给用户生成的token,用户如何获取到呢?

rest_framework已经帮我们写好了,我们直接调用即可

在总的路由urls.py中

from django.contrib import admin
from django.urls import path, include
from rest_framework.authtoken import views

urlpatterns = [
    path("api-token-auth/", views.obtain_auth_token), # 获取token的接口
    path('api-auth/', include('rest_framework.urls')), # DRF的登录退出
    path("admin/", admin.site.urls),
    path('course/', include('course.urls'))
]
  • 测试-新建用户
    在这里插入图片描述
    user01 - mima123456
    在这里插入图片描述
    可以看到token表中有新增记录
    在这里插入图片描述
  • 测试,通过postman,通过接口获取token
    在这里插入图片描述
  • 认证成功
    在这里插入图片描述
    请求头
print(self.request.user, self.request.auth)
print(type(self.request.user), type(self.request.auth))
# user01 e1e4eb90e389feb76c8bb71ad73e3b3c63e8090c
# <class 'django.contrib.auth.models.User'> <class 'rest_framework.authtoken.models.Token'>

认证成功的话,request.user依旧是django的user类实例
request.auth是Token类实例
响应头里面没有和认证有关的信息

  • 认证失败
    在这里插入图片描述
  • 要对某一个接口,去使用不同的认证的方式

不论使用什么视图编程方式,都有对应的设置方法。下面按照四种不同的视图编程方式,分别讲解。

    • 1.对函数式编程
      导入对于认证的装饰类:
from rest_framework.decorators import api_view, authentication_classes
from rest_framework.authentication import BasicAuthentication, SessionAuthentication, TokenAuthentication

装饰器是有顺序的

对象上只添加密码认证的类装饰器

@api_view(["GET", "POST"])
@authentication_classes((BasicAuthentication, )) # 对象级别的认证设置,优先于全局设置
def course_list(request):
    """
    获取所有课程信息或新增一个课程
    :param request:
    :return:
    """
    if request.method == "GET":
        s = CourseSerializer(instance=Course.objects.all(), many=True)  # 序列化多个对象,所以需要many=True
        # 序列化后的数据可以通过s.data获得
        return Response(data=s.data, status=status.HTTP_200_OK)
    elif request.method == "POST":
        s = CourseSerializer(data=request.data, partial=True)  # 反序列化, partial=True表示部分更新
        if s.is_valid():
            s.save(teacher=request.user)  # 讲师是只读属性
            return Response(data=s.data, status=status.HTTP_201_CREATED)
        return Response(s.erros, status=status.HTTP_400_BAD_REQUEST)
  • 测试
    在这里插入图片描述

注意写法,必须是Token xxx

    • 2.对类视图编程
      直接加入类属性authentication_classes
class CourseList(APIView):

    authentication_classes = (BasicAuthentication, SessionAuthentication, TokenAuthentication)

通用类视图、视图集也是一样的,也都有类属性

5.3 DRF的权限控制

常用的权限类有哪些?应该如何设置权限策略?如何自定义对象级别权限?

DRF的权限都是在permissions模块里面,权限检查的过程和前面讲解的身份认证一样,也是会使用request.user和request.auth属性中的身份认证信息来决定请求是否传入。、

5.3.1 在settings.py中可以设置全局的权限类

在这里插入图片描述

  • 常用权限类有:

IsAuthenticatedOrReadOnly: 登录的用户可以增删改查,不登陆的用户可以查询
IsAuthenticated:只有登录的用户可以进行所有操作,不登陆的用户不能进行所有操作,get也不行
IsAdminUser:admin用户有权限访问接口,是不是admin是看auth_user表的is_staff字段,true表示是admin级别用户
AllowAny:允许所有请求

5.3.2 如何在接口上/对象级别设置权限

  • 导入装饰器
from rest_framework.decorators import api_view, authentication_classes, permission_classes
  • 导入权限类
在这里插入代码片

5.3.2.1 给函数视图设置权限

  • 给函数视图加上装饰器
@api_view(["GET", "POST"])
@authentication_classes((BasicAuthentication, ))  # 对象级别的认证设置,优先于全局设置
@permission_classes((IsAuthenticated, )) # 自定义的权限类
def course_list(request):

5.3.2.2 给类视图/通用类视图/视图集设置权限

  • 直接加入类属性authentication_classes
permission_classes = (IsAuthenticated, )

5.3.3 如何自定义对象级别权限?

实现只能修改自己的信息,别人的信息只可以查看,不能修改。

  • 在应用下面建立permission.py,并创建自定义的权限类
    permission.py
class IsOwnerReadOnly(permissions.BasePermission):
    """
    自定义权限:只有对象的所有者可以增删改查
    """

    def has_object_permission(self, request, view, obj):  # 重写BasePermission的方法
        """
        所有的request请求都有读权限,因此一律允许GET/HEAD/OPTIONS方法
        如果用户是自己,可以修改
        :param request:
        :param view:
        :param obj:
        :return: bool
        """
        if request.method in permissions.SAFE_METHODS:
            return True
        # 对象的所有者才有写权限
        return obj.teacher == request.user

重写has_object_permission方法
安全的方法("GET", "HEAD", "OPTIONS")可以用permissions.SAFE_METHODS代替

在这里插入图片描述

  • 将创好的自定义权限类导入视图
from .permission import IsOwnerReadOnly

class GCourseList(generics.ListCreateAPIView):
    queryset = Course.objects.all()
    serializer_class = CourseSerializer
    permission_classes = (IsAuthenticated, )

    def perform_create(self, serializer):
        serializer.save(teacher=self.request.user)


class GCourseDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Course.objects.all()
    serializer_class = CourseSerializer
    permission_classes = (IsAuthenticated, IsOwnerReadOnly)
  • 测试接口
    在这里插入图片描述
    在这里插入图片描述

6.1 如何生成API接口文档

6.1.1 如何生成API接口文档

通过请求url可以查看单个接口的格式等信息。但是有没有方法展示所有接口信息,即API在线文档
这样能方便前端查看,一目了然
下面操作必须要先pip install coreapi

  • 1.在settings.py的REST_FRAMEWORK中加入
REST_FRAMEWORK = {
    'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema',
    ...
    }
  • 2.在总的urls.py中添加路由
    先导入get_schema_view
    再创建get_schema_view实例
    再创建路由
from rest_framework.schemas import get_schema_view

schema_view = get_schema_view(title="DRF API文档", description="xxx")

urlpatterns = [
    path("api-token-auth/", views.obtain_auth_token), # 获取token的接口
    path('api-auth/', include('rest_framework.urls')), # DRF的登录退出
    path("admin/", admin.site.urls),
    path('course/', include('course.urls')),
    path('schema/', schema_view),
]
  • 测试
    在这里插入图片描述

建议在Chrome应用商店下载jsonView插件

点击GET-corejson
在这里插入图片描述

在这里插入图片描述

但是文档还是不够美观,下一节介绍使用coreapi的概要功能

6.2 DRF的概要使用方法介绍

coreapi应该如何配置,以及文档的使用方法。

  • 修改REST_FRAMEWORK的’DEFAULT_SCHEMA_CLASS’属性
    在这里插入图片描述
  • 在urls.py中
    • 1.导入模块,新增路由
from rest_framework.documentation import include_docs_urls

urlpatterns = [
    path("api-token-auth/", views.obtain_auth_token),  # 获取token的接口
    path('api-auth/', include('rest_framework.urls')),  # DRF的登录退出
    path("admin/", admin.site.urls),
    path('docs/', include_docs_urls(title='DRF API文档', description='Django REST framework快速入门')),
    path('course/', include('course.urls')),
]
  • 测试
    在这里插入图片描述

在这个API文档中,认证配置好之后,刷新之后需要重新配置

7.1 课程总结

  • 应该使用哪种视图开发?
    使用函数式视图编写灵活,但是这是面向过程的方式,重复率高
    使用类视图,可以用到python中类的特性,封装、继承、多态,减少代码重复率
    通用类视图,灵活度就比较低了
    视图集高度定制化,灵活度最低
    一般以类视图编程为主。

  • DRF的运行机制?APIView源码?测试?缓存?限流。。。都没有讲到

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/830784.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[openCV]基于拟合中线的智能车巡线方案V4V5

V4: import cv2 as cv import os import numpy as npimport time# 遍历文件夹函数 def getFileList(dir, Filelist, extNone):"""获取文件夹及其子文件夹中文件列表输入 dir&#xff1a;文件夹根目录输入 ext: 扩展名返回&#xff1a; 文件路径列表""…

Mybatis引出的一系列问题-Spring事务的探究

1 spring事务的传播特性 package com.zs.service;Service public class UserService {Autowiredprivate UserDao userDA0;Transactionalpublic void transfer(String fromName, String toName, Integer money) {userDA0.out(fromName, money);int a 1 / 0;userDA0.in(toName,…

在中国人民大学与加拿大女王金融硕士项目的岁月,不会负了每个有心人

学习&#xff0c;就像是一场战争&#xff0c;有时你觉得在这拼命撕杀的战场上&#xff0c;你是孤独与无助的&#xff0c;但你殊不知&#xff0c;你其实并不是孤身奋战的&#xff01;学习路上会遇见很多有心人&#xff0c;在中国人民大学与加拿大女王金融硕士项目的岁月&#xf…

关于 JavaScript 数组的遍历函数:map, reduce, filter 等

JavaScript 列表的 map 方法是一个常用的数组遍历方法&#xff0c;将会对每一个列表中的元素都应用所给的转换方法&#xff0c;也就是 u > (...)&#xff0c;这是 ES6 中匿名函数的表达式写法&#xff0c;在这种使用场景中是一种非常方便的语法糖&#xff0c;map 方法在遍历…

亚马逊鲲鹏系统是怎么引流的?

亚马逊鲲鹏系统有三种引流方式&#xff0c;可设置通过亚马逊站点搜索、站外引流、直接访问产品页面进入到相关产品页面进行操作。 1、通过亚马逊站点搜索 正常的登录到我们的亚马逊主页&#xff0c;然后通过设置关键词及asin&#xff0c;最后进入你指定的产品&#xff0c;进行…

景联文科技高质量成品数据集上新啦!

景联文科技近期上新多个成品数据集&#xff0c;包含图像、视频等多种类型的数据&#xff0c;涵盖丰富的场景&#xff0c;可满足不同模型的多元化需求。 高质量成品数据集可用于训练和优化模型&#xff0c;使得模型能够更加全面和精准地理解和处理任务&#xff0c;更好地应对复…

QT中使用ffmpeg的api进行视频的播放

在了解ffmpeg使用api进行视频的播放之前&#xff0c;我们首先了解一下视频的播放流程。 一、视频的播放流程 首先是我们最常见的视频文件&#xff0c;在播放流程中首先是要打开视频文件&#xff0c;将视频文件中的数据进行解封装&#xff0c;之后再将解封装之后的视频进行解码…

仅需6GB显存,拥有专属AI代码助手

清华GLM技术团队打造的多语言代码生成模型CodeGeeX近期更新了新的开源版本「CodeGeeX2-6B」。CodeGeeX2是多语言代码生成模型CodeGeeX的第二代模型&#xff0c;不同于一代 CodeGeeX &#xff0c;CodeGeeX2 是基于 ChatGLM2 架构加入代码预训练实现。得益于 ChatGLM2 的更优性能…

Dos常用命令有哪些?具体的使用方式是什么?

1. 常用命令 对我们来说&#xff0c;掌握几个常见的dos命令即可&#xff0c;如下&#xff1a; dir&#xff1a;列出当前目录下所有的文件及文件夹&#xff1b; md&#xff1a;创建一个文件夹&#xff1b; rd&#xff1a;删除一个空目录&#xff1b; cd&#xff1a;进入指定…

第二章 圣诞夜的滑雪场

系列文章目录 第一章 修学旅行&#xff08;凯撒密码、栅栏密码&#xff09; 前言 这一章借鉴了基德大人和青子小姐的剧情&#xff0c;感兴趣的童鞋可以看一看哟&#xff01;&#xff08;本人柯迷&#xff09; 摩斯密码 到了下午&#xff0c;老师将大家聚在一起&#xff0c;笑…

云主机OOM宕机原因分析及处理

一、故障现象 某次服务器告警宕机故障&#xff0c;无法ssh连入&#xff0c;控制台登录后查看&#xff0c;发生OOM事件&#xff0c;OOM就是我们常说的Out of Memory内存溢出&#xff0c;它是指需要的内存空间大于系统分配的内存空间&#xff0c;导致项目程序crash&#xff0c;甚…

Ubuntu20.04 + QT5.14.2 + VTK8.2.0 + PCL 1.10 环境配置

目录 Ubuntu20.04 QT5.14.2 VTK8.2.0 PCL 1.10 环境配置一、VTK 编译和安装1、库依赖&#xff1a;2、下载资源&#xff1a;[下载VTK8.2.0](https://www.vtk.org/files/release/8.2/VTK-8.2.0.tar.gz)3、编译&#xff1a;4、安装5、qtcreator 配置编译的libQVTKWidgetPlugin.…

直播录制怎么录?推荐这3个方法!

随着互联网的发展&#xff0c;直播已经成为了一种热门的社交和内容创作方式。然而&#xff0c;有时候我们可能会错过一些重要的直播内容&#xff0c;因此直播录制成为了很多用户的需求。本文将介绍几种直播录制的方法&#xff0c;通过本文的指导&#xff0c;您将学会如何简单易…

微信新功能,你都知道吗?

近日iOS 微信8.0.40正式版来了&#xff0c;一起来看看有哪些变化&#xff1f; 1、朋友圈置顶 几个月前微信开始内测「朋友圈置顶」功能&#xff0c;从网友们的反馈来看&#xff0c;iOS 微信 8.0.40 似乎扩大了内测范围&#xff0c;更多用户可以体验到该功能了。 大家可以去自己…

第一篇|研究数据哪里来——制造业

制造业是一个国家的立国之本&#xff0c;下面为大家介绍一些制造业行业数据的公开信息网站。对于制造业研究数据&#xff0c;您可以从以下几个途径获取&#xff1a; 1. 政府机构和统计局 许多国家和地区的政府机构会定期发布有关制造业的相关数据和统计报告。您可以访问该国或…

零基础挑战一周拿下2023数学建模国奖

1、 数学建模国赛介绍 1.1 数学建模国赛是什么&#xff1f;如何评奖 全国大学生数学建模竞赛是全国高校规模最大的课外科技活动之一。该竞赛每年9月&#xff08;一般在上旬某个周末的星期五至下周星期一共3天&#xff0c;72小时&#xff09;举行&#xff0c;竞赛面向全国大专院…

通用积分球的用途和工作原理

积分球辐射源是一种非常优异的定标光源&#xff0c;其输出的辐亮度面均匀性和稳定性是普通光源无法比拟的。在需要使用面光源的领域&#xff0c;被广泛用于光学探测器的实验室定标&#xff0c;空间光学遥感仪器发射前的地面辐射定标。因此辐射源的稳定性、准确性对于辐射定标非…

构建弹性可扩展的微服务架构:基于Spring Cloud Alibaba 的实践

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 前言 随着互联网业务的…

Netty:ByteBuf可以写入字节数

说明 可以用ByteBuf的maxWritableBytes()得到当前ByteBuf最多还可写入多少字节的数据&#xff0c;它的值等于ByteBuf的最大容量减去当前的writerIndex。 可以使用writableBytes()获得ByteBuf当前还可以写入多少字节的数据&#xff0c;它的值等于ByteBuf的容量减去当前的writer…

连接数据库报错:Bad pocket type 包校验失败

用db连接正式库报错 Bad pocket type 换一个 navicat一样的报错&#xff0c;后来发现是用的数据库不对&#xff0c;沙雕了 重新建立连接&#xff0c;更换成mysql 成功&#xff01;&#xff01;&#xff01;