Django Rest Framework 笔记

Django Rest Framework 笔记

仅为个人学习整理记录之用

Django REST framework的官网地址: https://www.django-rest-framework.org/

Django REST框架是构建 Web api 的工具。是一种后端 API 接口规范

  • 支持ORM和非ORM数据源的序列化器Serializer方法。
  • 多功能的类视图、Mixin 扩展类。
  • 字段验证功能,及多种身份认证和权限认证方式的支持
  • API web 页面

使用Django Rest Framework框架实现API开发。使用框架开发不仅能减少代码冗余,还可以规范代码的编写格式,这对企业级开发来说很有必要,毕竟每个开发人员的编程风格存在一定的差异,开发规范化可以方便其他开发人员查看和修改。


1 常识

1.1 请求类型

常用的请求类型:(括号里是对应的SQL命令):

  • GET(SELECT):从服务器取出资源(一项或多项)。
  • POST(CREATE):在服务器新建一个资源。
  • PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
  • DELETE(DELETE):从服务器删除资源。

不常用请求类型:

  • PATCH(UPDATE):在服务器更新(更新)资源(客户端提供改变的属性)。
  • HEAD:获取资源的元数据。
  • OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的

使用例子:

GET /zoos:列出所有动物园
POST /zoos:新建一个动物园(上传文件)
GET /zoos/ID:获取某个指定动物园的信息
PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
DELETE /zoos/ID:删除某个动物园
GET /zoos/ID/animals:列出某个指定动物园的所有动物
DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物
1.2 数据处理逻辑放哪

在 Django + rest framework 的后端项目中,数据处理逻辑可以写在序列化类(Serializers)或视图类(Views)内,这取决于你的具体需求和项目的结构。

写在序列化类内

序列化类主要用于将数据对象转换为可序列化的格式(如 JSON),以及将接收到的序列化数据转换回数据对象。由于序列化类主要关注数据的序列化和反序列化,因此将数据处理逻辑写在序列化类内可以保持数据的一致性和完整性。

在序列化类中处理数据的好处是,你可以在数据序列化和反序列化的过程中进行数据验证和转换,确保数据的有效性和准确性。此外,将数据处理逻辑写在序列化类内可以使数据处理的代码更加集中和易于维护。

写在视图类内

视图类主要用于处理 HTTP 请求和返回响应,可以包含数据处理、业务逻辑和数据展示等代码。将数据处理逻辑写在视图类内可以使数据处理与 HTTP 请求和响应的处理更加紧密,方便对数据进行进一步的处理和展示。

在视图类中处理数据的好处是,你可以根据具体的 HTTP 请求和响应来处理数据,实现更加灵活的数据处理逻辑。此外,视图类通常与特定的 URL 路由相关联,可以更加方便地对不同的 URL 路径进行数据处理。

区别

  1. 职责不同:序列化类主要关注数据的序列化和反序列化,而视图类主要关注 HTTP 请求和响应的处理。
  2. 灵活性不同:将数据处理逻辑写在视图类内可以更加灵活地处理数据,根据具体的 HTTP 请求和响应来进行数据处理。而写在序列化类内可以保持数据的一致性和完整性,便于进行数据验证和转换。
  3. 可维护性不同:将数据处理逻辑写在序列化类内可以使代码更加集中和易于维护,而写在视图类内可能会使代码更加分散和难以维护。

总结

个人初步感觉:统一的数据处理逻辑放在序列化类,不同用户的数据处理逻辑放在视图类中


2 安装配置

// 安装:
pip install djangorestframework

在项目 setting.py 文件内配置实例

# MyDjango的settings.py
INSTALLED_APPS = [
	...
    # 添加Django Rest Framework框架
    'rest_framework',
    ...
]

# ================================================= #
# *************** REST_FRAMEWORK配置 *************** #
# ================================================= #
REST_FRAMEWORK = {
    # 日期时间格式配置
    'DATETIME_FORMAT': "%Y-%m-%d %H:%M:%S",
    'DATE_FORMAT': "%Y-%m-%d",
    # 设置所有接口都需要被验证
    'DEFAULT_PERMISSION_CLASSES': (
        # IsAuthenticated权限类将拒绝任何未通过身份验证的用户访问,API只能由注册用户访问
        'rest_framework.permissions.IsAuthenticated',
        # IsAdminUser权限仅允许user.is_staff为True用户访问,其他任何用户都将被拒绝。
        # 'rest_framework.permissions.IsAdminUser',
        # IsAuthenticatedOrReadOnly未验证用户可以请求安全的方法:GET,HEAD或OPTIONS。
        # 'rest_framework.permissions.IsAuthenticatedOrReadOnly',
    ),
    # 用户登陆认证方式
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',  # 使用JWT进行授权
    ),
    # 过滤类配置
    'DEFAULT_FILTER_BACKENDS': (
        # 'django_filters.rest_framework.DjangoFilterBackend',  # rest_f官方的
        'rbac.utils.filters.CustomDjangoFilterBackend',  # 自定义过滤
        'rest_framework.filters.SearchFilter',
        'rest_framework.filters.OrderingFilter',

    ),
    
    # 官方分页设置
	# 'DEFAULT_PAGINATION_CLASS':
    # 'rest_framework.pagination.PageNumberPagination',
    # 每页显示多少条数据
    # 'PAGE_SIZE': 2
    
    # 自定义分页
    'DEFAULT_PAGINATION_CLASS': 'rbac.utils.pagination.CustomPagination',
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ),
    # 自定义的异常处理
    'EXCEPTION_HANDLER': 'rbac.utils.exception.CustomExceptionHandler',
}

3 序列化类 Serializer

  • 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串

    # 序列化的过程:模型对象数据(QuerySet) -转-> 字典(Dict) -转-> Json字符串
    
    
  • 反序列化,把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型

    # 反序列化的过程:Json字符串 ---> 字典(Dict)---> 模型对象数据(QuerySet)
    
    
  • 反序列化,完成数据校验功能

    # 反序列化后的模型对象数据(QuerySet)---> .is_valid() 校验对象数据,通过返回True,失败返回False
    # True:保存对象 serializer.save()
    
    
3.1 定义序列化类

Django REST framework中的Serializer使用类来定义,须继承自rest_framework.serializers.Serializer。

创建与项目应用对应的序列化文件 serializer.py 依据项目模型对象定义对应的序列化类

3.2 序列化字段的数据类型

常用字段类型

字段字段构造方式
BooleanFieldBooleanField()
NullBooleanFieldNullBooleanField()
CharFieldCharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
EmailFieldEmailField(max_length=None, min_length=None, allow_blank=False)
RegexFieldRegexField(regex, max_length=None, min_length=None, allow_blank=False)
SlugFieldSlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+
URLFieldURLField(max_length=200, min_length=None, allow_blank=False)
UUIDFieldUUIDField(format=‘hex_verbose’) format: 1) 'hex_verbose'"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex'"5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
IPAddressFieldIPAddressField(protocol=‘both’, unpack_ipv4=False, **options)
IntegerFieldIntegerField(max_value=None, min_value=None)
FloatFieldFloatField(max_value=None, min_value=None)
DecimalFieldDecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置
DateTimeFieldDateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
DateFieldDateField(format=api_settings.DATE_FORMAT, input_formats=None)
TimeFieldTimeField(format=api_settings.TIME_FORMAT, input_formats=None)
DurationFieldDurationField()
ChoiceFieldChoiceField(choices) choices与Django的用法相同
MultipleChoiceFieldMultipleChoiceField(choices)
FileFieldFileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ImageFieldImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ListFieldListField(child=, min_length=None, max_length=None)
DictFieldDictField(child=)

选项参数:

参数名称作用
max_length最大长度
min_length最小长度
allow_blank是否允许为空
trim_whitespace是否截断空白字符
max_value最大值
min_value最小值

通用参数:

参数名称说明
read_only设置序列化字段的只读属性。仅用于序列化输出,若为True,则序列化字段为只读。默认False。
write_only设置序列化字段的编辑属性。仅用于反序列化输入,默认False
required设置序列化字段的数据是否为空,默认值为True,在反序列化时必须输入。
default设置序列化字段的默认值,在反序列化时使用的默认值。
initial设置序列化字段的初始值。
source为序列化字段指定一个模型字段来源,如(email=‘user.email’)。
label设置生成label标签的网页内容。用于HTML展示API页面时,显示的字段名称。
help_text设置序列化字段的帮助提示信息。用于HTML展示API页面时,显示的字段帮助提示信息
style以字典格式表示,控制模板引擎如何渲染序列化字段。
validators该字段使用的验证器,这是自定义数据验证规则,以列表格式表示,列表元素为数据验证的函数名。
error_messages设置序列化字段的错误信息,以字典格式表示,包含null、blank、invalid、invalid_choice、unique等键值。包含错误编号与错误信息的字典
allow_null设置序列化字段是否为None,若为True,则序列化字段的值允许为None。默认False

实例演示

from rest_framework import serializers
from .models import Company, ProInitial, ProAuxiliary, Management, ProjectEnd


# 定义 Serializer 类

nameList = ProInitial.objects.values('name').all()
NAME_CHOICES = [item['name'] for item in nameList]


# 继承了父类 Serializer
class ManageSerializer(serializers.Serializer):
    """自定义M序列化类 anageSerializer """
    id = serializers.IntegerField(read_only=True)
    # name = serializers.ChoiceField(choices=NAME_CHOICES, default=1)
    # 模型 Management 的字段 name 是外键字段,它指向模型 ProInitial
    # 因此,外键字段可以使用 PrimaryKeyRelatedField
    name = serializers.PrimaryKeyRelatedField(queryset=nameList)

    # 重写 create 函数,将 API 数据保存到数据表 uuic_pro_manage 中
    def create(self, validated_data):
        return Management.objects.create(**validated_data)

    # 重写 update 函数,将 API 数据更新到数据表 uuic_pro_manage 中
    def update(self, instance, validated_data):
        return instance.update(**validated_data)

3.3 字段使用实例
DateField

