Django4.2 学习 - 模型 Model(四)
Django 的模型(Model)是框架的核心组件之一,用于定义数据结构并与数据库进行交互。
1. 模型的作用
- 定义数据库表的结构(字段、关联关系等)。
- 通过 Django ORM(对象关系映射)操作数据库,无需直接编写 SQL。
- 支持数据验证、业务逻辑封装(如保存前后的操作)。
2. 创建模型
在 Django 应用的 models.py
中定义模型类:
1 | from django.db import models |
3. 常用字段类型
3.1 字符串类字段
CharField
- 作用:存储段文本(如标题、名称)
- 必选参数:
max_length
- 🌰:
1
title = models.CharField(max_length=200)
TextField
- 作用:存储长文本(如文章内容)
- 与
CharField
的区别:无需max_length
,适合大段文本。 - 🌰:
1
content = models.TextField()
EmailField
- 作用:存储邮箱地址,自动验证格式。
- 底层实现:继承自
CharField
,但添加了邮箱格式验证。 - 🌰:
1
email = models.EmailField()
URLField
- 作用:存储 URL,自动验证格式。
- 🌰:
1
website = models.URLField()
3.2 数值类字段
IntegerField
- 作用:存储整数。
- 🌰:
1
age = models.IntegerField()
FloatField
- 作用:存储浮点数。
- 🌰:
1
rating = models.FloatField()
DecimalField
- 作用:存储精确小数(适用于金融数据)。
- 必选参数:
max_digits
:总位数(包括小数点后的位数)。decimal_places
:小数点后的位数。
- 🌰:
1
price = models.DecimalField(max_digits=5, decimal_places=2)
3.3 日期和时间类字段
DateField
- 作用:存储日期(年月日)。
- 常用参数:
auto_now
:每次保存时自动更新为当前日期。auto_now_add
:仅在创建时设置为当前日期。
- 🌰:
1
publish_date = models.DateField(auto_now_add=True)
DateTimeField
- 作用:存储日期和时间。
- ** 参数同
DateField
**。 - 🌰:
1
created_at = models.DateTimeField(auto_now_add=True)
3.4 布尔类字段
BooleanField
作用:存储布尔值(
True/False
)。注意:默认值为
False
,可通过default=True
修改。🌰:
1
is_published = models.BooleanField(default=True)
3.5 文件类字段
FileField
- 作用:存储文件路径(如上传的文件)。
- 常用参数:
upload_to
:文件上传目录(如"documents/"
)。
- 🌰:
1
document = models.FileField(upload_to="documents/")
ImageField
- 作用:存储图片路径,继承自
FileField
,额外验证图片格式。 - 依赖:需安装
Pillow
库。 - 🌰:
1
photo = models.ImageField(upload_to="photos/")
- 作用:存储图片路径,继承自
3.6 关联类字段
4. 常用字段参数
以下参数适用于大多数字段类型:
4.1 通用参数
null
- 作用:是否允许数据库存储
NULL
值。 - 🌰:
null=True
(默认为False
)。
- 作用:是否允许数据库存储
blank
- 作用:是否允许表单验证为空(与
null
无关)。 - 🌰:
blank=True
(默认为False
)。
- 作用:是否允许表单验证为空(与
default
- 作用:字段的默认值。
- 🌰:
default="draft"
。
unique
- 作用:是否要求字段值唯一。
- 🌰:
unique=True
。
choices
- 作用:限制字段值为预定义选项。
- 🌰:
1
2
3
4
5STATUS_CHOICES = [
('D', 'Draft'), # 数据库存储的是
('P', 'Published'),
]
status = models.CharField(max_length=1, choices=STATUS_CHOICES)verbose_name
- 作用:字段的人类可读名称(用于 Admin 后台)。
- 示例:
verbose_name="出版日期"
。
help_text
- 作用:字段的提示文本(用于 Admin 后台)。
- 示例:
help_text="请输入书籍标题"
。
db_index
- 作用:为字段创建数据库索引,加速查询。
- 示例:
db_index=True
。
4.2 关联字段专用参数
- **
on_delete
**(用于ForeignKey
和OneToOneField
)- 作用:定义关联对象删除时的行为。
- 常用选项:
models.CASCADE
:级联删除。models.PROTECT
:阻止删除关联对象。models.SET_NULL
:将字段设为NULL
(需null=True
)。models.SET_DEFAULT
:设为默认值(需default
参数)。
5. 数据库迁移
5.1 生成迁移文件
1 | python manage.py makemigrations |
5.2 执行迁移
1 | python manage.py migrate |
5.3 查看 SQL 语句
1 | python manage.py sqlmigrate <app_name> <migration_number> |
6. 操作模型
以下是 Django 模型增删改查(CRUD)操作:
6.1 增
方法 1:
方法 1 和方法 2 都是先实例化对象,然后再保存。
1 | # views.py |
方法 2:
1 | # views.py |
方法 3:
使用 create()
直接创建并保存到数据库。
1 | # views.py |
方法 4:
使用 get_or_create()
创建,与 create()
不同,get_or_create()
有返回值。
1 | # views.py |
方法 5:
- 使用
bulk_create()
批量插入多条数据(减少数据库查询次数)
1 | # views.py |
- 循环添加多条
1 | # views.py |
6.2 删
- 删除单条
1 | # views.py |
- 批量删除
1 | # views.py |
- 删除所有
1 | # views.py |
6.3 改
- 更新单条
1 | # views.py |
- 提高效率减少数据操作
1 | # views.py |
- 批量更新
1 | # views.py |
6.4 查
获取单条数据
get()
:精确匹配(找不到或找到多个会抛异常)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15def get_user(request):
try:
m6 = testModels6.objects.get(name='测试增加方法四-05')
print(m6.name)
print(m6.age)
print(m6.sex)
except testModels6.DoesNotExist as e:
# 处理不存在的情况
print(e)
except testModels6.MultipleObjectsReturned as e:
# 处理多个对象返回的情况
print(e)
return HttpResponse('获取成功')- **
first()
和last()
**:获取第一条或最后一条
1
2
3
4
5
6
7
8
9
10
11
12
13def get_user(request):
try:
m_first = testModels6.objects.first()
m_last = testModels6.objects.last()
print(m_first)
print(m_last)
except Exception as e:
print(e)
return HttpResponse('获取失败')
return HttpResponse('获取成功')过滤数据
- **
filter()
**:条件查询
1
users = testModels6.objects.filter(age=18)
- **
exclude()
**:排除条件
1
users2 = testModels6.objects.exclude(age=18)
- **
统计
- **
count()
**:统计查询集中对象个数
1
num = testModels6.objects.count()
- **
获取全部数据
- **
all()
**: 获取全部数据
1
all_data = testModels6.objects.all()
- **
获取指定列的值
- **
values()
**:获取指定列的值,可以传多个参数,返回字典列表(保存了字段名和对应的值)
1
2
3
4
5users = testModels6.objects.all().values('name', 'age')
print(users)
"""
<QuerySet [{'name': '测试增加方法四-01', 'age': 18}, {'name': '测试增加方法四-02', 'age': 18}, {'name': '测试增加方法四-03', 'age': 18}, {'name': '测试增加方法四-04', 'age': 18}, {'name': '测试增加方法四-05', 'age': 18}, {'name': '测试增加方法四-07', 'age': 18}, {'name': '测试增加方法四-08', 'age': 18}, {'name': '测试增加方法四-09', 'age': 18}, {'name': '测试增加方法四-10', 'age': 18}, {'name': '测试增加方法四-11', 'age': 18}, {'name': '测试增加方法四'age': 18}, {'name': '测试增加方法四-14', 'age': 18}, {'name': '测试增加方法四-15', 'age': 18}, {'name': '测试增加方法四-16', 'age': 18}, {'name': '测试增加方法四-17', 'age': 18}, {'name': '测试增加方法四-18', 'age': 18}, {'name': '测试增加方法四-20', 'age': 18}, '...(remaining elements truncated)...']>
"""- **
values_list()
**:获取指定列的值,可以传多个参数,返回元组列表(只保存了值)
1
2
3
4
5users2 = testModels6.objects.all().values_list('name','age')
print(users2)
'''
<QuerySet [('测试增加方法四-01', 18), ('测试增加方法四-02', 18), ('测试增加方法四-03', 18), ('测试增加方法四-04', 18), ('测试增加方法四-05', 18), ('测试增加方法四-06', 18), ('测试增加方法四-07', 18), ('测试增加方法四-08', 18), 8), ('测试增加方法四-12', 18), ('测试增加方法四-13', 18), ('测试增加方法四-14', 18), ('测试增加方法四-15', 18), ('测试增加方法四-16', 18), ('测试增加方法四-17', 18), ('测试增加方法四-18', 18), ('测试增加方法四-19', 18), ('测试增加方法四-20', 18), '...(remaining elements truncated)...']>
'''- **
判断是否存在
exists()
1
2user = testModels6.objects.filter(name='测试增加方法四-0511').exists()
print(user)查找参数相关
__lt
:Less Than- 表示 “小于”。
- 示例:
age__lt=18
表示筛选出 年龄小于 18 的记录。
1
user = testModels6.objects.filter(age__lt=18)
__gt
:Greater Than- 表示 “大于”。
- 示例:
gt__lt=18
表示筛选出年龄大于 18 的记录。
1
user = testModels6.objects.filter(gt__lt=18)
__lte
:Less Than or Equal to- 表示 “小于等于”。
- 示例:age__lte=18` 表示筛选出年龄小于等于 18 的记录。
1
users = testModels6.objects.filter(age__lte=18)
__gte
:Greater Than or Equal to- 表示 “大于等于”。
- 示例:
age__gte=18
表示筛选出年龄大于等于 18 的记录。
1
users = testModels6.objects.filter(age__gte=18)
**
__in
**:- 表示 “在某个列表中”。
- 示例:age__in=[1, 2, 3]
表示筛选出 ID 在列表
[1, 2, 3]` 中的记录。
1
2
3
4# 在列表
users = testModels6.objects.filter(age__in=[111, 222])
# 元组也行
users = testModels6.objects.filter(age__in=(111, 222))**
__isnull
**:- 表示 “是否为 null 值”。
- 示例:
age__isnull
表示筛选出年龄中是 null 的记录。
1
users = testModels6.objects.filter(age__isnull=True)
**
__range
**:- 表示:字段值在某个范围内的记录,有点像
between... and..
- 示例:
age__range
表示筛选出年龄是 18~40(包含 18,包含 40)的记录
1
users = testModels6.objects.filter(age__range=(18,40))
- 表示:字段值在某个范围内的记录,有点像
**
__contains
**:- 表示 “包含某个子字符串”,区分大小写。
- 示例:
name__contains="Alice"
表示筛选出名字中包含 “Ali” 的记录。
1
users = testModels6.objects.filter(name__contains='Ali')
**
__icontains
**:- 表示 “包含某个子字符串”,不区分大小写。
- 示例:
name__icontains="Alice"
表示筛选出名字中包含 “ali” 的记录。
1
users = testModels6.objects.filter(name__icontains='ali')
**
__regex
**:- 表示 “正则表达式匹配”。
- 示例:
name__regex="^A.*"
表示筛选出名字以大写字母 “A” 开头的记录。
1
users = testModels6.objects.filter(name__regex='^A.*')
**
__iregex
**:- 表示 “不区分大小写的正则表达式匹配”。
- 示例:
name__iregex="^a.*"
表示筛选出名字以字母 “a” 开头(不区分大小写)的记录。
1
2
3
4# 以下 2 种格式查询结果相同
users = testModels6.objects.filter(name__iregex='^A.*')
users = testModels6.objects.filter(name__iregex='^a.*')**
__startswith
**:- 表示 “以某个字符串开头”。
- 示例:
name__startswith="A"
表示筛选出名字以 “A” 开头的记录。
1
users = testModels6.objects.filter(name__startswith='A')
**
__istartswith
**:- 表示 “不区分大小写的以某个字符串开头”。
- 示例:
name__istartswith="a"
表示筛选出名字以 “a” 开头的记录(不区分大小写)。
1
2users = testModels6.objects.filter(name__istartswith='a')
users = testModels6.objects.filter(name__istartswith='A')**
__endswith
**:- 表示 “以某个字符串结尾”。
- 示例:
name__endswith="a"
表示筛选出名字以 “a” 结尾的记录。
1
users = testModels6.objects.filter(name__endswith='a')
**
__iendswith
**:- 表示 “不区分大小写的以某个字符串结尾”。
- 示例:
name__iendswith="smith"
表示筛选出名字以 “Smith” 结尾的记录(不区分大小写)。
1
2users = testModels6.objects.filter(name__iendswith='a') # sqlite中不生效,需要改配置
users = testModels6.objects.filter(name__iendswith='A')**
__exact
**:- 表示 “精确等于”。
- 示例:
name__exact="Alice"
表示筛选出名字精确等于 “Alice” 的记录。 - 注意:在 Django 中,
__exact
是默认的查找方式,如果不写查找参数,默认就是精确匹配。
1
users = testModels6.objects.filter(name__exact='Alice test name')
**
__iexact
**:- 表示 “不区分大小写的精确等于”。
- 示例:
name__iexact="alice"
表示筛选出名字精确等于 “Alice” 的记录(不区分大小写)。
1
users = testModels6.objects.filter(name__exact='alice test name')
**
__search
**:- 表示 “全文搜索”。
- 示例:
name__search="Alice"
表示筛选出名字中包含 “Alice” 的记录(通常用于全文搜索,具体行为可能依赖于数据库后端,PostgreSQL
数据库)。
1
2# sqlite 不支持, postgresql 支持,mysql还没试应该也不支持
users = testModels6.objects.filter(name__search='alice test name')
排序和去重
**
order_by()
**:排序1
2
3
4
5
6# 按照年龄正序排
users = testModels6.objects.all().order_by('age')
# 按照年龄倒序排
users = testModels6.objects.all().order_by('-age')
# 先按照年龄正序排,如果年龄相同,则按照 id 倒序排
users = testModels6.objects.all().order_by('age', '-id')**
distinct()
**:去重1
2# 按照年龄去重
users = testModels6.objects.values('age').distinct()
聚合与分组
- **
Max()
**:求最大值
1
max_age = testModels6.objects.aggregate(Max('age'))
- **
Min()
**:求最小值
1
min_age = testModels6.objects.aggregate(Min('age'))
- **
Sum()
**:求和
1
sum_age = testModels6.objects.aggregate(Sum('age'))
- **
Avg()
**:求最大值
1
avg_age = testModels6.objects.aggregate(Avg('age'))
- **
6.5 分页
手动分页
思路:
pageNum:页码
pageSize:每页条数
页码 每页显示 下标范围 切片范围 1 1~10 0~9 0:10 2 11~20 10~19 10:20 3 21~30 20~29 20:30 4 (4-1)*10+1~(4-1)*10
(4-1)*10 ~ 4*10-1
(4-1)*10 : 4*10
… … … … pageNum (pageNum-1)*pageSize+1~(pageNum-1)*pageSize
(pageNum-1)*pageSize ~ pageNum*pageSize-1
(pageNum-1)*pageSize : pageNum*pageSize
views.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16def manual_pagination(request, pageNum):
# 页码
pageNum = int(pageNum)
all_data = testModels7.objects.all()
# 总数据条数
total = all_data.count()
# 每页显示的数据条数
pageSize = 10
# 总页码数,ceil 向上取整
totalPage = math.ceil(total / pageSize)
# all_data = all_data[pageNum * pageSize:(pageNum + 1) * pageSize]
all_data = all_data[(pageNum - 1) * pageSize: pageNum * pageSize]
return render(request, 'manualPagination.html', {'all_data': all_data, 'totalPage': totalPage + 1})urls.py
1
path('manualPagination/<int:pageNum>', views.manual_pagination, name='manualPagination'),
manualPagination.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<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>手动分页</h2>
<hr>
<ul>
{% for item in all_data %}
<li>id:{{ item.id }}--名字:{{ item.name }}--年龄:{{ item.age }}--性别:{{ item.sex }} </li>
{% endfor %}
</ul>
<hr>
{# django 模板 #}
{#<a href="{% url 'manualPagination' pageNum=1 %}">第一页</a>#}
{# Jinja2 模板 #}
{#<a href="{{ url('manualPagination', kwargs={'pageNum': 1}) }}">第1页</a>#}
{% for page in range(1,totalPage) %}
<a href="{{ url('manualPagination', kwargs={'pageNum': page}) }}"> 第{{ page }}页</a>
{% endfor %}
</body>
</html>Django分页器
views.py
1 | def django_pagination(request, pageNum): |
urls.py
1 | path('djangoPagination/<int:pageNum>', views.django_pagination, name='djangoPagination'), |
djangoPagination.html
1 | <!DOCTYPE html> |