深色模式
书签应用
Vitepress + Django + DRF + Axios + Element Plus + ……
VitePress 官网中文 —— https://vitejs.cn/vitepress/
Django 官网中文文档 —— https://docs.djangoproject.com/zh-hans/
Django REST Framework 官网 —— https://www.django-rest-framework.org/
Axios 官网中文 —— https://axios-http.com/zh/
Element Plus 官网中文 —— https://element-plus.org/zh-CN/
TypeScript 官网中文 —— https://www.tslang.cn/
说明
本站 [ 技术资源 ] 栏目使用的一个书签应用,前后端分离模式,直接在 VitePress 的 page 布局下使用 Element Plus 作为前端显示页面内容,通过 Django 创建数据库模型及使用其管理员后台添加删除数据,采用 DRF 构建 API 供前端统一调用数据,前端请求则使用 Axios。
前后端分离模式
【 前端访问 】前端浏览器 -> 运行 JS 请求数据 ->【 后端静态文件服务器/应用服务器 】
【 后端返回 】静态文件服务器 -> HTML、CSS、JS 静态文件
【 后端返回 】应用服务器 -> 查询数据库 -> 返回统一API 数据格式(JSON、XML等)
开发环境
Axios —— 一个基于 promise 的网络请求库,可以用于浏览器和 node.js
Element Plus —— 一个 Vue 3 UI 框架 —— 基于 Vue 3,面向设计师和开发者的组件库 Django —— 适用于 Python 的 Web 应用程序框架
Django REST Framework —— 用于构建 Web API 的强大而灵活的工具包
WSL —— 适用于 Linux 的 Windows 子系统
Visual Studio Code —— 轻量级源代码编辑器
前端:Axios + Element Plus
后端:Django 4.1.7 + DRF 3.14.0
系统:Win11 + WSL + VS Code + Python 3.10.6
搭建开发环境
安装 WSL
安装 VS Code 和 WSL 扩展
安装 Python
WSL 一般默认安装 Python 3,但仍需安装 pip(Python 的标准包管理器)和 venv(用于创建和管理轻型虚拟环境的标准模块)。请记住,你可能需要更新你的 Linux 发行版,使其具有最新版本。
创建项目
创建虚拟环境
对于 Python 开发项目,使用虚拟环境是推荐最佳做法。 通过创建虚拟环境,可以将项目工具隔离开来,避免与其他项目的工具发生版本冲突。
Bash
mkdir django && cd django # 创建并进入一个新目录
python3 -m venv .venv # 创建名为 .venv 的虚拟环境
source .venv/bin/activate # 激活虚拟环境 (.venv)
pip list # 查看虚拟环境中安装的包
1
2
3
4
2
3
4
安装 Django
Bash
python3 -m pip install Django # 通过 pip 安装正式发布版本
# Django 访问慢可临时使用国内镜像
pip install django --index https://pypi.tuna.tsinghua.edu.cn/simple
python3 -m django --version # 查看安装的 Django 版本
1
2
3
4
2
3
4
创建 Django 项目
Bash
django-admin startproject mysite # 创建项目
cd mysite # 进入 mysite 目录下,查看创建的文件
1
2
2
django-admin startproject mysite
创建了什么
└── mysite
├── db.sqlite3
├── manage.py
└── mysite
├── __init__.py
├── asgi.py
├── settings.py
├── urls.py
└── wsgi.py
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
- 最外层的
mysite/
根目录只是你项目的容器,根目录名称对 Django 没有影响,你可以将它重命名为任何你喜欢的名称。 manage.py
: 一个让你用各种方式管理 Django 项目的命令行工具。- 里面一层的
mysite/
目录包含你的项目,它是一个纯 Python 包。它的名字就是当你引用它内部任何东西时需要用到的 Python 包名。 (比如mysite.urls
). mysite/__init__.py
:一个空文件,告诉 Python 这个目录应该被认为是一个 Python 包。mysite/settings.py
:Django 项目的配置文件。mysite/urls.py
:Django 项目的 URL 声明,就像你网站的“目录”。mysite/asgi.py
:作为你的项目的运行在 ASGI 兼容的 Web 服务器上的入口。mysite/wsgi.py
:作为你的项目的运行在 WSGI 兼容的Web服务器上的入口。
修改配置文件
Bash
# mysite 目录打开 VS Code
code .
1
2
2
编辑 mysite/settings.py
,这是 mysite 项目的 Django 设置文件。
- 这个配置文件使用 SQLite 作为默认数据库。
- 查找并修改时区:
TIME_ZONE = 'Asia/Shanghai'
- 设置管理界面语言为中文,修改
LANGUAGE_CODE = 'zh-Hans'
编辑 mysite/settings.py |
---|
启动 Django 开发服务器
Bash
# 启动 Django 开发服务器并查看效果
python3 manage.py runserver
# 虚拟环境下启动开发服务器 80 端口
sudo ../.venv/bin/python3 manage.py runserver 80
1
2
3
4
2
3
4
警告
千万不要 将这个服务器用于和生产环境相关的任何地方。这个服务器只是为了开发而设计的。
打开浏览器访问 http://127.0.0.1:8000/ 查看效果 |
---|
Django 创建应用
项目和应用程序
项目 描述了一个 Django 网络应用。项目的根目录(包含 manage.py
文件的目录)通常是项目中所有未单独安装的应用程序的容器。
应用程序 指的是提供了一些功能的 Python 包。应用程序 可在多个项目中重用。应用程序包括模型,视图,模板,模板标签,静态文件,URL,中间件等的一些组合。它们通常使用 INSTALLED_APPS
选项加入到项目中,也可以使用其他机制,如 URLconf
, MIDDLEWARE
配置或模板继承。
创建应用
Bash
python3 manage.py startapp app_bookmark_develop
1
生成应用的基础目录结构
app_bookmark_develop
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
创建应用模型
Django 提供了一个抽象的模型(“models”)层,用于结构化和操作你的网页应用程序的数据。
- 模型:模型准确且唯一的描述了数据。它包含您储存的数据的重要字段和行为。一般来说,每一个模型都映射一张数据库表。
- 字段:模型中最重要且唯一必要的是数据库的字段定义。字段在类属性中定义。
- 字段类型:字段类型用以指定数据库数据类型。
- 字段选项:每一种字段都需要指定一些特定的参数。这些参数不止于用来定义数据库结构,也用于验证数据。
- 自动设置主键:默认情况下,Django 给每个模型一个自动递增的主键,其类型在
AppConfig.default_auto_field
中指定,或者在DEFAULT_AUTO_FIELD
配置中全局指定。 - 字段备注名:除了
ForeignKey
,ManyToManyField
和OneToOneField
,任何字段类型都接收一个可选的位置参数verbose_name
,如果未指定该参数值, Django 会自动使用字段的属性名作为该参数值,并且把下划线转换为空格。 - 关联关系:显然,关系型数据库的强大之处在于各表之间的关联关系。 Django 提供了定义三种最常见的数据库关联关系的方法:多对一,多对多,一对一。
编辑 app_bookmark_develop/models.py
文件,添加/改变模型
点我查看代码
py
from PIL import Image
from django.db import models
from django.utils import timezone # timezone 用于处理时间相关事务
from django.contrib.auth.models import User # 导入内建 User 模型
class Category(models.Model):
"""分类 模型"""
# 字段类型 CharField 字符串。适用于小到大的字符串
# 选项 max_length 必须的。该字段的最大长度(以字符为单位)
# 选项 verbose_name 可选的。字段备注名
title = models.CharField(max_length=100, verbose_name='分类名称')
# 字段类型 DateTimeField 一个日期和时间
# 选项 auto_now_add 当第一次创建对象时,自动将该字段设置为现在。对创建时间戳很有用。
# 请注意,当前日期是 始终 使用的;它不是一个你可以覆盖的默认值。
# 因此,即使你在创建对象时为该字段设置了一个值,它也会被忽略。
# 如果你想修改这个字段,可以设置以下内容来代替 auto_now_add=True :
# 对于 DateField: default=date.today —— 来自 datetime.date.today()
# 对于 DateTimeField: default=timezone.now —— 来自 django.utils.timezone.now()
created = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
# 选项 auto_now 每次保存对象时,自动将该字段设置为现在。对于“最后修改”的时间戳很有用。
# 请注意,当前日期 总是 被使用,而不仅仅是一个你可以覆盖的默认值。
#
updated = models.DateTimeField(auto_now=True, verbose_name='更新时间')
# Meta 选项 使用内部 Meta类 来给模型赋予元数据
# 模型的元数据即“所有不是字段的东西”,
# 比如排序选项( ordering ),数据库表名( db_table ),
# 或是阅读友好的单复数名( verbose_name 和 verbose_name_plural )。
# 这些都不是必须的,并且在模型当中添加 Meta类 也完全是可选的。
class Meta:
# 对象的可读名称,单数
verbose_name = '分类'
# 对象的复数名称
verbose_name_plural = '分类'
# __str__() 一个 Python 的“魔法方法”,返回值友好地展示了一个对象。
# Python 和 Django 在要将模型实例展示为纯文本时调用。
# 最有可能的应用场景是交互式控制台或后台。
# 每当你对一个对象调用 str() 时,就会调用 __str__() 方法。
# 最主要的是,在 Django 管理站点中显示一个对象,以及作为模板显示对象时插入的值。
# 因此,你应该总是从 __str__() 方法中返回一个漂亮的、人类可读的模型表示。
def __str__(self):
# 将分类名称返回
return self.title
class Tag(models.Model):
"""标签 模型"""
# 关系字段 ForeignKey 多对一关联
# 一个分类可以与多个书签关联,但一个书签只可归属一个分类
# 选项 null 如果是 True, Django 将在数据库中存储空值为 NULL。默认为 False。
# 避免在基于字符串的字段上使用 null,如 CharField 和 TextField。
# 如果一个基于字符串的字段有 null=True,
# 这意味着它有两种可能的“无数据”值。NULL,和空字符串。
# 无论是基于字符串的字段还是非字符串的字段,如果希望在表单中允许空值,
# 还需要设置 blank=True,因为 null 参数只影响数据库的存储。
# 例如,当前书签的归属分类在数据库中字段存储空值时为 NULL。
# 选项 blank 注意,这与 null 不同。 null 纯属数据库相关,而 blank 则与验证相关。
# 如果一个字段有 blank=True,表单验证将允许输入一个空值。
# 如果一个字段有 blank=False,则该字段为必填字段,False 为默认值。
# 例如,当前书签的归属分类在表单验证时允许输入一个空值。
# 参数 on_delete 当一个由 ForeignKey 引用的对象被删除时,
# Django 将模拟 on_delete 参数所指定的 SQL 约束的行为。
# 例如,书签的归属分类可为空,当被引用的分类对象被删除时,它被设置为空
# 参数 related_name 用于从相关对象到这个对象的关系的名称。
category = models.ForeignKey(
Category,
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='归属分类的标签',
verbose_name='归属分类'
)
text = models.CharField(max_length=20, verbose_name='标签名称')
created = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
updated = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
verbose_name = '标签'
verbose_name_plural = '标签'
def __str__(self):
return self.text
class Bookmark(models.Model):
"""书签 模型"""
# 使用 ImageField 需要安装的 Pillow 支持你使用的图片格式
image_title = models.ImageField(
# 选项 upload_to 指定 MEDIA_ROOT 的子目录,用于上传文件。
# upload_to 中的 %Y/%m/%d 部分是 strftime() 格式化
# 例:上传后的文件可能将保存在
# mysite/media/app_bookmark_develop/image/20230118 目录下
upload_to='app_bookmark_develop/image/%Y%m%d',
null=True,
blank=True,
verbose_name='标题配图'
)
# 保存图片时按缩小到指定宽度或高度
# 覆盖 Model 类的默认 save 方法。
# 重写的方法首先使用 super().save(*args, **kwargs) 调用父 save 方法,
# 将模型实例保存到数据库中。之后,该方法检查实例是否具有 image_title 属性。
# 如果有,则使用 PIL 库中的 Image.open 方法打开图像文件,
# 并检查图像的宽度或高度是否大于 400 或 300 像素。
# 如果任一维度大于指定的限制,则该方法使用 thumbnail 方法调整图像大小以适应限制,
# 并使用 img.save(self.image_title.path) 将调整大小的图像保存回原始文件路径。
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if self.image_title:
img = Image.open(self.image_title.path)
if img.width > 400 or img.height > 300:
output_size = (400, 300)
img.thumbnail(output_size)
img.save(self.image_title.path)
category = models.ForeignKey(
Category,
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='归属分类的书签',
verbose_name='归属分类'
)
# 关系字段 ManyToManyField 多对多关联
# 一个书签可能在多个标签中显示,并且一个标签也有多个可显示的书签
tags = models.ManyToManyField(
Tag,
blank=True,
related_name='相关书签',
verbose_name='相关标签'
)
# 关系字段 ForeignKey 多对一关联
# 一个收藏者可以与多个书签关联,但一个书签只可归属一个收藏者
# 参数 on_delete 值为 CASCADE 级联删除。
# 当被引用的收藏者被删除时,也删除了包含该收藏者的书签对象
collector = models.ForeignKey(
User,
null=True,
blank=True,
on_delete=models.CASCADE,
related_name='归属用户的书签',
verbose_name='归属收藏者'
)
text = models.CharField(max_length=200, verbose_name='名称')
# 字段类型 URLField URL 的 CharField,由 URLValidator 验证。
href = models.URLField(max_length=500, verbose_name='网址')
# 字段类型 TextField 一个大的文本字段
icon = models.TextField(blank=True, verbose_name='图标')
title = models.CharField(max_length=200, blank=True, verbose_name='标题')
body = models.TextField(blank=True, verbose_name='正文')
# 选项 default 可选的。该字段的默认值
# default=timezone.now 则在创建数据时将当前时间写入该字段的默认值
created = models.DateTimeField(default=timezone.now, verbose_name='创建时间')
updated = models.DateTimeField(default=timezone.now, verbose_name='更新时间')
class Meta:
# 对象的默认排序,用于获取对象列表时
# ordering = ['-updated']
verbose_name = '书签'
verbose_name_plural = '书签'
def __str__(self):
return self.text
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
激活使用应用模型
编辑 app_bookmark_develop/apps.py
配置应用程序
要设置一个应用程序,在应用程序中创建一个 apps.py
模块,然后在那里定义一个 AppConfig
的子类。
点我查看代码
py
from django.apps import AppConfig
class AppBookmarkDevelopConfig(AppConfig):
# 配置属性 AppConfig.default_auto_field
# 隐式主键类型,用于添加到本应用中的模型。
# 你可以用它来保持 AutoField 作为第三方应用的主键类型。
default_auto_field = 'django.db.models.BigAutoField'
# 配置属性 AppConfig.name 指向此应用程序的完整的 Python 格式的路径
name = 'app_bookmark_develop'
# 配置属性 AppConfig.verbose_name 应用程序容易被人理解的名称
verbose_name = "书签应用 - 技术资源"
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
编辑 mysite/settings.py
启用书签应用
当 INSTALLED_APPS
中包含一个应用程序模块的点分隔路径时,默认情况下,如果 Django 在 apps.py
子模块中找到一个 AppConfig
子类,它就会将该配置用于应用程序。这个行为可以通过设置 AppConfig.default
为 False
来禁止。
py
INSTALLED_APPS = [
'django.contrib.admin', # 管理员站点
'django.contrib.auth', # 认证授权系统
'django.contrib.contenttypes', # 内容类型框架
'django.contrib.sessions', # 会话框架
'django.contrib.messages', # 消息框架
'django.contrib.staticfiles', # 管理静态文件的框架
# INSTALLED_APPS 默认包括了以上 Django 的自带应用,
# 这些应用被默认启用是为了给常规项目提供方便。
'app_bookmark_develop', # 启用 书签应用
]
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
数据库迁移
迁移是 Django 对于模型定义(也就是你的数据库结构)的变化的储存形式。迁移被分解成生成和应用两个命令,让开发和生产环境中使用更灵活。迁移能让你在开发过程中持续的改变数据库结构而不需要重新删除和创建表 - 它专注于使数据库平滑升级而不会丢失数据。
当编辑 models.py
文件,新建或改变模型后,可以执行迁移操作同步变更数据库
Bash
# 生成迁移
python3 manage.py makemigrations app_bookmark_develop
# 执行后会生成迁移数据文件 app_bookmark_develop/migrations/0001_initial.py
# 可通过 sqlmigrate 查看迁移会执行哪些 SQL 语句
python3 manage.py sqlmigrate app_bookmark_develop 0001
# 应用迁移
python3 manage.py migrate
# 删除应用下的所有数据
python3 manage.py migrate your_app_name zero
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
Django 使用管理员后台
Django 全自动地根据模型创建后台界面,站点管理人员在后台管理系统上添加的内容将被显示在公众页面上,通过为站点管理人员创建统一的内容编辑界面,让公众页面和内容发布者页面完全分离。
创建管理员登录后台
Bash
# 创建管理员账号
python3 manage.py createsuperuser
# 重置密码
python3 manage.py changepassword username
# 启动开发服务器
python3 manage.py runserver
1
2
3
4
5
6
2
3
4
5
6
打开浏览器,访问后台地址:http://127.0.0.1:8000/admin/
,尝试登录
向管理员页面加入书签应用
编辑 app_bookmark_develop/admin.py
点我查看代码
py
from django.utils.html import format_html
from django.utils.timezone import localtime
from django.contrib import admin
from .models import Category
from .models import Tag
from .models import Bookmark
class CategoryAdmin(admin.ModelAdmin):
# 控制哪些字段显示在管理的变更列表页面
list_display = ('id', 'title', 'created', 'updated')
# 控制 list_display 中的哪些字段可链接到对象的 “更改” 页面
list_display_links = ['title']
# 排序
ordering = ['id']
class TagAdmin(admin.ModelAdmin):
list_display = ('id', 'text', 'category', 'created', 'updated')
list_display_links = ['text']
# 在管理更改列表页面上启用搜索框,可设置某种文本字段进行相关查询
search_fields = ['text']
# 过滤器类型取决你你要过滤的字段的类型
list_filter = ['category']
ordering = ['category', 'id']
class BookmarkAdmin(admin.ModelAdmin):
list_display = ('id', 'text_icon', 'detail')
list_display_links = ['text_icon']
search_fields = ['text']
list_filter = ['category', 'tags']
ordering = ['updated']
# 装饰器 display 这个装饰器可以用来设置自定义显示函数的特定属性
# 图标显示为图片,网址可点击开新窗口访问
@admin.display(description='图标')
def text_icon(self, obj):
return format_html(
'标题:{}<br /><br /><img src="{}" />',
obj.text, obj.icon
)
@admin.display(description='图标 - 网址 - 标题 - 正文 - 归属收藏者')
def detail(self, obj):
return format_html(
'<a href="{}" target="_blank">{}</a><br />\
标题:{}<br />正文:{}<br />\
创建时间:{}<br />更新时间:{}<br />归属收藏者:{}',
obj.href, obj.href[:40], obj.title, obj.body[:200],
localtime(obj.created).strftime("%Y年%m月%d日 %H:%M"),
localtime(obj.updated).strftime("%Y年%m月%d日 %H:%M"),
obj.collector
)
admin.site.register(Category, CategoryAdmin)
admin.site.register(Tag, TagAdmin)
admin.site.register(Bookmark, BookmarkAdmin)
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
再次访问:http://127.0.0.1:8000/admin/
,登录后,可看到已安装的应用栏目,尝试发布信息
Django 批量生成演示数据
Django 具有 “视图” 的概念,负责处理用户的请求并返回响应。
你的视图可以从数据库里读取记录,可以使用一个模板引擎(比如 Django 自带的,或者其他第三方的),可以生成一个 PDF 文件,可以输出一个 XML,创建一个 ZIP 文件,你可以做任何你想做的事,使用任何你想用的 Python 库。
视图函数的执行结果只可能有两种:返回一个包含请求页面元素的 HttpResponse 对象,或者是抛出 Http404 这类异常。至于执行过程中的其它的动作则由你决定。
创建一个视图提交随机数据
定义一个可随机生成演示数据并批量导入数据库的函数
编辑 app_bookmark_develop/views_random.py
点我查看代码
py
import random
from itertools import islice
from django.shortcuts import render
from django.http import HttpResponse
from .models import User
from .models import Bookmark
from .models import Category
from .models import Tag
# 删除所有数据
# Bookmark.objects.all().delete()
# Category.objects.all().delete()
# Tag.objects.all().delete()
def Bookmark_bulk_create():
"""随机生成演示数据批量导入数据库"""
user_id = User.objects.get(username='admin')
category_total = 10 # 新建分类总数
category_tag_total = 5 # 每个分类下新建标签总数
objs_total = 50 # 每个分类下新建对象总数
batch_size = 10 # 新建对象的批次数目
output = '创建 %s 个分类<br />' % category_total
for i in range(1, category_total + 1):
output += '<br />===================================================<br />'
# 是否已有分类,没有则新建
# 返回 (object, created) 元组,其中 object 是检索或创建的对象,created 是指定是否创建新对象的布尔值。
category_obj, created = Category.objects.get_or_create(
title='分类%s' % i)
print('====== %s 创建%s' % (category_obj, created))
output += '%s' % (category_obj)
output += ' | 当前分类下创建 %s 个标签( | ' % (category_tag_total)
for j in range(1, category_tag_total + 1):
tag_obj, created = Tag.objects.get_or_create(
text='标签%s-%s' % (i, j),
category=category_obj
)
print('---------- %s 创建%s' % (tag_obj, created))
output += '%s | ' % (tag_obj)
output += ')<br />--------------------------------------------------------------------<br />'
output += '当前分类下创建 %s 个对象数据,以每 %s 个对象为一个批次处理' % (
objs_total, batch_size)
output += '<br />--------------------------------------------------------------------<br />'
category_id = Category.objects.get(title=category_obj)
objs = (
Bookmark(
category=category_id,
collector=user_id,
text='百度 %s' % i,
href='http://www.baidu.com',
icon='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAADCklEQVQ4jXWTTWhUdxTFz/3/33w2CdNMQkMaqfmAGqziIiRtESutkqKgmEUJootSKF0I4kK3ChoUa6ltLWiRbrqsUlsrWlMVP6K2BCtBtDZ+JdFM6ryYmHHezHsv754uEo2bXjhwuPdyFgd+INlNMkcympFyRqTqnOfsZu6POZLdIOnOniKSOjEZ6ZZted25x6WqMjcW8tNNYzxwcEJnMl+EkKSLWRP5gep0RN23/4lW1d1hdt4d9l3x+MXX40xnB1nffI/nL3o6Pa0ahnMhBoCSkHhMxBrI4N1AMhnLRELwKBfCdSNkaywI4fBIKNaKOA6EhABQo6pGhPLTLwUc/fkZutZWUEQlU2XR0ZZCR3sKnqdoabayZHGCh76fRP81HyIUVTUgqRf7PK1ruqfVDXf15OkCb94q88FQ8KK6a9dLfJwPuXOPSyczqG8vH9G8GypJNQDk+kBZrIFUVYqcu1BC64IEnhUVm7fmsb3HRUtTDLU1FuculNDc6EhuLJTb/wQCQBySaG9LIpEAxp8o3luawtBwgM41oyiVCWuBP/t9HP+xHu1tSXz5zQSWvpNC64I4lAqJoojGCK78UYYfEIsWxrG6axRNjTEYI+i7WkLgEx91VaBnRxanz5SwZHEc89+IQRUQVaUqYa3B8EiI9R+PYWg4xA+HX8NfAz6+/W4Kla8YeCXFyg/S2L+3Fo4VRJHCGIEhhdYa/n074IrVo1z1YRqXfm/AorcSKEwRh76qRUWFYN/uGjwYCrFq3SgLBaUxhoDQqKoAlAMHJyWdhmz6LMNlnQ9x7HgRmVcNkklB3lWEIfB6XYyXLnvSe7YoIpQoUjHGGAUArww+nSLv3w9l765arnw/Bb9MKIHPe7J4tyNJpYq1loUCCQggojIdkdZAfz1ZlI2f/IuGBgdda9JiLXCq10MsZtC5IgXPI44cK9IY4MTRejTOj5GEEZIugCyg6D1b5onfiggDoKXZkdY34/B9YuCGj4ePIlZXW2zorsTCVkcAAwDjeI7zDI0vo/t/o8/JzZHs/g8akjdG32H2lQAAAABJRU5ErkJggg==',
title='国内最大搜索引擎 %s' % i,
body='百度一下,你就知道 %s (备注:测试,%s' % (i, category_obj)
) for i in range(objs_total)
)
while True:
batch = list(islice(objs, batch_size))
if not batch:
break
output += '<br />分批创建:%s' % batch_size
random_tag_1 = random.choice(Tag.objects.all())
random_tag_2 = random.choice(Tag.objects.all())
random_tag_3 = random.choice(Tag.objects.all())
output += ',随机插入3个标签:| %s | %s | %s<br />' % (random_tag_1,
random_tag_2,
random_tag_3)
for b in batch:
b.body += ',%s %s %s)' % (random_tag_1,
random_tag_2,
random_tag_3)
print(b.body)
output += '%s | %s | %s | <img src="%s"/> %s | %s | %s<br />' % (
b.category,
b.collector,
b.text,
b.icon,
b.href,
b.title,
b.body
)
if Bookmark.objects.bulk_create(batch, batch_size):
for b in batch:
b.save()
b.tags.add(random_tag_1, random_tag_2, random_tag_3)
return output
def index(request):
output = Bookmark_bulk_create()
html = '<html><body><h1>app_bookmark_develop.views.index</h1><div style="font-size: xx-small;">%s</div></body></html>' % output
return HttpResponse(html)
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
创建一个 URL 路径调用视图函数
新建书签应用自己的 URLconf app_bookmark_develop/urls.py
py
from django.urls import path
from . import views_random
urlpatterns = [
path('random', views_random.index),
]
1
2
3
4
5
6
2
3
4
5
6
编辑 mysite/urls.py
在根 URLconf 文件中引用书签应用的 URLconfs
点我查看代码
py
from django.contrib import admin
from django.urls import path
from django.urls import include
urlpatterns = [
# 每当 Django 遇到 include() 时,它会截断与此项匹配的 URL 的部分,
# 并将剩余的字符串发送到 URLconf 以供进一步处理。
path('app_bookmark_develop/', include('app_bookmark_develop.urls')),
# 当包括其它 URL 模式时你应该总是使用 include(),admin.site.urls 是唯一例外。
path('admin/', admin.site.urls)
]
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
设计 include()
的理念是使其可以即插即用。因为投票应用有它自己的 URLconf(app_bookmark_develop/urls.py)
,他们能够被放在 "/bookmark_develop/"
,"/bookmark/develop/"
,或者其他任何路径下,这个应用都能够正常工作。
启动 Django 服务器并访问 http://127.0.0.1:8000/app_bookmark_develop/random 返回结果 |
---|
再次访问:http://127.0.0.1:8000/admin/
,登录后,可看到书签应用已有演示数据
Django 图片上传与下载
在模型中使用 FileField
或 ImageField
需要完成如下步骤:
所有这些将被存储在你的数据库中的是一个文件的路径(相对于 MEDIA_ROOT
)。你很可能要使用 Django 提供的方便的 url
属性 attr:~django.db.models.fields.files.FieldFile.url
。例如,如果 class:~django.db.models.ImageField
叫做 mug_shot
,你可以在模板中使用 获取图片的绝对路径。
如果你想检索上传文件的盘上文件名,或者文件的大小,可以分别使用 name
和 size
属性;关于可用属性和方法的更多信息,请参见 File
类参考和 管理文件
主题指南。
文件在数据库中作为保存模型的一部分,因此在模型被保存之前,不能依赖磁盘上使用的实际文件名。
上传的文件的相对 URL 可以通过 url
属性获得。内部调用底层 Storage
类的 store()
方法。
请注意,无论何时你处理上传的文件,你都应该密切注意你在哪里上传文件以及它们是什么类型的文件,以避免安全漏洞。 验证所有上传的文件 ,这样你就能确定文件是你认为的那样。例如,如果你盲目地让别人上传文件,而不进行验证,到你的网站服务器的文件根目录中,那么有人就可以上传 CGI 或 PHP 脚本,并通过访问你网站上的 URL 来执行该脚本。不要允许这样做。
另外要注意的是,即使是上传的 HTML 文件,由于可以被浏览器执行(虽然不能被服务器执行),也会造成相当于 XSS 或 CSRF 攻击的安全威胁。
安装 Pillow 图像库
在模型中使用 ImageField 需要安装的 Pillow 支持你使用的图片格式。如果你在上传图片时遇到 corrupt image 错误,通常意味着 Pillow 不理解图片格式。要解决这个问题,请安装相应的库并重新安装 Pillow。
Pillow —— Python 图像库,提供了广泛的文件格式支持和强大的图像处理功能。
https://pillow.readthedocs.io/en/latest/installation.html
Bash
# 先升级 pip 避免安装失败
python3 -m pip install --upgrade pip
# 安装 Pillow
python3 -m pip install --upgrade Pillow
1
2
3
4
2
3
4
定义上传路径及 URL
修改 mysite/settings.py
py
# ...
# 存储上传文件的目录的完整路径。(为了提高性能,这些文件不会存储在数据库中。)
MEDIA_ROOT = BASE_DIR / 'media/'
# 该目录的基本公共 URL。确保这个目录是可以被网络服务器的用户账户写入的。
MEDIA_URL = 'media/'
# ...
1
2
3
4
5
6
2
3
4
5
6
ImageField
加入模型
修改 app_bookmark_develop/models.py
点我查看代码
py
class Bookmark(models.Model):
"""书签模型"""
# ...
# 使用 ImageField 需要安装的 Pillow 支持你使用的图片格式
image_title = models.ImageField(
# 选项 upload_to 指定 MEDIA_ROOT 的子目录,用于上传文件。
# upload_to 中的 %Y/%m/%d 部分是 strftime() 格式化
# 例:上传后的文件可能将保存在
# mysite/media/app_bookmark_develop/image/20230118 目录下
upload_to='app_bookmark_develop/image/%Y%m%d',
blank=True,
verbose_name='标题配图'
)
# ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
改变模型后,可以执行迁移操作同步变更数据库
Bash
# 生成迁移
python3 manage.py makemigrations app_bookmark_develop
# 应用迁移
python3 manage.py migrate
1
2
3
4
2
3
4
开发期间保存用户上传的文件
开发期间,你能用 django.views.static.serve()
视图为用户上传的媒体文件提供服务。
这不适合生产环境!常见的部署策略请参考 Django 官方文档 如何部署静态文件。
修改 mysite/urls.py
点我查看代码
py
from django.contrib import admin
from django.urls import path
from django.urls import include
from django.conf import settings //[!code ++]
from django.conf.urls.static import static //[!code ++]
urlpatterns = [
# 每当 Django 遇到 include() 时,它会截断与此项匹配的 URL 的部分,
# 并将剩余的字符串发送到 URLconf 以供进一步处理。
path('app_bookmark_develop/', include('app_bookmark_develop.urls')),
# 当包括其它 URL 模式时你应该总是使用 include(),admin.site.urls 是唯一例外。
path('admin/', admin.site.urls)
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) //[!code ++]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
打开浏览器访问 http://127.0.0.1:8000/admin 查看图片上传及访问效果 |
---|
DRF 构建 Web API
Django REST 框架是用于构建 Web API 的强大而灵活的工具包。
DRF 安装
安装 DRF 及 可选包
sh
pip install djangorestframework
# 为 browsable API 提供 Markdown 支持
pip install markdown
# Filtering 支持
pip install django-filter
1
2
3
4
5
2
3
4
5
编辑 mysite/settings.py
启用 DRF 应用
点我查看代码
py
INSTALLED_APPS = [
'django.contrib.admin', # 管理员站点
'django.contrib.auth', # 认证授权系统
'django.contrib.contenttypes', # 内容类型框架
'django.contrib.sessions', # 会话框架
'django.contrib.messages', # 消息框架
'django.contrib.staticfiles', # 管理静态文件的框架
# INSTALLED_APPS 默认包括了以上 Django 的自带应用,
# 这些应用被默认启用是为了给常规项目提供方便。
'app_bookmark_develop', # 启用 书签应用
'rest_framework', # 启用 DRF //[!code ++]
'django_filters', # 启用 过滤器 //[!code ++]
]
REST_FRAMEWORK = { //[!code ++]
# 使用 Django 的标准 `Django.contrib.auth` 权限, //[!code ++]
# 或允许未经身份验证的用户进行只读访问。 //[!code ++]
'DEFAULT_PERMISSION_CLASSES': [ //[!code ++]
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly' //[!code ++]
], //[!code ++]
# 启用分页 //[!code ++]
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', //[!code ++]
'PAGE_SIZE': 10 //[!code ++]
} //[!code ++]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
DRF 序列化器
开发 Web API 的第一件事是为 Web API 提供一种将代码片段实例序列化和反序列化为诸如 json
之类的表示形式的方式。DRF 则通过声明与 Django forms
非常相似的序列化器(serializers
)来实现。
序列化器允许把像查询集和模型实例这样的复杂数据转换为可以轻松渲染成 JSON,XML 或其他内容类型的原生 Python 类型。序列化器还提供反序列化,在验证传入的数据之后允许解析数据转换回复杂类型。
Serializer
类,它为你提供了强大的通用方法来控制响应的输出。ModelSerializer
类,它为创建用于处理模型实例和查询集的序列化程序提供了有用的快捷实现方式。 HyperlinkedModelSerializer
类,它可以使用超链接来表示关联关系而不是主键。
新建 app_bookmark_develop/serializers.py
点我查看代码
py
from rest_framework import serializers
from rest_framework.validators import UniqueValidator
from .models import Category, Tag, Bookmark
# 分类 视图集 可选的序列化器,
# 用在查看和更新一个分类详情时,才显示关联的标签和书签记录
class CategoryDetailSerializer(serializers.HyperlinkedModelSerializer):
"""分类详情 序列化器"""
# 序列化字段 负责在原始数据和内部数据类型之间转换。
# 它们同样负责校验输入的值,以及从父级对象查询和设置值。
# 序列化字段 DateTimeField 表示日期和时间
# 参数 format 代表输出格式的字符串
# %H:%M:%S 表示小时、分钟和秒,格式为 时:分:秒
# %X 表示本地时间的时间部分,格式可以是任何与本地时间相关的格式(例如 12:30:45 PM)。
# 在大多数情况下,它等同于 %H:%M:%S。
# 参数 required=False 表示在创建/更新时可以不设置此字段
# 参数 label 可用作 HTML 表单字段或其他描述性元素中的字段名称。
created = serializers.DateTimeField(
format='%Y-%m-%d %H:%M:%S', required=False, label='创建时间')
updated = serializers.DateTimeField(
format='%Y-%m-%d %X', required=False, label='更新时间')
class Meta:
model = Category
fields = ['url', 'id', 'title',
'归属分类的标签', '归属分类的书签', 'created', 'updated']
# 附加关键字参数 extra_kwargs
# - 因为使用 app_name(应用程序命名空间)、HyperlinkedModelSerializer、DefaultRouter
# - 所以需要一种确定哪些视图能应用超链接到模型实例的方法。
# - 因此可设置 view_name 和 lookup_field 中的一个或两个来重写URL字段视图名称和查询字段。
extra_kwargs = {
'url': {'view_name': 'app_bookmark_develop:category-detail'},
'归属分类的标签': {'view_name': 'app_bookmark_develop:tag-detail'},
'归属分类的书签': {'view_name': 'app_bookmark_develop:bookmark-detail'}
}
# 分类 视图集 默认使用的序列化器,
# 但可以通过覆写 get_serializer_class() 方法来根据条件访问不同的序列化器
# 当前 分类 序列化器 则可用在查看分类列表时,只显示分类 id 和 分类名称
class CategorySerializer(serializers.HyperlinkedModelSerializer):
"""分类 序列化器"""
class Meta:
model = Category
fields = ['url', 'id', 'title']
extra_kwargs = {
'url': {'view_name': 'app_bookmark_develop:category-detail'}
}
# 用在查看和更新一个标签详情时,才显示归属的分类和相关书签记录
class TagDetailSerializer(serializers.HyperlinkedModelSerializer):
"""标签详情 序列化器"""
created = serializers.DateTimeField(
format='%Y-%m-%d %H:%M:%S', required=False, label='创建时间')
updated = serializers.DateTimeField(
format='%Y-%m-%d %H:%M:%S', required=False, label='更新时间')
# 在序列化类中,error_messages 是一个字典,用于指定在反序列化期间可能引发的不同错误的自定义错误消息。
# 重复的标签名称没有意义,我们可以在序列化类中使用 Django 框架的验证器
# UniqueValidator 验证器接受一个查询集作为必要参数,用于检查该字段是否在数据库中唯一。
# 如果该字段已经存在于数据库中,则会引发验证错误。
# 参数 message 当验证器失败时应该使用的错误信息。
text = serializers.CharField(
max_length=20,
error_messages={'max_length': '标签名称不能超过 20 个字符'},
validators=[
UniqueValidator(
queryset=Tag.objects.all(),
message='标签名称不能重复存在'
)
],
label='标签名称'
)
class Meta:
model = Tag
fields = ['url', 'id', 'category',
'text', '相关书签', 'created', 'updated']
extra_kwargs = {
'url': {'view_name': 'app_bookmark_develop:tag-detail'},
'category': {'view_name': 'app_bookmark_develop:category-detail'},
'相关书签': {'view_name': 'app_bookmark_develop:bookmark-detail'}
}
# 用在查看标签列表时,只显示标签 id 和 标签名称
class TagSerializer(serializers.HyperlinkedModelSerializer):
"""标签 序列化器"""
class Meta:
model = Tag
fields = ['url', 'id', 'text']
extra_kwargs = {
'url': {'view_name': 'app_bookmark_develop:tag-detail'}
}
class BookmarkSerializer(serializers.HyperlinkedModelSerializer):
"""书签 序列化器"""
created = serializers.DateTimeField(
format='%Y-%m-%d %H:%M:%S', required=False, label='创建时间')
updated = serializers.DateTimeField(
format='%Y-%m-%d %H:%M:%S', required=False, label='更新时间')
# 参数 use_url 如果设置为True则URL字符串值将用于输出表示。如果设置为False则文件名字符串值将用于输出表示。
# image_title = serializers.ImageField(use_url=False, label='标题配图')
# 归属的分类名称 嵌套序列化字段
# ReadOnlyField 仅返回该字段的值而无需修改
category_title = serializers.ReadOnlyField(source='category.title')
# SlugRelatedField 字段,用于在序列化器中将外键关联对象的某个字段的值作为字符串表示返回。
# 当使用 SlugRelatedField 作为读写字段时,通常需要确保 slug 字段对应于 unique=True 的模型字段。
tags = serializers.SlugRelatedField(
queryset=Tag.objects.all(),
many=True,
required=False,
slug_field='text'
)
# collector_username 收藏者的用户名 嵌套序列化字段
collector_username = serializers.ReadOnlyField(source='collector.username')
class Meta:
model = Bookmark
# 可以将 fields 属性设置成 '__all__' 来表明使用模型中的所有字段
# 强烈建议使用 fields 属性显式的设置要序列化的字段。这样就不太可能因为修改了模型而无意中暴露了数据。
# fields = '__all__'
fields = ['url', 'id', 'image_title', 'category', 'category_title', 'tags',
'collector', 'collector_username', 'text', 'href', 'icon',
'title', 'body', 'created', 'updated']
extra_kwargs = {
'url': {'view_name': 'app_bookmark_develop:bookmark-detail'},
'category': {'view_name': 'app_bookmark_develop:category-detail'},
'tags': {'view_name': 'app_bookmark_develop:tag-detail'}
}
# depth 选项可以轻松生成嵌套关联,其应设置一个整数值,表明应该遍历的关联深度。
# depth = 1
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
DRF 视图集
REST 框架包括一个用于处理 ViewSets
的抽象,它允许开发人员集中精力对 API 的状态和交互进行建模,并根据常规约定自动处理 URL 构造。
ModelViewSet
类继承自 GenericAPIView
,并通过混合各种 mixin
类的行为来实现各种操作。
编辑 app_bookmark_develop/views.py
点我查看代码
py
from rest_framework import viewsets
from django_filters.rest_framework import DjangoFilterBackend
from .models import Category, Tag, Bookmark
from .serializers import CategorySerializer, CategoryDetailSerializer
from .serializers import TagSerializer, TagDetailSerializer
from .serializers import BookmarkSerializer
from .pagination import StandardResultsSetPagination # 引入分页类
class CategoryViewSet(viewsets.ModelViewSet):
"""分类 视图集"""
queryset = Category.objects.all()
serializer_class = CategorySerializer
def get_serializer_class(self):
if self.action == 'list':
return CategorySerializer
else:
return CategoryDetailSerializer
class TagViewSet(viewsets.ModelViewSet):
"""标签 视图集"""
queryset = Tag.objects.order_by('category', 'id')
serializer_class = TagSerializer
# 将分页样式应用于视图集
pagination_class = StandardResultsSetPagination
# django-filter 库包含一个为 DRF 提供高度可定制字段过滤的 DjangoFilterBackend 类
# 使用 Django 过滤后端
filter_backends = [DjangoFilterBackend]
# 指定筛选字段
filterset_fields = ['category_id']
def get_serializer_class(self):
if self.action == 'list':
return TagSerializer
else:
return TagDetailSerializer
class BookmarkViewSet(viewsets.ModelViewSet):
"""书签 视图集"""
queryset = Bookmark.objects.order_by('updated')
serializer_class = BookmarkSerializer
pagination_class = StandardResultsSetPagination
filter_backends = [DjangoFilterBackend]
filterset_fields = ['category_id', 'tags']
# 序列一个非常棒的属性就是可以通过打印序列化器类实例的结构(representation)查看它的所有字段。
# serializer = TagDetailSerializer()
# print(repr(serializer))
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
44
45
46
47
48
49
50
51
52
53
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
44
45
46
47
48
49
50
51
52
53
除了在 mysite/settings.py
中设置全局分页样式,还可调整分页样式应用在单个视图上。
引入的分页类:新建 app_bookmark_develop/pagination.py
点我查看代码
py
from rest_framework.pagination import PageNumberPagination
# 调整分页样式
class LargeResultsSetPagination(PageNumberPagination):
page_size = 100
page_size_query_param = 'page_size'
max_page_size = 1000
class StandardResultsSetPagination(PageNumberPagination):
page_size = 30
page_size_query_param = 'page_size'
max_page_size = 600
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
DRF 路由器
REST 框架增加了对自动 URL 路由到 Django 的支持,并为您提供了一种简单、快速、一致的方式,将视图逻辑连接到一组 URL。
因为使用 viewsets
类,所以可通过路由器来自动确定 API URL。SimpleRouter
路由器包括标准集合 list,create,retrieve,update,partial_update,destroy
动作的路由。DefaultRouter
这个路由器类似于上面的 SimpleRouter
,但是还包括一个默认返回所有列表视图的超链接的 API 根视图。它还生成可选的 .json
样式格式后缀的路由。
编辑 app_bookmark_develop/urls.py
点我查看代码
py
from django.urls import path
from django.urls import include
from rest_framework import routers # 引入 DRF 路由器
from . import views_random
from . import views
# 创建路由器并注册视图
router = routers.DefaultRouter()
router.register(r'category', views.CategoryViewSet)
router.register(r'tag', views.TagViewSet)
# URL 应用程序命名空间
app_name = 'app_bookmark_develop'
urlpatterns = [
# 一个默认返回所有列表视图的超链接的 API 根视图
path('api/', include(router.urls)),
# 批量生成演示数据
path('random', views_random.index),
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
DRF API 测试
打开浏览器访问 http://127.0.0.1:8000/app_bookmark_develop/api/ |
---|