DateField(format=api_settings.DATE_FORMAT, input_formats=None) 是一个Django REST framework中的序列化字段,用于处理日期数据。它有两个参数:

  1. format:这个参数用于指定日期的格式。在Django中,日期通常以字符串形式表示,例如:“2022-08-15”。通过设置format参数,可以告诉序列化器如何解析和显示日期。在这个例子中,api_settings.DATE_FORMAT 是Django REST framework中定义的默认日期格式。
  2. input_formats:这个参数用于指定输入日期的格式。当从客户端接收到日期数据时,可以通过设置input_formats参数来告诉序列化器如何处理这些数据。如果不提供此参数,序列化器将使用format参数指定的格式进行解析。

总之,DateField 是一个用于处理日期数据的序列化字段,可以根据需要自定义日期格式和输入格式。

注意事项:

formatinput_formats 两个参数不能同时使用。

format 参数用于指定日期的格式,而 input_formats 参数用于指定输入日期的格式。这两个参数分别对应不同的场景,因此不能同时使用。

如果需要自定义日期格式,可以使用 format 参数;如果需要处理不同格式的输入日期,可以使用 input_formats 参数。

3.4 重写方法

django_rest_framework.serializers.Serializer 是 Django REST framework 中用于序列化和反序列化数据的类。它提供了一些重写方法,以便在子类中自定义序列化和反序列化的行为。以下是一些常用的重写方法:

  1. __init__(self, *args, **kwargs): 构造函数,用于初始化序列化器实例。可以在这里设置默认值、验证规则等。
  2. validate(self, data): 验证方法,用于对输入的数据进行验证。如果数据不符合要求,可以抛出异常。
  3. create(self, validated_data): 创建方法,用于将验证后的数据转换为模型实例并保存到数据库。
  4. update(self, instance, validated_data): 更新方法,用于更新已存在的模型实例。
  5. to_representation(self, instance): 将模型实例转换为可序列化的数据表示。例如,将查询集(QuerySet)转换为 JSON 格式。
  6. to_internal_value(self, data): 将可序列化的数据转换为模型实例所需的内部表示。例如,将 JSON 格式的数据转换为 Python 字典。
  7. get_fields(self): 获取字段的方法,用于定义序列化器中的字段。可以在这里设置字段的只读属性、自定义字段等。
  8. get_extra_kwargs(self): 获取额外关键字参数的方法,用于定义序列化器中的额外参数。这些参数通常用于自定义验证规则或处理特殊情况。
  9. get_validators(self): 获取验证器的方法,用于定义序列化器中的验证规则。可以在这里添加自定义的验证器。
  10. get_attribute(self, instance): 获取属性的方法,用于从模型实例中获取属性值。可以在这里实现自定义的属性访问逻辑。
  11. run_validation(self, data=None): 运行验证的方法,用于执行所有验证规则。如果验证失败,可以抛出异常。
  12. is_valid(self, raise_exception=False): 检查数据是否有效的方法。如果数据有效,返回 True;否则,根据 raise_exception 参数决定是否抛出异常。
  13. save(self, **kwargs): 保存方法,用于将模型实例保存到数据库。可以在这里实现自定义的保存逻辑。
  14. delete(self, instance): 删除方法,用于从数据库中删除模型实例。可以在这里实现自定义的删除逻辑。
3.5 常用方法详解
3.5.1 反序列化钩子

反序列化过程中用于对输入的数据进行验证。如果数据不符合要求,可以抛出异常。

  • validate_字段名 :局部钩子,对指定字段校验

    # 页面提交过来的数据关于一对多中的班级字段是字符串,我们需要将字符串变为模型表对象,方便后面的创建以及更新。
    def validate_student_class(self, data):
    	# data是提交过来的这一个字段的数据
        class_obj = Classes.objects.filter(class_name=data).first()
        if not class_obj:
            raise exceptions.ValidationError("班级不存在")
        data = class_obj  # 将字符串替换为对象
        return data
    
  • validate :是全局钩子,对象的校验

    # 全局钩子使用也是一样。如下,验证学生名和班级名是否相同,如果相同则抛出异常
    def validate(self, validate_data):
        student_name = validate_data.get("student_name")
        class_obj = validate_data.get("student_class")  # 由于局部钩子中,这里被替换成了对象,所以我们拿到对象不能直接作比较
        if student_name == class_obj.class_name:
            raise exceptions.ValidationError("学生名不能和班级名相同")
        return validate_data
    
3.5.2 数据转化

允许改变我们反序列化的输出

  • to_internal_value()

    def to_internal_value(self, data):
    	"""
    	反序列化第一步:拿到的是提交过来的原始数据: QueryDict => request.GET, request.POST
    	"""
    	print(data)
    	return super(StudentSerializer, self).to_internal_value(data)
    
3.6 序列化关系型字段

自定义关系型字段,需要重写了父类BaseSerializer的create和update函数。关系字段可以在源码文件relations.py中找到定义过程,每个关系字段都有代码注释说明,

关联对象嵌套序列化字段

  • PrimaryKeyRelatedField 此字段将被序列化为关联对象的主键
    • 包含read_only=True参数时,该字段将不能用作反序列化使用
    • 包含queryset参数时,将被用作反序列化时参数校验使用
  • StringRelatedField 此字段将被序列化为关键对象的字符串表达方式(即__str__方法的返回值)
    • 例:creator = serializers.StringRelatedField(label=“创建人”)
  • 使用关联对象的序列化器,定义关联所属序列化器
    • 例:creator = UsersSerializer()
  • HyperlinkedRelatedField 此字段将被序列化为获取关联对象数据的接口链接
    • 例:hbook = serializers.HyperlinkedRelatedField(label=‘图书’, read_only=True, view_name=‘books-detail’)
    • 必须指明view_name参数,以便DRF根据视图名称寻找路由,进而拼接成完整URL。
  • SlugRelatedField 此字段将被序列化为关联对象的指定字段数据
    • 例:creator = serializers.PrimaryKeyRelatedField(label=“创建人”, read_only=True, slug_field=‘name’)
    • slug_field指明使用关联对象的哪个字段
  • ReadOnlyField 定点指向
    • 例:xmid = serializers.ReadOnlyField(source=‘xmid.id’)
  • 重写 to_representation 方法
    • 序列化器的每个字段实际都是由该字段类型的to_representation方法决定格式的,可以通过重写该方法来决定格式。
    • 注意,to_representations方法不仅局限在控制关联对象格式上,适用于各个序列化器字段类型。
3.7 实现API接口

serializer.py 文件

from rest_framework import serializers
from .models import Company, ProInitial, ProAuxiliary, Management, ProjectEnd
from backend_uiic import settings

# 定义 Serializer 类

class CompanySerializer(serializers.Serializer):
    """单位模型序列化"""
    id = serializers.IntegerField(read_only=True)
    type = serializers.ChoiceField(choices=Company.TYPE_CHOICES, default=0, label='类别')
    name = serializers.CharField(max_length=64, label='单位名称')
    owner = serializers.CharField(max_length=16, allow_null=True, allow_blank=True, label="负责人")
    phone = serializers.CharField(max_length=11, allow_null=True, allow_blank=True, label="联系电话")
    status = serializers.BooleanField(default=True, label="单位状态")

    def validate_type(self, value):
        """验证输入的type字段值是否0、1、2或3之一。如果不是,抛出一个验证错误。"""
        if value not in [0, 1, 2, 3]:
            raise serializers.ValidationError("类型必须是0、1、2或3之一")
        return value


class ProInitialSerializer(serializers.Serializer):
    # 由于model模型的 pro_no 字段带有 unique=True 属性所以序列化要考虑唯一性判断
    def validate_pro_no(value):  # 定义验证函数
        """检查是否存在重复的项目编号"""
        # 检查数据库中是否已经存在一个具有相同名字的pro_no对象。如果存在,就抛出一个ValidationError。
        if ProInitial.objects.filter(pro_no=value).exists():
            raise serializers.ValidationError('该编号已存在')
        return value

    area = serializers.CharField(max_length=32, label='行政区域')
    name = serializers.CharField(max_length=64, label='项目名称')
    # 将验证函数未作为 pro_no 字段的验证器,在序列化时,如果 项目编号已经存在,就会抛出一个错误。
    pro_no = serializers.CharField(max_length=32, validators=[validate_pro_no], label='项目编号')
    year = serializers.IntegerField(label='项目年度', min_value=1000, max_value=9999)
    manager = serializers.CharField(max_length=16, label='项目经理')
    design_len = serializers.DecimalField(max_digits=8, decimal_places=3, label='设计长度')
    type = serializers.CharField(max_length=32, allow_blank=True, allow_null=True, label='项目类型')
    planned_batch = serializers.CharField(max_length=64, allow_blank=True, allow_null=True, label='计划批次')

    def validate_year(self, value):
        # 检查year字段是否为4位数字,且大于1000,小于9999
        if not value.isdigit() or len(value) != 4 or int(value) < 1000 or int(value) > 9999:
            raise serializers.ValidationError("项目年度必须为4位数字,且大于1000,小于9999")
        return value

    def create(self, validated_data):
        # 创建并保存ProInitial对象的方法
        return ProInitial.objects.create(**validated_data)

    def update(self, instance, validated_data):
        # 更新ProInitial对象的方法
        for key, value in validated_data.items():
            setattr(instance, key, value)
        instance.save()
        return instance


nameList = ProInitial.objects.values('name').all()
NAME_CHOICES = [item['name'] for item in nameList]
companyList = Company.objects.values('name').all()  # 需要补充设置根据单位类别筛选施工单位
COMPANY_CHOICES = [item['name'] for item in companyList]

