前言

Django REST Framework(简称DRF)是一个用于构建Web API的强大框架,基于Django开发。它提供了许多工具和库,使得构建和管理Web API变得简单和高效。DRF结合了Django的强大特性和灵活性,使开发人员能够轻松地创建功能强大、安全和可扩展的API。

Django REST Framework(DRF)是基于Django框架构建的,并且依赖于Django的核心功能。DRF扩展了Django,提供了用于构建Web API的额外功能和工具。要使用Django REST Framework,需要首先安装和配置Django。DRF是作为Django的一个扩展包提供的,可以在Django项目中引入DRF,以便利用其提供的API构建功能和特性。

以下是一些Django REST Framework的主要特性:

  1. 序列化:DRF提供了一个强大的序列化系统,用于将数据库模型和其他数据类型转换为JSON、XML或其他格式的数据。它使得数据的转换和传输变得简单,并且可以轻松地处理数据验证和反序列化。
  2. 视图:DRF提供了一组视图类,用于处理API请求和生成响应。它支持基于函数的视图和基于类的视图,并提供了一些常用的视图类,如通用视图和混合视图,以帮助开发人员更轻松地构建API。
  3. 路由:DRF的路由系统使得定义API的URL变得简单和可维护。它允许开发人员将URL模式与视图类关联起来,从而实现API的映射。
  4. 认证和权限控制:DRF提供了可配置的认证和权限控制系统,用于保护API的安全性。开发人员可以选择使用基于令牌、基于Session、基于OAuth等不同的认证方式,并且可以定义细粒度的权限控制规则。
  5. 流量限制和缓存:DRF支持流量限制和缓存机制,可以帮助开发人员管理API的流量和性能。它允许设置访问频率限制,以避免滥用和DDoS攻击,并提供了缓存机制,以减少对数据库的频繁访问。
  6. 文档生成:DRF自带了一个强大的文档生成工具,可以自动生成API的交互式文档。开发人员可以轻松地查看API的细节、参数和示例,并通过文档进行API的测试和调试。

服务器协议

常见协议WSGI ASGI:WSGI 适用于传统的同步 Web 应用程序,而 ASGI 则适用于异步的 Web 应用程序,它们在处理请求和响应的方式上有所不同,以满足不同类型的 Web 应用程序的需求。

WSGI

  • WSGI 是 Python Web 应用程序与服务器之间的标准接口,定义了一种通用的方式来处理 HTTP 请求和响应。
  • WSGI 是同步的,即每个请求都会在自己的线程中处理。这意味着在传统的 WSGI 服务器中,对于每个请求,都会阻塞一个线程来处理它,直到请求处理完毕。
  • WSGI 适用于传统的同步 Web 应用程序,如 Django 和 Flask。

ASGI

  • ASGI 是一种异步的 Web 应用程序与服务器之间的接口,它支持异步处理请求和响应,可以更高效地处理大量的并发请求。
  • ASGI 适用于异步 Web 框架,如 FastAPI、Starlette 等。它们通常使用异步 IO(例如 asyncio)来处理请求,并且可以与异步服务器(如 uvicorn、daphne)一起工作,以实现更高效的异步处理。

Django

参考:

Django官网文档

django中文网-3756404@qq.com :Django常用 命令

腾讯云-老齐:彻底搞懂Django中的数据迁移

stack Overflow-Liondancer:startapp with manage.py to create app in another directory

基础项目

虚拟环境

项目中的依赖全部包含在venv,此时选用的Python3.8进虚拟环境

1
python3.8 -m venv venv 

激活虚拟环境

1
source venv/bin/activate     

激活成功后打印python --version即是你已经激活的虚拟环境

安装Django

1
pip install django

创建项目

进入你的项目根目录,如果你不加./它会创建application文件夹包裹/application+manage.py

1
django-admin startproject application ./

此命令创建了一个项目到项目根目录

1
2
$ ls
application manage.py venv

创建应用模块

我的习惯是将所有的应用模块放在项目根目录中专门放置应用模块的一个文件夹/modules,以下先创建sms项目模块的目录,再执行以下

1
python manage.py startapp msm ./modules/msm 

创建了一个名为sms的应用模块到./modules/msm

尝试启动服务

1
2
3
4
5
6
7
8
9
10
11
12
$ python manage.py runserver 
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).

