单表操作
1、表记录的添加
方式一: Book() + obj.save()
b=Book(name="python基础",price=99,author="yuan",pub_date="2017-12-12")
b.save()#将b存到数据库
方式二:create
Book.objects.create(name="linux",price=78,author="HK",pub_date="2018-10-10")
#or
dict = {"name":"java精通","price":88,"author":"HK","pub_date":"2023-5-5"}
Book.objects.create(**dict)
2、表记录的修改
方式一:update 推荐,运行效率相对高
Book.objects.filter(author="Pony").update(price=990)
方式二:save
b = Book.objects.get(author="Pony")
b.price = 120
b.save()
update与save的区别:
update对应的SQL语句只更新指定字段;
save则引起所有字段的更新。
查看ORM操作数据库时执行的sql语句
在settings.py中加上LOGGING
LOGGING = {
'version':1,
'disable_existing_loggers':False,
'handlers':{
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers':{
'django.db.backends':{
'handlers':['console'],
'propagate':True,
'level':'DEBUG',
},
}
}
3、删除表记录
Book.objects.filter(author="Pony").delete()
4、all 查询所有记录
Book.objects.all()
5、对结果切片
Book.objects.all()[:3] # 取前三条记录
Book.objects.all()[::2] # 隔条取记录
Book.objects.all()[::-1] # 倒序
6、get
只能取一个记录,如果有多个记录符合条件,会报错
7、values()
取得查询结果中指定字段:列表
# 获取author=“yuan”的集合中name的值
Book.objects.filter(author="Pony").values("name")
# 结果类型为[{'name':'***'}, {'name':'***'},]
# 取两列的值
Book.objects.filter(author="Pony").values("name","price")
# 结果类型为[{'name':'***', 'price':23}, {'name':'***', 'price':32},]
8、values_list
取得查询结果中指定字段:元组
# 获取author=“yuan”的集合中name的值
Book.objects.filter(author="Pony").values_list("name")
# 结果类型为({'name':'***'}, {'name':'***'},)
# 取两列的值
Book.objects.filter(author="Pony").values_list("name","price")
# 结果类型为({'name':'***', 'price':23}, {'name':'***', 'price':32},)
9、exclude:排除
Book.objects.exclude(author="Pony")
# 查询author = "Pony"以外的Book
10、distinct: 去重
Book.objects.all().values("name").distinct()
11、count : 数量
book_list = Book.objects.all()
c = book_list.count()
12、模糊匹配: 万能的双下划线__
a、__gt :大于
Book.objects.filter(price__gt=50)
# 查询price大于50的Book
b、contains: 包含
Book.objects.filter(name__icontains="p")
# icontains 不区分大小写 包含“p”的book.name
更多:http://www.cnblogs.com/yuanchengqi
多表操作
1、一对多
ForeignKey
class Book(models.Model):
...
publish = models.ForeignKey("Publish") # 出版社字段
# 外键建在多的一方
...
book表当中将拥有字段publish_id,即对应publish外键。
另:将Publish类放在Book之前,此时Book的ForeignKey中Publish不需要加引号。
添加记录
方式一:publish_id =
Book.objects.create(name=..,price=..,pub_date=..,publish_id=2)
方式二:publish = object
publish = Publish.objects.filter(name=..)[0]
Book.objects.create(name=..,price=..,pub_date=..,publish=publish)
关联查询
正向查询:book ==> publish
# 方式一
book = Book.objects.get(name='GO')
print(book.publish)
print(book.publish.city)
# 方式二
# 双下划线查询
pub = Publish.objects.filter(book__name="GO")
反向查询:publish ==> book
# 方式一:
pub = Publish.objects.filter(name=..)
book = Book.objects.filter(publish=pub)
# 方式二: book_set
print(pub.book_set.all())
# 方式三:双下划线 (推荐)
book = Book.objects.filter(publish__name=..)
两种查出版社名字的方式都被翻译为一样的SQL语句:
pub1 = Publish.objects.filter(book__name="GO").values("name")
pub2 = Book.objects.filter(name="GO").values("publish__name")
by the way: 查询出版日期大于某日期的Book
Book.objects.filter(pub_date__gt="2017-11-02").values("name")
related_name
class Book(models.Model):
...
publish = models.ForeignKey("Publish", related_name='books') # 出版社字段
# 外键建在多的一方
...
# 当ForeignKey设置了related_name之后
publish_obj.books.all() # 查询book
# 不设置related_name时
publish_obj.book_set.all()
2、多对多
ManyToManyField
class Book(models.Model):
...
authors = models.ManyToManyField("Author") # 作者
...
ManyToManyField 属性并不会让表增加字段,而是新建一个book_authors表,用于记录book与author的对应关系
a、add
给book设置author : add(author)
book = Book.objects.get(id=3)
author = Author.objects.get(id=2)
book.authors.add(author) # 添加book与author的对应关系
给book设置多个author : add(*authors)
book = Book.objects.get(id=3)
authors = Author.objects.all()
book.authors.add(*authors) # 添加book与多个author的对应关系
b、remove
删除对应关系
book = Book.objects.get(id=4)
authors = Author.objects.all()
book.authors.remove(*authors) # 移除authors
#or
book.authors.remove(2) # 移除id = 2的author
手动建立多对多的表(但不推荐)
class Book_Author(models.Model):
book = models.ForeignKey("Book")
author = models.ForeignKey("Author")
用自建表添加对应关系
Book_Author.objects.create(book_id=.., author_id=..)
book.book_author_set
obj = Book.objects.get(id=3)
print(obj.book_author_set.all())
双下划线查询
list = Book.objects.filter(book_author__author__name='Pony')
.values('name','price')
不使用自建第三张表时,
list = Book.objects.filter(authors__name="Pony")
.values('name','pirce')
因此推荐使用models.ManyToMany
● 建立多对多关系:author=models.ManyToManyField(“Author”)
● Book的所有关联作者 obj=book_obj.authors.all()
● 绑定多对多关系 obj.add(*QuerySet)
● 移除 obj.remove(author_obj)
● 掌握:通过filter values (双下划线)进行多对多的关联查询(形式和一对多相同)
其它
from django.db.models import Avg,Min,Sum,Max
1、聚合函数:aggregate
#平均值
p = Book.objects.all().aggregate(Avg("price")) # 所有Book price的平均值
#总值
s = Book.objects.filter(authors__name=..)
.aggregate(Sum('price'))
#自定义输出结果的字典键值名称
m = Book.objects.filter(authors__name='Tom')
.aggregate(tom_money=Sum('price'))
# 输出 : {’tom_money': ..}
# 统计数量
c = Book.objects.filter(authors__name=..).aggregate(Count('name'))
2、分组:annotate
# 通过名字分组,然后按组统计它们的价格总结
r = Book.objects.values("authors__name").annotae(Sum('price'))
# 输出 [{'price__sum":.., 'authors__name':..}, {'price__sum":.., 'authors__name':..}, ...]
SELECT `app01_author`.`name`,
SUM(`app01_book`.`price`) AS `price__sum`
FROM `app01_book` LEFT OUTER JOIN `app01_book_authors` ON (`app01_book`.`id` = `app01_book_authors`.`book_id`) LEFT OUTER JOIN `app01_author` ON (`app01_book_authors`.`author_id` = `app01_author`.`id`) GROUP BY `app01_author`.`name` ORDER BY NULL LIMIT 21; args=()
3、F查询和Q查询
# 查询name=GO,且price=77的书:
Book.objects.get(name='GO', price=77)
# Q 查询Price=87 或 name=GO的书
Book.objects.filter(Q(price=87)|Q(name='GO'))
# ~Q Price=97 或 name不等于GO的书
Book.objects.filter(Q(price=87)|~Q(name='GO'))
# Q查询放在关键字查询前: Name含有“o”,价格大于100
Book.objects.filter(
Q(name__contains='o'),
price__gt=100
)
# 将所有书的价格加10
Book.objects.all().update(price=F('price')+10)
惰性查询
ret = Book.objects.filter(id = 4)
当只有这语句时,并没有开始查询数据库。只生成QuerySet,所以它是惰性查询。
从刷新网页,后台日记没有打印SQL语句可知。且惰性查询只查询一次:
ret = Book.objects.filter(price=87)
for i in ret: # 这时才进行查询
print(i)
# 数据库只查询一次,第一次查询后django给ret已缓存查询结果
for i in ret: # 不再查询
print(i)
Book.objects.all().update(price=F('price')+10)
for i in ret: # 数据库被修改了,依然不会再查询
print(i)
QuerySet是具有cache的:当你遍历queryset时,所有匹配的记录会从数据库获取,然后转换成Django的model。这被成为执行(evaluation),这些model会保存在queryset内置的cache中,这样如果你再次遍历这个queryset,你不需要重复运行通用的查询。
exists:
ret = Book.objects.filter(id=1)
if ret: # 这个语句会对数据库进行“执行”evaluation
print('OK')
# 使用exists
if ret.exists(): # 不会进行evaluation,节省性能
print('OK')
生成器:iterator
当queryset非常巨大时,cache会成为问题。
处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的queryset可能会锁住系统 进程,让你的程序濒临崩溃。要避免在遍历数据的同时产生queryset cache,可以使用iterator()方法 来获取数据,iterator并不会产生缓存,处理完数据后就丢弃了,要想重新获取数据得重新拿到iterator(),如下所示。
objs = Book.objects.all().iterator()
# iterator()可以一次只从数据库获取少量数据,这样可以节省内存
for obj in objs:
print(obj.name)
# but, 再次遍历没有打印,因为迭代器已经在上一次遍历(next)到最后一次了,没得遍历了
for obj in objs:
print(obj.name)
# 注意:使用iterator()方法来防止生成cache,意味着遍历同一个queryset时会重复执行查询。
#所以使用iterator()的时候要当心,确保你的代码在操作一个大的queryset时没有重复执行查询。
总结:
queryset的cache是用于减少程序对数据库的查询,在通常的使用下会保证只有在需要的时候才会查询数据库。 使用exists()和iterator()方法可以优化程序对内存的使用。不过,由于它们并不会生成queryset cache,可能 会造成额外的数据库查询。
跨表查询的优化
select_related()
(1)简单使用
对于一对一字段(OneToOneField)和外键字段(ForeignKey),可以使用select_related 来对QuerySet进行优化,select_related底层就是链表操作
简单说,在对QuerySet使用select_related()函数后,Django会获取相应外键对应的对象,从而在之后需要的时候不必再查询数据库了,例如
res = Book.objects.all()
for obj in res:
print(obj.publish.name) # 每次执行该行代码都会触发新的sql执行,效率极低
"""
底层会先去book表里查出所有的书籍id
然后每次for循环都会拿着一个书籍的id去publish表里查到对应出版社的名字
"""
# 优化
res = Book.objects.select_related('publish')
for obj in res:
print(obj.publish.name)
"""
底层会先将Book与Publish对应的两张表连接成一张大表,然后一次性将大表的所有数据一次性封装给查询出来的对象
此时对象无论是点击book表的数据还是publish表的数据都无需再走额外的数据库查询了
"""
for obj in res:
print(obj.publish.city)
"""
第二次for循环直接使用上述缓存即可
"""
但需要注意的是:select_related()括号内只能放外键Foreignkey字段
因为一对多、一对一关系均使用ForeignKey,而多对多则不是,所以select_related不支持多对多关系,若想优化多对多的查询请看下一小节 (2)多外键查询
如果一个模型中存在多个ForeignKey字段
res = Book.objects.select_related("publish")
此时我们查询publish相关的时候,不会重复查询,如下
for obj in res:
print(obj.publish.name)
for obj in res:
print(obj.publish.city)
但如果我们查询的是Book内的其他ForeignKey字段,还是会重新查询
for obj in res:
print(obj.other_fk.attr)
# 解决方案就是
res = Book.objects.select_related("publish","other_fk")
或者使用django1.7之后支持的链式操作
res = Book.objects.select_related("publish").select_related("detail")
(2)深层查询
对于跨越了n张表的深层查询, 依然需要查询多次,例如下述代码依然需要重复查询两次
article=models.Article.objects.select_related("blog").get(nid=1)
print(article.blog.user.username) # 需要重复两次进行查询
这是因为第一次查询没有query到userInfo表,所以,修改如下:
article=models.Article.objects.select_related("blog__user").get(nid=1)
print(article.blog.user.username)
(3)总结
select_related主要针一对一和多对一关系进行优化。
select_related使用SQL的JOIN语句进行优化,通过减少SQL查询的次数来进行优化、提高性能。
可以通过可变长参数指定需要select_related的字段名。也可以通过使用双下划线“__”连接字段名来实现指定的递归查询。
没有指定的字段不会缓存,没有指定的深度不会缓存,如果要访问的话Django会再次进行SQL查询。
也可以通过depth参数指定递归的深度,Django会自动缓存指定深度内所有的字段。如果要访问指定深度外的字段,Django会再次进行SQL查询。
也接受无参数的调用,Django会尽可能深的递归查询所有的字段。但注意有Django递归的限制和性能的浪费。
Django >= 1.7,链式调用的select_related相当于使用可变长参数。Django < 1.7,链式调用会导致前边的select_related失效,只保留最后一个。
prefetch_related()
对于多对多字段(ManyToManyField)和一对多字段,可以使用prefetch_related()来进行优化,prefetch_related()的底层就是子查询,在使用时select_related与prefetch_related是没有差别的,但是底层是有差别的
针对一对多字段
# 效果与select_related()一样
res = Book.objects.prefetch_related('publish')
for obj in res:
print(obj.publish.name)
for obj in res:
print(obj.publish.city)
针对多对多字段
books = Book.objects.prefetch_related('authors') # 底层就是inner join
for book in books:
for author in book.authors.all():
print(book.name,author.name)
注意
链表与子查询的效率不一定谁高谁低
# 链表有可能遇到很多张表,连在一起,会在耗费很多时间在链表上
# 而子查询,每次都一张表,分多步运行,不需要链表,但步骤多了,有可能影响效率
通常情况下:
数据量少的话,建议用select_related
数据量比较多,建议用prefetch_related
only与defer
only
# 下例中,因为res是.all()得到的结果集合,所以obj.任意书籍对象自己的属性,均不再走数据库
res = Book.objects.all()
for obj in res:
print(obj.name) # 不走数据库查询
print(obj.price) # 不走数据库查询
#因为上面是.all()所以拿到的结果集必然数据量大,如果我们只想要书的名字,可以使用.only()
res = Book.objects.only('name')
for obj in res:
print(obj.name) # .name不走数据库 .其他字段会重新走数据库查询
defer
# defer与only刚好相反,除了defer指定的属性外,其他都不需要查数据库
res = Book.objects.defer('name')
for obj in res:
print(obj.price) # 除了.name之外,点出的属性都不需要走数据库
# print(res) # 打印res,会触发多条sql的执行来拿到所有的结果集,
#因为访问了除了name之外的属性
整体插入
创建对象时,尽可能使用bulk_create()来减少SQL查询的数量。例如:
Entry.objects.bulk_create([
Entry(headline="Python 3.0 Released"),
Entry(headline="Python 3.1 Planned")
])
...更优于:
Entry.objects.create(headline="Python 3.0 Released")
Entry.objects.create(headline="Python 3.1 Planned")
注意该方法有很多注意事项,所以确保它适用于你的情况。
这也可以用在ManyToManyFields中,所以:
my_band.members.add(me, my_friend)
...更优于:
my_band.members.add(me)
my_band.members.add(my_friend)
...其中Bands和Artists具有多对多关联。