# 继承了父类 Serializer
class ManageSerializer(serializers.Serializer):
    """自定义序列化类 ManageSerializer """
    id = serializers.IntegerField(read_only=True)
    # name = serializers.ChoiceField(choices=NAME_CHOICES, default=1)
    # 模型 Management 的字段 name 是外键字段,它指向模型 ProInitial
    # 因此,外键字段可以使用 PrimaryKeyRelatedField
    name = serializers.PrimaryKeyRelatedField(queryset=nameList, label="项目名称")
    company_c = serializers.PrimaryKeyRelatedField(queryset=companyList, label="施工单位")
    status = serializers.ChoiceField(choices=Management.PRO_STATUS, label="工程状态")
    start_date = serializers.DateField(input_formats=["%Y-%m-%d"], label="开工日期")
    progress_total = serializers.DecimalField(max_digits=8, decimal_places=3, default=0, label="总进度")
    progress_details = serializers.CharField(max_length=200, allow_blank=True, allow_null=True, label='进度备注')
    current = serializers.DecimalField(max_digits=8, decimal_places=3, default=0, label='本年进度')
    procedures = serializers.BooleanField(default=False, label="办理手续")
    design_change = serializers.BooleanField(default=False, label="设计变更")
    outlay = serializers.CharField(max_length=200, allow_blank=True, allow_null=True, label='赔补费用')
    # 重写 create 函数,将 API 数据保存到数据表 uuic_pro_manage 中
    def create(self, validated_data):
        return Management.objects.create(**validated_data)

    # 重写 update 函数,将 API 数据更新到数据表 uuic_pro_manage 中
    def update(self, instance, validated_data):
        instance.status = validated_data.get('status',instance.status)
        instance.save()
        return instance
    

使用序列化类实现API开发,在urls.py中分别定义视图函数路由和视图类路由

# urls.py
from django.urls import path
from apps.projects import views

urlpatterns = [
    path('manage/def/', views.manageDef),
    path('manage/class/', views.ManageClass.as_view()),
    
]

在 views.py 中分别定义视图函数路由和视图类

from .models import ProInitial, Company, Management
from .serializers import ManageSerializer

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.pagination import PageNumberPagination
from rest_framework.decorators import api_view


class ProjectListView(APIView):

    def get(self, request, *args, **kwargs):
        data = ProInitial.objects.all().values()

        return Response(data)


class CompanyListView(APIView):

    def get(self, request, *args, **kwargs):
        data = Company.objects.all().values()

        return Response(data)


class ManageView(APIView):

    def get(self, *args, **kwargs):
        data = Management.objects.all().values()

        return Response(data)


@api_view(['GET', 'POST'])
def manageDef(request):
    if request.method == 'GET':
        q = Management.objects.all()
        # 分页查询,需要在 settings.py 中设置 REST_FRAMEWORK 属性
        pg = PageNumberPagination()
        p = pg.paginate_queryset(queryset=q, request=request)
        # 将分页后的数据传递给 CompanySerializer,生成 JSON 数据对象
        serializer = ManageSerializer(instance=p,many=True)
        # 返回对象 Response 由 Django Rest Framework 实现
        return Response(serializer.data)
    elif request.method == 'POST':
        # 获取请求数据
        data = request.data
        # 将请求参数id作为模型字段id的查询条件。
        id = data['name']
        data['name'] = ProInitial.objects.filter(id=id).first()
        # 在模型Management中进行数据查询。
        instance = Management.objects.filter(id=data.get('id', 0))
        if instance:
            # 如果存在查询对象,模型已存在相应数据,对当前POST请求视为更新修改已有数据
            ManageSerializer().update(instance, data)
        else:
            # 如果不存在查询对象,把当前POST请求数据添加到Management模型中。
            ManageSerializer().create(data)
        return Response('Done', status=status.HTTP_201_CREATED)


class ManageClass(APIView):

    def get(self,request):
        q = Management.objects.all()
        # 分页查询,需要在 settings.py 中设置 REST_FRAMEWORK 属性
        pg = PageNumberPagination()
        p = pg.paginate_queryset(queryset=q, request=request)
        # 将分页后的数据传递给 CompanySerializer,生成 JSON 数据对象
        serializer = ManageSerializer(instance=p, many=True)
        # 返回对象 Response 由 Django Rest Framework 实现
        return Response(serializer.data)

    def post(self,request):
        # 获取请求数据
        data = request.data
        # 将请求参数id作为模型字段id的查询条件。
        id = data['name']
        data['name'] = ProInitial.objects.filter(id=id).first()
        # 在模型Management中进行数据查询。
        instance = Management.objects.filter(id=data.get('id', 0))
        if instance:
            # 如果存在查询对象,模型已存在相应数据,对当前POST请求视为更新修改已有数据
            ManageSerializer().update(instance, data)
        else:
            # 如果不存在查询对象,把当前POST请求数据添加到Management模型中。
            ManageSerializer().create(data)
        return Response('Done', status=status.HTTP_201_CREATED)

注意事项:

视图函数vocationDef和视图类vocationClass实现的功能是一致的,若使用视图函数开发API接口,则必须对视图函数使用装饰器api_view;若使用视图类,则必须继承父类APIView,这是Django Rest Framework明确规定的。


3.8 模型序列化类 ModelSerializer

序列化类Serializer可以与模型结合使用,从而实现模型的数据读写操作。但序列化类Serializer定义的字段必须与模型字段相互契合,否则在使用过程中很容易提示异常信息。为了简化序列化类Serializer的定义过程,Django Rest Framework定义了模型序列化类ModelSerializer

ModelSerializer,它能与Models模型完美结合,无须开发者定义序列化字段

3.8.1 常用属性

Django Rest Framework的模型序列化类ModelSerializer有以下属性设置:

  1. model:指定要序列化的模型类。

    from rest_framework import serializers
    from myapp.models import MyModel
    
    class MyModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = MyModel
            fields = '__all__'
    
  2. fields:指定要包含在序列化器中的字段列表。可以使用字符串(表示单个字段),列表(表示多个字段)或字典(表示嵌套字段)、字段对象或字段名称的元组来指定字段。默认情况下,将包含所有字段。

    class MyModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = MyModel
            fields = ['field1', 'field2']
    
  3. exclude:指定要排除的字段。与fields属性一起使用。默认情况下,不包含任何排除字段。

    class MyModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = MyModel
            exclude = ['field3']
    
  4. read_only_fields:指定只读字段列表。这些字段在创建和更新对象时不可修改。但可以在保存实例时进行验证和设置。

    class MyModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = MyModel
            read_only_fields = ['field4']
    
  5. write_only_fields:指定只写字段列表。指定只写字段,这些字段在查询对象时不可返回。但可以在保存实例时进行验证和设置。

    class MyModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = MyModel
            write_only_fields = ['field5']
    
  6. many:指定是否将序列化器配置为处理多个实例。如果设置为True,则序列化器将支持批量操作,如创建和更新多个实例。默认情况下,设置为False。

    # many属性用于指定序列化器是否应用于多个对象。它接受一个布尔值作为参数,默认为False。
    from django.db import models
    
    class Author(models.Model):
        name = models.CharField(max_length=100)
    
    class Book(models.Model):
        title = models.CharField(max_length=100)
        author = models.ForeignKey(Author, on_delete=models.CASCADE)
    
    from rest_framework import serializers
    from .models import Author, Book
    
    class AuthorSerializer(serializers.ModelSerializer):
        class Meta:
            model = Author
            fields = '__all__'
    
    class BookSerializer(serializers.ModelSerializer):
        class Meta:
            model = Book
            fields = '__all__'
    
    # 当需要序列化多个作者时,将many属性设置为True
    authors = Author.objects.all()
    author_serializer = AuthorSerializer(authors, many=True)
    
    # 当需要序列化多个书籍时,将many属性设置为True
    books = Book.objects.all()
    book_serializer = BookSerializer(books, many=True)
    
    # 在这个示例中,我们首先定义了两个序列化器:AuthorSerializer和BookSerializer。然后,我们分别获取所有的作者和书籍对象,并将它们的many属性设置为True,以便在序列化时处理多个对象。
    
  7. partial:指定是否允许部分更新。如果设置为True,则序列化器将支持部分更新,即可以仅更新某些字段而不必发送整个实例。默认情况下,设置为False。

  8. create:指定一个可调用的对象,用于创建新的实例。默认情况下,使用模型类的构造函数创建新实例。

    from django.db import models
    
    class Person(models.Model):
        name = models.CharField(max_length=100)
        age = models.IntegerField()
        email = models.EmailField()
    
        
    from rest_framework import serializers
    from .models import Person
    
    class PersonSerializer(serializers.ModelSerializer):
        class Meta:
            model = Person
            fields = '__all__'
    
        # create属性用于在创建新对象时自定义保存逻辑。它接受一个可调用对象,该对象接收一个包含所有字段值的字典作为参数,并返回一个模型实例。
        def create(self, validated_data):
            # 在这里自定义保存逻辑,例如发送电子邮件通知
            send_email_notification(validated_data['name'], validated_data['email'])
    
            # 调用父类的create方法创建并保存对象
            return super().create(validated_data)
    # 在这个示例中,我们在create方法中首先调用了一个名为send_email_notification的函数,该函数负责发送电子邮件通知。然后,我们调用了父类的create方法来实际创建并保存Person对象。这样,我们就可以在创建新对象时执行自定义的保存逻辑。
    
  9. update:指定一个可调用的对象,用于更新现有实例。默认情况下,使用模型类的save方法更新实例。

    # update属性用于在更新现有对象时自定义保存逻辑。它接受一个可调用对象,该对象接收两个参数:一个是包含所有字段值的字典,另一个是要更新的对象实例。然后返回一个更新后的对象实例。
    
    from rest_framework import serializers
    from .models import Person
    
    class PersonSerializer(serializers.ModelSerializer):
        class Meta:
            model = Person
            fields = '__all__'
    
        def update(self, instance, validated_data):
            # 在这里自定义保存逻辑,例如发送电子邮件通知
            send_email_notification(validated_data['name'], validated_data['email'])
    
            # 调用父类的update方法更新对象
            return super().update(instance, validated_data)
    # 在这个示例中,我们在update方法中首先调用了一个名为send_email_notification的函数,该函数负责发送电子邮件通知。然后,我们调用了父类的update方法来实际更新Person对象。这样,我们就可以在更新现有对象时执行自定义的保存逻辑。
    
  10. validate:指定一个可调用的对象,用于验证序列化后的实例数据。默认情况下,使用模型类的clean方法进行验证。

    # validate属性用于在保存对象之前对数据进行验证。它接受一个可调用对象,该对象接收一个包含所有字段值的字典作为参数,并返回一个包含所有字段值的字典或引发ValidationError异常
    from rest_framework import serializers
    from .models import Person
    
    class PersonSerializer(serializers.ModelSerializer):
        class Meta:
            model = Person
            fields = '__all__'
    
        def validate(self, data):
            # 在这里自定义验证逻辑,例如检查年龄是否大于18岁
            if data['age'] < 18:
                raise serializers.ValidationError("年龄必须大于18岁")
    
            # 如果验证通过,返回更新后的数据
            return data
    # 在这个示例中,我们在validate方法中添加了一个自定义验证逻辑,用于检查年龄是否大于18岁。如果验证失败,我们引发一个ValidationError异常。如果验证通过,我们返回更新后的数据。这样,我们就可以在保存对象之前对数据进行验证。
    
  11. validators:指定自定义验证器。可以是一个函数或一个验证器类的实例。

    def custom_validator(value):
        # 自定义验证逻辑
        return value
    
    class MyModelSerializer(serializers.ModelSerializer):
        field1 = serializers.IntegerField(validators=[custom_validator])
        class Meta:
            model = MyModel
    
  12. error_messages:指定自定义错误消息字典,用于覆盖默认的错误消息。键是字段名,值是错误消息。也可以是一个函数,接收一个错误对象并返回自定义错误消息。

    class MyModelSerializer(serializers.ModelSerializer):
        field1 = serializers.IntegerField()
        field2 = serializers.CharField()
        class Meta:
            model = MyModel
            error_messages = {
                'field1': {'invalid': '无效的值'},
                'field2': {'blank': '此字段不能为空'}
            }
    
  13. extra_kwargs:指定额外的关键字参数传递给视图或模型字段。可以是一个字典,键是视图或模型字段的名称,值是额外的关键字参数。也可以是一个函数,接收一个视图或模型字段对象并返回额外的关键字参数。用于传递给模型类的构造函数或验证方法。这可以用于传递额外的上下文信息或自定义参数。

    class MyModelSerializer(serializers.ModelSerializer):
        field1 = serializers.IntegerField()
        field2 = serializers.CharField()
        class Meta:
            model = MyModel
            extra_kwargs = {
                'field1': {'required': True},
                'field2': {'help_text': '请输入描述信息'}
            }
    