You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
June 16, 2023 - 09:06:27
Django version 4.2.2, using settings 'application.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

上面的意思是你的项目中存在18个迁移文件尚未应用到数据库中的相关应用程序

这些迁移是由Django内置的应用程序(admin、auth、contenttypes和sessions)生成的。

数据库迁移是Django用于管理数据库模式更改的机制。当你在Django应用程序中修改了模型(Model)的定义时,你需要生成迁移文件来记录这些变更。然后,通过应用这些迁移,可以将数据库模式与你的模型定义同步。

数据迁移

一般情况下更改了某个模块的models.py,就需要进行生成migrations文件夹中与models.py对应生成一组迁移文件(migration files)

1
python manage.py makemigrations

一旦你运行了 makemigrations 并生成了迁移文件,migrate 命令会读取你的迁移文件并在数据库中执行相应的 SQL 语句,使得你的数据库表结构与你的模型保持同步。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying auth.0012_alter_user_first_name_max_length... OK
Applying sessions.0001_initial... OK

默认情况下,数据会被迁移到项目根目录下的db.sqlite3文件中,此文件使用sqlite3 db.sqlite3命令访问数据库,图形化使用sqlitebrowser

数据迁移就是按照模型只是将表结构(无数据)导入到了数据库,如图所示

DB Browser for SQLite

数据库中的数据可以使用Json方式/sql文件导入

再次启动服务

image-20230616222823449

Django管理界面

访问http://127.0.0.1:8000/admin/,出现Django administration登陆对话框

image-20230616232256024

用户名&密码需要运行:

1
2
3
4
5
6
7
8
9
10
$ python manage.py createsuperuser 
Username (leave blank to use 'fuding'): admin
Email address: f_ding@126.com
Password: [admin]
Password (again): [admin]
The password is too similar to the username.
This password is too short. It must contain at least 8 characters.
This password is too common.
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.

创建了用户admin,密码admin,即可登陆

image-20230616231851504

manage.py

可执行命令行操作Django项目的相关命令,它们涵盖了开发、测试、数据库管理和部署等方面的常用操作。

常用

1
python manage.py <options>
  1. runserver:启动开发服务器,用于在本地运行Django应用程序。
  2. startapp <app_name>:创建一个新的Django应用程序。
  3. makemigrations:根据模型的更改生成数据库迁移文件。
  4. migrate:应用数据库迁移,将数据库模式与模型定义同步。
  5. createsuperuser:创建超级用户,用于管理Django管理界面。
  6. shell:启动Django的交互式Python Shell,可以在其中执行Python代码和与数据库交互。
  7. test:运行应用程序的测试套件,执行单元测试和集成测试。
  8. collectstatic:收集静态文件到指定的静态文件目录,用于生产环境的部署。
  9. flush:清空数据库中的所有数据,包括所有应用程序的表格和数据。
  10. dbshell:通过命令行进入数据库的交互式Shell,用于执行原生的SQL查询和操作。
  11. dumpdata:将数据库中的数据导出为JSON或XML格式,用于备份或数据迁移。
  12. loaddata:从JSON或XML文件中加载数据到数据库中,用于恢复备份或导入数据。
  13. check:检查项目的完整性和一致性,包括模型的验证和配置问题。
  14. showmigrations:显示每个应用程序中的迁移状态,包括已应用和未应用的迁移文件。

setting.py

Django项目的总配置文件

INSTALLED_APPS

作用

这个配置项告诉Django(如果你是在Django项目中使用settings.py)需要启用哪些应用程序,并将它们添加到项目中。这样,Django会自动加载这些应用程序并为你提供它们所提供的功能。

实例

1
2
3
4
5
6
7
8
9
10
11
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'modules.sms',
'drf_yasg',
]
  1. 项目内部应用程序:这些应用程序是你自己在项目中开发的应用程序,可以通过相对路径或模块路径指定,如'myapp''myproject.myapp'
  2. 第三方应用程序:这些应用程序是由其他开发者或组织开发的,用于提供特定的功能或服务。你可以通过包名称指定它们,如'django.contrib.admin''rest_framework'
  3. Django内置应用程序:Django本身提供了一些内置应用程序,用于处理常见的任务和功能,如用户认证、管理后台等。你可以通过包名称指定它们,如'django.contrib.auth''django.contrib.contenttypes'

