Django - 模型层 - 查询操作 -2

跨越关系查询

Django提供了强大并且直观的方式解决跨越关联的查询. 它在后台自动执行包含JOIN的SQL语句.
要跨越某个关联, 只需使用关联的模型字段名, 并使用双下划线分割

# 返回所有Blog的name为'Beatles Blog'的Entry对象
>>> Entry.objects.filter(blog__name='Beatles Blog')
<QuerySet [<Entry: laowang blog>]>

F表达式

将模型的一个字段与同一个模型的另外一个字段进行比较, 使用Django提供的F表达式

# 查找comments数目多于pingbacks数目的Entry
>>> Entry.objects.filter(number_of_comments__gt=F('number_of_pingbacks'))
<QuerySet [<Entry: laowang blog>]>

Django支持对F()对象进行加、减、乘、除、求余以及幂运算等算术操作
例如查找comments数目比pingbacks两倍还要多的Entry

>>> Entry.objects.filter(number_of_comments__gt=F('number_of_pingbacks') * 2)

还可以在F()中使用双下划线来进行跨表查询
例如查询author的名字与blog名字相同的Entry

>>> Entry.objects.filter(authors__name=F('blog__name'))

pk

pk就是primary key的缩写, 通常一个模型的主键为’id’.

>>> Blog.objects.get(pk=1)
<Blog: Beatles Blog>

可以跨表操作

>>> Entry.objects.filter(blog__pk=1)

转义百分符号和下划线

在原生SQL语句中%符号有特殊的作用。Django自动转义了百分符号和下划线,可以和普通字符一样使用它们

>>> Entry.objects.filter(headline__contains='%')

缓存与查询集

  • 对于新创建的QuerySet, 它的缓存是空的. 当QuerySet第一次被提交后, 数据库执行实际的查询操作, Django会把查询的结果保存在QuerySet的缓存内. 随后的对于该QuerySet的提交将重用这缓存的数据
>>> print([e.headline for e in Entry.objects.all()])
>>> print([e.pub_date for e in Entry.objects.all()])

上面的例子会加倍数据库的负载.
为了避免上面的问题, 可以使用以下方式

>>> queryset = Entry.objects.all()
>>> print([p.headline for p in queryset]) # 提交查询
>>> print([p.pub_date for p in queryset]) # 重用查询缓存

什么时候不会被缓存

有一些操作不会缓存QuerySet , 例如切片和索引.
例如 :

>>> queryset = Entry.objects.all()
>>> print(queryset[5])  
>>> print(queryset[5])

上面的两个print都会执行实际的数据库查询.
遍历过整个QuerySet,后续的操作则会使用缓存

>>> queryset = Entry.objects.all()
>>> [entry for entry in queryset] # 查询数据库
>>> print(queryset[5]) # 使用缓存
>>> print(queryset[5]) # 使用缓存

Q对象

用于封装关键字参数的集合

from django.db.models import Q
Q(question__startswith='What')

可以使用&或者|~来组合Q对象

Q(question__startswith='who') | Q(question_startswith='What')

比较对象

比较两个模型实例, 只需要使用python提供的双等号比较符就可以, 在后台, 其实比较的是两个实例的主键的值

some_entry.id == other_entry.id

判断基于下面的逻辑 :

  • 同一模型下, 主键相等则实例相等
  • 实例等于自己本身
  • 代理模型的实例等于相同主键的父类实例

复制模型实例

将原实例的pk设置为None, 这会创建一个新的实例copy

>>> blog = Blog(name='My blog', tagline='Blogging is easy')
>>> blog.save()
>>> blog.pk = None
>>> blog.save()

在继承父类的时候, 必须同时将pk和id设为None

class ThemeBlog(Blog):
    theme = models.CharField(max_length=200)

django_blog = ThemeBlog(name='Django', tagline='Django is easy', theme='python')
django_blog.save()
django_blog.pk = None
django_blog.id = None
django_blog.save()

批量更新对象

只可以对普通字段和ForeignKey字段使用这个方法.
使用update() 方法可以批量为QuerySet中所有的对象进行更新操作

>>> Entry.objects.filter(pub_date__year=2021).update(headline='xiaoming blog')

如果要更新ForeignKey字段, 需设置新值为你想指向的新模型实例

>>> b = Blog.objects.get(pk=4)
>>> Entry.objects.all().update(blog=b)

关联对象的查询

一对多 (外键)

  • 正向查询 : 直接通过圆点加属性, 访问外键对象
>>> e = Entry.objects.get(id=1)
>>> e.blog
<Blog: xiaoming>
  • 如果一个外键字段设置有null=True属性, 可以通过给该字段赋值为None的方法移除外键值.
>>> e = Entry.objects.get(id=1)
>>> e.blog=None
>>> e.save()

正常来说保存就相当于执行SQL语句 : update blog_entry set blog_id = null; , 但是这里的id不允许为空, 所以保存后会报错

反向查询

如果一个模型有ForeignKey, 那么该ForeignKey所指向的外键模型的实例可以通过一个管理器 FOO_set 返回原模型的所有实例

>>> b = Blog.objects.get(id=4)
>>> b.entry_set.all()

可以在ForeignKey字段的定义中,通过设置related_name来重写FOO_set的名字

多对多

多对多关系的两端都会自动获得访问另一端的API

>>> e = Entry.objects.get(id=1)
>>> e.authors.all()
<QuerySet [<Author: joe>, <Author: John>, <Author: Paul>, <Author: George>

一对一

一对一关系可以通过模型的属性访问关联的模型

class EntryDetail(models.Model):
    entry = models.OneToOneField(Entry, on_delete=models.CASCADE)
    details = models.TextField()

ed = EntryDetail.objects.get(id=2)
ed.entry

可以给反向关联进行赋值

e.entrydetail = ed

关联对象的增删改

  • add(obj1, obj2, …):添加指定的模型对象到关联的对象集中。

  • create(**kwargs):创建一个新的对象,将它保存并放在关联的对象集中。返回新创建的对象。

  • remove(obj1, obj2, …):从关联的对象集中删除指定的模型对象。

  • clear():清空关联的对象集。

  • set([obj1,obj2…]):重置关联的对象集。

# 增加
>>> e = Entry.objects.get(id=1)
>>> b = Blog.objects.get(id=2)
>>> b.entry_set.add(e)

# 创建
>>> e = b.entry_set.create(
...     headline='Hello',
...     body_text='Hi',
...     number_of_comments=1,
...     number_of_pingbacks=1,
...     rating=1)

# 删除
>>> b = Blog.objects.get(id=1)
>>> e = Entry.objects.get(id=2)
>>> b.entry_set.remove(e) 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值