实例01
from rest_framework import serializers
from .models import Company, ProInitial, ProAuxiliary, Management, ProjectEnd

class CompanySerializer(serializers.ModelSerializer):
    """单位模型序列化"""
    class Meta:
        model = Company
        fields = '__all__'

class ProInitialSerializer(serializers.ModelSerializer):
    """项目初始信息"""
    class Meta:
        model = ProInitial
        fields = '__all__'

class ProAuxiliarySerializer(serializers.Serializer):
    """项目辅助信息"""
    class Meta:
        model = ProAuxiliary
        fields = '__all__'

class ManageSerializer(serializers.Serializer):
    """项目管理"""
    class Meta:
        model = Management
        fields = '__all__'

class ProjectEndSerializer(serializers.Serializer):
    """完工信息"""
    class Meta:
        model = ProjectEnd
        fields = '__all__'
# 视图 views.py
from .models import ProInitial, Company, Management
from .serializers import CompanySerializer, ManageSerializer

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.pagination import PageNumberPagination
from rest_framework.decorators import api_view

@api_view(['GET', 'POST'])
def manageDef(request):
    if request.method == 'GET':
        q = Management.objects.all()
        # 分页查询,需要在 settings.py 中设置 REST_FRAMEWORK 属性
        pg = PageNumberPagination()
        p = pg.paginate_queryset(queryset=q, request=request)
        # 将分页后的数据传递给 CompanySerializer,生成 JSON 数据对象
        serializer = ManageSerializer(instance=p,many=True)
        # 返回对象 Response 由 Django Rest Framework 实现
        return Response(serializer.data)
    elif request.method == 'POST':
        # 获取请求数据
        id = request.data.get('id', 0)
        # 判断请求参数 id 在模型 Management 中是否存在
        # 若存在,则执行数据修改;否则新增数据
        operation = Management.objects.filter(id=id).first()
        # 数据验证
        serializer = ManageSerializer(data=request.data)
        if serializer.is_valid():
            if operation:
                data = request.data
                id = data['name']
                data['name'] = ProInitial.objects.filter(id=id).first()
                serializer.update(operation, data)
            else:
                # 如果不存在查询对象,把当前POST请求数据保存到数据库。
                serializer.save()
            # 返回对象 Response 由 Django Rest Framework 实现
            return Response(serializer.data)
        return Response(serializer.errors, status=404)

class ManageClass(APIView):

    def get(self,request):
        q = Management.objects.all()
        # 分页查询,需要在 settings.py 中设置 REST_FRAMEWORK 属性
        pg = PageNumberPagination()
        p = pg.paginate_queryset(queryset=q, request=request, view=self)
        # 将分页后的数据传递给 CompanySerializer,生成 JSON 数据对象
        serializer = ManageSerializer(instance=p, many=True)
        # 返回对象 Response 由 Django Rest Framework 实现
        return Response(serializer.data)

    def post(self,request):
        # 获取请求数据
        id = request.data.get('id', 0)
        # 判断请求参数 id 在模型 Management 中是否存在
        # 若存在,则执行数据修改;否则新增数据
        operation = Management.objects.filter(id=id).first()
        # 数据验证
        serializer = ManageSerializer(data=request.data)
        if serializer.is_valid():
            if operation:
                data = request.data
                id = data['name']
                data['name'] = ProInitial.objects.filter(id=id).first()
                serializer.update(operation, data)
            else:
                # 如果不存在查询对象,把当前POST请求数据保存到数据库。
                serializer.save()
            # 返回对象 Response 由 Django Rest Framework 实现
            return Response(serializer.data)
        return Response(serializer.errors, status=404)
实例02
class ManageSerializer(serializers.ModelSerializer):
    """对应项目管理"""
    # 设置一对多关系(ForeignKey)字段所映射的数据模型
    name = ProInitialSerializer()
    company_c = CompanySerializer()

    class Meta:
        model = Management
        fields = '__all__'
        # fields = ("id", "name", "company_c", "contract_no", "status", "start_date")

    # 重写数据添加函数方法 create
    def create(self, validated_data):
        """
        :param validated_data:用户的请求参数
        :return:
        """
        # 从 validated_data 中获取模型 ProInitial 的数据
        name = validated_data.get('name', '')
        id = name.get('id', 0)
        p = ProInitial.objects.filter(id=id).first()
        # 根据 id 判断模型 ProInitial 是否存在数据对象
        # 存在数据对象,则只对 Management 新增数据
        # 不存在,则先对 ProInitial 新增数据,在对 Management 新增数据
        if not p:
            p = ProInitial.objects.create(**name)
        data = validated_data
        data['name'] = p
        m = Management.objects.create(**data)
        return m

    # 重写数据更新函数方法 update
    def update(self, instance, validated_data):
        name = validated_data.get('name', '')
        id = name.get('id', 0)
        p = ProInitial.objects.filter(id=id).first()
        # 判断外键 name 是否存在模型 ProInitial
        if p:
            # 存在,则先更新模型 ProInitial 的数据
            ProInitial.objects.filter(id=id).update(**name)
            #
            data = validated_data
            data['name'] = p
            id = validated_data.get('id', '')
            m = Management.objects.filter(id=id).update(**data)
            return m
# 视图文件 views.py
from .models import ProInitial, Company, Management
from .serializers import CompanySerializer, ManageSerializer
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.pagination import PageNumberPagination
from rest_framework.decorators import api_view


@api_view(['GET', 'POST'])
def manageDef(request):
    if request.method == 'GET':
        q = Management.objects.all()
        # 分页查询,需要在 settings.py 中设置 REST_FRAMEWORK 属性
        pg = PageNumberPagination()
        p = pg.paginate_queryset(queryset=q, request=request)
        # 将分页后的数据传递给 CompanySerializer,生成 JSON 数据对象
        serializer = ManageSerializer(instance=p,many=True)
        # 返回对象 Response 由 Django Rest Framework 实现
        return Response(serializer.data)
    elif request.method == 'POST':
        # 获取请求数据
        id = request.data.get('id', 0)
        # 判断请求参数 id 在模型 Management 中是否存在
        # 若存在,则执行数据修改;否则新增数据
        operation = Management.objects.filter(id=id).first()
        # 数据验证
        serializer = ManageSerializer(data=request.data)
        if serializer.is_valid():
            if operation:
                # 与前面的实例01不同,ManageSerializer重写了update方法
                serializer.updata(operation, request.data)
            else:
                # 如果不存在查询对象,把当前POST请求数据保存到数据库。
                serializer.save()
            # 返回对象 Response 由 Django Rest Framework 实现
            return Response(serializer.data)
        return Response(serializer.errors, status=404)


class ManageClass(APIView):

    def get(self,request):
        q = Management.objects.all()
        # 分页查询,需要在 settings.py 中设置 REST_FRAMEWORK 属性
        pg = PageNumberPagination()
        p = pg.paginate_queryset(queryset=q, request=request, view=self)
        # 将分页后的数据传递给 CompanySerializer,生成 JSON 数据对象
        serializer = ManageSerializer(instance=p, many=True)
        # 返回对象 Response 由 Django Rest Framework 实现
        return Response(serializer.data)

    def post(self,request):
        # 获取请求数据
        id = request.data.get('id', 0)
        # 判断请求参数 id 在模型 Management 中是否存在
        # 若存在,则执行数据修改;否则新增数据
        operation = Management.objects.filter(id=id).first()
        # 数据验证
        serializer = ManageSerializer(data=request.data)
        if serializer.is_valid():
            if operation:
                # 与前面的实例01不同,ManageSerializer重写了update方法
                serializer.updata(operation, request.data)
            else:
                # 如果不存在查询对象,把当前POST请求数据保存到数据库。
                serializer.save()
            # 返回对象 Response 由 Django Rest Framework 实现
            return Response(serializer.data)
        return Response(serializer.errors, status=404)

3.9 序列化的嵌套