DjangoRestFramework

简单总结 2023-6-20

  • models.py

    • 类名对应数据库中的数据表,其中属性用于定义数据库表结构中的字段,迁移时将models.py中的代码可生成各种数据库中的表[Django提供]
  • serializers.py

    • Serializer设置内部类中设置model = <某个models>用于查询数据在数据库中是那个表,fields = <字段范围>可理解为存取数据的位置

    • code = serializers.CharField(),提供了一些配置选项,如最大长度、是否唯一、是否必填、默认值等,请求的时候这个参数是用来接收&验证请求中的Json字段code

路由(Router)

DefaultRouter()/SimpleRouter()

无论默认路由/简单路由都会有一个常用的方法:

1
2
3
4
system_url = routers.SimpleRouter()
system_url.register(r'user', UserViewSet)
# 需要将生成的Django路由添加到 urlpatterns
urlpatterns += system_url.urls

此方法可根据View中Djangorestframework提供的增删改查方法自动生成CURD接口路由提供给Django传统路由urlpatterns

UserViewSet中如果继承常用的

  • mixins.CreateModelMixin — POST
  • mixins.RetrieveModelMixin — GET
  • mixins.UpdateModelMixin — PUT / PATCH
  • mixins.DestroyModelMixin — DELETE
  • mixins.ListModelMixin — GET

使用system_url.register(r'user', UserViewSet)会自动注册相关以上对应的RestFull接口5大方法,如图所示(来自一个开源项目)。

image-20230628123154489

Django中的urlpatterns例如需要手动添加路由映射到增删改查的方法。

1
UserViewSet.as_view({'get': 'import_data', 'post': 'import_data'})

视图(View)

ModelViewSet

综合常用操作的视图,通过阅读以下源码可知,DRF已经将 增删改查 & GenericViewSet 已经继承到ModelViewSet中,等于说一般增删改查只需继承这一个类就可实现对数据库表的常用基础操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
from rest_framework.viewsets import ModelViewSet

class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
"""
A viewset that provides default `create()`, `retrieve()`, `update()`,
`partial_update()`, `destroy()` and `list()` actions.
"""
pass
  • 混合类
    • mixins.CreateModelMixin:该混合类提供了创建模型实例的功能。它包含了一个 create 方法,用于处理 POST 请求并创建新的模型实例。
    • mixins.UpdateModelMixin:该混合类提供了更新模型实例的功能。它包含了一个 update 方法,用于处理 PUT 或 PATCH 请求并更新现有的模型实例。
    • mixins.DestroyModelMixin:该混合类提供了删除模型实例的功能。它包含了一个 destroy 方法,用于处理 DELETE 请求并删除指定的模型实例。
    • mixins.ListModelMixin:这是DRF提供的一个混合类,用于实现获取列表数据的功能。它添加了list()方法,用于处理 GET 请求并返回模型实例的列表。
    • mixins.RetrieveModelMixin:这是DRF提供的另一个混合类,用于实现获取单个数据对象的功能。它添加了retrieve()方法,处理 GET 请求并返回指定模型实例的详细信息。
  • 通用视图基类
    • viewsets.GenericViewSet:这是DRF提供的通用视图集基类,它结合了不同的混合类,并提供了默认的视图行为。通过继承GenericViewSet,可以创建具有常用CRUD(创建、读取、更新、删除)操作的视图集。

queryset

作用

  • 数据范围控制:通过指定 queryset,可以明确指定视图操作的数据范围。这样可以确保只在特定的数据集上执行操作,而不会影响其他数据。例如,可以使用 queryset 仅查询特定用户的数据或特定时间范围内的数据。
  • 数据过滤:queryset 允许你使用过滤器和查询条件对数据进行过滤。这样可以根据特定的条件从数据集中筛选出满足条件的记录。例如,可以使用 queryset 来仅获取状态为已发布的文章或价格在特定范围内的商品。
  • 数据排序:queryset 允许对数据进行排序。你可以根据某个字段对数据进行升序或降序排序。这样可以确保在返回给客户端或进行后续处理时,数据以特定的顺序呈现。
  • 数据性能优化:通过使用 queryset,可以利用数据库的查询优化功能,提高数据访问和操作的性能。数据库可以针对 queryset 执行适当的索引和优化操作,从而提供更高效的查询和操作速度。

