Django学习篇04-Django REST framework框架

REST API 风格

RESTful是一种开发理念,英文 Representational state transfer 表述性状态转移 其实就是对 资源 的表述性状态转移。

风格对比

传统风格:

  • 获取用户信息:URL:/getUser?id=123方法:GET

  • 创建用户:URL:/createUser方法:POST

  • 更新用户信息:URL:/updateUser方法:POST

  • 删除用户:URL:/deleteUser?id=123方法:GET

RESTful风格:

  • 获取用户信息:URL:/users/123方法:GET

  • 创建用户:URL:/users方法:POST

  • 更新用户信息:URL:/users/123方法:PUT

  • 删除用户:URL:/users/123方法:DELETE

传统风格的接口设计中,URL中包含动词和参数,并使用GET和POST方法进行操作。而RESTful风格的接口设计中,URL使用名词来表示资源,使用HTTP方法(GET、POST、PUT、DELETE)进行操作。RESTful风格的接口设计更加简洁和直观,利于理解和遵循统一的规范。

RESTful 设计理念

当谈到 RESTful 设计理念时,以下是每个理念的详细解释:

  1. 资源(Resources):

    • RESTful 设计将系统中的实体抽象为资源。资源可以是任何具体或抽象的事物,如用户、订单、文章等。

    • 每个资源都有一个唯一的标识符(URL),客户端通过操作资源的 URL 来进行操作。

    • 资源可以具有不同的表示形式,如 JSON、XML 或 HTML。

  2. 统一接口(Uniform Interface):

    • 使用统一的接口对资源进行操作,包括使用标准的 HTTP 方法,如 GET、POST、PUT、DELETE。

    • GET:用于获取资源的表示形式。例如:GET /users 获取所有用户的列表。

    • POST:用于创建新资源。例如:POST /users 创建新用户。

    • PUT:用于更新现有资源。例如:PUT /users/123 更新用户 ID 为 123 的用户信息。

    • DELETE:用于删除资源。例如:DELETE /users/123 删除用户 ID 为 123 的用户。

    • 使用这些标准方法使得接口易于理解和使用,并且允许客户端和服务器之间的解耦。

  3. 无状态(Stateless):

    • RESTful 接口是无状态的,即服务器不会存储客户端的状态信息。

    • 每个请求都是完整的,服务器可以独立处理每个请求,而不依赖于之前的请求。

    • 客户端必须在每个请求中包含所有必要的信息,这使得接口更加可靠和可伸缩。

  4. 超媒体驱动(HATEOAS):

    • RESTful 设计倡导使用超媒体链接来驱动客户端的行为。

    • 服务器在响应中提供资源的相关链接,客户端通过这些链接发现和执行进一步的操作。

    • 这种方式使得应用程序的状态转换更加灵活和可扩展,允许服务器的接口演化而不破坏客户端的使用。

  5. 缓存(Caching):

    • RESTful 设计支持缓存机制,可以有效地利用缓存提高性能和减少网络流量。

    • 服务器可以为资源响应设置适当的缓存头信息,如 ETag 和 Last-Modified。

    • 客户端根据缓存头信息判断是否使用缓存的资源,减少不必要的网络请求。

通过遵循这些 RESTful 设计理念,我们能够构建出具有良好可维护性、可扩展性和可靠性的 Web API。RESTful 接口设计具备清晰的结构和易于理解的语义,使得不同系统之间的通信变得更加简单和可靠,同时也促进了前后端的分离和微服务架构的实现。

REST 常用的设计规则

EST(Representational State Transfer)是一种设计风格,有一些常用的规则可以帮助我们设计出符合 REST 原则的 API:

  1. 使用清晰的资源命名:

    • 资源应该使用名词进行命名,而不是动词。

    • 使用复数形式表示资源的集合,如 /users 表示所有用户的集合,而不是单个用户。

  2. 使用合适的 HTTP 方法:

    • 使用标准的 HTTP 方法来定义对资源的操作。

    • GET:用于获取资源的表示形式。

    • POST:用于创建新资源。

    • PUT:用于更新现有资源。

    • DELETE:用于删除资源。

  3. 使用合适的 HTTP 状态码:

    GET
        安全且幂等
        获取表示
        变更时获取表示(缓存)
    
        200(OK) - 表示已在响应中发出
    
        204(无内容) - 资源有空表示
        301(Moved Permanently) - 资源的URI已被更新
        303(See Other) - 其他(如,负载均衡)
        304(not modified)- 资源未更改(缓存)
        400 (bad request)- 指代坏请求(如,参数错误)
        404 (not found)- 资源不存在
        406 (not acceptable)- 服务端不支持所需表示
        500 (internal server error)- 通用错误响应
        503 (Service Unavailable)- 服务端当前无法处理请求
    POST
        不安全且不幂等
        使用服务端管理的(自动产生)的实例号创建资源
        创建子资源
        部分更新资源
        如果没有被修改,则不过更新资源(乐观锁)
        200(OK)- 如果现有资源已被更改
        201(created)- 如果新资源被创建
        202(accepted)- 已接受处理请求但尚未完成(异步处理)
        301(Moved Permanently)- 资源的URI被更新
        303(See Other)- 其他(如,负载均衡)
        400(bad request)- 指代坏请求
        404 (not found)- 资源不存在
        406 (not acceptable)- 服务端不支持所需表示
        409 (conflict)- 通用冲突
        412 (Precondition Failed)- 前置条件失败(如执行条件更新时的冲突)
        415 (unsupported media type)- 接受到的表示不受支持
        500 (internal server error)- 通用错误响应
        503 (Service Unavailable)- 服务当前无法处理请求
    PUT
        不安全但幂等
        用客户端管理的实例号创建一个资源
        通过替换的方式更新资源
        如果未被修改,则更新资源(乐观锁)
        200 (OK)- 如果已存在资源被更改
        201 (created)- 如果新资源被创建
        301(Moved Permanently)- 资源的URI已更改
        303 (See Other)- 其他(如,负载均衡)
        400 (bad request)- 指代坏请求
        404 (not found)- 资源不存在
        406 (not acceptable)- 服务端不支持所需表示
        409 (conflict)- 通用冲突
        412 (Precondition Failed)- 前置条件失败(如执行条件更新时的冲突)
        415 (unsupported media type)- 接受到的表示不受支持
        500 (internal server error)- 通用错误响应
        503 (Service Unavailable)- 服务当前无法处理请求
    DELETE
        不安全但幂等
        删除资源
        200 (OK)- 资源已被删除
        301 (Moved Permanently)- 资源的URI已更改
        303 (See Other)- 其他,如负载均衡
        400 (bad request)- 指代坏请求
        404 (not found)- 资源不存在
        409 (conflict)- 通用冲突
        500 (internal server error)- 通用错误响应
        503 (Service Unavailable)- 服务端当前无法处理请求
    
  4. 使用适当的 HTTP 头信息:

    • 使用适当的 HTTP 头信息来描述和控制资源的表示形式和缓存机制。

    • Content-Type:指定响应的表示形式,如 application/json。

    • ETag 和 Last-Modified:用于支持缓存机制。

  5. 使用嵌套资源和关联链接:

    • 当资源之间存在关联关系时,可以使用嵌套资源或关联链接表示。

    • 例如,可以通过 /users/123/orders 获取用户 123 的订单列表,或者通过链接关联相关资源。

  6. 版本控制:

    • 当 API 的变化可能影响到已有客户端时,应该进行版本控制。

    • 可以在 URL 中包含版本号,如 /v1/users。

  7. 安全性和权限:

    • 使用适当的身份验证和授权机制来保护 API 的安全性和权限。

    • 使用 HTTPS 来保证通信的安全性。

这些规则可以帮助我们设计出清晰、一致和易于使用的 RESTful API。同时,还应该根据具体业务需求和项目特点进行灵活调整。

Django REST framework

Django REST framework 是一个基于 Django 的开源库,用于构建和开发 Web API。它提供了一组工具和功能,帮助开发人员轻松地创建和管理 RESTful API。

Django REST framework 提供了以下主要功能:

  1. 序列化:提供了强大的序列化工具,可以将数据库模型对象转换为 JSON、XML 等格式的数据,并支持反序列化将请求数据转换为模型对象。

  2. 视图:支持编写基于类的视图(Class-based Views)和函数视图(Function-based Views),用于处理 API 请求和生成响应。

  3. 路由:可以定义 API 路由,将 URL 映射到相应的视图处理函数或类。

  4. 认证与授权:提供了各种认证和授权选项,包括基本身份验证、Token 认证、OAuth 2.0 等。

  5. 请求和响应处理:提供了灵活的请求和响应处理机制,支持内容协商(Content Negotiation)和格式化输出。

  6. 视图集和路由器:通过视图集(Viewsets)和路由器(Routers),可以更简洁地定义资源的 CRUD 操作。

  7. 过滤和排序:支持对查询结果进行过滤和排序,方便客户端进行高级查询操作。

  8. 分页和限流:提供了分页和限流的功能,用于处理大数据集和控制 API 的请求频率。

Django REST framework 遵循 Django 的设计理念和风格,提供了一种简单、灵活和可扩展的方式来构建 Web API。它广泛应用于构建各种类型的 API,包括移动应用程序后端、网站后端、微服务等。

安装和配置步骤

  • 安装 Django REST framework:使用 pip 在虚拟环境中安装 Django REST framework。运行以下命令:

pip install djangorestframework
  • 配置 Django REST framework:打开项目目录下的 myproject/settings.py 文件,并将以下代码添加到 INSTALLED_APPS 列表中:

INSTALLED_APPS = [
    ...
    'rest_framework',
]

序列化器

在 Django REST framework 中,序列化器提供了数据校验、数据转换(序列化/反序列化)的功能。

序列化

序列化是指将数据对象转换为特定格式(如 JSON、XML)的过程,以便在网络传输或存储中使用。在 Django REST framework 中,序列化器(Serializer)用于实现数据的序列化和反序列化。

序列化器的作用是将复杂的数据类型(如模型实例)转换为简单的 Python 基本数据类型,比如字典。这样可以方便地将数据传输到前端或存储到数据库中。

以下是使用序列化器进行数据序列化的一般步骤:

定义模型(Model):

首先需要定义数据库模型,即数据的结构和字段。

projects\models.py

from django.db import models


class Project(models.Model):
    id = models.AutoField(primary_key=True, verbose_name='id主键', help_text='id主键')
    name = models.CharField(verbose_name='项目名称', help_text='输入项目名称', unique=True,
                            max_length=50)
    leader = models.CharField(verbose_name='项目负责人', help_text='输入项目负责人',
                              max_length=20, null=True)
    is_execute = models.BooleanField(verbose_name='是否启动项目', help_text='请选择是否启动项目',
                                     default=True)
    desc = models.TextField(verbose_name='项目描述', help_text='输入项目描述',
                            null=True, blank=True)
                                 auto_now=True)
    create_time = models.DateTimeField(verbose_name='创建时间', help_text='这是创建时间,会自动设置',
                                       auto_now_add=True)
    update_time = models.DateTimeField(verbose_name='修改时间', help_text='这是修改时间,每次数据改变时会自动更新',
                                       auto_now=True)
    class Meta:
        db_table = 'tb_projects'
        verbose_name = '项目表'
        verbose_name_plural = '项目表'
        ordering = ['id']

在这个示例中,定义了一个名为 Project 的模型,包含了多个字段。

创建序列化器(Serializer)类:

在 Django 中,使用序列化器来定义数据的序列化和反序列化规则,将模型对象转换为可传输的数据格式(如 JSON)。

如何定义序列化器类?

1.一般在子应用中创建serializers.py文件,用于定义序列化器类

2.必须继承Serializer类或者Serializer的子类

3.序列化输出的参数名要与创建的序列化器字段(序列化器类中的类属性)名称保持一致

4.序列化输出的参数类型要与创建的序列化器字段(序列化器类中的类属性)类型保持一致

5.序列化器字段必须为Field子类

6.序列化器字段类型种类?

projects\serializers.py

from rest_framework import serializers

class ProjectSerializer(serializers.Serializer):
    # 需求:
    # 在创建项目时,前端不需要传递id,但是必须将项目id返回给前端
    id = serializers.IntegerField(max_value=1000000, min_value=1,
                                  label='项目id', help_text='输出项目id', read_only=True)
    name = serializers.CharField(max_length=50, min_length=2, required=True)
    leader = serializers.CharField(max_length=20,
                                   error_messages={
                                       'min_length': '项目负责人长度不能少于1个字符',
                                       'max_length': '项目负责人长度不能超过10个字符',
                                       'null': '项目负责人不能为null',
                                       'required': '项目负责人为必填参数',
                                       'blank': '项目负责人不能为空字符串'
                                   },
                                   allow_blank=True,
                                   trim_whitespace=True)

    is_execute = serializers.BooleanField(write_only=True)

    create_time = serializers.DateTimeField(read_only=True,format='%Y年%m月%d日 %H:%M:%S')