模型之间必须存在数据关系才能实现数据嵌套,数据关系可以是一对一、一对多或多对多的,不同的数据关系对数据嵌套的读写操作会有细微的差异。

在 序列化类中设置关系对应的类的映射,就完成了嵌套设置,

class ManageSerializer(serializers.ModelSerializer):
	# 设置一对多关系(ForeignKey)字段所映射的数据模型
    name = ProInitialSerializer()
    company_c = CompanySerializer()
    
    class Meta:
        model = Management
        fields = '__all__'
        # fields = ("id", "name", "company_c", "contract_no", "status", "start_date")
        
        
3.10 depth 控制嵌套深度

Django Rest Framework的ModelSerializer类提供了depth属性,用于控制序列化时的嵌套深度。通过设置depth的值,你可以控制序列化结果中嵌套对象的层次。

**depth**属性的用法如下:

  1. Meta类中设置depth属性,指定嵌套的层次。例如,将depth设置为1表示序列化结果中嵌套一层,将depth设置为2表示序列化结果中嵌套两层,以此类推。
  2. 你可以在自定义的序列化器中使用depth属性,以便在序列化过程中控制嵌套的层次。

4 视图

Django Rest Framework 除了在数据序列化部分简写代码以外,还在视图中提供了简写操作。所以在 Django 原有的 django.views.View 类基础上,drf封装了多个子类。

drf提供的请求和响应类只能在drf封装过的子视图类中使用,也就是说不能再django.view.View中使用

只要类视图直接或者间接继承了APIView,则视图方法中使用的request,就是rest_framework.request.Request,同时,只有在APIVIew的子视图类中才可以使用rest_framework.respone.Response

在Django Rest Framework中,APIView是一个特殊的类视图,它提供了处理请求和响应的方法。当你的视图类直接或间接继承自APIView时,你可以在视图方法中使用rest_framework.request.Request作为请求对象,以及rest_framework.response.Response作为响应对象。

但是,这并不意味着你不能在其他类型的视图(如django.views.View)中使用这些类。实际上,你可以通过创建一个自定义的请求和响应类,然后在你的视图中使用它们。这样,你就可以在任何类型的视图中使用Django Rest Framework的功能。

4.1 请求与响应
4.1.1 Request

Django REST framework 传入视图的 request 对象不再是 Django 默认的 HttpRequest 对象,而是 Django REST framework 提供的扩展了 HttpRequest 类的 Request 类的对象。

Django REST framework 提供了 Parser 解析器,在接收到请求后会自动根据 Content-Type 指明的请求数据类型(如JSON、表单等)将请求数据进行parse 解析,解析为类字典 [QueryDict] 对象保存到 Request 对象中。这里我们可以自行写一个接口测试一下 django 原来的模式是解析不了 json 数据的,drf 可以解析。但是需要注意的是:客户端如果传递过来的是json数据,那么request.data获取到的字典类型数据,不是 querydict 类型,也就没有了 getlist 方法,多选的数据,通过 get 就能取出来。

Request对象的数据是自动根据前端发送数据的格式进行解析之后的结果。

无论前端发送的哪种格式的数据,我们都可以以统一的方式读取数据。

常用属性

.data

request.data 返回解析之后的请求体数据。类似于Django中标准的request.POSTrequest.FILES属性,但提供如下特性:

  • 包含了解析之后的文件和非文件数据
  • 包含了对POST、PUT、PATCH请求方式解析后的数据
  • 利用了 Django REST framework 的 parsers 解析器,不仅支持表单类型数据(urlencoded,form-data),也支持JSON数据(application/json)

.query_params

request.query_params 与 Django 标准的 request.GET 相同,只是更换了名称而已。

简单示例
from django.shortcuts import render

from rest_framework.views import APIView
from rest_framework.response import Response

class StudentAPIView(APIView):
    def get(self,request):
        print(request)

        # http://127.0.0.1:8000/req/students/?a=1&b=2&b=3  b可能为多选数据
        print(request.query_params)  # -- request.GET  <QueryDict: {'a': ['1'], 'b': ['2', '3']}>
        return Response({'msg': 'ok'})

    def post(self,request):
        # 获取post请求体中的数据
        print(request.data)
        print(request.data.get('hobby'))
        print(request.data.getlist('hobby'))
        '''
        结果:
            <QueryDict: {'name': ['小黑'], 'hobby': ['篮球', '美女']}>
            美女
            ['篮球', '美女']
        '''
        return Response({'msg':'ok'})
4.1.2 Response
rest_framework.response.Response
# 对 django 的 HTTPResponse 进行了封装,加了一些其他的功能

Django REST framework 提供了一个响应类 Response,使用该类构造响应对象时,响应的具体数据内容会被转换(render渲染)成符合前端需求的类型。

http://127.0.0.1:8000/req/students/ -- 看到的是页面效果
        
http://127.0.0.1:8000/req/students/?format=json  -- 看到是json数据

后端通过 Response 来想用数据时,通过接口工具发送 get 请求获取数据,你会发现得到的是纯json数据,但是通过浏览器访问时会的到一个页面。这是为什么呢?

drf 的 APIView 在响应内容的时候会自动通过请求头中的浏览器信息来进行数据的回复(Response),如果是浏览器,那么返回的就是个页面(当然我们也可以不让他返回页面,可设置的)如果不是浏览器,给你返回的就是纯 json 数据。这是由 drf 中配置的两个不同的响应类产生的效果,看下面的配置。

Django REST framework 提供了**Renderer** 渲染器,用来根据请求头中的**Accept(客户端希望接收的数据类型声明)来自动转换响应数据到对应格式。如果前端请求中未进行Accept**声明,则会采用默认方式处理响应数据,我们可以通过配置来修改默认响应格式。

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': (                         # 默认响应渲染类
        'rest_framework.renderers.JSONRenderer',          # json渲染器
        'rest_framework.renderers.BrowsableAPIRenderer',  # 浏览器API渲染器
    )
}

# 通过from rest_framework import settings 查看一下 settings 文件,可以找到这个默认配置
4.1.2.1 构造方式
Response(data, status=None, template_name=None, headers=None, content_type=None)

data 数据不要是 render 处理之后的数据,只需传递 python 的内建类型数据即可,Django REST framework 会使用 renderer 渲染器处理 data

data 不能是复杂结构的数据,如 Django 的模型类对象,对于这样的数据我们可以使用 Serializer 序列化器序列化处理后(转为了 Python 字典类型)再传递给 data 参数。

参数说明:

  • data: 为响应准备的序列化处理后的数据;

  • status: 状态码,默认200;

    return Response({'msg': 'ok'},status=204)  # 直接写数字形式的
    # 204等等这些数字代表什么,其实在drf中有文本形式的写法,如下
    from rest_framework import status  # 点击查看status就能看到了
    return Response({'msg': 'ok'},status=status.HTTP_204_NO_CONTENT)  # 这样写也可以,文本形式的
    
  • template_name: 模板名称,如果使用 HTMLRenderer 时需指明;就是有些人觉得 Response 返回的那个页面丑,那么就可以通过这个模板自行定制。

  • headers: 用于存放响应头信息的字典;比如放一些 cookie 啊或者一些自定制的响应头啊都可以,例如:return Response({‘msg’: ‘ok’},status=204,headers={‘xx’:‘oo’})

  • content_type: 响应数据的 Content-Type,通常此参数无需传递,Django REST framework会根据前端所需类型数据(accept请求头)来设置该参数。

4.1.2.2 状态码

为了方便设置状态码,Django REST framework 在 rest_framework.status 模块中提供了常用状态码常量。

1)信息告知 - 1xx

HTTP_100_CONTINUE
HTTP_101_SWITCHING_PROTOCOLS  # 一般直接通过socket建立连接会看到101

2)成功 - 2xx

HTTP_200_OK  # [GET]:服务器成功返回用户请求的数据
HTTP_201_CREATED  # [POST/PUT/PATCH]:用户新建或修改数据成功。
HTTP_202_ACCEPTED  #  [*]:表示一个请求已经进入后台排队(异步任务)
HTTP_203_NON_AUTHORITATIVE_INFORMATION
HTTP_204_NO_CONTENT  # [DELETE]:用户删除数据成功。
HTTP_205_RESET_CONTENT
HTTP_206_PARTIAL_CONTENT
HTTP_207_MULTI_STATUS

3)重定向 - 3xx

HTTP_300_MULTIPLE_CHOICES
HTTP_301_MOVED_PERMANENTLY
HTTP_302_FOUND
HTTP_303_SEE_OTHER
HTTP_304_NOT_MODIFIED
HTTP_305_USE_PROXY
HTTP_306_RESERVED
HTTP_307_TEMPORARY_REDIRECT

4)客户端错误 - 4xx

HTTP_400_BAD_REQUEST  # [*]:表示用户没有权限(令牌、用户名、密码错误)。
HTTP_401_UNAUTHORIZED
HTTP_402_PAYMENT_REQUIRED
HTTP_403_FORBIDDEN  # [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
HTTP_404_NOT_FOUND  # [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
HTTP_405_METHOD_NOT_ALLOWED
HTTP_406_NOT_ACCEPTABLE  #  [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
HTTP_407_PROXY_AUTHENTICATION_REQUIRED
HTTP_408_REQUEST_TIMEOUT
HTTP_409_CONFLICT
HTTP_410_GONE  # [GET]:用户请求的资源被永久删除,且不会再得到的。
HTTP_411_LENGTH_REQUIRED
HTTP_412_PRECONDITION_FAILED
HTTP_413_REQUEST_ENTITY_TOO_LARGE
HTTP_414_REQUEST_URI_TOO_LONG
HTTP_415_UNSUPPORTED_MEDIA_TYPE
HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE
HTTP_417_EXPECTATION_FAILED
HTTP_422_UNPROCESSABLE_ENTITY  # [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
HTTP_423_LOCKED
HTTP_424_FAILED_DEPENDENCY
HTTP_428_PRECONDITION_REQUIRED
HTTP_429_TOO_MANY_REQUESTS
HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE
HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS

5)服务器错误 - 5xx