实验

常见报错

1
2
3
4
class VerySMSViewSet(viewsets.GenericViewSet):
pass
# 加上以下就会解决
# queryset = VerifyCode.objects.all()
1
2
assert queryset is not None, '`basename` argument not specified, and could ' \
AssertionError: `basename` argument not specified, and could not automatically determine the name from the viewset, as it does not have a `.queryset` attribute.

这个错误是由于在使用 Django REST Framework (DRF) 中的视图集(Viewset)时,没有指定视图集的 basename 参数,并且 DRF 无法自动从视图集中确定名称,因为它没有 .queryset 属性。

在 DRF 的视图集中,需要提供一个 basename 参数,用于指定视图集的名称。basename 用于在路由中生成 URL 名称,以确保正确地匹配视图集的操作。

解决这个错误的方法是在视图集中指定 basename 参数,例如:

1
2
3
4
5
6
pythonCopy codeclass MyViewSet(viewsets.ViewSet):
queryset = MyModel.objects.all()
serializer_class = MySerializer
basename = 'my-model' # 指定视图集的名称

# 其他视图集的代码

确保将 basename 参数设置为适当的名称,并与视图集的其他配置一起使用。

如果视图集没有 .queryset 属性,你还可以考虑重写视图集的 get_queryset() 方法来提供查询集。

1
2
3
4
5
6
7
8
9
pythonCopy codeclass MyViewSet(viewsets.ViewSet):
serializer_class = MySerializer
basename = 'my-model' # 指定视图集的名称

def get_queryset(self):
# 返回适当的查询集
return MyModel.objects.all()

# 其他视图集的代码

请根据你的视图集的具体配置进行相应的调整,确保提供正确的 basename 参数或重写 get_queryset() 方法。

源码

/rest_framework/viewsets.py

1
2
3
4
5
6
7
class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
"""
The GenericViewSet class does not provide any actions by default,
but does include the base set of generic view behavior, such as
the `get_object` and `get_queryset` methods.
"""
pass

/rest_framework/generics.py

1
2
3
4
5
6
7
8
9
10
11
class GenericAPIView(views.APIView):
"""
Base class for all other generic views.
"""
# You'll need to either set these attributes,
# or override `get_queryset()`/`get_serializer_class()`.
# If you are overriding a view method, it is important that you call
# `get_queryset()` instead of accessing the `queryset` property directly,
# as `queryset` will get evaluated only once, and those results are cached
# for all subsequent requests.
queryset = None

总结:queryset = MyModel.objects.all()加上就会解决,指定 queryset 是为了限定视图操作的数据范围和条件。在 Django 或 Django REST Framework 中,queryset 参数用于指定要在视图中查询、过滤和操作的数据集。

序列化器(Serializer)

Field

设置与数据库字段相关名称的Field,才可以使用RestFul接口向设置的Field属性传值,Field参数可以设置限制输入值的一些参数(如下)限制条件。

  • CharField: 用于表示字符类型的字段,如字符串。可用于输入和输出数据。
  • IntegerField: 用于表示整数类型的字段。可用于输入和输出数据。
  • FloatField: 用于表示浮点数类型的字段。可用于输入和输出数据。
  • Boolea nField: 用于表示布尔类型的字段。可用于输入和输出数据。
  • DateTimeField: 用于表示日期和时间类型的字段。可用于输入和输出数据。
  • DateField: 用于表示日期类型的字段。可用于输入和输出数据。
  • TimeField: 用于表示时间类型的字段。可用于输入和输出数据。
  • EmailField: 用于表示电子邮件地址类型的字段。可用于输入和输出数据。
  • URLField: 用于表示 URL 类型的字段。可用于输入和输出数据。
  • SerializerMethodField: 用于表示计算属性或自定义方法的字段。通常用于输出数据,可以根据其他字段的值动态生成字段的值。
  • ListField: 用于表示列表类型的字段。可用于输入和输出数据。
  • DictField: 用于表示字典类型的字段。可用于输入和输出数据。
  • FileField: 用于表示上传文件类型的字段。可用于输入和输出数据。
  • ImageField: 用于表示上传图像类型的字段。可用于输入和输出数据。

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
code = serializers.CharField(required=True, 
write_only=True,
max_length=4, min_length=4, label="验证码",
error_messages={
"blank": "请输入验证码",
"required": "请输入验证码",
"max_length": "验证码格式错误",
"min_length": "验证码格式错误"
},
help_text="验证码")
username = serializers.CharField(label="用户名",
help_text="用户名",
required=True,
allow_blank=False,
validators=[UniqueValidator(queryset=User.objects.all(), message="用户已经存在")])

