数据库操作
操作对象: 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
orm/models.py
1 增加数据
方法 | 描述 | 代码 |
save | 通过创建模型类对象,执行对象的save()方法保存到数据库中。 |
from django.views import View from django.http import HttpResponse from .models import Student class Student1(View): def get(self, request): """添加一条数据""" # ORM对于数据的所有操作都是有子类objects来提供的。objects,可以被修改,开发中一般叫objects为"模型管理器" student = Student( name="刘德华", age=17, xingbie=True, classmate=301, description="一杯忘情水", ) # 自动执行添加数据 student.save() # 添加后的模型会多出一个主键数据,可以通过id或者pk来读取 print(student.id) # print(student.pk) return HttpResponse('ok') |
create | 通过模型类.objects.create()保存。 |
# 返回值就是添加后的模型对象,会有ID主键的 student = Student.objects.create( name="赵本山", age=50, sex=True, classmate=301, description="一段小品" ) print(student)# <User 赵本山> print(student.pk) return HttpResponse('ok') |
bulk_create | 通过模型类.objects.bulk_create() 批量添加数据 |
stu1 = Student(name="小黄1号", age=17, xingbie=True, classmate=301, description="hello") stu2 = Student(name="小黄2号", age=17, xingbie=False, classmate=301, description="hello") stu3 = Student(name="小黄3号", age=17, xingbie=True, classmate=301, description="hello") stu_list = [stu1,stu2,stu3] ret = Student.objects.bulk_create(stu_list) print(ret) return HttpResponse('ok') |
2 基本查询
方法 | 描述 | 代码 |
get |
查询单一结果,如果不存在或者返回多个结果会抛出异常。 查询不到, 则返回模型类.DoesNotExist异常。 查询多个, 则返回模型类.MultipleObjectsReturned异常。 |
def get(self,request): """基本查询数据""" """get 获取一条数据""" try: student = Student.objects.get(name="小黄") print(student, type(student)) print(student.name) print(student.description) except Student.DoesNotExist: print("没有查询结果!") except Student.MultipleObjectsReturned: print("当前数据不是唯一的结果!") return HttpResponse("OK") |
first | 查询一个结果, 查询不到,则返回None,查询多个,返回查询结果列表的第一个。 |
def get(self, request): student = Student.objects.first() # SQL: select * from tb_student limit 1; print(student) # <User 赵华> if student: # 判断如果student不是None print(student.description) return HttpResponse('ok') |
all | 查询所有结果。查询不到,则返回空列表对象。默认为all。 |
def get(self, request): student_list = Student.objects.all() print(student_list) # 获取结果列表 <QuerySet [<Student: <User 赵华>>, print(len(student_list)) # 获取结果列表的长度 # 把结果列表中的所有模型对象转化成字典结构 student_list = Student.objects.all().values() print(student_list) # <QuerySet [{'id': 1, # 把结果列表中的所有模型对象转换成元组结构 student_list = Student.objects.all().values_list() print(student_list) # <QuerySet [(1, """使用filter查询过滤数据,并返回所有符合条件的结果 filter(字段名=条件值) filter(字段名=条件值, 字段名=条件值) # 相当于and """ student_list = Student.objects.filter(classmate=302).all() print(student_list) return HttpResponse('ok') |
3 更新数据
方法 | 描述 | 代码 |
save | 修改模型类对象的属性,然后执行save()方法同步到数据库中 |
def get(self, request): # 先把要更新的数据查询出来,得到一个模型类对象 student = Student.objects.filter(name="小白").first() # SQL: select * from tb_student where name='小白' limit1; if student: student.name = "小黑" student.age = 18 student.save() # 把当前模型的中字段值同步到数据库 return HttpResponse('ok') |
update | 使用模型类.objects.filter().update() ,基于update来完成更新满足条件的所有数据,结果是受影响的行数 |
def get(self, request): """update 更新多条数据""" # 基于update这种操作在数据库操作中,一般称之为"乐观锁" update操作的执行效率比save要高! # update如果条件设置宽松,可以修改多条数据 Student.objects.filter(name="刘德华").update(name="刘福荣") # SQL: update tb_student set name="刘福荣" where name="刘德华"; return HttpResponse('ok') |
4 删除数据
方法 | 描述 | 代码 |
模型类对象.delete() | 删除一条 |
student = Student.objects.filter(name="小白").first() if student: # 调用模型对象的delete方法进行删除 student.delete() |
模型类.objects.filter().delete() | 删除多条 |
Student.objects.filter(name="小黄").delete() |
5 过滤条件
ORM在内部生成SQL中的where子句时,提供3个方法可以帮我们实现where过滤操作,包括:
-
filter 过滤出符合条件的多个结果
-
exclude 排除掉符合条件的多个结果,与filter相反,与filter互斥。
-
get 过滤单一结果, 结果不是一个,会报错。
对于过滤条件的使用,上述三个方法相同,但是互斥的,只能使用任意1个。以下内容以filter为例。
5.1 语法
# 单表的过滤: 模型类.objects.filter(属性名称__运算符=值) # 此处的运算符是django的ORM提供的英文单词的运算符,与python的运算符不一样。 # 多表的过滤 模型类.objects.filter(外键属性名称__外键模型的属性名称__运算符=值) # 属性名称和比较运算符间使用两个英文下划线
5.2 过滤条件
过滤条件 | 运算符 | 描述 | 代码 |
相等 | exact | 表示判断值是否相等 |
def get(self, request): student_list = Student.objects.filter(name__exact="吴杰").all() # student_list = Student.objects.filter(name="吴杰").all() # 常用!! print(student_list) return HttpResponse('ok') |
模糊查询 | contains | 是否包含。 |
student1 = Student.objects.filter(name__contains='华') print(student1) # <QuerySet [<Student: <User 赵华>>, <Student: <User 龙华>>, |
startswith | 指定值开头 |
student2 = Student.objects.filter(name__endswith='华') print(student2) # QuerySet [<Student: <User 赵华>>, <Student: <User 龙华>>, |
|
endswith | 指定值结尾 |
student3 = Student.objects.filter(name__startswith="江") print(student3) # <QuerySet [<Student: <User 江宸轩>>, |
|
空查询 | isnull | 字段值是否为null。空查询 |
student = Student.objects.filter(description__isnull=True) print(student) |
范围查询 | in | 是否包含在范围内 |
student = Student.objects.filter(classmate__in=[301, 302, 303]).all().values("id", "name", "classmate") print(student) # <QuerySet [{'id': 2, 'name': '程星云', 'classmate': '301'}, |
取值范围 | range | 设置开始值与结束值范围,进行数值判断,符合范围的数据被查询出来。也可以设置时间范围。 |
# SQL: SELECT ... WHERE id BETWEEN 51 and 67; student_list = Student.objects.filter(id__range=(51, 67)).values("id", "name") print(student_list) # <QuerySet [{'id': 51, 'name': '董晴'}, |
比较查询 | gt(e) | 大于(等于) |
def get(self, request): # # 年龄大于20的 # # age__gt=20 ---->>> where age > 20 student_list = Student.objects.filter(age__gt=22).values("name", "age") # 后面任何方法,默认补充all() print(student_list) # <QuerySet [{'name': '欧阳博', 'age': 23}, # # 年龄小于19的 # # age__lt=19 ---->>> where age < 19 student_list = Student.objects.filter(age__lt=19).all().values("name","age") print(student_list) # <QuerySet [{'name': '张小玉', 'age': 18}, # 年龄不等于19的 # 使用exclude把符合条件的排除掉。不等于的运算符,使用exclude()过滤器。 student_list = Student.objects.exclude(age=19).all().values("name","age") print(student_list) return HttpResponse('ok') |
lt(e) | 小于(等于) | ||
日期查询 | year | 对日期时间类型的属性进行运算 |
from django.views import View from django.http import HttpResponse from .models import Student from datetime import datetime, timedelta class Student1(View): def get(self, request): student_list = Student.objects.filter( created_time__year=2022, created_time__month=7, created_time__day=20 ).values("name", "created_time") print(student_list) # 精确时间查询 from datetime import datetime add_time = "2021-08-18 16:19:38" timestamp = datetime.strptime(add_time, "%Y-%m-%d %H:%M:%S") student_list = Student.objects.filter( created_time=timestamp, ).all() print(student_list) # 查询时间范围在 "2021-08-18 16:19:38" ~ "2021-08-18 16:21:56" 的数据 start_time = "2021-08-18 16:19:38" end_time = "2021-08-18 16:21:56" end_time_timestamp = datetime.strptime(end_time, "%Y-%m-%d %H:%M:%S") end_time_next = end_time_timestamp + timedelta(seconds=1) student_list = Student.objects.filter( created_time__gte=start_time, created_time__lt=end_time_next, ).all() print(student_list) return HttpResponse('ok') |
month | |||
day | |||
weekday | |||
hour | |||
minute | |||
second | |||
F对象 | 用于在SQL语句中针对字段之间的值进行比较的查询 |
"""F对象,字段间的值比较查询""" from django.db.models import F # 查询出入学以后,数据没有被修改过的学生信息 student = Student.objects.filter(created_time=F("updated_time")).values("name","created_time","updated_time") print(student) |
|
Q对象 | 多个过滤器逐个调用表示逻辑与关系,同sql语句中where部分的and关键字。 |
"""Q对象,复杂逻辑查询,针对多条件进行与或非处理""" from django.db.models import Q # 多个与 and Q(条件) & Q(条件) # 查询出301班的男生 # student = Student.objects.filter(Q(classmate=301) & Q(xingbie=1)).values("name","xingbie","classmate") # 上面也可以简写成 # student = Student.objects.filter(classmate=301, xingbie=1).values("name", "xingbie", "classmate") """单纯的多个条件并立的情况下,没必要使用到Q对象进行处理,直接编写多个条件,使用逗号串联即可""" # NOT student = Student.objects.filter(~Q(classmate=301)).values("name", "age", "sex") # 多个或,or Q(条件) | Q(条件) # 查询出301班的男生 或者 302班的男生 # student = Student.objects.filter(Q(classmate=301, xingbie=1) | Q(classmate=302, xingbie=1)).values("name", # 上面也可以简写成 # student = Student.objects.filter(classmate__in=[301, 302], xingbie=1).values("name", "xingbie", "classmate") # print(student) |
6 结果排序
方法 | 描述 | 代码 |
order_by |
order_by(“第一排序字段”,”第二排序字段”,….) 当前第一字段的值一样时,参考第二字段进行排序,第二字段的值一样时,参考第三字段进行排序.. |
# order_by("id") # 表示按id字段的值进行升序排序,id数值从小到大 # order_by("-id") # 表示按id字段的值进行降序排序,id数值从大到小 # 调用了order_by以后,如果没有后续声明返回结果的all方法时,默认使用all()进行结果查询 student = Student.objects.order_by("-id") print(student) |
7 限制查询
7.1 说明
ORM中针对查询结果的数量限制,提供了一个查询集对象[QuerySet].这个QuerySet,是ORM中针对查询结果进行临时保存数据的一个容器对象,我们可以通过对QuerySet进行使用,达到查询优化的目的,也或者限制查询结果数量的作用。
7.2 查询集 QuerySet
1 查询集,也称查询结果集、QuerySet,表示从数据库中获取的对象集合。当调用如下ORM提供的过滤器方法时,Django会返回查询集(而不是简单的列表):
-
all():返回所有数据。
-
filter():返回满足条件的数据。filter会默认调用all方法。
-
exclude():返回满足条件之外的数据。exclude会默认调用all方法
-
order_by():对结果进行排序。order_by会默认调用all方法
2 判断某一个查询集中是否有数据:
方法 | 描述 | 代码 |
exists() | 判断查询集中是否有数据,如果有则返回True,没有则返回False。 |
student_list = Student.objects.filter(classmate="301") student_list = student_list.order_by("-age") student_list = student_list.filter(sex=1) ret = student_list.exists() print(ret) # True |
values() | 把结果集中的模型对象转换成字典,并可以设置转换的字段列表,达到减少内存损耗,提高性能。 |
student_list = Student.objects.filter(classmate="301") student_list = student_list.order_by("-age") student_list = student_list.filter(sex=1) ret1 = student_list.values() # 默认把所有字段全部转换并返回 ret2 = student_list.values_list() # 默认把所有字段全部转换并返回 ret3 = student_list.values_list("id", "name", "age") # 可以通过参数设置要转换的字段并返回 print(ret1, type(ret1)) # <QuerySet [{'id': 102,..}]><class 'django.db.models.query.QuerySet'> print(ret2, type(ret2)) # <QuerySet [(102,..)]> <class 'django.db.models.query.QuerySet'> print(ret3, type(ret3)) # <QuerySet [(102, '赵本山', 50),..]> <class 'django.db.models.query.QuerySet'> |
values_list() | 把结果集中的模型对象转换成列表,并可以设置转换的字段列表(元祖),达到减少内存损耗,提高性能 |
3 QuerySet的两大特性
惰性执行 |
QuerySet查询集在创建时是不会访问数据库执行SQL语句, 直到模型对象被调用输出或者调用模型对象的属性时,才会真正的访问数据库执行SQL语句, 调用模型的情况包括循环迭代、序列化、与if合用,print的时候。 |
def get(self, request): '''惰性执行,可以让重复的查询操作,只执行一次''' # 创建了一个查询集对象student_list,此时没有执行SQL语句的 student_list = Student.objects.all() # 继续执行遍历迭代、或打印操作之后操作后,才进行了数据库的查询 for student in student_list: pass return HttpResponse('ok') |
缓存结果 | 使用同一个查询集,第一次使用时会发生数据库的查询,然后Django会把结果缓存下来,再次使用这个查询集时会使用缓存的数据,减少了数据库的查询次数。 |
"""两个查询集,无法重用缓存,每次查询都会与数据库进行一次交互,增加了数据库的负载""" [student.id for student in Student.objects.all()] # 因为没有缓存查询集到变量中,所以此处第一次执行了SQL语句 [student.id for student in Student.objects.all()] # 因为没有缓存查询集到变量中,所以此处第一次执行了SQL语句 '''经过存储后,可以重用查询集,第二次使用缓存中的数据''' student_list = Student.objects.all() [student.id for student in student_list] # 因为上面保存到查询到变量中,所以此处执行了SQL语句 [student.id for student in student_list] # 此处调用了之前的缓存数据 |
4 限制结果数量
django中可以对查询集QuerySet进行取下标或切片操作,等同于SQL中的limit和offset子句。对查询集QuerySet进行切片后返回一个新的查询集,但还是不会立即执行查询。
注意:QuerySet毕竟不是真正的列表,所以它不支持负数索引。
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')
8 聚合函数
django中,可以使用aggregate()过滤器调用聚合函数。聚合函数包括:Avg 平均,Count 总数,Max 最大,Min 最小,Sum 求和,导入from django.db.models import Max, Min, Count
中。
注意:使用count时一般不使用aggregate()过滤器。
# 查询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
9 分组函数
django中,可以使用annotate()调用分组函数。等同于SQL中的select GROUP BY语句,对一个或多个列对结果集进行分组。
注意:SQL原生语句中分组之后可以使用having过滤,在django中并没有提供having对应的方法,但是可以使用filter对分组结果进行过滤。所以filter在annotate之前,表示where,在annotate之后代表having。同理,values在annotate之前,代表分组的字段,在annotate之后代表数据查询结果返回的字段列
# 针对多个字段进行分组 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},..'''
10 原生查询
在django中,可以引入pymysql执行SQL,也可以调用ORM提供的raw方法来执行SQL语句。如果使用raw方法执行SQL语句,则返回结果是QuerySet,这个返回结果在操作字段时,会有额外性能损耗。
# 查询所有学生的班级、年龄、姓名和性别 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)
11 多库共存
在django中,settings.py配置的DATABASES配置项允许注册多个数据库,可以支持在项目中随时切换操作不同的数据库。
"""ORM模型操作切换数据连接""" # 类名,可以使用django.db.models.Model 或者Model的子类,当然最好是为当前操作的数据库中的数据表简历一个对应的模型 ret = Student.objects.using("数据库名").raw("SELECT * FROM mf_user") for row in ret: print(row.__dict__)
原创文章,作者:端木书台,如若转载,请注明出处:https://blog.ytso.com/276594.html