在这个示例中,创建了一个名为 ProjectSerializer 的序列化器类,定义了与模型对应的字段以及相应的序列化器字段类型。

序列化器字段是用于定义模型类或普通对象在序列化和反序列化过程中的转换规则和参数设置。常用的参数包括:

  1. 默认定义的序列化器字段:默认会对字段进行序列化输出。

  2. 字段名称和类型必须与模型类中的字段名或对象的属性名保持一致。

  3. 公共参数:

    • labelhelp_text:与ORM模型类中的verbose_namehelp_text参数一致。

    • write_only=True:指定该字段仅进行反序列化输入,不进行序列化输出。

    • read_only=True:指定该字段仅进行序列化输出,不进行反序列化输入。

    • required:指定该字段是否为必传字段,默认为True。若设置为False,前端未传递该字段时不会输出,传递时会输出。

    • default:给字段指定默认值。

    • allow_null:指定字段是否允许传递null值,默认不允许。若设置为True,允许传递null

    • error_messages:定制字段的错误提示信息,需传递字典类型。

    • validators:指定自定义校验规则(校验器),为列表类型。(下面会详细说明)

  4. CharField类型字段:

    • 默认不允许传递空字符串,可以设置allow_blank=True允许传递空字符串。

    • max_length指定最大字节数,min_length指定最小字节长度。

    • 前端传递的值为str、int、float类型会自动转化为字符串类型,传递其他类型会校验失败。

    • trim_whitespace=True(默认)会自动清除左右两侧的空格。

  5. IntegerField类型字段:

    • max_value指定最大值。

    • min_value指定最小值。

  6. DateTimeField类型字段:

    • format指定日期时间的格式化字符串。

实例化序列化器:

定义好Serializer类后,就可以创建Serializer对象了。

Serializer的构造方法为:

Serializer(instance=None, data=empty, **kwarg)

说明:

  • 用于序列化时,将模型类对象传入instance参数

  • 用于反序列化时,将要被反序列化的数据传入data参数

如何使用序列化器类实现序列化输出操作?

1.先创建序列化器对象

2.在创建序列化器对象时,使用instance传递参数

3.序列化操作的对象有四种?

a.模型对象 -- 无需传递many关键字参数

b.普通对象 -- 无需传递many关键字参数

c.查询集 -- 必须传递many=True

d.嵌套普通对象的列表 -- 必须传递many=True

4.使用创建序列化器对象.data属性获取序列化之后的数据(字典或者嵌套字典的列表)

projects\views.py

import json
from django.http import HttpResponse, JsonResponse, HttpRequest
from django.views import View
from .models import Project
from . import serializers


class ProjectDetailView(View):
    """
        GET /projects/<int:pk>/     获取某一条项目数据(将这一条项目数据以json对象形式返回)
    """

    def get(self, request, pk):
        data = {
            'code': 0,
            'msg': '',
        }
        try:
            # 1、从数据库中获取项目模型对象
            project_obj = Project.objects.get(id=pk)
            # 2、将模型对象转化为字典
        except Exception as e:
            data['code'] = 1
            data['msg'] = f'项目id({pk})不存在'

        serializer = serializers.ProjectSerializer(instance=project_obj)
        data['msg'] = '成功'
        data.update(serializer.data)
        return JsonResponse(data, status=200, json_dumps_params={
      'ensure_ascii': False
        # return JsonResponse(serializer.data, status=200, json_dumps_params={
        #     'ensure_ascii': False
        # })
 })

在第二段代码示例中,通过 Project.objects.get(id=pk) 从数据库中获取指定ID的项目对象,并使用 ProjectSerializer 实例化了一个序列化器。

  • 序列化数据:调用序列化器的 data 属性,将数据对象序列化为字典或其他数据类型。

data.update(serializer.data)

在第二段代码示例中,通过 serializer.data 获取到了对应项目对象的序列化数据,即一个包含字段及其值的字典。

  • 返回序列化的数据:根据需求,将序列化后的数据作为响应返回给客户端。

return JsonResponse(data, status=200, json_dumps_params={
      'ensure_ascii': False
 })

在第二段代码示例中,通过 JsonResponse 将序列化后的数据作为 JSON 格式的响应返回给客户端,其中设置了状态码为 200,并通过 json_dumps_params 参数确保非 ASCII 字符正确显示。

综合以上步骤,我们可以看到,在这个示例中,使用了模型类 Project 和序列化器类 ProjectSerializer,通过实例化序列化器并传入数据对象,将数据对象转换为序列化后的数据,并返回给客户端。这样,就完成了数据的序列化过程。

反序列化与数据校验

如何使用序列化器类实现反序列化输入操作?

1.在创建序列化器对象时,使用data传递待校验的参数(需要接收json转化为字典之后的数据)

2.传递给data的参数,必须为待校验的字典数据

3.必须得调用序列化器对象.is_valid()方法,才会开始对数据进行校验

4.仅仅只有调用is_valid()方法之后,才能通过序列化器对象.errors属性获取校验的结果(一般字典类型)

仅仅只有调用is_valid()方法之后,才能通过序列化器对象.validated_data属性获取校验通过的数据(一般字典类型)

5.默认定义的序列化器字段,都会进行反序列化输入(前端都必须得传递),也会进行序列化输出操作

6.往往把前端向后端发起请求的过程称为写(write),也称为反序列化输入

7.往往把后端向前端响应数据的过程称为读(read),也称为序列化输出

8.如果当前字段仅仅只需反序列化输入,可以设置write_only=True

9.如果当前字段仅仅只需序列化输出,可以设置read_only=True

10.可以在调用序列化器对象.is_valid()方法时,指定raise_exception=True,那么校验不通过时,会抛出异常,否则不会抛出异常

11.如果未调用序列化器对象.is_valid()方法,那么是不会开始校验的,

所以不能调用序列化器对象的errors属性(获取错误提示字典信息)、data属性(序列化输出的数据)、validated_data属性(获取校验通过的数据)

创建序列化器与添加校验规则

projects\serializers.py

from rest_framework import serializers, validators
from . import models


def validate_project_name(value):
    if '项目' not in value:
        raise serializers.ValidationError('项目名称中必须包好"项目"关键词')


class ProjectSerializer(serializers.Serializer):

    # 需求:
    # 在创建项目时,前端不需要传递id,但是必须将项目id返回给前端
    id = serializers.IntegerField(max_value=1000000, min_value=1,
                                  label='项目id', help_text='输出项目id', read_only=True)
    name = serializers.CharField(max_length=50, min_length=2, required=True,
                                 validators=[validate_project_name,
                                             validators.UniqueValidator(queryset=models.Project.objects.all(), message='项目名称已经存在')])
    leader = serializers.CharField(max_length=20,
                                   error_messages={
                                       'min_length': '项目负责人长度不能少于1个字符',
                                       'max_length': '项目负责人长度不能超过10个字符',
                                       'null': '项目负责人不能为null',
                                       'required': '项目负责人为必填参数',
                                       'blank': '项目负责人不能为空字符串'
                                   },
                                   allow_blank=True,
                                   trim_whitespace=True)
    is_execute = serializers.BooleanField(write_only=True)
    create_time = serializers.DateTimeField(read_only=True,
                                            format='%Y年%m月%d日 %H:%M:%S')

    def validate_name(self, value):
        if not str(value).endswith('22'):
            raise serializers.ValidationError('项目名称必须以22结尾')
        return value

    def validate(self, attrs):
        field1 = attrs.get('field1')
        field2 = attrs.get('field2')

        # 自定义校验逻辑
        if field1 == field2:
            raise serializers.ValidationError("Field1 and Field2 cannot be the same.")

        return attrs

当使用Django REST Framework进行序列化器字段校验时,以下是一些常见的校验用法的总结:

validators参数指定自定义校验规则(校验器),为列表类型

  1. 内置校验规则:

    • 在字段声明中使用参数来设置校验规则,例如min_lengthmax_lengthrequired等。

    • 使用DRF提供的内置校验器,例如UniqueValidator用于判断字段唯一性。

    • 可以使用DRF提供的UniqueValidator对字段是否唯一性进行校验,queryset参数为所有模型对象的查询集,message指定校验失败的错误提示信息

  2. 自定义校验器:

    • 在序列化器类中的validators参数中添加自定义校验器函数,函数名作为列表中的元素。校验器函数应接受一个参数,表示待校验的值。如果校验不通过,应抛出serializers.ValidationError异常,并可以指定具体的报错字符串作为ValidationError参数,如果校验通过,无需返回

    • 在序列化器类外部定义校验函数,并将其作为validators参数值列表中的元素。

  3. 单字段校验:

    • 在序列化器类中,定义以validate_字段名称命名的方法来对单个字段进行校验。该方法会在字段的其他验证规则通过后被自动调用。校验方法应接受一个参数,表示待校验的值。如果校验不通过,应抛出serializers.ValidationError异常,并可指定具体错误提示信息。

  4. 多个字段:

    • validate 是一个整体字段校验的方法,用于对多个字段进行相关校验逻辑。它接收一个参数 attrs,代表了序列化器中所有字段的值。在示例中,校验逻辑是判断 field1field2 是否相同,如果相同,则抛出 ValidationError 异常。

  5. 校验顺序:

    • 序列化器会按照以下顺序进行校验:

      • 按字段类型验证(例如CharField验证max_length、min_length等)。

      • 执行字段的所有校验规则项(包括字段的关键字参数和validators参数列表中的校验规则)。

      • 执行单字段校验方法。

      • 整体字段校验方法 validate。

总体而言,可以通过内置校验规则、自定义校验器函数和单字段校验方法来对序列化器字段进行校验。这些校验方式相互配合,可以实现对字段的多样化校验需求。在校验过程中,如果校验不通过,应抛出serializers.ValidationError异常,并可指定具体的错误提示信息。

实例化反序列化器与校验数据

data 参数是用于传递需要反序列化的数据,python_dict 是前端传递的项目参数的字典形式数据。

projects\views.py

import json
from django.http import HttpResponse, JsonResponse, HttpRequest
from django.urls import reverse
from django.views import View

from .models import Project
from . import serializers

class ProjectDetailView(View):
    def put(self, request, pk):
        """
        反序列化输入过程
        数据校验过程
        数据库操作过程
        序列化输出过程
        :param request:
        :param pk:
        :return:
        """
        # 从前端获取项目数据
        data = {
            'code': 0,
            'msg': '',
        }
        try:
            # 反序列化输入过程:将前端传递的字符串数据转化为python中的数据类型
            python_dict = json.loads(request.body)
        except Exception as e:
            data['code'] = 1
            data['msg'] = '参数有误'
            return JsonResponse(data, status=400, json_dumps_params={'ensure_ascii': False})

        serializer1 = serializers.ProjectSerializer(data=python_dict)
        if not serializer1.is_valid():
            return JsonResponse(serializer1.errors, status=400, json_dumps_params={'ensure_ascii': False})

        try:
            # 从数据库中读取id为pk的项目模型对象
            obj = Project.objects.get(id=pk)
        except Exception as e:
            data['code'] = 1
            data['msg'] = '项目id不存在'
            return JsonResponse(data, status=400, json_dumps_params={'ensure_ascii': False})

        # 更新项目数据
        obj.name = serializer1.validated_data.get('name') or obj.name
        obj.leader = serializer1.validated_data.get('leader') or obj.leader
        obj.is_execute = serializer1.validated_data.get('is_execute') or obj.is_execute
        obj.desc = serializer1.validated_data.get('desc') or obj.desc
        obj.save()
        serializer = serializers.ProjectSerializer(instance=obj)
        return JsonResponse(serializer.data, status=200, json_dumps_params={'ensure_ascii': False})