password = serializers.CharField(style={'input_type': 'password'},
help_text="密码",
label="密码",
write_only=True,
)

常用的配置参数

  • required: 指定字段是否为必填字段,接受布尔值(TrueFalse)。默认为 True
  • default: 指定字段的默认值,当字段未提供值时将使用该默认值。
  • allow_null: 指定字段是否允许为 None,接受布尔值。默认为 False
  • read_only: 指定字段是否只用于输出,不允许输入。接受布尔值。默认为 False
  • write_only: 指定字段是否只用于输入,不会在输出中显示。接受布尔值。默认为 False
  • max_length: 用于 CharField 的选项,指定字符字段的最大长度。
  • min_length: 用于 CharField 的选项,指定字符字段的最小长度。
  • validators: 指定字段的验证器列表,用于验证字段的值的有效性。
  • choices: 指定字段的可选值列表或可选值元组,用于限制字段的值。
  • label: 指定字段的标签,用于显示在序列化输出中或表单中。
  • help_text: 指定字段的帮助文本,用于显示在表单或 API 文档中。

Serializer

  • Serializer 是 DRF 中的基础序列化类,用于将数据转换为可传输可存储的格式(如JSON)。
  • Serializer 提供了灵活的字段定义,可以手动定义每个字段的类型验证规则序列化/反序列化方法
  • Serializer 不直接与数据库模型关联,因此需要手动编写字段定义和数据操作逻辑
  • Serializer 适用于处理非模型数据或需要更多自定义逻辑的场景。

ModelSerializer

如果将本Serializer放入了一个继承ModelViewSet中的View中,设置的对每个输入字段的限制将会互相影响需要上传Body数据的Restful接口,比如,PUT、PATCH,所以需要单独设置PUT、PATCH动作的Serializer,通过动作限制使用的Serializer,从而不会互相干扰上传数据检测。

  • ModelSerializerSerializer子类,专门用于与数据库模型进行交互的序列化器。
  • ModelSerializer 自动根据模型定义生成字段,并提供了一些默认的序列化/反序列化逻辑。
  • ModelSerializer 自动关联模型的字段验证规则简化了序列化器的编写。
  • ModelSerializer 自动处理与数据库模型的创建、更新和查询等操作,减少了手动编写数据操作逻辑的工作量。
  • ModelSerializer 适用于简单的数据库模型交互场景,能够快速创建符合模型结构的序列化器。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class VerySMSSerializer(ModelSerializer):
code = serializers.CharField(required=True, write_only=True, max_length=4, min_length=4, label="验证码",
error_messages={
"blank": "请输入验证码",
"required": "请输入验证码",
"max_length": "验证码格式错误",
"min_length": "验证码格式错误"
},
help_text="验证码")
mobile = serializers.CharField(max_length=11,
error_messages={
"max_length": "手机号码长度超出"
}
)
add_time = serializers.DateTimeField()

def validate_mobile(self, mobile):

if not re.match(REGEX_MOBILE,mobile):
raise serializers.ValidationError('手机号码格式不正确')
return mobile

def check_code(self, mobile, code):
code_obj = VerifyCode.objects.filter(mobile=mobile).order_by("-add_time")
v_time = datetime.now() - timedelta(minutes=6)

if len(code_obj):
if not code_obj[0].add_time > v_time:
raise serializers.ValidationError({'code': '验证码过期'})
if code_obj[0].code != code:
raise serializers.ValidationError({'code': '验证码错误'})
else:
raise serializers.ValidationError({'code': '未生成验证码'})

def validate(self, attrs):
mobile = attrs['mobile']
code = attrs['code']
self.check_code(mobile, code)
return attrs

class Meta:
model = VerifyCode
fields = ('code', 'mobile', 'add_time')

实验

  • Field 属性作用,在调用接口的时候,对传入数据进行检测,设置参数限制传入数据的合法性

  • Meta 中 fields属性定义参与查询和传入字段