首页 > 其他分享 >十五、Django的ORM

十五、Django的ORM

时间:2024-02-11 21:22:19浏览次数:39  
标签:name objects author ORM Django Book 十五 查询 book

单表操作

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")

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,可能 会造成额外的数据库查询。

跨表查询的优化

(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失效,只保留最后一个。

对于多对多字段(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具有多对多关联。

QuerySet对象与查询优化

标签:name,objects,author,ORM,Django,Book,十五,查询,book
From: https://www.cnblogs.com/zhlforhe/p/18013541

相关文章

  • 十四、MySQL与Django之Model基础
    数据库Django默认支持sqlite、mysql、oracel、postgresql等数据库1、sqlitedjango默认使用sqlite数据库Django.db.backends.sqlite3DATABASES={'default':{'ENGINE':'django.db.backends.sqlite3','NAME':os.path.join(BA......
  • 十三、Django Admin
    DjangoAdmin是django项目数据库的一个后台管理工具一、注册models项目中,应用创建之后,应用文件夹就有一个admin.py文件。在admin.py中注册需要在admin后台管理的modelsfromdjango.contribimportadminfromwebimportmodelsadmin.site.register(models.Host)admin.site.......
  • 十二、Django视图函数和模版相关
    视图相关HTTPRequest对象:属性:path函数:get_full_path()HTTPResponse对象:render()render_to_response()locals():局部变量redirect()重定向例子:用户登录成功后跳转deflogin(request):...#判断登录成功后,跳转到indexreturnrender(request,"index.html"......
  • 十、Django静态文件
    django网络相应基础:1、配置url路由在urls.py中urlpatterns添加路径fromdjango.conf.urlsimporturlfromdjango.contribimportadminfromapp01importviewsurlpatterns=[url(r'^admin/',admin.site.urls),url(r'^index.html$',views.index),......
  • 十一、Django url控制系统
    urlpatterns=[url(正则表达式,views视图函数,参数,别名),]匹配1、完全匹配urlpatterns=[url(r'^admin/',admin.site.urls),url(r'^showtime/',views.showtime),url(r'^blog/',include('blog.urls')),url(r'^logi......
  • 九、Django环境搭建(基于anaconda环境)
    Django特点:具有完整的封装,开发者可以高效率的开发项目,Django将大部分的功能进行了封装,开发者只需要调用即可,如此,大大的缩短了开发的时间,同时也因为太多封装导致开发灵活度和项目的运行效率不足。通常在工作中,开发者可以使用Django快速的搭建项目,然后基于项目进行个别部分的效率优......
  • 小白入门clang_format
    如果没时间研究折腾,可以接受这样↓的风格 那么安装这个↓,默认在代码界面按alt+shift+f即可 ========================================================== 如果你想稍微个性化一点在上面的基础上,做以下配置 什么意思自己看上面↑截图的文字介绍 如何回到代码界面按a......
  • Ubuntu服务器使用 Daphne + Nginx + supervisor部署Django项目
    视频:https://www.bilibili.com/video/BV1e6421G7uM/?vd_source=36191bed2c30378060ff2efe6831b331Django从3.0版开始加入对ASGI的支持,使Django开始具有异步功能。截止目前的5.0版,对异步支持逐步也越来越好,相信在未来的版本中异步将会支持的更加完善。所以说,我们也需要适时的......
  • dremio 的InformationSchemaCatalog 服务三
    以前简单写过一些关于dremio的InformationSchemaCatalog,也说过dremio为了方便提供标准的INFORMATION_SCHEMA自己开发了存储扩展,以下是关于存储扩展的创建以及刷新说明创建创建是在CatalogService中处理的,具体的实现是CatalogServiceImpl参考处理if(roles.conta......
  • CF741E Arpa’s abnormal DNA and Mehrdad’s deep interest
    我永远喜欢数据结构。感觉\(\color{maroon}*3400\)虚高,但是第一眼不会做/ng。太菜了。CF洛谷给出两个字符串\(s,t\),记\(r_i\)表示在\(s_i\)和\(s_{i+1}\)插入\(t\)得到的字符串。若\(i=0\)表示在开头插入,若\(i=|s|\)表示在结尾插入。形式化的,\(r_i=\ov......