class ProjectView(View):
    """
    对于url与请求方法有如下要求
        GET /projects/              获取所有的项目数据(将所有的项目数据以json数组形式返回)
        POST /projects/             创建一条项目数据(前端需要以json形式传递项目参数,
                                        创建成功之后将项目数据以json对象形式返回)
    """

    def get(self, request):
        """
        数据库操作过程
        序列化输出过程
        :param request:
        :return:
        """
        # 从数据库中读取所有的项目数据(QuerySet对象)
        queryset = Project.objects.all()
        serializer = serializers.ProjectSerializer(instance=queryset, many=True)

        # 将嵌套字典的列表数据返回至前端
        return JsonResponse(serializer.data, safe=False, json_dumps_params={
            'ensure_ascii': False
        })

    def post(self, request: HttpRequest):
        """
        反序列化输入过程
        数据校验过程
        数据库操作过程
        序列化输出过程
        :param request:
        :return:
        """
        # 从前端获取项目数据
        data = {
            'code': 0,
            'msg': '',
        }
        try:
            # 反序列化输入过程:将前端传递的字符串数据转化为python中的数据类型
            python_dict = json.loads(request.body)
        except Exception as e:
            data['code'] = 1
            data['msg'] = '参数有误'
            return JsonResponse(data, status=400, json_dumps_params={'ensure_ascii': False})

        # 需要对前端请求参数进行大量的校验
        serializer1 = serializers.ProjectSerializer(data=python_dict)
        try:
            serializer1.is_valid(raise_exception=True)
        except Exception as e:
            return JsonResponse(serializer1.errors, status=400)

        # 创建项目数据
        try:
            # 数据库操作过程
            # project_obj = Project.objects.create(**python_dict)
            project_obj = Project.objects.create(**serializer1.validated_data)
        except Exception as e:
            data['code'] = 1
            data['msg'] = '参数有误'
            return JsonResponse(data, status=400, json_dumps_params={'ensure_ascii': False})

        # 将字典返回至前端
        # return JsonResponse(data, status=200, json_dumps_params={'ensure_ascii': False})
        serializer = serializers.ProjectSerializer(instance=project_obj)
        return JsonResponse(serializer.data, status=200, json_dumps_params={'ensure_ascii': False})

可以在调用序列化器对象.is_valid()方法时,指定raise_exception=True,那么校验不通过时,会抛出异常,否则不会抛出异常

如果未调用序列化器对象.is_valid()方法,那么是不会开始校验的,所以不能调用序列化器对象的errors属性(获取错误提示字典信息)、data属性(序列化输出的数据)、validated_data属性(获取校验通过的数据)

  • 在序列化器中,data参数用于接收要进行反序列化的数据。在这种情况下,python_dict是从请求体中解析出的数据,包含了要更新项目数据的字段和对应的值。

  • 通过调用is_valid()方法,可以对传入的数据进行验证。如果数据符合序列化器定义的规则,即通过验证,则is_valid()方法返回True;否则,返回False。

  • serializer1.validated_data是一个包含验证通过数据的字典,其中键是字段名称,值是验证通过的数据。

  • Project.objects.create(**serializer1.validated_data) 是在数据库中创建一个新的项目对象(Project 模型的实例)的操作。在这里,serializer1.validated_data 是经过校验后的有效数据字典,其中包含了前端传递的符合要求的项目参数。

补充说明常用的内置校验器

Django REST Framework提供了一些常用的内置校验器,用于对序列化器字段进行验证。以下是一些常见的内置校验器以及它们的用法:

  1. UniqueValidator(唯一性校验器):

    • 用于确保字段的值在数据库中是唯一的。

    • 在字段声明中的validators参数中使用该校验器。

    • 可以通过queryset参数指定用于检查唯一性的查询集。

    示例代码:

    from rest_framework import serializers, validators
    
    class MySerializer(serializers.Serializer):
        email = serializers.EmailField(validators=[validators.UniqueValidator(queryset=User.objects.all())])
    
  2. RegexValidator(正则表达式校验器):

    • 用于确保字段的值符合指定的正则表达式模式。

    • 在字段声明中的validators参数中使用该校验器。

    • 通过regex参数指定正则表达式模式。

    • 可选参数包括message(错误提示信息)和code(错误代码)。

    示例代码:

    from rest_framework import serializers
    
    class MySerializer(serializers.Serializer):
        phone_number = serializers.CharField(validators=[validators.RegexValidator(regex='^(\+\d{1,2}\s?)?(\d{3,4}-?)?\d{7,8}$', message='Invalid phone number.')])
    
  3. URLValidator(URL 校验器):

    • 用于确保字段的值是有效的URL。

    • 在字段声明中的validators参数中使用该校验器。

    • 可选参数包括message(错误提示信息)和code(错误代码)。

    示例代码:

    from rest_framework import serializers
    
    class MySerializer(serializers.Serializer):
        website = serializers.CharField(validators=[validators.URLValidator(message='Invalid URL.')])
    
  4. EmailValidator(电子邮件校验器):

    • 用于确保字段的值是有效的电子邮件地址。

    • 在字段声明中的validators参数中使用该校验器。

    • 可选参数包括message(错误提示信息)和code(错误代码)。

    示例代码:

    from rest_framework import serializers
    
    class MySerializer(serializers.Serializer):
        email = serializers.EmailField(validators=[validators.EmailValidator(message='Invalid email address.')])
    

以上是一些常用的内置校验器的说明和用法。这些内置校验器可以帮助您在序列化器中对字段进行验证,确保数据的合法性和完整性。

关联字段序列化:

关联字段序列化是指在序列化器中处理模型之间的关联关系,将关联模型的数据嵌套到主要模型的序列化结果中。

Django REST Framework 提供了多个字段类型来处理关联字段序列化。下面是一些常用的关联字段序列化的字段类型:

  1. PrimaryKeyRelatedField:使用关联模型的主键作为序列化输出和反序列化输入的值。

  2. StringRelatedField:使用关联模型的__str__()方法返回的字符串作为序列化输出的值。

  3. SlugRelatedField:使用指定字段作为关联模型对象的标识符进行序列化和反序列化。

  4. HyperlinkedRelatedField:使用关联模型的 URL 作为序列化输出和反序列化输入的值(适用于模型有自定义 URL 的情况)。

  5. Nested序列化器:嵌套使用另一个序列化器来序列化关联模型。

这些字段类型可以根据你的需求选择使用。例如,在 ModelSerializer 内部定义一个关联字段,或者在嵌套的序列化器中使用关联字段进行嵌套序列化。

使用关联字段序列化,你可以轻松地处理模型之间的关联关系,获取到相关模型的详细信息而不仅仅是外键或主键。

这里有两个模型类:Project 和 Interfaces 之间存在一对多的关系。具体来说,一个项目可以有多个接口,而一个接口只属于一个项目。

项目模型Project(父表)

from django.db import models

from utils.base_model import BaseModel
# Create your models here.
# 创建用户名、年龄、性别字段
#      varchar、integer、bool

class Project(BaseModel):
    name = models.CharField(verbose_name='项目名称', help_text='输入项目名称', unique=True,
                            max_length=50)
    leader = models.CharField(verbose_name='项目负责人', help_text='输入项目负责人',
                              max_length=20, null=True)
    is_execute = models.BooleanField(verbose_name='是否启动项目', help_text='请选择是否启动项目',
                                     default=True)
    desc = models.TextField(verbose_name='项目描述', help_text='输入项目描述',
                            null=True, blank=True)


    class Meta:
        db_table = 'tb_projects'
        verbose_name = '项目表'
        verbose_name_plural = '项目表'
        ordering = ['id']

    def __str__(self):
        return f'Project<id={self.id}, name={self.name}, leader={self.leader}>'

项目接口模型Interfaces(从表)

class Interfaces(BaseModel):

    name = models.CharField(verbose_name='接口名称', help_text='请输入接口名称', max_length=50)
    projects = models.ForeignKey('projects.Project', on_delete=models.CASCADE,
                                 verbose_name='所属项目id', help_text='请输入所属项目id',
                                 related_name='interfaces'
                                 )
    class Meta:
        db_table = 'tb_interfaces'
        verbose_name = '接口表'
        verbose_name_plural = '接口表'
        ordering = ['id']

    def __str__(self):
        return f'Interfaces<id={self.id}, name={self.name}>'

1. 如果需要获取关联表的数据,需要定义RelatedField类型的字段

