文章目录
原文:https://docs.djangoproject.com/en/2.0/ref/models/querysets/
相关文章:https://blog.youkuaiyun.com/youyou1543724847/article/details/86425408
1. when QuerySet evaluated
在创建QuerySet后,对QuerySet 进行filter、slice、传递操作,但是这些操作并不会立即导致相关SQL的执行。只有当对Query Set进行evaluate操作时,才会导致操作落库。
什么操作导致Query Set evaluated呢?
- 迭代:当你对Query Set进行迭代方法时
for e in Entry.objects.all():
print(e.headline)
PS:当你只需要确认Query Set中是否存储数据时(而不需要知道具体数据内容,请使用exist函数(该种方式比迭代访问更快),例如:
if some_queryset.filter(pk=entry.pk).exists():
print("Entry contained in queryset")
- slicing : QuerySet支持slicing操作。但是对一个没有计算的Query Set 进行slicing操作还是返回一个未切片的Query Set。但是如果你在切片访问时,使用了step参数,那么Django会执行数据库操作,返回一个list对象。对一个被评估了的QuerySet进行slicing操作也是返回一个list。
注意:对一个没有评估计算的queryset 进行slicing 操作还是返回没有评估计算的queryset。如果之后通过slicing 操作对数据进行修改,则是不允许的。 - picking/caching操作
- 执行repr()操作、len(),count()操作
- list()操作:强制执行评估计算,例如:
entry_list = list(Entry.objects.all())
- bool测试操作,如 bool,or,and 或是if 语句(如果query set 中包含一条数据,则结果为真)
if Entry.objects.filter(headline="Test"):
print("There is at least one Entry with the headline Test")
1.1. picking Query Set (对QuerySet 对象进行序列化)
当你对一个Query set对象进行序列化时,会导致queryset 操作落库,且查询出来的数据都加载到内存中。通常来说,序列化是进行cache 操作的前序操作。另外当你对一个Query Set 进行反序列化时,你可以获取到对该QuerySet序列化时数据库的相关状态(例如,在对Query set序列化时,model中含有3条符合条件的数据,之后,model进行了其他的增删改查操作。但是当你反序列化后,得到的Query Set对象还是和之前的3条数据绑定了)。
如果你只想对Queryset 中的必要信息进行序列化(方便之后能重新构建出一个新的Queryset,而不需要绑定的数据),则可以只对Query Set对象的query属性进行处理(You can then recreate the original QuerySet (without any results loaded) 。
例如:
>>> import pickle
>>> query = pickle.loads(s) # Assuming 's' is the pickled string.
>>> qs = MyModel.objects.all()
>>> qs.query = query # Restore the original 'query'.
2.QuerySet API
特点:
- 支持filter chain;
- 常用的对象属性包括:ordered,db;
- 支持的API主要分为两种:返回一个新的Query Set;返回其他数据类型的;
下面主要对QuerySet API进行简单描述。
2.1 返回新的QuerySet 的API
API | 参数说明 | 含有 |
---|---|---|
filter(**kwargs) | kwargs 为 域查询参数(field lookup) | 返回包含满足条件对象的QuerySet 对象 。当存储多个查询参数时,参数通过and 相连 |
exclude(**kwargs) | kwargs 为 域查询参数(field lookup) | 返回包含 不满足条件对象的QuerySet 对象。当存储多个查询参数时,参数通过and 相连,然后在外面用一个not 扩起来,形如: where not ( A and B and C) |
annotate(*args, **kwargs) | 数据库聚合操作(sun,average等),详细信息见:https://docs.djangoproject.com/en/2.0/ref/models/querysets/#id5 | |
order_by(*fields) | 用于排序的字段 | 通常来说,在model meta中指定了排序的信息。但是可以通过order_by 对每个QuerySet进行排序定制 。例如:Entry.objects.filter(pub_date__year=2005).order_by('-pub_date', 'headline') 。其中,字段前面的负号指示递减排序。递增是默认选项。如果要随机排序,使用?号。同时,order_by 支持多表的排序 |
reverse() | 对结果进行逆序。两次reverse之后,结果和原始的顺序一致 | |
distinct(*fields) | 字段名 | 去除重复的行 |
values(*fields, **expressions) | 返回一个Query Set,该Queryset evaluate 之后,返回的是一个字典(一般来说,QuerySet evaluate返回的是model 实例对象),该方法用于抽取model 记录中的部分字段,优点就是减少了构建python model 实例的花销. | |
values_list(*fields, flat=False, named=False) | 和values类似,但是Query Set中包含的是满足条件的元组列表。但flat=true时,直接返回Query Set,该queryset表示满足条件的元素列表 | |
dates(field, kind, order=‘ASC’) | ||
datetimes(field_name, kind, order=‘ASC’, tzinfo=None) | ||
none() | ||
all() | 返回一个当前QuerySet中一个 copy。当对该QuerySet进行了evaluate后,结果会在内存中缓存。当在evaluate后,数据库中的数据更新了,则可以再次调用QuerySe.all方法,更新缓存 | |
union(*other_qs, all=False) | 合并多个Query Set,即SQL中的union操作 | |
dintersection(*other_qs) | ||
difference(*other_qs) | 多个Query set进行比较,返回只存在当前Query Set中的元素 | |
select_related(*fields) | 在查询本model的满足条件的数据记录时,也会将相关的(其他model 表)的数据查询出来。并且,会将查询进行合并,减少SQL执行次数。扩展查询主要针对Foreign key 和 OneToOneField | |
prefetch_related(*lookups) | 返回一个QuerySet,该QuerySet会一次性的获取相关的对象信息(非一条SQL),但是多条SQL是一次性执行的。和select_related功能类似,但是实现策略不同。select_related是通过join语句实现的,因此,能在一条SQL中查询出所有相关的数据,缺点就是如果join的数据表数据很大,则join效率很低。prefetch_select 是通过每次搜索一条关系,使用Python自己做“join” | |
extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None) | 描述复杂的sql 语句。old API,尽量不要使用。 | |
defer(*fields) | ||
defer(*fields) | 当model数据项过多时,且你并不需要这个所有的字段,使用defer进行刷选,去掉fields指定的字段。当某些字段没有获取到后,以后需要访问时,会再次触发新的SQL语句 | |
only(*fields) | 和defer类似,但是fields 指定是需要留下的(返回的是一个Query Set,该Query Set表示满足条件的model实例列表,但是实例中的数据不全,有的字段没有填充获取 | |
using(alias) | 指定QuerySet对应的数据库 | |
select_for_update(nowait=False, skip_locked=False, of=()) | 返回一个Query Set,该Query Set 会锁定数据库中的相关的行,直到事务完成 | |
raw(raw_query, params=None, translations=None) | 用于执行Raw SQL语句,详情见:https://docs.djangoproject.com/en/2.0/topics/db/sql/ |
举例:
操作如下;
In [50]: cs=q.choice_set.all()
In [60]: cs_dict=cs.values('choice_text','votes')
#返回的是一个QuerySet ,但是QuerySet中的每条记录都是一个dict 普通对象。如;{'choice_text': 'not null', 'votes': 0}
In [61]: cs_dict
Out[61]: <QuerySet [{'choice_text': 'not null', 'votes': 0}, {'choice_text': 'the sky', 'votes': 0}, {'choice_text': 'just hacking again', 'votes': 0}, {'choice_text': 'test', 'votes': 0}, {'choice_text': 'test bulk', 'votes': 1}, {'choice_text': 'create method', 'votes': 2}, {'choice_text': 'normal create', 'votes': 1}]>
In [58]: cs_list=cs.values_list('choice_text','votes')
#返回的是一个QuerySet ,但是QuerySet中的每条记录都是普通的元组
In [59]: cs_list
Out[59]: <QuerySet [('not null', 0), ('the sky', 0), ('just hacking again', 0), ('test', 0), ('test bulk', 1), ('create method', 2), ('normal create', 1)]>
#返回的是一个QuerySet,QuerySet中的每个元素都是一个model对象,只是这个对象的某些field没有填充。当获取这些没有填充的字段时,会触发SQL查询。
In [62]: cs_model=cs.only('choice_text')
In [63]: cs_model
Out[63]: <QuerySet [<Choice: Choice:Question:what's new?,not null>, <Choice: Choice:Question:what's new?,the sky>, <Choice: Choice:Question:what's new?,just hacking again>, <Choice: Choice:Question:what's new?,test>, <Choice: Choice:Question:what's new?,test bulk>, <Choice: Choice:Question:what's new?,create method>, <Choice: Choice:Question:what's new?,normal create>]>
模型定义如下:
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date pulbished')
def was_published_recently(self):
return self.pub_date >= timezone.now()
def __str__(self):
return "Question:%s" % self.question_text
class Choice(models.Model):
question = models.ForeignKey(Question,on_delete=models.CASCADE, null=True)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
def __str__(self):
return "Choice:%s,%s" % (str(self.question), self.choice_text)
2.1.1 API示例
- values(*fields, **expressions)
# This list contains a Blog object.
>>> Blog.objects.filter(name__startswith='Beatles')
<QuerySet [<Blog: Beatles Blog>]>
# This list contains a dictionary.
>>> Blog.objects.filter(name__startswith='Beatles').values()
<QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>
- values_list(*fields, flat=False, named=False)
>>> Entry.objects.values_list('id').order_by('id')
<QuerySet[(1,), (2,), (3,), ...]>
>>> Entry.objects.values_list('id', flat=True).order_by('id')
<QuerySet [1, 2, 3, ...]>
- select_related(*fields)
#普通的查询
# Hits the database.
e = Entry.objects.get(id=5)
# Hits the database again to get the related Blog object.
b = e.blog
# select_related
# Hits the database.
e = Entry.objects.select_related('blog').get(id=5)
# Doesn't hit the database, because e.blog has been prepopulated
# in the previous query.
b = e.blog
- only 与values的区别:
见下面示例:
假设Blabla模型含有4个字段:field1,field2,field3,field4。
执行如下语句:
Blabla.objects.only('field1', 'field2', 'field3')[0].field4
#执行结果:通过一个新的SQL查询,获取field4的值。
Blabla.objects.values('field1', 'field2', 'field3')[0].field4
#执行结果:抛出异常。AttributeError: 'dict' object has no attribute 'field4'
2.1.2 关于Q对象
class Q[source]
A Q() object, like an F object, encapsulates a SQL expression in a Python object that can be used in database-related operations.
In general, Q() objects make it possible to define and reuse conditions. This permits the construction of complex database queries using | (OR) and & (AND) operators; in particular, it is not otherwise possible to use OR in QuerySets.
https://docs.djangoproject.com/en/2.0/ref/models/expressions/#django.db.models.F
2.2 返回值不是QuerySet的API
The following QuerySet methods evaluate the QuerySet and return something other than a QuerySet.These methods do not use a cache (see Caching and QuerySets). Rather, they query the database each time they’re called.
(每个Query Set 对象都有对应的cache区域。每个新建的QuerySet的cache 区域都是空的。当Query Set 被首次evaluate时,数据库操作落盘,Django会将结果放到该QuerySet的cache中,以后重用数据,例如迭代。另外,如果对QuerySet不是完整的evaluate,例如进行slice或是index,则结果不会进行缓存)
-
get(**kwargs): 返回满足条件的数据。当多条数据或没有数据时,都会抛错。kwargs为filed 查询参数。
-
create(**kwargs):创建对象并将对象保存到数据库中;
p = Person.objects.create(first_name="Bruce", last_name="Springsteen")
- get_or_create(defaults=None, **kwargs):查询或是创建对象。使用方式:
obj, created = Person.objects.get_or_create(
first_name='John',
last_name='Lennon',
defaults={'birthday': date(1940, 10, 9)},
)
等同于如下代码
try:
obj = Person.objects.get(first_name='John', last_name='Lennon')
except Person.DoesNotExist:
obj = Person(first_name='John', last_name='Lennon', birthday=date(1940, 10, 9))
obj.save()
- update_or_create(defaults=None, **kwargs):
- bulk_create(objs, batch_size=None): 批量插入
- count(): 对结果计数
- in_bulk(id_list=None, field_name=‘pk’):field_name为model的字段名字。id_list 为一个值列表(field_name的值,即id_list 为值列表,field_name指定这些值是那些字段的值。默认情况下,field_name为主键)如果没有指定id_list,则返回Query Set中的所有的记录。该函数返回值是一个字典,字典的key 为id_list中的值,value为field_name 字段等于id_list 中的值的对象。
>>> Blog.objects.in_bulk([1])
{1: <Blog: Beatles Blog>}
>>> Blog.objects.in_bulk([1, 2])
{1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>}
>>> Blog.objects.in_bulk([])
{}
>>> Blog.objects.in_bulk()
{1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>, 3: <Blog: Django Weblog>}
>>> Blog.objects.in_bulk(['beatles_blog'], field_name='slug')
{'beatles_blog': <Blog: Beatles Blog>}
- iterator()
- With server-side cursors
- Without server-side cursors
- latest(*fields):取最近的/最新的一个对象。
- earliest(*fields):类似于latest
- first()
- last()
- aggregate()
- exists()
- update()
- delete()
- as_manager()
2.3 Field 查询参数
https://docs.djangoproject.com/en/2.0/topics/db/queries/#lookups-that-span-relationships
Filed 查询参数格式形如:field__lookuptype=value。支持的lookuptype包括:
exact,iexact,contains,icontains,in,gt,gte,lt
lte,startswith,istartswith,endswith,iendswith,range,date,year,month,day,week,week_day,quarter,time,hour,minute,second,isnull,regex,iregex
注意:在字段查询中,field 必须是model 中定义的field。但是,如果某个字段是外建关联字段,则可以使用_id
。
也可以通过field lookup 进行多表之间的联合查询。
查询支持正向查询(本model 中包含关联关系字段)和反向查询(本model 中不包含关联关系字段)。
正向查询:
格式:‘本model的字段名’__'关联model中的字段名’
(注意:上面是双下划线)
另外,可以支持多级关联。多级关联时,直接在后面叠加。
例如:
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def __str__(self):
return self.name
class Author(models.Model):
name = models.CharField(max_length=200)
email = models.EmailField()
def __str__(self):
return self.name
class Entry(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateField()
mod_date = models.DateField()
authors = models.ManyToManyField(Author)
n_comments = models.IntegerField()
n_pingbacks = models.IntegerField()
rating = models.IntegerField()
def __str__(self):
return self.headline
查询关联blog的名字为‘Beatles blog’的entry:
Entry.objects.filter(blog__name='Beatles Blog')
反向查询:
格式:’被关联的model的名称的小写‘__'被关联的model的field字段‘
例如:查询至少关联了一条headline 为“Lennon”的entry的所有blog。
Blog.objects.filter(entry__headline__contains='Lennon')
2.4 聚合函数
所有的聚合一般都有如下参数:
- expression:一个表示model 字段的字符串或者是一个Query表达式
- output_field:可选参数,表示返回值所表示的model 字段;
- filter: 可选的Q对象,用于缩小聚合的行;
- **extra:附加参数
支持的聚合函数包括:
- Avg(expression, output_field=FloatField(), filter=None, **extra)
- Count(expression, distinct=False, filter=None, **extra)
- Max(expression, output_field=None, filter=None, **extra)
- Min(expression, output_field=None, filter=None, **extra)
- StdDev(expression, sample=False, filter=None, **extra)
- Sum(expression, output_field=None, filter=None, **extra)
- Variance(expression, sample=False, filter=None, **extra)
3. Query-related tools
3.1 Q() objects
Q对象用于封装一个SQL表达式到一个Python 对象中。通过Q对象,可以构造一个的条件,如多个条件的or 和and。
3.2 Prefetch() objects
Prefetch(lookup, queryset=None, to_attr=None):用于控制prefetch_related()的执行。
3.3 prefetch_related_objects()
prefetch_related_objects(model_instances, *related_lookups)
3.4 FilteredRelation() objects
FilteredRelation(relation_name, *, condition=Q()):用于在annotate 函数中,当执行join语句时,构建on 子句。