HTTP_500_INTERNAL_SERVER_ERROR  # [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
HTTP_501_NOT_IMPLEMENTED
HTTP_502_BAD_GATEWAY
HTTP_503_SERVICE_UNAVAILABLE
HTTP_504_GATEWAY_TIMEOUT
HTTP_505_HTTP_VERSION_NOT_SUPPORTED
HTTP_507_INSUFFICIENT_STORAGE
HTTP_511_NETWORK_AUTHENTICATION_REQUIRED
4.1.2.3 错误处理

如果状态码是4xx,服务器就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可

{
    error: '错误提示信息'
}
4.1.2.4 返回结果

针对不同操作,服务器向用户返回的结果应该符合以下规范。服务器返回的数据格式,应该尽量使用JSON

  • GET /collection:返回资源对象的列表(数组)
  • GET /collection/resource:返回单个资源对象
  • POST /collection:返回新生成的资源对象
  • PUT /collection/resource:返回完整的资源对象
  • PATCH /collection/resource:返回完整的资源对象
  • DELETE /collection/resource:返回一个空文档
4.2 视图

Django REST framework 提供了众多的通用视图基类与扩展类,以简化视图的编写。

4.2.1 两个视图基类
4.2.1.1 APIView
rest_framework.views.APIView

APIView 是 Django REST framework 提供的所有视图的基类,继承自Django的View父类。

APIViewView 的不同之处在于:

  • 传入到视图方法中的是 Django REST framework 的 Request 对象,而不是Django的 HttpRequeset 对象;
  • 视图方法可以返回 Django REST framework 的 Response 对象,视图会为响应数据设置(render)符合前端要求的格式;
  • 任何 APIException 异常都会被捕获到,并且处理成合适的响应信息;
  • 在进行 dispatch() 分发前,会对请求进行身份认证、权限检查、流量控制。

支持定义的类属性

  • authentication_classes 列表或元祖,身份认证类
  • permissoin_classes 列表或元祖,权限检查类
  • throttle_classes 列表或元祖,流量控制类

APIView 中仍以常规的类视图定义方法来实现get() 、post() 或者其他请求方式的方法。

举例:比如我们创建一个demo的应用,针对合作单位名单数据管理我们写了下面5个接口

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.pagination import PageNumberPagination

from apps.projects import models
from .serializers import CompanySerializer, ManageSerializer

# 单位数据管理接口
class CompaniesView(APIView):
    # 获取所有对象数据
    def get(self, request):
        # 请求:127.0.0.1:8000/api/pro/company/?type=6
        print(request.GET)  # <QueryDict: {'type': ['6']}>
        print(request.query_params)  # <QueryDict: {'type': ['6']}>
        # 获取所有单位信息
        q = models.Company.objects.all()
        # 对获取到的数据进行序列化
        ser_obj = serializers.CompanySerializer(instance=q, many=True)
        # 将序列化后的数据作为响应返回
        # return Response(ser_obj.data, status=400)
        return Response(ser_obj.data)

    # 创建单条对象数据
    def post(self, request):
        # 对请求中的数据进行反序列化
        ser_obj = serializers.CompanySerializer(data=request.data)
        # 检查反序列化后的数据是否有效
        if ser_obj.is_valid():
            # 有效.创建一条新的单位信息,并将其保存到数据库中
            new_obj = models.Company.objects.create(**ser_obj.validated_data)
            # 再次使用serializers.CompanySerializer对新创建的对象进行序列化
            obj = serializers.CompanySerializer(instance=new_obj)
            # 将序列化后的数据作为响应返回,并设置HTTP状态码为201(表示资源已成功创建)
            return Response(obj.data, status=status.HTTP_201_CREATED)

class CompanyView(APIView):
    # 获取单个对象数据
    def get(self,request, pk):
        q = models.Company.objects.get(pk=pk)
        ser_obj = serializers.CompanySerializer(instance=q)
        return Response(ser_obj.data)

    # 更新单个对象数据
    def put(self, request, pk):
        q = models.Company.objects.get(pk=pk)
        data = request.data
        obj = serializers.CompanySerializer(instance=q, data=data, partial=True)
        if obj.is_valid():
            instance = obj.save()
            new_obj = serializers.CompanySerializer(instance=instance)
            return Response(new_obj.data, status=status.HTTP_202_ACCEPTED)
        else:
            return Response({'error': '校验失败'})

    # 删除单个对象数据
    def delete(self,request, pk):
        models.Company.objects.get(pk=pk).delete()
        return Response('', status=status.HTTP_204_NO_CONTENT)

对应的 urls.py

from django.urls import path
from apps.projects import views

urlpatterns = [
    path('company/', views.CompaniesView.as_view()),
    path('company/<pk>/', views.CompanyView.as_view()),
]
4.2.1.2 GenericAPIView[通用视图类]
rest_framework.generics.GenericAPIView

继承自APIVIew主要增加了操作序列化器和数据库查询的方法,作用是为 Mixin 扩展类的执行提供方法支持。通常在使用时,可搭配一个或多个 Mixin 扩展类。

1)提供的关于序列化器使用的属性与方法

  • 属性:

    • serializer_class 指明视图使用的序列化器
  • 方法:

    • get_serializer_class(self)

      当出现一个视图类中调用多个序列化器时,那么可以通过条件判断在get_serializer_class方法中通过返回不同的序列化器类名就可以让视图方法执行不同的序列化器对象了。

      返回序列化器类,默认返回serializer_class,可以重写,例如:

          # 当视图中使用多个序列化器类时,可以使用该方法来区分
          def get_serializer_class(self):
              if self.request.method == 'GET':
                  return CompanySerializer01
            else:
                  return CompanySerializer02
      
    • get_serializer(self, *args, \**kwargs)

      返回序列化器对象,主要用来提供给Mixin扩展类使用,如果我们在视图中想要获取序列化器对象,也可以直接调用此方法。

      注意,该方法在提供序列化器对象的时候,会向序列化器对象的 context 属性补充三个数据:request、format、view,这三个数据对象可以在定义序列化器时使用。

      比如 serializer = self.get_serializer(instance=self.get_object(),context={'pk':pk}),下面的 request 和 view 我们后面会用到,现在先了解一下,后面使用就知道了

      • request 当前视图的请求对象
      • view 当前请求的类视图对象
      • format 当前请求期望返回的数据格式

2)提供的关于数据库查询的属性与方法

  • 属性:

    • queryset 指明使用的数据查询集
  • 方法:

    • get_queryset(self)

      返回视图使用的查询集,主要用来提供给Mixin扩展类使用,是列表视图与详情视图获取数据的基础,默认返回queryset属性,可以重写,例如:

      def get_queryset(self):
          user = self.request.user
          return user.accounts.all()
      
    • get_object(self)

      返回详情视图所需的模型类数据对象,主要用来提供给 Mixin 扩展类使用。

      在试图中可以调用该方法获取详情信息的模型类对象。

      若详情访问的模型类对象不存在,会返回404。

      该方法会默认使用 APIView 提供的 check_object_permissions 方法检查当前对象是否有权限被访问。

      举例:

      # url(r'^books/(?P<pk>\d+)/$', views.BookDetailView.as_view()),
      class BookDetailView(GenericAPIView):
          queryset = BookInfo.objects.all()
          serializer_class = BookInfoSerializer
      
          def get(self, request, pk):
              book = self.get_object() # get_object()方法根据pk参数查找queryset中的数据对象
              serializer = self.get_serializer(book)
              return Response(serializer.data)
      

3)采用 GenericAPIView 实例

采用 GenericAPIView,对 6.2.1.1的实例代码进行简化

# 单位数据管理接口,采用GenericAPIView视图
class CompanysGenericAPIView(GenericAPIView):
    # 本次视图类中要操作的数据[必填]
    queryset = models.Company.objects.all()
    # 本次视图类中要调用的默认序列化器[选填]
    serializer_class = serializers.CompanySerializer

    # 处理HTTP GET请求。它获取查询集中的所有对象,并使用序列化器将它们转换为JSON格式,然后返回响应。
    def get(self, request):
        """获取所有项目初始信息"""
        obj = self.get_serializer(isinstance=self.get_queryset(), many=True)
        return Response(obj.data)

    def post(self, request):  # 处理HTTP POST请求。
        data = request.data  # 从请求中获取数据
        obj = self.get_serializer(data=data)  # 使用序列化器将数据解析为模型实例
        obj.is_valid(raise_exception=True)  # 验证解析的数据,如果数据无效,将抛出异常。如果数据有效,继续
        instance = obj.save()  # 保存模型实例
        obj = self.get_serializer(instance=instance)  # 再次使用序列化器将实例转换为JSON格式
        return Response(obj.data)  # 返回响应


class CompanyGenericAPIView(GenericAPIView):
    queryset = models.Company.objects.all()  # 定义查询集
    serializer_class = serializers.CompanySerializer  # 定义序列化器类

    # 一个视图中使用多个序列化类的方法,目前的例子是:get请求获取数据时,我们只给他两个字段数据,其他方法时我们给他所有字段数据,定义了这个get_serializer_class方法之后(其实是对父类的方法进行了重写),其实上面的serializer_class就可以不同写了
    # def get_serializer_class(self):
    #     """重写获取序列化器类方法"""
    #     if self.request.method == 'GET':
    #         return 序列化文件1
    #     else:
    #         return 序列化文件2

    # 在使用GenericAPIView实现获取操作单个数据时,我们视图方法中的参数变量pk最好是pk名,别叫id什么的,不然还需要进行一些其他的配置,比较麻烦一些了,简单看一下源码就知道了
    def get(self, request, pk):
        serializer = self.get_serializer(isinstance=self.get_object())
        return Response(serializer.data)

    def put(self,request, pk):  # 处理带有特定主键(pk)的HTTP PUT请求
        data = request.data  # 从请求中获取数据
        # 使用序列化器将数据解析为具有指定主键的模型实例
        serializer = self.get_serializer(instance=self.get_object(), data=data)
        serializer.is_valid(raise_exception=True)  # 验证解析的数据,如果数据无效,将抛出异常。如果数据有效,继续。
        serializer.save()  # 保存模型实例
        serializer = self.get_serializer(instance=self.get_object())  # 再次使用序列化器将实例转换为JSON格式,
        return Response(serializer.data)  # 返回响应。

