数据库操作
操作对象: orm/view.py # orm为创建的子应用

from django.db import models # 模型类必须要直接或者间接继承于 models.Model class BaseModel(models.Model): created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") # SQL:created_time datetime(6) null comment="创建时间" updated_time = models.DateTimeField(auto_now=True, verbose_name="更新时间") # SQL:updated_time datetime(6) null comment="更新时间" # auto_now_add 当数据添加时设置当前时间为默认值 # auto_now= 当数据添加/更新时, 设置当前时间为默认值 class Meta(object): abstract = True # 设置当前模型为抽象模型, 当系统运行时, 不会认为这是一个数据表对应的模型. # Create your models here. class Student(BaseModel): SEX_CHOICES = ( (0, "女生"), (1, "男生"), (2, "保密"), ) # 以类属性的方式声明表结构的字段 # 属性名 = models.数据类型(db_column="表字段名", max_length=最大长度, db_index=是否为当前字段创建普通索引, verbose_name="中文提示") # 注意:db_column属性如果不写则默认使用属性名作为表的字段名进行对应 # django中默认会自动给模型创主键名为ID,所以我们一般不需要手动创建 # 如果要手动创建主键ID则代码如下: # id = models.BigAutoField(primary_key=True, verbose_name="主键") # django会自动在创建数据表的时候生成id主键/还设置了一个调用别名 pk # SQL: id bigint primary_key auto_increment not null comment="主键", name = models.CharField(max_length=20, db_index=True, verbose_name="姓名") # SQL: name varchar(20) not null comment="姓名" # SQL: index(name), age = models.SmallIntegerField(verbose_name="年龄") # SQL: age smallint not null comment="年龄" sex = models.BooleanField(default=0, verbose_name="性别", help_text="辅助提示") # SQL: sex tinyint not null default 0 comment="性别" # sex = models.SmallIntegerField(choices=SEX_CHOICES, default=2, verbose_name="性别") # SQL = sex smallint not null default 2 comment="性别" classmate = models.CharField(db_column="class", max_length=5, db_index=True, verbose_name="班级") # SQL: class varchar(5) not null comment="班级" # SQL: index(class) description = models.TextField(blank=True, null=True, verbose_name="个性签名") # SQL: description longtext default "" null comment="个性签名" class Meta: # 子类中,可以针对当前模型类对应的数据设置一些独立的选项 db_table = "tb_student" # 设置当前Student模型对象对应的数据库的表名, 如果没有指定表名,则默认为子应用目录名_模型名称,例如: student_student verbose_name = '学生信息表' # 在admin站点中显示的名称 verbose_name_plural = verbose_name # 显示的复数名称 # 3. 自定义数据库操作方法 def __str__(self): """定义每个数据对象的显示信息""" return "<User %s>" % self.name
一 数据库基本操作
一 增加数据
方法 | 描述 | 代码 | ||
save | 通过创建模型类对象,执行对象的save()方法保存到数据库中。 |
|
||
create | 通过模型类.objects.create()保存。 |
|
||
bulk_create |
|
二 基本查询
方法 | 描述 | 代码 | ||
get |
查询单一结果,如果不存在或者返回多个结果会抛出异常。 查询不到, 则返回模型类.DoesNotExist异常。 查询多个, 则返回模型类.MultipleObjectsReturned异常。 |
|
||
first |
|
|||
all | 查询所有结果。查询不到,则返回空列表对象。默认为all。 |
|
三 更新数据
方法 | 描述 | 代码 | ||
save | 修改模型类对象的属性,然后执行save()方法同步到数据库中 |
|
||
update |
|
四 删除数据
方法 | 描述 | 代码 | ||
模型类对象.delete() | 删除一条 |
|
||
模型类.objects.filter().delete() | 删除多条 |
|
二 数据库进阶操作
一 过滤条件
ORM在内部生成SQL中的where子句时,提供3个方法可以帮我们实现where过滤操作,包括:
-
filter过滤出符合条件的多个结果
-
exclude 排除掉符合条件的多个结果,与filter相反,与filter互斥。
-
get 过滤单一结果, 结果不是一个,会报错。
对于过滤条件的使用,上述三个方法相同,但是互斥的,只能使用任意1个。以下内容以filter为例。
1 语法
1 2 3 4 5 | # 单表的过滤: 模型类.objects. filter (属性名称__运算符 = 值) # 此处的运算符是django的ORM提供的英文单词的运算符,与python的运算符不一样。 # 多表的过滤 模型类.objects. filter (外键属性名称__外键模型的属性名称__运算符 = 值) # 属性名称和比较运算符间使用两个英文下划线 |
2 过滤条件
过滤条件 | 运算符 | 描述 | 代码 | ||
相等 | exact | 表示判断值是否相等 |
|
||
模糊查询 | contains | 是否包含。 |
|
||
startswith | 指定值开头 |
|
|||
endswith | 指定值结尾 |
|
|||
空查询 | isnull | 字段值是否为null。空查询 |
|
||
范围查询 | in | 是否包含在范围内 |
|
||
取值范围 | range | 设置开始值与结束值范围,进行数值判断,符合范围的数据被查询出来。也可以设置时间范围。 |
|
||
比较查询 | gt(e) | 大于(等于) |
|
||
lt(e) | 小于(等于) | ||||
日期查询 | year | 对日期时间类型的属性进行运算 |
|
||
month | |||||
day | |||||
week_day | |||||
hour | |||||
minute | |||||
second | |||||
F对象 | 用于在SQL语句中针对字段之间的值进行比较的查询 |
|
|||
Q对象 | 多个过滤器逐个调用表示逻辑与关系,同sql语句中where部分的and关键字。 |
|
二 结果排序
方法 | 描述 | 代码 | ||
order_by |
order_by("第一排序字段","第二排序字段",....) 当前第一字段的值一样时,参考第二字段进行排序,第二字段的值一样时,参考第三字段进行排序.. |
|
三 限制查询
1 说明
2 查询集 QuerySet
2.1 定义
查询集,也称查询结果集、QuerySet,表示从数据库中获取的对象集合。
当调用如下ORM提供的过滤器方法时,Django会返回查询集(而不是简单的列表):
-
all():返回所有数据。
-
filter():返回满足条件的数据。filter会默认调用all方法。
-
exclude():返回满足条件之外的数据。exclude会默认调用all方法
-
order_by():对结果进行排序。order_by会默认调用all方法
对查询集可以再次调用过滤器进行过滤,如
# 查询集可以含有零个、一个或多个filter过滤器。过滤器基于所给的参数限制查询的结果。 # 从SQL的角度讲,查询集与select语句等价,过滤器像where、limit、order by子句。 Student.objects.filter(pk__gt=30).order_by('age')
方法 | 描述 | 代码 | ||
exists() | 判断查询集中是否有数据,如果有则返回True,没有则返回False。 |
|
||
values() |
把结果集中的模型对象转换成字典,并可以设置转换的字段列表,达到减少内存损耗,提高性能。 ⚠️ 如果需要返回数据的过程中进行优化,则一般我们选择使用values(),values()返回字段,比我们操作模型对象效率更高! |
|
||
values_list() | 把结果集中的模型对象转换成列表,并可以设置转换的字段列表(元祖),达到减少内存损耗,提高性能 |
2.3 QuerySet的两大特性
惰性执行 |
|
|
||
缓存结果 |
|
2.4 限制结果数量
django中可以对查询集QuerySet进行取下标或切片操作,等同于SQL中的limit和offset子句。对查询集QuerySet进行切片后返回一个新的查询集,但还是不会立即执行查询。
注意:QuerySet毕竟不是真正的列表,所以它不支持负数索引。
1 2 3 4 5 6 7 8 9 10 11 12 | def get( self , request): '''如果获取一个对象,直接使用[0],等同于[0:1].get(), 但是如果没有数据,[0]引发IndexError异常, [0:1].get()如果没有数据引发DoesNotExist异常。''' # 查询集结果数量的下标和切片操作 qs = Student.objects. all () # print(qs[0]) # 第1条数据, ORM会自动识别这个操作并转化成SQL语句的 limit 1 # print(qs[2]) # 第3条数据, ORM会自动识别这个操作并转化成SQL语句的 limit 1 offset 2【表示跳过2条数据,从第3条数据开始取,取1条数据,也就是取3这条数据。】 # print(qs[:2]) # 前2条数据, ORM会自动识别这个操作并转化成SQL语句的 limit 2 # print(qs[1:4]) # 第1,2,3 数据, ORM会自动识别这个操作并转化成SQL语句的 limit 3 offset 1。 # print( qs[-1] ) # 报错!!!不能使用负数 return HttpResponse( 'ok' ) |
四 聚合分组
一 聚合函数
from django.db.models import Max, Min, Count
1 2 3 4 5 6 7 8 9 10 11 | # 查询301班年龄最大的学生 ret = Student.objects. filter (classmate = 301 ).aggregate( Max ( "age" )) # print(ret) # {'age__max': 23} # 查新301班入学最早的学生[也就是ID最小的] ret = Student.objects. filter (classmate = 301 ).aggregate(c = Min ( "id" )) print (ret) # {'id__min': 2} ==> {'c': 2} # 调用count方法即可,不需要经过aggregate的调用 ret = Student.objects. filter (classmate = 301 ).count() print (ret) # 11 |
⚠️ aggregate的返回值是一个字典类型,格式如下:
{'属性名__聚合类小写':值} 如:{'id__min': 2} ==> {'c': 2}
二 分组函数
django中,可以使用annotate()调用分组函数。等同于SQL中的select GROUP BY语句,对一个或多个列对结果集进行分组。
注意:SQL原生语句中分组之后可以使用having过滤,在django中并没有提供having对应的方法,但是可以使用filter对分组结果进行过滤。所以filter在annotate之前,表示where,在annotate之后代表having。同理,values在annotate之前,代表分组的字段,在annotate之后代表数据查询结果返回的字段列
1 2 3 4 5 6 7 8 9 10 11 | # 针对多个字段进行分组 values("classmate","xingbie").annotate(total=Count("id")) 按 班级和性别 统计人数 # ret = Student.objects.values("classmate","sex").annotate(total=Count("id")) # print(ret) '''<QuerySet [{'classmate': '307', 'sex': True, 'total': 3}, {'classmate': '301', 'sex': True, 'total': 8}, {'classmate': '301', 'sex': False, 'total': 2},..''' # 查询出女生数量在2个以上的班级 ret = Student.objects. filter (sex = True ).values( "classmate" ).annotate(total = Count( "id" )).values( "classmate" , "total" ). filter (total__gte = 2 ) print (ret) '''<QuerySet [{'classmate': '307', 'sex': True, 'total': 3}, {'classmate': '301', 'sex': True, 'total': 8},..''' |
五 原生查询
在django中,可以引入pymysql执行SQL,也可以调用ORM提供的raw方法来执行SQL语句。如果使用raw方法执行SQL语句,则返回结果是QuerySet,这个返回结果在操作字段时,会有额外性能损耗。
1 2 3 4 5 6 7 8 | # 查询所有学生的班级、年龄、姓名和性别 sql = "SELECT id,name,sex,age,class FROM `db_student`" ret = Student.objects.raw(sql) # 针对原生SQL语句中已经查询出来的字段,只会查询一遍, # 但是如果SQL语句没有查询出来的字段,而在模型中调用,则会由ORM再次调用数据库查询,把数据临时查询出来。 for student in ret: print (student) print (student.description) |
多库共存
在django中,settings.py配置的DATABASES配置项允许注册多个数据库,可以支持在项目中随时切换操作不同的数据库。
1 2 3 4 5 | """ORM模型操作切换数据连接""" # 类名,可以使用django.db.models.Model 或者Model的子类,当然最好是为当前操作的数据库中的数据表简历一个对应的模型 ret = Student.objects.using( "数据库名" ).raw( "SELECT * FROM mf_user" ) for row in ret: print (row.__dict__) |
附:查看SQL的运行日志
MySQL数据库中, 一般存在多种不同日志,每种日志功能和作用不一样。
1 普通日志(general_log)
任何执行的sql语句都会写入这个日志中。
永久开启方案:找到mysql配置文件,默认路径:/etc/my.cnf,添加配置代码如下:
general_log = 1
general_log_file = /var/lib/mysql/general.log
2 慢查询日志(slow_log)
记录超过"慢查询时间"的语句,用于数据库优化
临时开启方案:参考上面的普通开启的方式即可
永久开启方案:找到mysql配置文件,/etc/my.cnf,添加配置代码如下:
slow_query_log=1 slow_query_log_file=/var/lib/mysql/slow.log long_query_time = 2
慢查询,一般借助归类工具mysqldumpslow来使用:mysqldumpslow -a /var/lib/mysql/slow.log
3 二进制日志(bin_log)
记录所有更改数据的SQL语句,可用于数据拷贝或数据恢复。【生产环境中务必设置的】
show variables like "%log_bin%"; # OFF 表示关闭
找到mysql配置文件,/etc/my.cnf,添加配置代码如下:
server-id=1 log-bin=mysql-bin log-bin-index=mysql-bin.index """ server-id 必须唯一 mysql终端下,查看日志: show master logs; show master status; mysqlbinlog mysql-bin.*****(日志文件号) """
4 错误日志(error_log)
mysql启动、停止、运行过程中的报错信息。默认开启。
SHOW VARIABLES LIKE 'log_error';
5 中继日志(relay_log)
主从复制的日志。
SHOW VARIABLES LIKE 'relay_log';
查看SQL的运行日志
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 | mysql> show variables like "%general_log%" ; --+------------------+-------------------------------------------+ --| Variable_name | Value | --+------------------+-------------------------------------------+ --| general_log | ON | # ON表示开启了普通日志功能 --| general_log_file | /usr/local/mysql/data/general.log | # 记录普通日志开启以后的日志文件路径 --+------------------+-------------------------------------------+ --临时开启普通日志功能 set global general_log = 'ON' ; -- 修改日志路径 set global general_log_file = '/var/lib/mysql/general.log' ; -- mysql日志文件中时间一般跟系统时间是对不上的。原因是mysql的市区是0时区,我们这边是东八区。 select @@log_timestamps; -- +------------------+ -- | @@log_timestamps | -- +------------------+ -- | UTC | # 可以发现是UTC,0时区 -- +------------------+ set global log_timestamps=SYSTEM; # 如果要永久设置,在上面配置文件中,添加 log_timestamps = SYSTEM -- +------------------+ -- | @@log_timestamps | -- +------------------+ -- | SYSTEM | # 时区参考当前操作系统 -- +------------------+ -- 退出数据库终端 exit |
-- 终端下输入以下命令,实时观察日志变化
1 | tail -f /usr/local/mysql/data/general .log |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端