Django 中有一个强大的功能,就是ORM,他可以不写sql语句,使用python语法,即可操作数据库。当然也有自己的书写规则。大概看一下django中的ORM的使用规则。
前奏
-
在settings中配置,可显示sql语句:
# 显示SQL语句 LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level': 'DEBUG', }, } }
-
加载环境,以单文件运行
import os if __name__ == '__main__': os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings") # mysite为自己的项目中settings的父级目录 import django django.setup()
一、单个表的增删改查
表:
class User(models.Model):
username=models.charFiled(max_lenth=255)
password=models.charFiled(max_lenth=255)
创建:User.objects.create(username='zhangsan',password='123')
查询:objs = User.objects.all()
删除:objs = bjects.filter('id=1')
objs.delete()
更新:obj = objs[0]
obj.username = "new_name"
obj.save()
接下来看一下ORM中其他的一些查询语句。
为什么查询那么重要?
操作数据库,简单来说就是对数据进行增删改查,在数据的增删改查四步操作中,只有增加不用查询,删除、更改 都是在查询出结果的前提下才能进行,所以ORM查询尤为重要。
举个简单的例子,来练习一下 django的ORM查询语句 。
二、常见的单表查询(13种)
QuerySet 对象的形成:
按照需求执行sql语句,从数据库查询出一个表,
表里的每个记录封装成对象,每个字段封装成属性,
而这个表就可以看成是QuerySet对象。
class Person(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
birth = models.DateField(auto_now=True)
def __str__(self):
return '<Person obj:{}--{}>'.format(self.name,self.id)
# class Meta:
# ordering = ('id',)
# 为此类指定默认排序方式。
-
【1】all():返回QuerySet对像,类似于列表的东西,查询所有。
ret = models.Person.objects.all() print(ret) # 【此刻执行了sql语句】 # <QuerySet [<Person: <Person obj:赵昭昭--1>>, <Person: <Person obj:钱芊芊--2>>, # <Person: <Person obj:孙笋笋--3>>, <Person: <Person obj:李莉莉--4>]>
-
【2】get():返回一个对象,找不到或找到多个报错。
ret = models.Person.objects.get(id=3) # 【此刻执行了sql语句】 print(ret) # (0.001) SELECT `app01_person`.`id`, `app01_person`.`name`, `app01_person`.`age`, `app01_person`.`birth` FROM `app01_person` WHERE `app01_person`.`id` = 3; args=(3,) # <Person obj:孙笋笋--3>
-
【2.1】 only():【使用only可以降低查询压力,增加查询速度】
ret = models.Person.objects.only('name').get(id=3) # 【此刻执行了sql语句】 print(ret) # (0.001) SELECT `app01_person`.`id`, `app01_person`.`name` FROM `app01_person` WHERE `app01_person`.`id` = 3; args=(3,) # <Person obj:孙笋笋--3>
-
【3】filter():返回QuerySet,包含符合条件的所有对象,查不到就是空的QuerySet对象<QuerySet []>
ret = models.Person.objects.filter(id=3) print(ret) # 【此刻执行了sql语句】 # <QuerySet [<Person: <Person obj:孙笋笋--3>>]> # ret = models.Person.objects.filter(id=3)[0] # filter的时候没有执行sql语句,filter[0]的时候,执行了sql
-
【4】exclude():返回QuerySet,和filter相反。
ret = models.Person.objects.exclude(id=3) print(ret) # 【此刻执行了sql语句】 # <QuerySet [<Person: <Person obj:赵昭昭--1>>, <Person: <Person obj:钱芊芊--2>>,<Person: <Person obj:李莉莉--4>]>,
-
【5】values():返回QuerySet,里面是字典
ret = models.Person.objects.values() print(ret) # 【此刻执行了sql语句】 # <QuerySet [{'id': 1, 'name': '赵昭昭', 'age': 20, 'birth': datetime.date(1998, 8, 5)}, # {'id': 2, 'name': '钱芊芊', 'age': 18, 'birth': datetime.date(2000, 8, 6)}, # {'id': 3, 'name': '孙笋笋', 'age': 16, 'birth': datetime.date(2002, 8, 6)}, # {'id': 4, 'name': '李莉莉', 'age': 22, 'birth': datetime.date(1996, 6, 8)} ]> ret = models.Person.objects.values('name', 'age') # 指定字段就只显示字段的内容,找不到指定字段报错。 print(ret) # <QuerySet [{'name': '赵昭昭', 'age': 20}, {'name': '钱芊芊', 'age': 18}, # {'name': '孙笋笋', 'age': 16}, {'name': '李莉莉', 'age': 22}]>
-
【6】value_list():返回QuerySet,里面是元组,不包括健
ret = models.Person.objects.values_list() print(ret) # 【此刻执行了sql语句】 # <QuerySet [(1, '赵昭昭', 20, datetime.date(1998, 8, 5)), # (2, '钱芊芊', 18, datetime.date(2000, 8, 6)), # (3, '孙笋笋', 16, datetime.date(2002, 8, 6)), # (4, '李莉莉', 22, datetime.date(1996, 6, 8))]> ret = models.Person.objects.values_list('age', 'name') print(ret) # <QuerySet [(20, '赵昭昭'), (18, '钱芊芊'), (16, '孙笋笋'), (22, '李莉莉')]> ret = models.Person.objects.values_list('age') print(ret) # <QuerySet[(20,), (18,), (16,), (22,)]>
【flat=True】:将单个字段的数据直接放到列表里面(只有一个字段时才可用)
ret = models.Person.objects.values_list('age', flat=True) print(ret) # <QuerySet [20, 18, 16, 22]>
-
【7】order_by():按某字段排序,可反序,可按多字段排序
ret = models.Person.objects.all().order_by('-id') print(ret) # 【此刻执行了sql语句】 # <QuerySet [<Person: <Person obj:李莉莉--4>>, <Person: <Person obj:孙笋笋--3>>, # <Person: <Person obj:钱芊芊--2>>, <Person: <Person obj:赵昭昭--1>>]>
-
【8】reverse():对排序后(使用了order_by)的QuerySet进行反转。(类中设置了默认排序也可以用)
ret = models.Person.objects.all().order_by('-age').reverse() print(ret) # 【此刻执行了sql语句】 # <QuerySet [<Person: <Person obj:孙笋笋--3>>, <Person: <Person obj:钱芊芊--2>>, # <Person: <Person obj:赵昭昭--1>>, <Person: <Person obj:李莉莉--4>>]> # 年龄:16,18,20,22
-
【9】distinct():去重
-
【10】count():对QuerySet对象进行计数。
ret = models.Person.objects.all().count() # 【此刻执行了sql语句】 print(ret) # 4
-
【11】first():取QuerySet中的第一个元素。
ret = models.Person.objects.all().first() # 【此刻执行了sql语句】 # ret = models.Person.objects.first() 可不写,默认全部 print(ret) # <Person obj:赵昭昭--1>
-
【12】last():取QuerySet中的最后一个元素。
-
【13】exists():如果QuerySet包含数据,就返回True,否则返回False
ret = models.Person.objects.filter(id=1).exists() # 【此刻执行了sql语句】 print(ret) # True ret = models.Person.objects.filter(id=10).exists() print(ret) # False
-
返回QuerySet对象的有 (QuerySet对象有个
.query
属性,可以查看sql语句。返回 QuerySet对象的方法,都没有真正执行sql语句。当 print QuerySet对象,或者显示QuerySet对象的结果时,才执行了sql):1.all() 2.filter() 3.exclude() 4.values() 5.values_list() 6.order_by() 7.reverse() 8.distinct()
-
返回具体对象的方法有:
1.get() 2.first() 3.last()
-
返回数字的方法有:
1.count()
-
返回布尔值的方法有:
1.exists()
三、单表查询的双下划线方法
-
【查询 id = 1 的】
ret = models.Person.objects.filter(id=1) print(ret) """ <QuerySet [<Person: <Person obj:赵昭昭--1>>]> """
-
【查询 id > 1 的】( greater than )
ret = models.Person.objects.filter(id__gt=1) print(ret) """ <QuerySet [<Person: <Person obj:钱芊芊--2>>, <Person: <Person obj:孙笋笋--3>>, <Person: <Person obj:李莉莉--4>>]> """
-
【查询 id <3 的】( less than )
ret = models.Person.objects.filter(id__lt=3) print(ret) """ <QuerySet [<Person: <Person obj:赵昭昭--1>>, <Person: <Person obj:钱芊芊--2>>]> """
-
【查询 id >=3 的】( equal )
ret = models.Person.objects.filter(id__gte=3) print(ret) """ <QuerySet [<Person: <Person obj:孙笋笋--3>>, <Person: <Person obj:李莉莉--4>>]> """
-
【查询 id <=3 的】
ret = models.Person.objects.filter(id__lte=3) print(ret) """ <QuerySet [<Person: <Person obj:赵昭昭--1>>, <Person: <Person obj:钱芊芊--2>>, <Person: <Person obj:孙笋笋--3>>]> """
-
【查询 2 <= id <= 4 的】
ret = models.Person.objects.filter(id__gte=2, id__lte=4) print(ret) """ <QuerySet [<Person: <Person obj:钱芊芊--2>>, <Person: <Person obj:孙笋笋--3>>, <Person: <Person obj:李莉莉--4>>]> """
-
【查询 id 在 。。。里的】
ret = models.Person.objects.filter(id__in=[1, 2, 4]) print(ret) """ <QuerySet [<Person: <Person obj:赵昭昭--1>>, <Person: <Person obj:钱芊芊--2>>, <Person: <Person obj:李莉莉--4>>]> """
-
【查询 id 不在。。。里的】
ret = models.Person.objects.exclude(id__in=[1, 2, 4]) print(ret) """ <QuerySet [<Person: <Person obj:孙笋笋--3>>]> """
-
【查看某字段中包含。。。字眼的】( 找不到为空 )
ret = models.Person.objects.filter(name__contains='赵') # ret = models.Person.objects.filter(name__icontains='h') 此项是英文不区分大小写。 print(ret) """ <QuerySet [<Person: <Person obj:赵昭昭--1>>]> """
-
【查询 id 在。。。范围内】2,3,4
ret = models.Person.objects.filter(id__range=[2, 4]) print(ret) """ <QuerySet [<Person: <Person obj:钱芊芊--2>>, <Person: <Person obj:孙笋笋--3>>, <Person: <Person obj:李莉莉--4>>]> """
-
【查询 某字段以。。。为开头的】
ret = models.Person.objects.filter(name__startswith='钱') # models.Person.objects.filter(name__istartswith="v") # istartswith大小写不敏感 print(ret) """ <QuerySet [<Person: <Person obj:钱芊芊--2>>]> """
-
【查询 某字段以。。。为结尾的】( iendswith 模糊查询 )
ret = models.Person.objects.filter(name__endswith='莉') print(ret) """ <QuerySet [<Person: <Person obj:李莉莉--4>>]> """
-
【查询 关于年,月,日 的】
ret = models.Person.objects.filter(birth__year=1998) ret = models.Person.objects.filter(birth__month=6) ret = models.Person.objects.filter(birth__day=10) # mysql 数据库可以用这样逗号隔开and的 方式。用django自带的数据库可能不支持。 ret = models.Person.objects.filter(birth__year=1998, birth__month=6) ret = models.Person.objects.filter(birth__contains='1998-06') print(ret) # 有警告,但是可以查出来。
四、有外键关系的查询
-
先定义三个模型类,出版社 <---1对多---> 书籍 <---多对多---> 作者。
from django.db import models class Publisher(models.Model): name = models.CharField(max_length=32) class Book(models.Model): title = models.CharField(max_length=32) publisher = models.ForeignKey(to='Publisher') # publisher = models.ForeignKey(to='Publisher',related_name='books') # publisher = models.ForeignKey(to='Publisher',related_name='books',related_query_name='xx') # 这个related_name 反向查询,基于对象时用 【和下面【↓】的对应】。 # 这个related_query_name 【反向查询,基于字段】时用。 price = models.DecimalField(max_digits=5,decimal_places=2,null=True) class Author(models.Model): name = models.CharField(max_length=32) books = models.ManyToManyField(to='Book')
-
查询时先看清楚是正向还是反向,看清是基于对象的查询还是基于字段的查询。
外键的正反向查询.png
1,【正向查询】---> 从多往1找(一般外键都设在多的一方)
-
基于对象查询
book_obj = models.Book.objects.filter(title='北京生活')[0] #【先有一个对象,拿出一本书,再查有关书的一些东西】 print(book_obj.title) # 书的名字(没有设外键的字段) print(book_obj.publisher) # Publisher object 出版社对象(设外键的字段) print(book_obj.publisher.name) # 北京出版社(跨表查询)
-
基于字段查询 (跨表查询)(不是本表中的字段,是关联表中的字段)
ret = models.Book.objects.filter(publisher__name='北京出版社') # 站在书籍的角度,查询'北京出版社'出版的所有书籍 print(ret)
2,【反向查询】---> 从1往多找
-
基于对象查询
publisher_obj = models.Publisher.objects.get(name='北京出版社') # 拿到一个出版社对象 print(type(publisher_obj.book_set)) # "关联管理器" print(publisher_obj.book_set.all()) # "关联管理器".all()查询出版社出版的所有书籍。 # Book表中的: # publisher = models.ForeignKey(to='Publisher',related_name='books') # print(publisher_obj.books.all()) (【和上面【↑】对应】,如果关联对象的相关字段写了related_name='books',本语句就可这样写。) # pub_obj = models.Publisher.objects.first() # 拿到北京出版社 # pub_obj.book_set.create(title='北京生活') # 利用管理对象,创建书籍(为本出版社添加书籍,但是书籍没有作者)
-
基于字段查询 (跨表查询)
ret = models.Publisher.objects.filter(book__title="北京生活") # 查询"北京生活"的出版社。 print(ret) # 如果上面加了related_name = 'bookssss',那么就得写成(bookssss__title="北京生活"【和上面【↑】对应】)。 # 如果上面在加了related_name = 'books'之后,又加了 related_query_name='xx',就得写(xx__title="北京生活")。 # 也就是说: # 如果外键字段只定义了 related_name = 'books' ,那么反向查询时,不管基于对象还是基于字段都可以使用 books 名称代替。 # 如果外键字段定义了 related_name = 'books' ,又定义了 related_query_name='xx' 那么基于字段的反向查询时只能使用 xx__title 名称代替。 '''外键的"关联管理器",在被关联那一边,反向查找时可用,是book_set(表名_set),可起别名''' # 如果把出版社的所有书籍都删了,也就是删除了有关书籍和这个出版社的对应关系,对于书籍而言,出版社就为空了。 # 如果想要在多对一的关系中,基于1的一面删除多,就得在多的一面设置可以为空。
五、多对多关系的增删改查
1,【正向查询】
-
基于对象
多对多的正向查询.pngauthor_obj = models.Author.objects.first() print(author_obj.name) # 作者的姓名 李白 print(author_obj.books) # "关联管理器"
# print(author_obj.books.all()) # author_obj.books.create(title='一起来学习',publisher_id=5) # "关联管理器".create()创建book对象 # author_obj.books.add(10,11) # 给作者添加几本书,之前在出版社创建了几本书,现在是给作者增加几个关联对象 # author_obj.books.remove(12,13) # 和add相反,去除几个书籍。 # author_obj.books.clear() # 清空
# "关联管理器".add(*args),添加几个关联,(前提是有这几本书)相当于在第三张关联表中,增加了几个关联。 # 和 author_obj.books.set([]) 不同: # set是创建对象时或者创建对象后,给多对多字段设置属性,可以是id列表,可以是QuerySet # add是有了对象,给对象增加属性,可以是id,可以是对象。如果是QuerySet,必须打散
# booklist = models.Book.objects.filter(author__id=1) # print(booklist) # print(*booklist) # author_obj.books.remove(*booklist)
# publisher_obj = models.Publisher.objects.get(id=1) # booklist2 = publisher_obj.book_set.all() # print(publisher_obj) # print(booklist2) # author_obj.books.set(booklist2)
'''多对多的"关联管理器",在关联那一边,名字为,设置了多对多关系的字段名books'''
六、聚合与分组
from django.db.models import Avg, Sum, Max, Min, Count
1、聚合函数
- 可自己写名称,默认是以 字段__聚合函数 为名称
avg = models.Book.objects.aggregate(Avg('price')) print(avg) # {'price__avg': 80.0} avg = models.Book.objects.aggregate(平均=Avg('price')) print(avg) # {'平均': 80.0} ret = models.Book.objects.aggregate(Sum('price'),Max('price'),Min('price'),Count('id')) print(ret) # {'price__sum': Decimal('1040.00'), 'price__max': Decimal('140.00'), # 'price__min': Decimal('20.00'), 'id__count': 13}
2、分组
-
【统计书的作者个数】 书 <------> 作者
ret = models.Book.objects.annotate(Count('author')).values() for i in ret: print(i)
-
【出版社最便宜的书的价格】 出版社 <------> 书
ret = models.Publisher.objects.annotate(Min('book__price')).values() for i in ret : print(i)
-
【作者个数大于1的书】 作者 <------> 书
ret = models.Book.objects.annotate(num=Count('author')).filter(num__gt=1).values() for i in ret: print(i)
-
【查询各个作者出了几本书】 作者 <------> 书
ret = models.Author.objects.annotate(Count('books')).values() for i in ret: print(i)
-
【查询各个作者出的书的总价格】 作者 <------> 书
ret = models.Author.objects.annotate(Sum('books__price')).values() for i in ret: print(i)
'''annotate前面是什么就按什么分组,没有默认是以id为准'''
七、F查询、Q查询
class Book(models.Model):
title = models.CharField(max_length=32)
publisher = models.ForeignKey(to='Publisher')
price = models.DecimalField(max_digits=5,decimal_places=2,null=True)
# 此字段为以后聚合函数使用 最大长度 小数点后长度 可以为空
# 后加的字段,需要设置默认值,要不就设置可以为空
kucun = models.IntegerField(default=0)
sale = models.IntegerField(default=100)
# 后加字段,为测试 F查询、Q查询使用
from django.db.models import F, Q
-
【F 查询】
# 原来查询方法,比较时,符号后面是外界得到的条件,是提前给好的数据。 ret = models.Book.objects.filter(id__gt=10) # 过滤条件是跟常量比较。 print(ret) # 想要在表中自己和自己的数据比较就得用 F 查询
# 【查询库存数小于出售数的】 ret = models.Book.objects.filter(kucun__lt=F('sale')) for i in ret: print(i) print(i.title, i.kucun, i.sale)
# 原来的方法,修改对象的属性时,是这样操作: book_obj = models.Book.objects.get(title='红楼梦') book_obj.title = '葫芦娃' book_obj.save() # 新的修改方法:QuerySet对象.update(字段=‘’) models.Book.objects.filter(title='葫芦娃').update(title='金刚葫芦娃') #【给所有书的库存增加数量】 models.Book.objects.all().update(kucun=F('kucun')+100) #【给书籍名字统一加字符串】 from django.db.models.functions import Concat from django.db.models import Value models.Book.objects.all().update(title=Concat(F('title'),Value('(第一版)')))
-
【Q 查询】
# 原来查询两边的查询: ret = models.Book.objects.filter(id__gt=3, id__lt=6) # 4,5 print(ret) ret = models.Book.objects.exclude(id__gt=3, id__lt=6) # 没有4,5 print(ret) ret = models.Book.objects.exclude(id=3) # 没有3 print(ret) # 现在查询两边的查询: ret = models.Book.objects.filter(Q(id__gt=3) & Q(id__lt=6)) # 4,5,并且 print(ret) ret = models.Book.objects.filter(Q(id__lt=3) | Q(id__gt=6)) # 小于3,大于6,没有3,4,5,6,或 print(ret) ret = models.Book.objects.filter(~Q(id=3)) # 没有3 print(ret) ret = models.Book.objects.filter(~Q(id=3), id__lt=5) # 小于5,且没有3【注意,Q放前面】 print(ret)
# Q 查询的另一种用法。 q = Q() # 实例出一个对象。 q.children.append(('title', '西游记')) # 元组里放字符串 obj = models.Book.objects.filter(q) print(obj) q1 = Q() # 空的Q对象,等同于all(). q1.connector = 'or' # 将查询条件改成“或”,默认是“与” q1.children.append(('title', '西游记')) q1.children.append(('price' + '__icontains', 20)) # 可添加多个搜索条件,支持模糊查询。 ret = models.Book.objects.filter(q1) print(ret)
八、面试题
from django.db import models
class WfModel(models.Model):
"""
工作流
"""
model_id = models.AutoField(primary_key=True) # 主键ID
model_name = models.CharField(max_length=255) # 工作流名称
create_time = models.DateTimeField(auto_now_add=True) # 添加时间
def __str__(self):
return "ID为%s的%s号" % (self.model_id, self.model_name)
class WfStep(models.Model):
"""
工作流步骤
"""
step_id = models.AutoField(primary_key=True) # 主键ID
model = models.ForeignKey(WfModel, models.DO_NOTHING) # 流程ID
step_name = models.CharField(max_length=255) # 步骤名称
def __str__(self):
return self.step_name
class WfStepCheck(models.Model):
"""
工作节流点人员表
"""
check_id = models.AutoField(primary_key=True) # 主键
step = models.ForeignKey(WfStep, models.DO_NOTHING) # 步骤ID
check_user_id = models.IntegerField() # 角色ID或用户ID
is_design = models.BooleanField(default=1) # 是否设计;1是;0否;
def __str__(self):
return "%s的%s号人员,check_id为%s" % (self.step, self.check_user_id, self.check_id)
- 【1】、如上3个实体表,其中知道实体 WfModel 的 model_id 值为:[1, 2, 3],要查询 WfStepCheck 中 is_design 的值,请用 django 的 orm 或 sql 写出查询语句
# sql = "select step_id from app_wfstep where model_id in (1,2,3)"
sql = "select is_design from app_wfstepcheck where step_id in(select step_id from app_wfstep where model_id in (1,2,3))"
from django.db import connection
cursor = connection.cursor()
cursor.execute(sql)
row = cursor.fetchall()
print(row)
或者使用以下的orm查询语句↓ (执行一次数据库查询)
objects = WfStepCheck.objects.select_related('step').filter(step__model__model_id__in=[1, 2, 3]).values('is_design')
print(objects)
- 【2】、实体 WfModel 中 model_id 值为:[1, 2, 3],要更新 WfStepCheck 中 is_design 的值为 False,请用 django 的 orm 或 sql 写出更新语句
objects = WfStepCheck.objects.filter(step__model__model_id__in=[1, 2, 3])
#print(objects)
#如果单独查询,直接print的话会执行多次sql,但是如果使用objects.update()会执行一次sql。
#所以:如果是更新的话,不用使用select_related,但要是查询的时候建议使用select_related,减少数据库查询次数。
objects.update(is_design=0)
- 【3】、通过 WfModel 的 model_id 为3的条件,查询 WfStepCheck 的信息,函数为 get_wf_check(),此函数是否有问题,为什么?
def get_wf_check():
"""获取步骤的审批人员信息"""
step_check = []
for item in WfModel.objects.filter(model_id=3):
# print("item-->",item)
for step in WfStep.objects.filter(model=item):
# print("step-->",step)
for stepcheck in WfStepCheck.objects.filter(step=step):
# print("stepcheck-->",stepcheck)
step_check.append(stepcheck)
return step_check
ret = get_wf_check()
print(ret)
# for obj in ret:
# print(obj.check_id, obj.check_user_id, obj.step, obj.is_design)
这样不好,他会多次去查询数据库,增加数据库压力。最好这样,只查询一次:
ret = WfStepCheck.objects.select_related('step').filter(step__model__model_id=3)
print(ret)
1,每一张表在数据库中,都是以app的名字开头
2,外键的字段在数据库中,会自动加上_id
3,__str__中必须返回字符串,如果想要在models类里面显示数字类的信息,需要str一下。
网友评论