其实GenericAPIView只是帮我们把数据库查询和调用序列化器类进行了一步封装,目的是为了将一些公共性质的代码挑出去封装一下,其他感觉没啥大用,但其实这个公共性质的代码都不要我们写了,看下面几个扩展类来增强GenericAPIView的作用。

4.2.2 Mixin 扩展类

在 Django Rest Framework 中,Mixin 是非常有用的工具,被视为一种插件功能,可以脱离于所有子类数据。允许你在视图层提供额外的功能或行为,它的主要作用是为实现某种特定功能提供一种方式,例如验证、授权或分页等。通过使用Mixin,可以将一些共同的功能抽取出来,将常见的代码逻辑封装在一起,然后在多个不同的视图类中重用它。使得代码更加简洁、可重用和易于维护。

意义

  1. 代码重用:当你需要在多个视图类中实现相同的功能时,可以使用 Mixin 来避免重复的代码。例如,你可能有一个 Mixin 用于处理分页逻辑,而另一个 Mixin 用于处理权限验证。
  2. 增强功能:通过使用 Mixin,你可以为现有的视图类添加额外的功能。例如,你可以创建一个 Mixin 来为 API 响应添加额外的元数据或响应格式。
  3. 简化代码结构:通过将常用逻辑封装在 Mixin 中,可以使视图类更加简洁,只关注核心的业务逻辑。
  4. 提高可维护性:如果需要修改某个功能,你只需要修改对应的 Mixin,而不需要在多个视图类中查找和修改代码。

总的来说,Mixin在DRF中的主要用途是为了简化视图类的编写,提高代码的复用性和可读性。

简单使用方法

  1. 定义 Mixin 类

    在 DRF 中,你可以定义自己的 Mixin 类来封装特定的功能。一个简单的示例如下:

    from rest_framework import mixins  
    from rest_framework.response import Response  
      
    class MyMixin(mixins.GenericMixin):  
        def get(self, request, *args, **kwargs):  
            # 这里实现你的逻辑  
            content = {"message": "Hello, World!"}  
            return Response(content
    
  2. 在视图类中使用 Mixin

    一旦定义了 Mixin,你就可以将其应用到任何视图类中。例如,你可以将上面的 MyMixin 应用到一个基于类的视图上:

    from rest_framework import generics  
      
    class MyView(MyMixin, generics.GenericAPIView):  
        pass
    

    在这个例子中,当调用 MyViewget 方法时,会首先执行 MyMixin 中的 get 方法逻辑,然后是 GenericAPIView 的默认行为。

  3. 继承多个 Mixin

    你也可以在一个视图类中继承多个 Mixin,以便组合多个功能。例如:

    from rest_framework import mixins, generics  
    from rest_framework.response import Response  
      
    class PaginationMixin:  
        def get(self, request, *args, **kwargs):  
            # 实现分页逻辑  
            ...  
            return Response(data)  
      
    class PermissionMixin:  
        def get(self, request, *args, **kwargs):  
            # 实现权限验证逻辑  
            ...  
            return Response(data)  
      
    class MyView(PaginationMixin, PermissionMixin, generics.GenericAPIView):  
        pass
    

    在这个例子中,MyView 同时继承了 PaginationMixinPermissionMixin,并使用了 DRF 的 GenericAPIView。这意味着 MyView 将同时拥有分页和权限验证的功能。

在DRF中,有五种常用的Mixin:CreateModelMixin、UpdateModelMixin、ListModelMixin、RetrieveModelMixin 和 DestroyModelMixin。它们分别与 HTTP 方法(如GET、POST、PUT、DELETE等)对应,用于实现对模型的基本操作(如创建、更新、列表、获取和删除等)。

例如,如果要实现增加新记录的功能,可以使用 CreateModelMixin;如果要实现更新现有记录的功能,可以使用 UpdateModelMixin;如果要列出所有记录,可以使用 ListModelMixin;如果要获取某个特定的记录,可以使用 RetrieveModelMixin;如果要删除某个特定的记录,可以使用 DestroyModelMixin。

提供了几种后端视图(对数据资源进行曾删改查)处理流程的实现,如果需要编写的视图属于这五种,则视图可以通过继承相应的扩展类来复用代码,减少自己编写的代码量。

这五个扩展类需要搭配GenericAPIView父类,因为五个扩展类的实现需要调用GenericAPIView提供的序列化器与数据库查询的方法。

4.2.2.1 ListModeMixin

获取多条数据的 列表视图扩展类,提供list(request, *args, **kwargs)方法快速实现列表视图,返回200状态码。

该Mixin的list方法会对数据进行过滤和分页。

源代码:

class ListModelMixin(object):
    """
    List a queryset.
    """
    def list(self, request, *args, **kwargs):
        """
        list 方法
        它接受 request 作为参数(通常是一个 HTTP 请求对象),以及可变数量的位置参数和关键字参数。
        """
        # 过滤
        # 首先通过调用 self.get_queryset() 获取原始查询集。
        # 然后,使用 self.filter_queryset() 方法对查询集进行过滤。
        # 这允许你根据请求参数(例如 URL 中的查询参数)来定制返回的查询集。
        queryset = self.filter_queryset(self.get_queryset())
        # 分页
        # 如果查询集很大,你可能希望将其分页。这部分代码首先尝试对查询集进行分页,
        # 然后如果分页成功(page is not None),它将序列化分页的数据并返回分页的响应。
        # 如果没有分页(例如,查询集很小),则跳过此部分。
        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)
        # 序列化
        # 如果查询集不需要分页,这部分代码将直接序列化整个查询集,并返回序列化后的数据。
        # self.get_serializer() 用于获取用于序列化的 serializer 对象,而 many=True 参数表示要序列化的对象是一个列表(而非单个对象)。
        serializer = self.get_serializer(queryset, many=True)
        # 使用 Response 函数(这似乎是 DRF 的一个工具)来创建并返回 HTTP 响应。
        return Response(serializer.data)

示例

from rest_framework.mixins import ListModelMixin

class BookListView(ListModelMixin, GenericAPIView):
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer

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

添加数据的创建视图扩展类,提供create(request, *args, **kwargs)方法快速实现创建资源的视图,成功返回201状态码。

如果序列化器对前端发送的数据验证失败,返回400错误。

源代码:

class CreateModelMixin(object):
    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        """
        名为 create 的方法
        它接受 request 作为参数(通常是一个 HTTP 请求对象),以及可变数量的位置参数和关键字参数
        """
        # 获取序列化器
        # 使用 self.get_serializer() 方法获取一个序列化器对象,
        # 该对象用于将请求数据转换为 Python 数据类型。这里,请求数据从 request.data 中获取。
        serializer = self.get_serializer(data=request.data)
        # 验证序列化器数据:
        # 使用 is_valid() 方法验证序列化器中的数据。
        # 如果数据无效,并且 raise_exception=True,则会引发异常。
        serializer.is_valid(raise_exception=True)
        # 保存序列化器数据:
        # 调用 self.perform_create() 方法来保存序列化器中的数据。
        # 这通常涉及将数据保存到数据库中。
        self.perform_create(serializer)
        # 获取成功的响应头:
        # 调用 self.get_success_headers() 方法来获取响应头。
        # 这里,响应头是关于新创建的资源的位置信息。如果无法获取位置信息,将返回一个空字典。
        headers = self.get_success_headers(serializer.data)
        # 返回响应:
        # 使用 Response 函数,来创建并返回 HTTP 响应。
        # 响应包含序列化器中的数据,状态码为 201(表示新资源已成功创建),以及前面获取的响应头。
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def perform_create(self, serializer):
        """
        这个方法简单地将序列化器中的数据保存到数据库中。
        在 DRF 中,save() 方法执行实际的数据库操作。
        """
        serializer.save()

    def get_success_headers(self, data):
        """
        这个方法尝试从序列化器数据中获取新创建资源的 URL,并将其作为响应头的一部分返回。
        如果无法获取 URL(例如,由于数据类型错误或键不存在),则返回一个空字典。
        """
        try:
            return {'Location': str(data[api_settings.URL_FIELD_NAME])}
        except (TypeError, KeyError):
            return {}

示例:

def post(self,request):
  
  return self.create(requset)
4.2.2.3 RetrieveModelMixin

获取单条数据,详情视图扩展类,提供retrieve(request, *args, **kwargs)方法,可以快速实现返回一个存在的数据对象。

如果存在,返回200, 否则返回404。

源代码:

class RetrieveModelMixin(object):
    """
    Retrieve a model instance.
    """
    def retrieve(self, request, *args, **kwargs):
    	"""
    	名为 retrieve 的方法,它接受 request 作为参数(通常是一个 HTTP 请求对象),以及可变数量的位置参数和关键字参数。
    	"""
        # 获取对象,会检查对象的权限
        # 使用 self.get_object() 方法获取要检索的模型实例。
        # 这个方法通常会检查对象的权限,确保请求者有权访问该对象。
        instance = self.get_object()
        # 序列化
        # 使用 self.get_serializer() 方法获取一个序列化器对象,该对象用于将模型实例转换为 Python 数据类型。
        # 这里,序列化器用于将之前获取的 instance 对象序列化。
        serializer = self.get_serializer(instance)
        # 返回响应:
        # 使用 Response 函数来创建并返回 HTTP 响应。
        # 响应包含序列化器中的数据,即已经序列化的模型实例。
        return Response(serializer.data)

示例:

class BookDetailView(RetrieveModelMixin, GenericAPIView):
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer

    def get(self, request, pk):
        return self.retrieve(request,pk)
        return self.retrieve(request) #pk也可以不传
4.2.2.4 UpdateModelMixin

更新视图扩展类,提供update(request, *args, **kwargs)方法,可以快速实现更新一个存在的数据对象。

同时也提供partial_update(request, *args, **kwargs)方法,可以实现局部更新。

成功返回200,序列化器校验数据失败时,返回400错误。

源代码:

class UpdateModelMixin(object):
    """
    Update a model instance.
    """
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}

        return Response(serializer.data)

    def perform_update(self, serializer):
        serializer.save()

    def partial_update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)