2. 字段名称有要求

  • 如果是父表获取从表的数据,那么关联字段的名称默认为从表模型类名小写_set(这里Interfaces是从表因为使用了ForeignKey 字段来关联到 Project表中的主键

  • 如果在定义从表模型类外键字段时,指定了related_name参数,那么related_name参数值与关联字段的名称一致

3. 如果关联字段为PrimaryKeyRelatedField,那么操作的关联表的主键id

  • 如果需要进行反序列化输入(对数据进行校验),那么必须指定queryset参数,为关联表的查询集对象

  • 如果不需要进行反序列化输入,那么必须指定read_only=True

  • 如果关联字段的数据有多条,那么必须指定many=True

from rest_framework import serializers, validators
from . import models
from interfaces.models import Interfaces

class ProjectSerializer(serializers.Serializer):

    # 需求:
    # 在创建项目时,前端不需要传递id,但是必须将项目id返回给前端
    id = serializers.IntegerField(max_value=1000000, min_value=1,
                                  label='项目id', help_text='输出项目id', read_only=True)
    name = serializers.CharField(max_length=50, min_length=2, required=True,
                                 validators=[validators.UniqueValidator(queryset=models.Project.objects.all(), message='项目名称已经存在')])
    leader = serializers.CharField(max_length=20,
                                   error_messages={
                                       'min_length': '项目负责人长度不能少于1个字符',
                                       'max_length': '项目负责人长度不能超过10个字符',
                                       'null': '项目负责人不能为null',
                                       'required': '项目负责人为必填参数',
                                       'blank': '项目负责人不能为空字符串'
                                   },
                                   allow_blank=True,
                                   trim_whitespace=True)

    # 需求:
    # 在创建项目时,前端必须传递is_execute,但是不要将项目is_execute返回给前端
    is_execute = serializers.BooleanField(write_only=True)

    create_time = serializers.DateTimeField(read_only=True,
                                            format='%Y年%m月%d日 %H:%M:%S')

    # interfaces_set = serializers.PrimaryKeyRelatedField(queryset=Interfaces.objects.all(), many=True)
    interfaces = serializers.PrimaryKeyRelatedField(queryset=Interfaces.objects.all(), many=True)
    # interfaces = serializers.PrimaryKeyRelatedField(many=True, read_only=True)

4. 如果关联字段为StringRelatedField,那么会将关联模型类中定义的__str__方法返回值作为输出内容

  • StringRelatedField默认指定了read_only=True

  • 如果关联字段的数据有多条,那么必须指定many=True

interfaces = serializers.StringRelatedField(many=True)

5. 如果关联字段为SlugRelatedField,那么操作的关联表的特定字段

  • 需要进行反序列化输入(对数据进行校验),那么必须指定queryset参数,为关联表的查询集对象,同时关联字段最好要有唯一约束

  • 如果不需要进行反序列化输入,那么必须指定read_only=True

  • 如果关联字段的数据有多条,那么必须指定many=True

interfaces = serializers.SlugRelatedField(slug_field='name', read_only=True, many=True)

6. 由于定义的任意序列化器类最终为Field子类,所有可以作为任意一个序列化器类中的字段来使用

class InterfaceXXX(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    name = serializers.CharField()
    projects = serializers.StringRelatedField()
interfaces = InterfaceXXX(read_only=True, many=True)

ModelSerializer

ModelSerializer 是 Django REST Framework 中的一个序列化器类,用于简化基于模型的序列化和反序列化。

使用 ModelSerializer 可以自动根据模型类生成序列化器,并处理字段、验证、序列化和反序列化等操作,减少编写重复代码的工作量。

以下是 ModelSerializer 的一些重要特点和使用方式:

  1. 自动生成字段:ModelSerializer 会自动根据模型类生成序列化器字段,可以通过设置 fieldsexclude 属性来指定要包含或排除的字段。如果将 fields 设置为 '__all__',则表示包含模型的所有字段。

  2. 默认实现 CRUD 方法:ModelSerializer 继承自 serializers.ModelSerializer,它默认提供了 create()update() 等方法的实现。这些方法通过调用模型类的 create()save() 方法来完成对象的创建和更新操作。

  3. 自动生成验证器:ModelSerializer 会自动为模型字段生成一些常见的验证规则,例如字段类型、最大长度等。对于其他特殊的验证规则,可以在序列化器类中手动添加验证器。

  4. 关联字段处理:ModelSerializer 可以自动处理模型之间的关联字段,例如外键、一对一关系、多对多关系等。它会将关联字段反序列化为关联对象,并在序列化时嵌套展示关联对象的数据。

  5. 自定义字段和方法:除了自动生成的字段外,你还可以在 ModelSerializer 中定义额外的字段,以及自定义序列化方法和验证方法,以实现更高级的定制需求。

序列化器类与模型类的关系?

  1. 这两个类高度相似

  2. 当模型类中字段非常多时,定义序列化器类就相当麻烦

  3. 可以定义模型序列化器,来自动将模型类中的字段生成序列化器中的字段

  • 必须继承serializers.ModelSerializer父类

  • 必须在Meta内部类中,使用model类属性关联一个模型类

  • 需要在在Meta内部类中,使用fields指定模型类中哪些字段需要生成序列化器字段

  • 如果指定fields = '__all__',那么会将模型类中的所有字段生成序列化器字段

  • 在生成的字段中,会将主键id设置为IntegerField类型,同时会自动指定read_only=True

  • 会将DateTimeField类型中有添加auto_now_add或auto_now参数,会自动指定read_only=True

  • 会将unique=True的字段,自动生成validators=中UniqueValidator唯一约束

  • 如果只需要将模型类中某些字段生成序列化器字段,可以将这些字段组成元组或者列表,传递给fields

  • 可以在模型序列化器类中定义模型类中的字段(会覆盖自动生成的字段)

  • fields类属性,如果指定的说元组类型,那么必须包含所有的序列化器字段(模型类中的、模型类外的)

  • 如果fields为'__all__'或者exclude,那么无需指定模型类外的字段

  • 模型序列化器类自带create、update方法,一般无需重写

  • 可以在Meta内部类中的extra_kwargs类属性中对模型类中自动生成的字段进行修改

  • 将模型类字段名作为key,把具体要修改的参数key-value字典作为值

当使用 ModelSerializer 时,以下是一些针对上述特点的示例说明:

自动生成字段:

from rest_framework import serializers, validators
from . import models
from interfaces.models import Interfaces


def validate_project_name(value):
    if '项目' not in value:
        raise serializers.ValidationError('项目名称中必须包好"项目"关键词')   

class ProjectModelSerializer(serializers.ModelSerializer):

    # 需求1:
    #   需要定义一些在模型类中没有的字段(仅仅只进行反序列化输入操作),例如:确认密码
    xxx = serializers.CharField(write_only=True)

    # 需求2:
    #   需要定义一些在模型类中没有的字段(仅仅只进行序列化输出操作),例如:token
    token = serializers.CharField(read_only=True)

    class Meta:
        model = models.Project
        # fields = '__all__'
        fields = ['id', 'name', 'leader', 'is_execute', 'xxx', 'token']
        # exclude = ['create_time', 'update_time']
        extra_kwargs = {
            'name': {
                'min_length': 2,
                'label': '新的label',
                'write_only': True,
                'validators': [
                    validate_project_name, validators.UniqueValidator(queryset=models.Project.objects.all(), message='项目名称已经存在')
                ],
                'error_messages': {
                     'min_length': '项目负责人长度不能少于1个字符',
                     'max_length': '项目负责人长度不能超过10个字符',
                     'null': '项目负责人不能为null',
                     'required': '项目负责人为必填参数',
                     'blank': '项目负责人不能为空字符串'
                },
            }
        }

    def validate(self, attrs: dict):
        var = attrs.get('xxx')
        # 对xxx进行校验
        # if var != '密码':
        #     raise serializers.ValidationError('用户输入的确认密码与密码不一致')

        attrs.pop('xxx')
        # attrs['token'] = 'fh ggg'
        return attrs

    def create(self, validated_data):
        instance = super().create(validated_data)
        # 动态添加属性用于序列化输出
        instance.token = 'xxxdff ghgosfhsoh shfoshof'
        return instance


class ProjectModelSerializer1(serializers.ModelSerializer):
    class Meta:
        model = models.Project
        fields = '__all__'


class ProjectModelSerializer2(serializers.ModelSerializer):
    class Meta:
        model = models.Project
        fields = ['name', 'leader']

在上述示例中,我们通过指定 exclude 属性来排除模型中的特定字段,这样这些字段就不会在序列化器中生成。

extra_kwargs:

在序列化器类的Meta内部类的extra_kwargs属性中,可以对指定字段进行额外的配置

extra_kwargs = {
            'name': {
                'min_length': 2,
                'label': '新的label',
                'write_only': True,
                'validators': [validate_project_name],
                'error_messages': {
                     'min_length': '项目负责人长度不能少于1个字符',
                     'max_length': '项目负责人长度不能超过10个字符',
                     'null': '项目负责人不能为null',
                     'required': '项目负责人为必填参数',
                     'blank': '项目负责人不能为空字符串'
                },
            }
        }

以下是对字段'name'进行配置的详细说明:

  • 'min_length': 2:设置字段的最小长度为2。

  • 'label': '新的label':修改字段的显示标签为'新的label'。

  • 'write_only': True:将该字段设为只写字段,即仅用于反序列化输入操作,不会在序列化输出中显示。

  • 'validators': [validate_project_name]:设置字段的验证器为'validate_project_name'函数,用于对该字段的值进行其他复杂校验操作。

  • 'error_messages': {...}:自定义字段校验出错时的错误信息,包括最小长度、最大长度、null、required和blank,分别对应不满足最小长度、超过最大长度、为null、为空和为空字符串的错误提示。

默认实现 create与update方法:

 def create(self, validated_data):
        instance = super().create(validated_data)
        # 动态添加属性用于序列化输出
        instance.token = 'xxxdff ghgosfhsoh shfoshof'
        return instance
 def update(self, instance, validated_data):
        instance = super().update(instance, validated_data)
        
        # 执行更新逻辑
        instance.name = validated_data.get('name', instance.name)
        instance.description = validated_data.get('description', instance.description)
        instance.save()
        
        return instance

在上述示例中,我们可以重写 create()update() 方法,以应用于特定的创建和更新逻辑。这些方法接收 validated_data 参数,其中包含经过验证的数据。

自定义验证器:

'validators': [ validate_project_name],

'validators': [...] 中添加了一个自定义验证器 validate_project_name 验证器可以对 'name' 字段进行自定义的验证。

自定义字段:

你可以在 ModelSerializer 中定义额外的字段,这些字段不在模型中存在,但可以根据需要进行序列化和反序列化。


    # 需求1:
    #   需要定义一些在模型类中没有的字段(仅仅只进行反序列化输入操作),例如:确认密码
    xxx = serializers.CharField(write_only=True)

    # 需求2:
    #   需要定义一些在模型类中没有的字段(仅仅只进行序列化输出操作),例如:token
    token = serializers.CharField(read_only=True)

在上述示例中,我们在 YourModelSerializer 中添加了一个名为 xxx 和token 字段,并使用 SerializerMethodField 来定义它。

使用 ModelSerializer 进行序列化和反序列化

不同方式定义的序列化器对象的区别?

  1. 如果在创建序列化器对象时,仅仅只传递instance参数,那么当前只会进行序列化输出操作,不可能进行反序列化输入操作,不可以调用序列化器对象的save()的方法

  2. 如果在创建序列化器对象时,仅仅只传递data参数,那么在调用is_valid()方法后会进行反序列化输入操作,同时在调用save方法时,会自动调用序列化器对象的create方法(用于数据的创建操作),会把create方法返回的数据作为序列化输出的数据源

  3. 如果在创建序列化器对象时,仅仅只传递data参数,调用is_valid()方法,对数据进行校验之后,直接调用data属性(未调用save方法),会把校验通过的数据作为序列化输出的数据源,如果校验通过的数据中有相应的数据,同时该字段需要输出,那么输出的数据中才会有该字段

  4. 如果在创建序列化器对象时,同时传递instance和data参数,调用is_valid()方法,对数据进行校验,那么在调用save方法时,会自动调用序列化器对象的update方法(用于数据的更新操作),会把update方法返回的数据作为序列化输出的数据源

现在,我将分别解释如何处理序列化和反序列化的情况。

from rest_framework import serializers
from .models import Book

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ['id', 'title', 'author', 'publication_date']

然后,我们可以按照之前的用例进行演示。

用例1:仅进行序列化输出操作

book = Book(title="Python入门", author="张三", publication_date="2023-01-01")
serializer = BookSerializer(instance=book)
serialized_data = serializer.data
print(serialized_data)

输出结果:

{'id': None, 'title': 'Python入门', 'author': '张三', 'publication_date': '2023-01-01'}

用例2:进行反序列化输入操作并创建新对象

data = {'title': 'Java入门', 'author': '李四', 'publication_date': '2023-02-01'}
serializer = BookSerializer(data=data)

if serializer.is_valid():
    book = serializer.save()
    serialized_data = serializer.data
    print(serialized_data)
else:
    errors = serializer.errors
    print(errors)

输出结果:

{'id': 1, 'title': 'Java入门', 'author': '李四', 'publication_date': '2023-02-01'}

用例3:进行反序列化输入操作未调用save方法,会把校验通过的数据作为序列化输出的数据源

from rest_framework import serializers

class BookSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=100)
    author = serializers.CharField(max_length=100)
    publication_date = serializers.DateField()

data = {'title': 'Python入门', 'author': '张三', 'publication_date': '2023-01-01'}
serializer = BookSerializer(data=data)

if serializer.is_valid():
    validated_data = serializer.validated_data
    print(validated_data)
else:
    errors = serializer.errors
    print(errors)

输出结果:

{'title': 'Python入门', 'author': '张三', 'publication_date': datetime.date(2023, 1, 1)}

用例4:进行反序列化输入操作并更新对象

book = Book.objects.get(id=1)
data = {'title': 'Python高级编程', 'author': '张三', 'publication_date': '2023-03-01'}
serializer = BookSerializer(instance=book, data=data)

if serializer.is_valid():
    book = serializer.save()
    serialized_data = serializer.data
    print(serialized_data)
else:
    errors = serializer.errors
    print(errors)

输出结果:

{'id': 1, 'title': 'Python高级编程', 'author': '张三', 'publication_date': '2023-03-01'}

使用 ModelSerializer 简化了序列化器的定义,并提供了默认的创建和更新方法,使得代码更加简洁和易读。希望对你有所帮助!

REST Framework 中的 Request 对象

在 Django REST Framework (DRF) 中,Request 对象是一个封装了 HTTP 请求的类,它提供了用于访问和处理请求数据的一些便捷方法和属性。下面是一些关于 DRF 中的 Request 对象的重要说明:

  1. Request 对象是 DRF 自定义的类:虽然 DRF 的 Request 对象与 Django 的 HttpRequest 类在功能上有些相似,但它们并不是同一个对象。DRF 的 Request 对象属于 rest_framework.request.Request 类。

  2. Request 对象的创建:在 DRF 中,Request 对象是由框架自动创建的,你无需手动创建它。在视图函数或视图类中,它会作为参数传递给你的视图函数/方法,或者可以通过 self.request 来获取。

  3. request.data:类似于 Django 中的 request.POSTrequest.FILES。它用于解析请求体的参数,包括 POST、PUT 和 PATCH 请求的数据。无论是表单数据还是 JSON 数据,request.data 都可以自动进行解析。你可以使用它来获取和处理请求体参数。

  4. request.query_params:类似于 Django 中的 request.GET。它用于获取查询字符串参数,即 URL 中的键值对参数。你可以使用它来获取和处理查询字符串参数。

  5. request.method:获取请求方法,如 GET、POST、PUT、DELETE 等。

  6. request.user:获取当前用户对象,用于认证和权限控制。

  7. request.path:获取请求的路径,即 URL 的路径部分。

除了上述提到的一些属性和方法之外,DRF 的 Request 对象还提供了其他有用的属性和方法,例如:

  • request.content_type:获取请求体的内容类型。

  • request.accepted_renderer:获取用于响应内容渲染的渲染器对象。

  • request.version:获取请求使用的 API 版本。

  • request.query_params.get('key', default):以安全方式获取指定键的查询字符串参数,如果不存在,则返回提供的默认值。

总之,DRF 的 Request 对象提供了许多便捷的方法来处理和操作请求数据。你可以使用 request.data 解析请求体参数,使用 request.query_params 获取查询字符串参数,并使用其他属性和方法进行更高级的操作。

默认的解析器类

提供针对请求头中Content-Type参数,自动解析请求参数

在 Django REST Framework 中,默认提供了三种常用的解析器类:

  1. JSONParser:用于解析 JSON 格式的请求数据,Content-Type 为 "application/json"。

  2. FormParser:用于解析表单数据,Content-Type 为 "application/x-www-form-urlencoded"。

  3. MultiPartParser:用于解析多部分表单数据(例如文件上传),Content-Type 为 "multipart/form-data"。

这三种解析器类都是默认包含在 Django REST Framework 中的,并可以通过在全局的 settings.py 文件中的 REST_FRAMEWORK 配置中来设置

修改使用的解析器类

在 Django REST Framework 中,以下是两种修改解析器类的方式:

方式一:在全局的 settings.py 文件中的 REST_FRAMEWORK 配置中指定需要使用的解析器类。在 DEFAULT_PARSER_CLASSES 列表中配置所需的解析器类即可。示例如下:

# settings.py

REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES': [
        'rest_framework.parsers.JSONParser',
        'rest_framework.parsers.FormParser',
    ],
    ...
}