示例:

def put(self,request,pk):
  """更新一条数据"""
  return self.update(request,pk)
4.2.2.5 DestroyModelMixin

删除视图扩展类,提供destroy(request, *args, **kwargs)方法,可以快速实现删除一个存在的数据对象。

成功返回204,不存在返回404。

源代码:

class DestroyModelMixin(object):
    """
    Destroy a model instance.
    """
    def destroy(self, request, *args, **kwargs):
        """
        名为 destroy 的方法,它接受 request 作为参数(通常是一个 HTTP 请求对象),以及可变数量的位置参数和关键字参数。
        """
        # 获取对象:
        # 使用 self.get_object() 方法获取要删除的模型实例。
        # 这个方法通常会检查对象的权限,确保请求者有权删除该对象。
        instance = self.get_object()
        # 执行删除操作:
        # 调用 self.perform_destroy() 方法来实际执行删除操作。
        # 这里,它只是简单地调用了 instance.delete() 来删除模型实例。
        self.perform_destroy(instance)
        # 使用 Response 函数来创建并返回 HTTP 响应。
        # 响应的状态码设置为 204(表示没有内容),意味着请求成功,但响应体为空。这通常用于表示资源已被成功删除。
        return Response(status=status.HTTP_204_NO_CONTENT)

    def perform_destroy(self, instance):
        """
        这是一个辅助方法,用于执行实际的删除操作。它只是简单地调用了模型的 delete() 方法来删除给定的实例。
        """
        instance.delete()

示例:

def delete(self,request,pk):
        """删除一条数据"""
        return self.destroy(request,pk)
4.2.2.6 实例

使用GenericAPIView和视图扩展类,对6.2.1.2中的实例代码进行再简化:

from rest_framework.mixins import ListModelMixin, CreateModelMixin
class CompanysGenericAPIview(GenericAPIView, ListModelMixin, CreateModelMixin):
    # 本次视图类中要操作的数据[必填]
    queryset = models.Company.objects.all()
    # 本次视图类中要调用的默认序列化器[选填]
    serializer_class = serializers.CompanySerializer

    def get(self, request):
        """获取多个项目初始信息"""
        return self.list(request)

    def post(self, request):
        """添加项目信息"""
        return self.create(request)

from rest_framework.mixins import RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
class CompanyGenericAPIView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    # 本次视图类中要操作的数据[必填]
    queryset = models.Company.objects.all()
    # 本次视图类中要调用的默认序列化器[选填]
    serializer_class = serializers.CompanySerializer
    # 在使用GenericAPIView视图获取或操作单个数据时,视图方法中的代表主键的参数最好是pk
    def get(self,request,pk):
        """获取一条数据"""
        return self.retrieve(request,pk)

    def put(self,request,pk):
        """更新一条数据"""
        return self.update(request,pk)

    def delete(self,request,pk):
        """删除一条数据"""
        return self.destroy(request,pk)

下面还能对我们上面的代码进行简化。视图子类,也叫通用视图子类。

4.2.3 GenericAPIView的视图子类
from rest_framework.generics import ListAPIView...
4.2.3.1 reateAPIView

提供 post 方法

继承自: GenericAPIView、CreateModelMixin

4.2.3.2 ListAPIView

提供 get 方法

继承自:GenericAPIView、ListModelMixin

4.2.3.3 RetrieveAPIView

提供 get 方法

继承自: GenericAPIView、RetrieveModelMixin

4.2.3.4 DestoryAPIView

提供 delete 方法

继承自:GenericAPIView、DestoryModelMixin

4.2.3.5 UpdateAPIView

提供 put 和 patch 方法

继承自:GenericAPIView、UpdateModelMixin

4.2.3.6 RetrieveUpdateAPIView

提供 get、put、patch方法

继承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin

4.2.3.7 RetrieveUpdateDestoryAPIView

提供 get、put、patch、delete方法

继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin

4.2.3.8 实例

采用GenericAPIView的视图子类对 6.2.2.6 的实例进行简化

from rest_framework.generics import ListAPIView, CreateAPIView
class CompanysGenericAPIView(ListAPIView, CreateAPIView):
    queryset = models.Company.objects.all()
    serializer_class = serializers.CompanySerializer

from rest_framework.generics import RetrieveAPIView,UpdateAPIView,DestroyAPIView
from rest_framework.generics import RetrieveUpdateDestroyAPIView # 结合了上面三个子类的功能
class CompanyGenericAPIView(RetrieveUpdateDestroyAPIView):
    queryset = models.Company.objects.all()
    serializer_class = serializers.CompanySerializer
4.3 视图集
4.3.1 ViewSet

继承自APIViewViewSetMixin,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。

ViewSet主要通过继承ViewSetMixin来实现在调用as_view()时传入字典(如{‘get’:‘list’})的映射处理工作。

在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。

使用视图集ViewSet,可以将一系列逻辑相关的动作放到一个类中:

  • list() 提供一组数据
  • retrieve() 提供单个数据
  • create() 创建数据
  • update() 保存数据
  • destory() 删除数据

ViewSet视图集类不再实现get()、post()等方法,而是实现动作 action 如 list() 、create() 等。

视图集只在使用as_view()方法的时候,才会将 action 动作与具体请求方式对应上。如:

4.3.2 GenericViewSet

使用 ViewSet 通常并不方便,因为 list、retrieve、create、update、destory 等方法都需要自己编写,而这些方法与前面讲过的 Mixin 扩展类提供的方法同名,所以我们可以通过继承 Mixin 扩展类来复用这些方法而无需自己编写。但是 Mixin 扩展类依赖于GenericAPIView,所以还需要继承 GenericAPIView

GenericViewSet 就帮助我们完成了这样的继承工作,继承自GenericAPIViewViewSetMixin,在实现了调用as_view()时传入字典(如{'get':'list'})的映射处理工作的同时,还提供了GenericAPIView提供的基础方法,可以直接搭配Mixin扩展类使用。

4.3.3 ModelViewSet

继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。

from rest_framework.viewsets import ModelViewSet

class CompanyModelViewSet(ModelViewSet):
    """单位名录视图"""
    queryset = Company.objects.all()
    serializer_class = CompanySerializer
4.3.4 ReadOnlyModelViewSet

继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin。

4.3.5 视图集中自定义 action 动作

在视图集中,除了上述默认的方法动作外,还可以添加自定义动作,进行扩展。

4.3.6 action 属性

在视图集中,我们可以通过action对象属性来获取当前请求视图集时的action动作是哪个。


5 路由 Routers

对于视图集ViewSet,我们除了可以自己手动指明请求方式与动作action之间的对应关系外,还可以使用Routers来帮助我们快速实现路由信息。

REST framework提供了两个router

  • SimpleRouter
  • DefaultRouter
5.1 使用方法
5.1.1 创建 router 对象,并注册视图集,

使用Django Rest Framework的DefaultRouter类,可以轻松地为你的视图集(ViewSet)生成URL路由。

from rest_framework import routers
# 创建路由实例
router = routers.DefaultRouter()
# 注册了一个名为DomeModelViewSet的视图集到router,并为其指定了一个基础名称dome
router.register(r'router_dome', DomeModelViewSet, base_name='dome')
"""
register(prefix, viewset, base_name)
	- prefix 该视图集的路由前缀
	- viewset 视图集
	- base_name 路由别名的前缀
"""

上述代码生成的 URL 路由大致如下:

  1. List API:获取所有对象的URL。格式为 /router_dome/
  2. Retrieve API:获取单个对象的URL。格式为 /router_dome/<object_id>/
  3. Create API:用于创建新对象的URL。格式为 /router_dome/
  4. Update API:用于更新已有对象的URL。格式为 /router_dome/<object_id>/
  5. Destroy API:用于删除已有对象的URL。格式为 /router_dome/<object_id>/
  6. Partial Update API:用于部分更新已有对象的URL。格式为 /router_dome/<object_id>/partial-update/
  7. Retrieve Update History API:用于获取特定对象更新历史的URL。格式为 /router_dome/<object_id>/history/
  8. Retrieve Relations API:获取与给定对象关联的其他对象的URL。格式为 /router_dome/<object_id>/<relation_name>/
  9. List Create API:用于在给定关系中创建新对象的URL。格式为 /router_dome/<object_id>/<relation_name>/
  10. Destroy Relations API:用于删除与给定对象关联的特定关系的URL。格式为 /router_dome/<object_id>/<relation_name>/<related_object_id>/

注意:上述URL路径和对应的HTTP方法可能会因你使用的Django Rest Framework的版本而有所不同。上述信息基于DRF的常见用法和版本(例如,DRF 3.11)。

5.1.2 添加路由数据

可以有两种方式:

方式一:

urlpatterns = [
    ...
]
urlpatterns += router.urls

方式二:

urlpatterns = [
    ...
    url(r'^', include(router.urls))
]

附录

提问

请介绍Django Rest Framework的模型序列化类ModelSerializer的create属性详细用法及示例

请介绍Django Rest Framework的模型序列化类ModelSerializer的 depth 属性详细用法及示例

Django ORM 常用的QuerySet 方法:
  1. all():返回 QuerySet 中所有的对象。
  2. filter(**kwargs):返回与所给筛选条件相匹配的对象。如果没有找到符合条件的对象,则返回空的 QuerySet。
  3. exclude(**kwargs):类似于 filter(),但排除与给定条件匹配的对象。
  4. get(**kwargs):返回与所给筛选条件相匹配的对象,返回结果有且只有一个。如果符合筛选条件的对象超过一个或者没有都会抛出异常。
  5. order_by(*fields):根据给定的字段对查询结果进行排序。
  6. reverse():反转查询结果的顺序。
  7. first()last():分别返回查询结果中的第一个和最后一个对象。如果没有对象,则抛出异常。
  8. values(*fields):返回一个字典列表,其中每个字典代表一行数据。
  9. distinct():返回不重复的对象集合。
  10. annotate(*args, **kwargs):添加注释到查询集中。
  11. only(*fields)、defer(*fields):限制查询集只包含某些字段或排除某些字段。
  • 21
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值