在上述示例中,我们将 JSONParserFormParser 指定为默认的解析器类。

方式二:在具体的某个类视图中,通过设置类属性 parser_classes 来指定特定的解析器类列表。这种方式会覆盖全局配置,使得该类视图仅使用指定的解析器类。示例如下:

from rest_framework.parsers import JSONParser
from rest_framework.views import APIView

class MyView(APIView):
    parser_classes = [JSONParser]
    
    # 视图的其他代码...

在上述示例中,我们在 MyView 类中指定只使用 JSONParser 解析器类。

REST Framework 中的 Response对象

在 Django REST Framework (DRF) 中,Response 对象用于封装响应数据并返回给客户端。它提供了一些便捷的方法和属性来构建和返回响应。下面是一些关于 DRF 中的 Response 对象的重要说明:

  1. Response 对象是 DRF 自定义的类:虽然 DRF 的 Response 对象与 Django 的 HttpResponse 类在功能上有些相似,但它们并不是同一个对象。DRF 的 Response 对象属于 rest_framework.response.Response 类。

  2. 创建和返回响应:你可以在视图函数/方法中创建 Response 对象,并将要返回的数据作为参数传递给它。然后,你可以通过返回 Response 对象来发送响应到客户端。

  3. Response 对象的内容:Response 对象的内容可以是任意类型的数据,如字典、列表等。DRF 会自动将这些数据进行序列化,并根据请求的 Accept 头部选择合适的渲染器来渲染响应数据。

  4. 状态码:你可以通过参数或属性设置 Response 对象的状态码,例如 status=200response.status_code = 200

  5. 其他属性和方法:Response 对象还提供了其他一些常用的属性和方法,例如 response.data 可以用于访问响应的数据,response.status_code 可以获取状态码,response.headers 可以设置响应的头部信息等。

下面是一个示例,展示了在视图函数中如何使用 Response 对象:

from rest_framework.response import Response

def my_view(request):
    data = {'message': 'Hello, World!'}
    return Response(data, status=200)

在上述示例中,我们定义了一个名为 my_view 的简单视图函数。在函数体内,我们创建了一个字典 data,其中包含我们要返回给客户端的数据。然后,我们通过创建一个 Response 对象并将 data 作为参数传递给它,来构建响应对象。最后,我们使用 return 语句返回这个 Response 对象,将响应发送到客户端。

修改使用的渲染器类

在 Django REST Framework (DRF) 中,你可以指定默认的响应渲染器类来确定如何将响应数据序列化和渲染成特定的格式。默认情况下,DRF 根据请求的 Accept 头部选择合适的渲染器进行渲染,但你也可以通过设置默认渲染器类来指定一个特定的渲染器作为默认选项。

方式一:在全局的 settings.py 文件中的 REST_FRAMEWORK 配置中指定需要使用的渲染器类(Renderer classes)。在 DEFAULT_RENDERER_CLASSES 中配置所需的渲染器类,以覆盖默认设置。示例如下:

# settings.py

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ],
    ...
}

在上述示例中,我们将 JSONRendererBrowsableAPIRenderer 指定为默认的渲染器类。

方式二:在具体的某个类视图中,通过设置类属性 renderer_classes 来指定特定的渲染器类列表。这种方式会覆盖全局配置,使得该类视图仅使用指定的渲染器类。示例如下:

from rest_framework.renderers import JSONRenderer
from rest_framework.views import APIView

class MyView(APIView):
    renderer_classes = [JSONRenderer]
    
    # 视图的其他代码...

在上述示例中,我们在 MyView 类中指定只使用 JSONRenderer 渲染器类。

需要注意的是,方式二的优先级高于方式一,即在同一个视图中设置了 renderer_classes 属性后,会忽略全局配置中的默认渲染器类。


Response 对象构造函数

在 Django REST Framework (DRF) 中,Response 对象的构造函数接受不同的参数来创建响应对象。下面是一些常用的 Response 对象构造函数中可以传递的值的示例说明:

数据(data)参数:

你可以将要返回给客户端的数据作为 data 参数传递给 Response 对象的构造函数。这可以是任何类型的数据,如字典、列表等。

序列化处理后的数据,一般为serializer.data(python基本数据类型, 字典, 嵌套字典的列表)

from rest_framework.response import Response

def my_view(request):
    data = {'message': 'Hello, World!'}
    return Response(data)
状态码(status)参数:

你可以使用 status 参数来设置响应的状态码。它可以是整数值,代表 HTTP 响应的状态码。

from rest_framework.response import Response

def my_view(request):
    data = {'message': 'Not found'}
    return Response(data, status=404)
响应头(headers)参数:

通过 headers 参数,你可以设置响应的头部信息。它是一个字典,包含键值对表示头部字段和对应的值。

from rest_framework.response import Response

def my_view(request):
    data = {'message': 'Success'}
    headers = {'X-Custom-Header': 'value'}
    return Response(data, headers=headers)
其他参数和选项:

Response 对象还有其他可选的参数和选项,例如设置 content_type 参数来指定响应的内容类型,或者使用 template_name 参数来指定响应的模板名称等。请参考 DRF 的文档以获取更多信息。

以上是一些常用的 Response 对象构造函数中可以传递的值的示例说明。你可以根据需要使用这些参数来创建自定义的响应对象,并根据具体情况设置合适的数据、状态码、头部或其他选项。

APIView

APIView 是 Django REST Framework 中的一个基础类,用于构建 RESTful API。它提供了处理 HTTP 请求的方法,并帮助我们编写视图函数。

在 Django 中,我们可以使用函数视图来处理请求和生成响应。而在 Django REST Framework 中,APIView 类提供了一种基于类的方式来编写视图,具有更强大的功能和更灵活的扩展性。

APIView 类是一个抽象基类,不能直接实例化,我们需要通过继承它来创建自己的视图类。子类需要至少实现 get()post()put()patch()delete() 等方法中的一种或多种来处理不同类型的请求。

get() 方法为例,当客户端发送 GET 请求时,Django REST Framework 会调用视图类的 get() 方法来处理该请求。类似地,其他方法会处理对应类型的请求(如 post() 处理 POST 请求,put() 处理 PUT 请求等)。

除了这些方法,APIView 还提供了其他有用的功能。例如:

  • 身份验证和权限控制:可以在视图类中配置身份验证方式,并根据用户权限限制对资源的访问。

  • 序列化和反序列化:可以在视图类中使用序列化器来将数据对象转换为 JSON 或其他格式,并在接收请求时将数据反序列化为对象。

  • 分页和过滤:可以轻松实现数据的分页和过滤,以控制数据的展示和访问。

  • 错误处理:可以在视图类中定义错误处理逻辑,并返回合适的错误响应。

模型代码

from django.db import models


class Book(models.Model):
    name = models.CharField(verbose_name='书名', max_length=32)
    price = models.CharField(verbose_name='价格', max_length=32)

    # 外键 书跟出版社是一对多
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    # 外键 书跟作者是多对多
    authors = models.ManyToManyField(to='Author')


class Publish(models.Model):
    name = models.CharField(verbose_name='出版社名称', max_length=32)
    address = models.CharField(verbose_name='出版社地址', max_length=32)


class Author(models.Model):
    name = models.CharField(verbose_name='作者姓名', max_length=32)
    phone = models.CharField(verbose_name='电话号码', max_length=11)

路由代码

from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', views.BookView.as_view()),
    path('books/<int:pk>/', views.BookDetailView.as_view()),
]

序列化器代码

"""ModelSerializer的用法"""


from rest_framework import serializers
from .models import Book

class BookSerializer(serializers.ModelSerializer):

    publish_detail = serializers.SerializerMethodField()
    author_list = serializers.SerializerMethodField()

    class Meta:
        model = Book
        fields = '__all__'
        extra_kwargs = {
            'name': {'max_length': 8},
            'publish_detail': {'read_only': True},
            'author_list': {'read_only': True},
            'publish': {'write_only': True},
            'authors': {'write_only': True}
        }

    def validate_name(self, name):
        if name.startswith('sb'):
            raise serializers.ValidationError('书名不能以sb开头')
        return name

    def validate(self, attrs):
        if attrs.get('name') == attrs.get('publish'):
            raise serializers.ValidationError('书名和出版社名不能一样')
        return attrs
    
    def get_publish_detail(self, obj):
        return {'name': obj.publish.name, 'address': obj.publish.address}

    def get_author_list(self, obj):
        authors = obj.authors.all()
        author_list = [{'name': author.name, 'phone': author.phone} for author in authors]
        return author_list

这里添加了两个额外的字段 publish_detail 和 author_list。

publish_detail author_list 都使用了 SerializerMethodField 类来实现。这个类可以根据你提供的方法名,将它们的计算结果加入到序列化输出中。

然后定义了两个计算方法,用于获取模型中额外的信息:

get_publish_detail() 方法获取 publish 关联对象的 nameaddress 字段的值,并将其放入一个字典中。

get_author_list() 方法从 authors 关联对象集合中获取每个作者的 namephone 字段的值,并将其放入一个列表中。

    def get_publish_detail(self, obj):
        return {'name': obj.publish.name, 'address': obj.publish.address}

    def get_author_list(self, obj):
        authors = obj.authors.all()
        author_list = [{'name': author.name, 'phone': author.phone} for author in authors]
        return author_list

SerializerMethodField定制,该方法能够序列化定制所有的任何的字段先想好定制成什么样子,之后新字段名跟get_后面即可,记得定制一个就一定要配合一个get_方法,可以自定义返回格式,就说明个性化能力强。

视图代码

from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.throttling import AnonRateThrottle
from rest_framework.views import APIView

class BookView(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = (IsAuthenticated,)
    throttle_classes = (AnonRateThrottle,)

    def get(self, request):  # 查询所有
        # 查询所有数据
        books = Book.objects.all()
        # 序列化数据
        ser = BookSerializer(instance=books, many=True)
        return Response(ser.data)

    def post(self, request):
        ser = BookSerializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '新增成功', 'result': ser.data})
        else:
            return Response({'code': 101, 'msg': ser.errors})


class BookDetailView(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = (IsAuthenticated,)
    throttle_classes = (AnonRateThrottle,)

    def put(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(data=request.data, instance=book)
        if ser.is_valid():
            ser.save()
            return Response({'code':100, 'msg':'修改成功'})
        else:
            return Response({'code':101, 'msg':ser.errors})

    def get(self, request, pk):
        book = Book.objects.filter(pk=pk)
        ser = BookSerializer(instance=book)
        return Response(ser.data)

    def delete(self, request, pk):
        Book.objects.filter(pk=pk).delete()
        return Response('删除成功')

def post(self, request):定义了一个名为 post() 的方法,并接收 self 和 request 两个参数。self 表示视图实例本身,而 request 是客户端发送的 HTTP 请求对象。

ser = BookSerializer(data=request.data):创建一个名为 ser 的序列化器实例,使用 BookSerializer 类对客户端发送的数据进行反序列化。data=request.data 参数指定要反序列化的数据来源,通常是请求的数据体。

这里为 为BookView 和 BookDetailView 类添加了与身份认证、权限检查和流量控制相关的常用类属性。这样,当请求到达这些视图时,系统会根据设置进行相应的身份认证、权限检查和流量控制。当请求到达对应的URL路径时,Django会自动进行身份认证、权限检查和流量控制,并调用相应的视图处理函数。

这些设置放在 settings.py 中可以让你在整个项目中统一设置这些功能,减少重复代码,并能够轻松地进行全局的更改。


REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.TokenAuthentication',
        # 其他认证类...
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
        # 其他权限检查类...
    ],
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',
        # 其他流量控制类...
    ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/day',  # 匿名用户每天最多100个请求
        # 其他流量控制速率...
    },
}
上述代码片段配置了默认的认证类、权限检查类和流量控制类,并设置了默认的流量控制速率。

GenericAPIView

GenericAPIView 是 Django Rest Framework 中的一个视图类,它提供了一些常用的通用功能和方法,使编写 API 视图更加简单和高效。

GenericAPIView 类是一个抽象基类,不能直接使用,需要通过继承来创建自定义的视图类。这个类结合了 Django 的 View 类和 DRF 的 mixin 类,提供了一些常见的功能,如序列化、查询集过滤、分页、认证和权限等。

GenericAPIView 类有一些常用的属性和方法,用于处理序列化、查询集、过滤、分页等功能。以下是对这些属性和方法的简要说明:

属性

  • queryset:指定视图所使用的查询集。比如 queryset = Book.objects.all()

  • serializer_class:指定视图所使用的序列化类。比如 serializer_class = BookSerializer

  • lookup_field:指定在单个对象查询时使用的字段,默认为 'pk'。比如 lookup_field = 'id'

  • filter_backends:指定过滤器后端类的配置列表,用于对查询集进行过滤。比如 filter_backends = [SomeFilterBackend]

  • pagination_class:指定分页器类的配置,用于对查询结果进行分页。比如 pagination_class = SomePaginationClass

方法

  • get_queryset():返回视图使用的查询集。可以根据需要在子类中重写该方法来实现自定义的查询集逻辑。

  • get_object():根据给定的查询参数,返回查询集中的单个对象。默认使用 lookup_field 和 URL 关联的值来进行查询。

  • get_serializer():返回视图使用的序列化器实例。可以根据需要在子类中重写该方法来实现自定义的序列化器逻辑。

  • filter_queryset(queryset):根据视图的过滤器后端配置列表,对查询集进行过滤,并返回过滤后的结果。这是在视图中显式调用的方法。

通过使用这些属性和方法,可以很方便地实现序列化、查询集过滤、分页等功能,简化了 API 视图的编写过程。

表模型代码

# 表模型代码
from django.db import models


class Book(models.Model):
    name = models.CharField(verbose_name='书名', max_length=32)
    price = models.CharField(verbose_name='价格', max_length=32)

    def __str__(self):
        return self.name

    # 外键 书跟出版社是一对多
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    # 外键 书跟作者是多对多
    authors = models.ManyToManyField(to='Author')

    def publish_detail(self):
        return {'name': self.publish.name, 'address': self.publish.address}

    def author_list(self):
        list = []
        for author in self.authors.all():
            list.append({'name': author.name, 'phone': author.phone})
        return list


class Publish(models.Model):
    name = models.CharField(verbose_name='出版社名称', max_length=32)
    address = models.CharField(verbose_name='出版社地址', max_length=32)

    def __str__(self):
        return self.name


class Author(models.Model):
    name = models.CharField(verbose_name='作者姓名', max_length=32)
    phone = models.CharField(verbose_name='电话号码', max_length=11)

    def __str__(self):
        return self.name

注意:定制字段的方法是模型表在里写的, 序列化类里面只需要写新定制字段

    def publish_detail(self):
        return {'name': self.publish.name, 'address': self.publish.address}

    def author_list(self):
        list = []
        for author in self.authors.all():
            list.append({'name': author.name, 'phone': author.phone})
        return list
....

序列化类代码

# 序列化类代码
class BookSerializer(serializers.ModelSerializer):
    class Meta:
        # 跟book表有强关联
        model = Book
        # fields = ['写需要序列化的字段名',[]···]
        # 如果fields = '__all__'这样写就表明序列化所有字段
        fields = '__all__'
        # extra_kwargs = {'字段名': {'约束条件': 约束参数},是反序列化字段
        extra_kwargs = {'name': {'max_length': 8},
                        'publish_detail': {'read_only': True},
                        'authors_list': {'read_only': True},
                        'publish': {'write_only': True},
                        'authors': {'write_only': True}
                        }

    def validate_name(self, name):
        if name.startswith('sb'):
            raise ValidationError('书名不能以sb开头')
        else:
            return name

视图代码

from rest_framework.generics import GenericAPIView
from .models import Book    # 引入模型类
from .serializer import BookSerializer    # 引入序列化器类
from rest_framework.response import Response


class BookView(GenericAPIView):
    queryset = Book.objects.all()    # 指定查询集为所有的图书对象
    serializer_class = BookSerializer    # 指定序列化器类为图书序列化器

    def get(self, request):
        objs = self.get_queryset()    # 获取查询集中的所有对象
        ser = self.serializer_class(instance=objs, many=True)    # 序列化查询结果
        return Response(ser.data)    # 返回序列化后的数据列表

    def post(self, request):
        ser = self.get_serializer(data=request.data)    # 创建序列化器实例,传入请求数据
        if ser.is_valid():
            ser.save()    # 验证通过后保存数据到数据库
            return Response({'code': 100, 'msg': '新增成功', 'result': ser.data})    # 返回成功响应
        else:
            return Response({'code': 101, 'msg': ser.errors})    # 返回错误响应
        
       # ser.is_valid(raise_exception=True)    # 验证数据有效性,若验证失败会抛出异常
       # ser.save()    # 保存数据到数据库

       # return Response({'code': 100, 'msg': '新增成功', 'result': ser.data})    # 返回成功响应


class BookDetailView(GenericAPIView):
    queryset = Book.objects.all()    # 指定查询集为所有的图书对象
    serializer_class = BookSerializer    # 指定序列化器类为图书序列化器

    def get(self, request, pk):
        obj = self.get_object()    # 根据给定的主键获取对应的图书对象
        ser = self.get_serializer(instance=obj)    # 序列化获取到的图书对象
        return Response(ser.data)    # 返回序列化后的数据

    def put(self, request, pk):
        obj = self.get_object()    # 根据给定的主键获取对应的图书对象
        ser = self.get_serializer(instance=obj, data=request.data)    # 创建序列化器实例,并传入请求数据和要更新的对象
        if ser.is_valid():
            ser.save()    # 验证通过后更新数据
            return Response({'code':102, 'msg':'修改成功'})    # 返回成功响应
        else:
            return Response({'code':103, 'msg':ser.errors})    # 返回错误响应

    def delete(self, request, pk):
        self.get_object().delete()    # 根据给定的主键获取对应的图书对象并删除它
        return Response({'code':104, 'msg':'删除成功'})    # 返回成功响应

这个视图类包括两个方法:BookView 用于处理列表查询和创建操作,BookDetailView 用于处理详情查询、更新和删除操作。

BookView 中:

  • get() 方法使用 get_queryset() 获取查询集并将其序列化返回。

  • post() 方法使用 get_serializer() 创建序列化器实例,并在验证通过后保存数据。

BookDetailView 中:

  • get() 方法根据给定的主键获取单个对象,并将其序列化返回。

  • put() 方法根据给定的主键获取单个对象,并在验证通过后更新数据。

  • delete() 方法根据给定的主键获取单个对象,并将其删除。

这些方法都以 Response 对象的形式返回相应的结果。

这个示例代码演示了如何使用 GenericAPIView 来快速实现常见的列表和详情操作。通过继承 GenericAPIView 类并设置 querysetserializer_class 属性,可以轻松地实现数据的 CRUD 操作。

注意:get_serializer() 方法和 serializer_class 属性可以选择性使用。

首先,self.serializer_class 是一个类属性,它是视图类中定义的序列化器类。通过这种方式实例化序列化器时,不会自动传递请求上下文给序列化器。因此,如果序列化器需要访问请求上下文中的某些信息(例如用户认证信息),就需要手动将其传递给序列化器。

示例:

ser = self.serializer_class(data=request.data, context={'request': request})

在Django REST Framework中,可以通过在序列化器实例化时传递一个包含请求上下文的context参数来让序列化器访问请求的上下文。context参数是一个字典,其中可以包含任意的键值对,但常见的用法是将request对象传递给序列化器。

另一方面,self.get_serializer(data=request.data) 是一个方法调用,它会根据视图类中定义的 serializer_class 属性来实例化序列化器,并正确传递请求上下文。这样,在序列化器中可以轻松地访问请求上下文中的各种信息。

示例:

ser = self.get_serializer(data=request.data)

因此,如果你需要在实例化序列化器时自动传递请求上下文,那么推荐使用 self.get_serializer(data=request.data) 方法。如果你只是简单地实例化序列化器,而不需要访问请求上下文,那么可以使用 self.serializer_class(data=request.data)

过滤

在Django REST Framework的GenericAPIView中,可以使用过滤器来过滤API视图中的数据集。在配置过滤器时,需要使用filter_backends属性。

  • 在视图中指定过滤引擎

如果你想在具体的视图中指定过滤器引擎,而不使用全局默认的设置,可以在视图的filter_backends属性中指定过滤器后端类。

from rest_framework.generics import GenericAPIView
from django_filters.rest_framework import DjangoFilterBackend

class BookListView(GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [DjangoFilterBackend]

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

我们添加了filter_backends属性,将DjangoFilterBackend作为过滤器后端类进行配置。这会覆盖全局默认设置。

  • 或者在settings.py文件中全局指定过滤引擎

INSTALLED_APPS = [
    'django_filters',
]
REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS':
['django_filters.rest_framework.DjangoFilterBackend']
}

settings.py文件中进行了正确的配置,将django_filters添加到了INSTALLED_APPS列表中,并将'django_filters.rest_framework.DjangoFilterBackend'作为默认的过滤器后端类。

这样设置后,django_filters库将被安装并变为可用。而且,在整个Django项目中,DEFAULT_FILTER_BACKENDS会被设置为['django_filters.rest_framework.DjangoFilterBackend'],这是由REST_FRAMEWORK配置字典中的设置决定的。

现在,你可以在任何视图中使用过滤器功能,无需为每个视图单独指定过滤器后端类。

案例1:将书名、作者名称中包含指定内容的书籍过滤出来(模糊过滤)

from rest_framework import status
from rest_framework.generics import GenericAPIView
from rest_framework.filters import SearchFilter
from rest_framework.response import Response

class BookListView(GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [SearchFilter]
    search_fields = ['name', 'author']
    
    def get(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
        ser = self.get_serializer(instance=queryset, many=True)    # 序列化获取到的图书对象
        return Response(ser.data, status=status.HTTP_200_OK)    # 返回序列化后的数据
  • filter_queryset()是Django REST Framework中的一个方法,用于对查询集进行过滤。它接受一个查询集作为参数,并根据视图类中指定的过滤后端进行过滤。在BookListViewget()方法中,我们使用filter_queryset()来对self.get_queryset()返回的查询集进行过滤。

  • search_fields用于指定要进行搜索的字段,以进行模糊搜索。它通过使用SearchFilter作为过滤器后端来实现。你可以在URL查询参数中使用search参数来指定搜索关键字,例如?search=100

在 Django REST Framework 的 SearchFilter 中,你可以使用不同的匹配方式前缀来指定搜索字段的匹配方式。以下是常见的匹配方式前缀:

  • ^:以关键字开头匹配(starts with)

  • =:精确匹配(exact)

  • @:全文搜索匹配(full-text search)

  • $:正则表达式匹配(regex)

  • >:大于匹配(greater than)

  • <:小于匹配(less than)

  • >=:大于等于匹配(greater than or equal to)

  • <=:小于等于匹配(less than or equal to)

  • @=:高级搜索匹配(advanced search)

通过在 search_fields 中指定相应的匹配方式前缀,你可以控制搜索字段的匹配方式。例如,search_fields = ['^name', '@description', '$email'] 将分别使用以关键字开头、全文搜索和正则表达式来匹配字段。

案例2:将书名中包含指定内容的书籍过滤出来(特定过滤)

首先安装django-filter

pip insall -i https://pypi.douban.com/simple django-filter

from rest_framework import status
from rest_framework.generics import GenericAPIView
from rest_framework import filters
from rest_framework.response import Response
from .models import Book

class BookListView(GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [filters.SearchFilter]
    filterset_fields = ['name', 'author']
    
    def get(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
        ser = self.get_serializer(instance=queryset, many=True)    # 序列化获取到的图书对象
        return Response(ser.data, status=status.HTTP_200_OK)    # 返回序列化后的数据

filterset_fields用于定义可用于过滤的字段,以进行精确的过滤。它通过使用DjangoFilterBackend作为过滤器后端来实现。这意味着你可以在URL查询参数中指定要过滤的字段及其相应的值,例如?name=John&author=Science

注意:filterset_fields需要安装django-filter库,而search_fields则无需额外的库依赖。另外,filterset_fields只能进行精确的过滤,而search_fields可以进行模糊搜索。

除了常见的查询参数传递方式之外,还可以使用 filterset_class 来定义自定义的过滤器类。filterset_class 允许你在视图中使用更复杂的过滤逻辑。

在filters.py中定义BookFilter

from rest_framework import filters

from .models import Book


class BookFilter(filters.FilterSet):
    """
    1.必须继承FilterSet
    2.必须指定model(用于指定哪个模型类用于过滤)
    3.往往需要指定fields(用于指定哪些字段进行过滤)
    """
    name = filters.CharFilter(field_name='name', lookup_expr='contains')

    class Meta:
        model = Book
        fields = ['name', 'author']
        # fields = {
        #     # 字段名作为key,把支持的查询类型字符串所在列表作为value
        #     'name': ['contains'],
        #     'leader': ['exact'],
        # }

视图

from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.request import Request
from rest_framework import filters
from .filters import BookFilter


class BookListView(GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [filters.SearchFilter]
    filterset_class = BookFilter
    
    def get(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
        ser = self.get_serializer(instance=queryset, many=True)
        return Response(ser.data, status=status.HTTP_200_OK)

排序

  • 在视图中指定排序滤引擎

from rest_framework.generics import ListAPIView
from .models import Book
from .serializers import BookSerializer
from rest_framework import filters

class BookListView(ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [filters.OrderingFilter]
    ordering_fields = ['title', 'publication_date']
    ordering = ['title']

在这个示例中,我们导入了filters模块,并将filter_backends属性设置为[filters.OrderingFilter],以指示Django REST Framework使用OrderingFilter作为排序过滤引擎。

我们还设置了ordering_fields属性,以指定可用于排序的字段列表。最后,我们设置了ordering属性,以指定默认排序方式。

当客户端发送GET请求时,Django框架将使用OrderingFilter作为排序引擎,并根据ordering_fieldsordering属性对查询集进行排序。

  • 在settings.py文件中全局指定排序引擎

settings.py文件中全局指定排序引擎和过滤器引擎,可以在DEFAULT_FILTER_BACKENDS中添加'rest_framework.filters.OrderingFilter',以指定排序引擎为OrderingFilter

以下是一个示例:

REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': [
        'django_filters.rest_framework.DjangoFilterBackend',
        'rest_framework.filters.OrderingFilter'
    ],
}

在这个示例中,我们将DEFAULT_FILTER_BACKENDS设置为包含'django_filters.rest_framework.DjangoFilterBackend''rest_framework.filters.OrderingFilter'的列表,以指定过滤器引擎和排序引擎。

这意味着,在全局范围内,Django REST Framework将使用DjangoFilterBackend作为过滤器引擎,并使用OrderingFilter作为默认排序引擎。

需要注意的是,在视图类中也可以使用定制的filter_backendsordering_fieldsordering属性来覆盖这些全局设置。

确保在settings.py文件中导入django_filtersrest_framework模块,并且已经安装了相应的包。

分页

  • 视图类中使用pagination_class属性进行定制

    from rest_framework.generics import ListAPIView
    from .models import Book
    from .serializers import BookSerializer
    from rest_framework import  pagination
    
    class BookListView(ListAPIView):
        queryset = Book.objects.all()
        serializer_class = BookSerializer
        pagination_class = pagination.PageNumberPagination
        pagination_class.page_size = 10

    在这个示例中,我们导入了分页模块pagination,并将pagination_class属性设置为pagination.PageNumberPagination,以使用默认的基于页码的分页器。

    注意,在使用分页器时,需要导入分页模块,并在视图类中指定pagination_class属性。如果你想要自定义分页器的属性,在视图类中覆盖分页器类的默认值即可。

    我们将分页器类设置为默认的pagination.PageNumberPagination,然后将page_size属性设置为10,表示每页显示10条数据。

前端可以通过查询参数的方式向后端传递想要显示的每页数据条数。通常情况下,使用page_size或类似的参数名来指定每页显示的条数。

以下是一个示例:

GET /books/?page=1&page_size=10

在这个例子中,通过page_size=10,前端告诉后端希望每页显示10条数据。

在视图类中,后端可以通过获取查询参数来获取前端传递的每页数据条数,并在分页器中进行设置。

例如,在Django REST Framework中,可以在视图类的get_queryset()方法中获取request.query_params,并通过request.query_params.get('page_size', None)获取前端传递的每页数据条数。

然后,可以在分页器类中使用pagination_class.page_size属性将每页数据条数设置为前端传递的值。

补充:

  1. PageNumberPagination: 使用页码进行分页,默认分页器。

  2. LimitOffsetPagination: 使用limitoffset参数进行分页。

  3. CursorPagination: 使用游标进行分页,适用于大量数据查询。

  • settings.py文件中全局指定分页引擎

    你可以使用DEFAULT_PAGINATION_CLASS设置来配置Django REST Framework。

    以下是一个示例:

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 3,
}

在这个示例中,我们将DEFAULT_PAGINATION_CLASS设置为'rest_framework.pagination.PageNumberPagination',以指定分页引擎为PageNumberPagination

然后,我们将PAGE_SIZE设置为3,意味着每页显示3条数据。

通过这样配置,Django REST Framework将会在全局范围内使用指定的分页引擎和每页显示的数据条数。

记住,这是全局配置,将应用于所有视图。如果你在某个特定视图中想要覆盖这些全局设置,可以在视图类中使用pagination_class属性进行定制。

Mixin

Mixin 是一种在面向对象编程中常用的技术,它允许将一些通用功能和行为添加到类中。Mixin 类是一种特殊的类,它通常不会独立使用,而是与其他类组合在一起。

Mixin 类通常定义了一些方法或属性,这些方法或属性可以被其他类继承或混入,以提供额外的功能。通过使用 Mixin 类,可以在不修改现有类的情况下,为其添加或替换一些功能。

在 Django 和 Django REST Framework 中,Mixin 类被广泛应用于视图类和模型类的扩展。通过使用 Mixin 类,可以方便地向现有视图类或模型类添加通用的功能,例如认证、权限检查、缓存、序列化等。

视图扩展类:

  • 新增数据,CreateModelMixin

    用于处理创建数据的操作。当我们需要在视图中实现创建对象的功能时,可以使用这个Mixin类。它提供了一个create方法,该方法使用传入的数据创建新的对象,并进行保存。创建成功, 则返回 201 Created、如果请求参数有误, 则返回 400 Bad Request。

  • 修改数据,UpdateModelMixin

    用于处理更新数据的操作。当我们需要在视图中实现更新对象的功能时,可以使用这个Mixin类。它提供了一个update方法,该方法接受传入的数据,并将其应用于现有对象。更新成功, 则返回 200 OK、如果请求参数有误, 则返回 400 Bad Request、如果不存在, 则返回 404 Not Found。

  • 删除数据,DestroyModelMixin

    用于处理删除数据的操作。当我们需要在视图中实现删除对象的功能时,可以使用这个Mixin类。它提供了一个destroy方法,该方法删除指定的对象。删除成功, 则返回 204 No Content、如果不存在, 则返回 404 Not Found

  • 获取单个,RetrieveModelMixin

    用于处理获取单个对象的操作。当我们需要在视图中实现获取单个对象的功能时,可以使用这个Mixin类。它提供了一个retrieve方法,该方法返回指定对象的详细信息。获取成功, 则返回 200 OK、如果不存在, 则返回 404 Not Found

  • 获取所有,ListModelMixin

    用于处理获取所有对象的操作。当我们需要在视图中实现获取所有对象的功能时,可以使用这个Mixin类。它提供了一个list方法,该方法返回所有对象的列表。获取成功, 则返回 200 OK

与GenericAPIView配合使用

from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin, CreateModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin
from .models import Book, Author, Publish
from .serializer import BookSerializer



class BookView(GenericAPIView,ListModelMixin,CreateModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request):
        return self.list(request)

    def post(self, request):
        return self.create(request)


class BookDetailView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        return self.destroy(request)

BookView 类继承了 GenericAPIView 和 ListModelMixin、CreateModelMixin 这两个Mixin类。GenericAPIView 提供了通用视图的基础功能,而 ListModelMixin 和 CreateModelMixin 分别处理获取所有书籍和创建书籍的操作。在 get 和 post 方法中,分别调用了 list 和 create 方法来处理相应的请求。

BookDetailView 类同样继承了 GenericAPIView 和 RetrieveModelMixin、UpdateModelMixin、DestroyModelMixin 这三个Mixin类。RetrieveModelMixin 处理获取单个书籍的操作,UpdateModelMixin 处理更新书籍的操作,DestroyModelMixin 处理删除书籍的操作。在对应的 get、put 和 delete 方法中,分别调用了 retrieve、update 和 destroy 方法来处理相应的请求。

泛型视图(Concrete Generic Views)

具体的泛型视图(Concrete Generic Views)是Django REST Framework中用于实现常见 CRUD 操作的预定义视图类。这些视图类已经为我们提供了一些常见的功能,可以轻松地进行数据操作。

首先注意不用跟GenericAPIView配合使用,直接导入模块继承使用即可,就会有某个或某几个接口。其实也算是更加接近完善的过程,因为不需要依赖GenericAPIView,成立独立了,而且原本五个的基础上又丰富起来了,其实没有创造出新的接口方法而是原有的五个进行排列组合的方法从而变得更加个性化了。几乎满足现有的基本需求了。用的数据和序列化组件是一样的,路由也是一样的。

分类

查询所有对象(ListAPIView):

from rest_framework.generics import ListAPIView
from .models import Book
from .serializers import BookSerializer

class BookListView(ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

新增对象(CreateAPIView):

from rest_framework.generics import CreateAPIView
from .models import Book
from .serializers import BookSerializer

class BookCreateView(CreateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

删除单个对象(DestroyAPIView):

from rest_framework.generics import DestroyAPIView
from .models import Book
from .serializers import BookSerializer

class BookDeleteView(DestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

修改单个对象(UpdateAPIView):

from rest_framework.generics import UpdateAPIView
from .models import Book
from .serializers import BookSerializer

class BookUpdateView(UpdateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

查询单个对象(RetrieveAPIView):

from rest_framework.generics import RetrieveAPIView
from .models import Book
from .serializers import BookSerializer

class BookDetailView(RetrieveAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

查询所有对象并新增一个对象(ListCreateAPIView):

from rest_framework.generics import ListCreateAPIView
from .models import Book
from .serializers import BookSerializer

class BookListCreateView(ListCreateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

查询单个对象并删除(RetrieveDestroyAPIView):

from rest_framework.generics import RetrieveDestroyAPIView
from .models import Book
from .serializers import BookSerializer

class BookDetailDeleteView(RetrieveDestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

查询单个对象并修改(RetrieveUpdateAPIView):

from rest_framework.generics import RetrieveUpdateAPIView
from .models import Book
from .serializers import BookSerializer

class BookDetailUpdateView(RetrieveUpdateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

查询单个对象并修改或删除(RetrieveUpdateDestroyAPIView):

from rest_framework.generics import RetrieveUpdateDestroyAPIView
from .models import Book
from .serializers import BookSerializer

class BookDetailUpdateDeleteView(RetrieveUpdateDestroyAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

以上示例代码基于 Django REST Framework 的常用泛型视图类,可以根据你的具体需求进行修改和扩展。同时,记得将这些视图类添加到你的 URL 配置中,以便能够通过相应的 URL 访问这些视图。

以上各个视图类对应的请求方式如下:

  • BookListView(查询所有对象):GET

  • BookCreateView(新增对象):POST

  • BookDeleteView(删除单个对象):DELETE

  • BookUpdateView(修改单个对象):PUT/PATCH

  • BookDetailView(查询单个对象):GET

  • BookListCreateView(查询所有对象并新增一个对象):GET/POST

  • BookDetailDeleteView(查询单个对象并删除):GET/DELETE

  • BookDetailUpdateView(查询单个对象并修改):GET/PUT/PATCH

  • BookDetailUpdateDeleteView(查询单个对象并修改或删除):GET/PUT/PATCH/DELETE

根据 RESTful API 设计原则,不同的请求方式与不同的操作相对应。例如,GET 请求用于查询资源,POST 请求用于创建资源,DELETE 请求用于删除资源,PUT 或 PATCH 请求用于更新资源。

请注意,具体的请求方式可能会因为项目需求和配置而有所不同,上述请求方式仅作为示例参考,需要根据实际情况进行调整。同时还需要根据你的项目要求,通过 URL 配置将这些请求与对应的视图类进行绑定。

9种视图子类对应的路由文件

from django.urls import path
from .views import (
    BookListView,
    BookCreateView,
    BookDeleteView,
    BookUpdateView,
    BookDetailView,
    BookListCreateView,
    BookDetailDeleteView,
    BookDetailUpdateView,
    BookDetailUpdateDeleteView,
)

urlpatterns = [
    path('books/', BookListView.as_view(), name='book_list'),
    path('books/create/', BookCreateView.as_view(), name='book_create'),
    path('books/<int:pk>/delete/', BookDeleteView.as_view(), name='book_delete'),
    path('books/<int:pk>/update/', BookUpdateView.as_view(), name='book_update'),
    path('books/<int:pk>/', BookDetailView.as_view(), name='book_detail'),
    path('books/list-create/', BookListCreateView.as_view(), name='book_list_create'),
    path('books/<int:pk>/detail-delete/', BookDetailDeleteView.as_view(), name='book_detail_delete'),
    path('books/<int:pk>/detail-update/', BookDetailUpdateView.as_view(), name='book_detail_update'),
    path('books/<int:pk>/detail-update-delete/', BookDetailUpdateDeleteView.as_view(), name='book_detail_update_delete'),
]

九个视图子类写五个接口只使用两个类视图

类视图


from rest_framework.generics import CreateAPIView, DestroyAPIView, ListAPIView, ListCreateAPIView, RetrieveAPIView, \
    RetrieveUpdateDestroyAPIView, RetrieveDestroyAPIView, RetrieveUpdateAPIView, UpdateAPIView
from .models import Book
from .serializer import BookSerializer

class BookView(ListCreateAPIView):
    '''查所有 新增数据'''
    queryset = Book.objects.all()
    serializer_class = BookSerializer


class BookDetailView(RetrieveUpdateDestroyAPIView):
    '''查单个 修改 删除 数据'''
    queryset = Book.objects.all()
    serializer_class = BookSerializer

路由

# 路由代码
from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', views.BookView.as_view()),
    path('books/<int:pk>/', views.BookDetailView.as_view()),

ModelViewSet类

ModelViewSet 是 Django REST Framework 中的一个视图类,它提供了对数据库模型进行常用的 CRUD(创建、读取、更新、删除)操作的默认实现。ModelViewSet 继承了 GenericAPIView 和 mixins 中的各种混合类,并提供了一些默认的行为和方法。

以下是 ModelViewSet 的一些特点和功能:

  1. 默认的 CRUD 操作:ModelViewSet 提供了默认的实现,包括列表查看(GET 请求)、创建新对象(POST 请求)、检索单个对象(GET 请求)、更新对象(PUT 请求或 PATCH 请求)和删除对象(DELETE 请求)。

  2. 自动生成 URL 路由:使用 ModelViewSet,你可以轻松地生成用于处理这些操作的 URL 路由。只需要将你的 ModelViewSet 实例添加到 URL 配置中,Django REST Framework 将自动为你生成 URL 路由。

  3. 自动序列化和反序列化:ModelViewSet 集成了 serializers.ModelSerializer,通过 serializer_class 属性指定序列化器类,它会自动处理请求数据的反序列化和响应数据的序列化。

  4. 自动查询数据集:ModelViewSet 具有 queryset 属性,该属性指定要在数据库中查询的数据集。ModelViewSet 会根据请求的类型自动执行相应的查询操作。

  5. 自定义行为:除了默认的 CRUD 操作之外,你还可以根据需要自定义其他行为。例如,你可以重写 ModelViewSet 中的某些方法来添加额外的逻辑或处理特殊的业务需求。

使用 ModelViewSet 可以大大简化编写 API 视图的代码,同时提供了一套常用的 CRUD 操作的默认实现。通过合理继承和配置,你可以快速构建出符合 RESTful 风格、安全可靠的 API 接口。

路由写法-映射

urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', views.BookView.as_view({'get': 'list', 'post': 'create'})),
    path('books/<int:pk>/', views.BookView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
]
  • GET /books/:返回所有书籍列表。as_view() 方法中传入的方法字典 {'get': 'list', 'post': 'create'} 将 GET 请求映射到 list 方法,POST 请求映射到 create 方法。

  • GET /books/{id}/:返回指定 ID 的书籍信息。{id} 参数是书籍的主键 ID,as_view() 方法中传入的方法字典 {'get': 'retrieve', 'put': 'update', 'delete': 'destroy'} 将 GET 请求映射到 retrieve 方法,PUT 请求映射到 update 方法,DELETE 请求映射到 destroy 方法。

简洁的视图

from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet
from .models import Book
from .serializer import BookSerializer

class BookView(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    
    
"""
ReadOnlyModelViewSet继承了这个类只有两个功能
一个是查询所有数据, 另一个是查询单个数据
"""

@action 装饰器

在 中,@action 装饰器用于为视图集添加自定义操作。

视图集中的自定义操作通常是一些特定功能的业务逻辑,不同于标准的 CRUD(Create, Retrieve, Update, Delete) 操作。例如,你可能需要为资源集合提供一个额外的非标准操作,或者在单个资源上执行一些自定义的操作。

通过在视图集方法上使用 @action 装饰器,你可以为视图集添加自定义操作,并为其指定 HTTP 请求方法。这些自定义操作可以表示为视图集的附加动作,扩展了原始的 CRUD 功能。

@action 装饰器的参数包括:

  • detail:detail 参数用于指示视图是否处理详情资源对象。根据不同的上下文,detail=True 表示处理详情资源对象,需要通过 URL 获取主键,而 detail=False 表示处理集合资源,不需要获取主键。具体如何使用 detail 参数取决于你的需求和视图的设计。

  • methods:表示这个操作支持的 HTTP 请求方法,默认为 GET。

  • url_path:表示这个操作在 URL 中的名称,默认为方法名。

  • url_name:表示这个操作生成 URL 的名称,默认为 "{basename}-{url_path}"

通过使用 @action 装饰器,你可以将自定义操作添加到视图集中,并在路由配置中自动生成相应的 URL。这样,你可以通过发送适当的请求来调用这些自定义操作。

请注意,@action 装饰器只能用于继承自 ViewSet 的类中。

案例:写登录接口来解释action的用法

路由

from django.contrib import admin
from django.urls import path, include
from app01 import views
from rest_framework.routers import SimpleRouter, DefaultRouter

router = SimpleRouter()

router.register('user', views.UserView, 'user')  # /api/v1/user/login     post 请求

urlpatterns = [
    path('api/v1/', include(router.urls)),
]

router.register('user', views.UserView, 'user') 的作用是将 views.UserView 视图注册到路由器中,以便为 /api/v1/user/ 路径创建相应的 URL 规则。

register() 方法是用于将视图集注册到路由器实例上的方法。它接受三个参数:

  1. prefix(前缀):指定 URL 路径的前缀。这个前缀会被添加到视图集中每个动作方法的 URL 路径之前,从而形成完整的 URL。例如,如果 prefix'user',则视图集中的动作方法的 URL 路径会以 /user/ 开头。

  2. viewset(视图集):要注册的视图集对象,通常是一个继承自 DRF 提供的视图集类的自定义视图集。视图集包含了处理不同 HTTP 请求方法(例如 GET、POST、PUT 等)的逻辑代码。

  3. basename:视图集的基准名称,用于生成 URL 路径的后缀。如果没有显式提供 basename,则会使用视图集的小写类名作为默认值。

示例代码中的 router.register('user', views.UserView, 'user') 中,'user' 是前缀和 basename,将会生成以下 URL 配置:

  • GET /api/v1/user/ - 列出所有用户

  • POST /api/v1/user/ - 创建新用户

  • GET /api/v1/user/{pk}/ - 检索特定用户

  • PUT /api/v1/user/{pk}/ - 完整更新特定用户

  • PATCH /api/v1/user/{pk}/ - 部分更新特定用户

  • DELETE /api/v1/user/{pk}/ - 删除特定用户

可以看到,前缀 'user' 被添加到每个动作方法的 URL 路径之前,形成了完整的 API 端点路径。

总结来说,register() 方法的参数包括前缀、视图集和基准名称,用于生成 URL 路径,并将视图集注册到路由器实例中。

SimpleRouter, DefaultRouter的区别

  • SimpleRouter 是 DRF 提供的简单路由器,为每个视图集提供默认的动作方法 URL 配置:listcreateretrieveupdatepartial_updatedestroy

  • DefaultRouter 是 DRF 提供的默认路由器,与 SimpleRouter 类似,但增加了一个名为 router 的动作方法,用于获取 API 根视图的 URL 配置。

  • SimpleRouter 中生成的 URL 配置相对较简单,只包含视图集中的默认动作方法;DefaultRouter 生成的 URL 配置更全面,包括所有默认动作方法和 router 动作方法。

  • 选择使用哪种路由器取决于你的需求,如果只需要基本的 CRUD 操作,可以使用 SimpleRouter;如果需要获取根视图的 URL 配置,可以使用 DefaultRouter

除此之外,它们的用法基本一致,都是通过 register() 方法将视图集注册到路由器实例上,并将其添加到 Django 的 URL 配置中

表模型

class User(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)
    user_type = models.IntegerField(choices=((1, '超级管理员'), (2, '普通用户'), (3, '2B用户')), default=2)


class UserToken(models.Model):  # 跟User是一对一
    token = models.CharField(max_length=32)
    user = models.OneToOneField(to='User', on_delete=models.CASCADE, null=True)
    # user :反向,表名小写,所有有user字段

视图类

from rest_framework.response import Response
from rest_framework.viewsets import ViewSet, GenericViewSet
from rest_framework.decorators import action
from .models import User, UserToken
import uuid


class UserView(ViewSet):
    @action(methods=['POST'], detail=False)
    def login(self, request):
        username = request.data.get('username')
        password = request.data.get('password')
        user = User.objects.filter(username=username, password=password).first()
        if user:
            # 用户存在,登陆成功,生成一个随机字符串UUID
            token = str(uuid.uuid4())
            UserToken.objects.update_or_create(user=user, defaults={'token': token})
            return Response({'code': '100', 'msg': '登录成功', 'token': token})
        else:
            return Response({'code': '101', 'msg': '用户名或密码错误'})
  1. 在视图类部分,使用了 ViewSet 基类创建了 UserView 视图类。视图类中定义了一个名为 login 的动作方法(action method),该方法处理 POST 请求,并根据提供的用户名和密码进行用户验证。如果验证成功,生成一个随机的 UUID 作为令牌,将令牌与用户关联保存到 UserToken 中,并返回登录成功的响应。如果验证失败,则返回用户名或密码错误的响应。

  2. 在 URL 配置部分,通过 urlpatterns 将路由器中的 URL 规则包含在项目的主 URL 配置中,即 /api/v1/ 路径下的所有 URL 规则都由 router.urls 处理。

综上所述,当发送 POST 请求到 /api/v1/user/login/ 路径时,将会调用 UserView 视图的 login 方法,进行用户登录验证,并返回相应的响应结果。

router

router 是 Django Rest Framework (DRF) 中用于自动生成 URL 路由的工具类。它提供了一种简化配置和注册视图的方式,使得在构建 RESTful API 时能够更加高效和方便。

使用 router 可以自动为视图集(ViewSet)生成标准的 URL 路由配置,包括集合资源和详情资源的 URL。以下是使用 router 的基本步骤:

  1. 导入 DefaultRouter 类:

    from rest_framework.routers import DefaultRouter
  2. 创建一个 DefaultRouter 实例:

    router = DefaultRouter()
  3. 在实例化视图集时,使用 router.register() 方法注册视图集:

    router.register('resources', MyViewSet)

    上面的代码将视图集 MyViewSet 注册到路由器中,其中 'resources' 是该视图集对应的 URL 路径前缀,可以根据实际需求指定。

  4. router.urls 添加到 Django 的 URL 配置中:

    urlpatterns = [
        # 其他 URL 配置...
        path('api/', include(router.urls)),
    ]
    

    这样就将生成的 URL 路由添加到了项目的主 URL 配置中,'api/' 是访问 API 的路径前缀,可以根据实际需求指定。

使用 router 后,它会自动为每个视图集中的操作生成相应的 URL 路由。例如,如果 MyViewSet 中有 listretrievecreate 等方法,则会生成以下路由:

  • 获取集合资源:GET /api/resources/

  • 创建资源:POST /api/resources/

  • 获取详情资源:GET /api/resources/{id}/

  • 更新资源:PUT /api/resources/{id}/

  • 删除资源:DELETE /api/resources/{id}/

总结:router 是 DRF 提供的工具类,用于自动生成 RESTful API 的 URL 路由配置。通过注册视图集,并将生成的路由添加到项目的 URL 配置中,可以大大简化 URL 路由的配置过程

设计原则

类视图的设计原则

  1. 如果父类有提供相应的资源(Mixin拓展类、具体通用的视图),并且能完全满足需求,就直接使用父类提供的

  2. 如果父类提供的资源仅仅只是部分不满足需求,重写父类的实现

  3. 如果父类提供的资源完全不满足需求,直接自定义

  4. 按需选择父类

  5. 默认情况下,直接使用ModelViewSet

序列化器类的设计原则

  1. 如果操作的是模型数据,一定使用ModelSerializer,生成的字段不满足要求,可修改

  2. 如果操作的是普通对象,那么才使用Serializer

  3. 复杂的查询逻辑,一般把其他序列化器类最为序列化器的字段来使用

案例参考:The Road of Learning 阿丽米热

推荐一篇教程