Django框架queryset常用操作

一、链式调用接口

1、all()

使用频率比较高,相当于SELECT * FROM table 语句,用于查询所有数据。

Model.objects.all()

2、filter()

使用频率比较高,根据条件过滤数据,常用的条件基本上字段等于、不等于、大于、小于。当然,还有其他的,比如能修改成产生LIKE查询的。

Model.objects.filter(content__contains="条件")

3、exclude()

与filter是相反的逻辑

Model.objects.exclude(content__contains="条件")

4、reverse()

将QuerySet中的结果倒叙排列

Model.objects.exclude(content__contains="条件").reverse()

5、distinct()

用来进行去重查询,产生SELECT DISTINCT这样的SQL查询

Model.objects.exclude(content__contains="条件").distinct()

6、none()

返回空的QuerySet

扫描二维码关注公众号,回复: 10491271 查看本文章

Model.objects.none()

二、非链式调用接口

1、get()

比如Post.objects.get(id=1)用于查询id为1的文章:如果存在,则直接返回对应的Post实例;如果不存在,则抛出DoesNotExist异常。所以一般情况下,要使用异常捕获处理

try:
    post = Post.objects.get(id=1)
except Post.DoesNotExist:
#做异常情况处理

2、create()

用来直接创建一个Model对象

post = Post.objects.create(title="一起学习")

3、get_or_create

根据条件查找,如果没查找到,就调用create创建。

# 原语句

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

# 用get_or_create改写后

obj, created = Person.objects.get_or_create(first_name='John', last_name='Lennon',
defaults={'birthday': date(1940, 10, 9)})

模型的字段数量较大的话,这种模式就变的非常不易用。

你可以通过ManyToManyField 属性和反向关联使用get_or_create()。在这种情况下,你应该限制查询在关联的上下文内部。如果你不一致地使用它,将可能导致完整性问题。

4、update_or_create

与get_or_create相同,只是用来做更新操作。

5、count

用于返回QuerySet有多少条记录,相当于SELECT COUNT(*) FROM table 。

cnt= Post.objects.create(title="一起学习").count()

6、latest

用于返回最新的一条记录,但要在Model的Meta中定义:get_latest_by= <用来排序的字段>。

Post.objects.all().latest()

7、earliest

同上,返回最早的一条记录。

Post.objects.all().earliest()

8、first

从当前QuerySet记录中获取第一条。

Post.objects.all().first()

9、last

同上,获取最后一条。

Post.objects.all().last()

10、exists

返回True或者False,在数据库层面执行SELECT (1) AS "a" FROM table LIMIT 1的查询,如果只是需要判断QuerySet是否有数据,用这个接口是最合适的方式。

不要用count或者len(queryset)这样的操作来判断是否存在。相反,如果可以预期接下来会用到QuerySet中的数据,可以考虑使用len(queryset)的方式来做判断,这样可以减少一次DB查询请求。

11、bulk_create

同create,用来批量创建记录。

querysetlist=[]
for i in resultlist:
    querysetlist.append(Account(name=i))        
Account.objects.bulk_create(querysetlist)

12、in_ bulk

批量查询,接收两个参数id_ list和filed_ name。可以通过Post.objects. in_ bulk([1, 2, 3])查询出id为1、2、3的数据,返回结果是字典类型,字典类型的key为查询条件。返回结果示例: {1: <Post 实例1>, 2: <Post实例2>,3:<Post实例3>}。

13、update

用来根据条件批量更新记录

Post.objects.filter(owner__name='123').update(title='测试更新')。

14、delete

同update,这个接口是用来根据条件批量删除记录。

Post.objects.filter(owner__name='123').delete()

15、values

当我们明确知道只需要返回某个字段的值,不需要Model实例时,用它,用法如下:

title_list = Post.objects.filter(category_id=1).values('title')

# 对于有关联关系的
Blog.objects.values('name', 'entry__headline')
[{'name': 'My blog', 'entry__headline': 'An entry'},
     {'name': 'My blog', 'entry__headline': 'Another entry'}, ...]

返回的结果包含dict的QuerySet,类似这样: <QuerySet [{'title' :xxx},]>

16、values_list

同values, 但是直接返回的是包含tuple的QuerySet:

titles_list = Post.objects.filter(category=1).values_list('title')

返回结果类似: <QuerySet[("标题",)]>

如果只是一个字段的话,可以通过增加flat=True参数,返回结果都是单独的值,而不是元组。

title_list = Post.objects.filter(category=1).values_list('title',flat=True)
 for title in title__list:
     print(title)

17、order_by(*fields)

Entry.objects.filter(pub_date__year=2005).order_by('-pub_date', 'headline')

要根据其他 model 字段排序,所用语法和跨关系查询的语法相同。就是说,用两个连续的下划线(__)连接关联 model 和 要排序的字段名称, 而且可以一直延伸。例如:

1

Entry.objects.order_by('blog__name', 'headline')

18、dates(field, kind, order='ASC')

返回一个 DateQuerySet ,就是提取 QuerySet 查询中所包含的日期,将其组成一个新的 datetime.date 对象的列表。

19、datetimes(field, kind, order='ASC')

返回一个 DateTimeQuerySet ,就是提取 QuerySet 查询中所包含的日期,将其组成一个新的 datetime.datetime 对象的列表。

三、进阶接口

除了上面介绍的常用接口外,还有其他用来提高性能的接口,在下面介绍。 在优化Django项目时,尤其要考虑这几种接口的用法。

1、defer

把不需要展示的字段做延迟加载。比如说,需要获取到文章中除正文外的其他字段,就可以通过posts = Post.objects.all() .defer('content'),这样拿到的记录中就不会包含content部分。但是当我们需要用到这个字段时,在使用时会去加载。代码:

posts = Post.objects.all().defer('content')
 for post in posts:  #此时会执行数据库查询
     print (post.content)  #此时会执行数据查询,获取到content

当不想加载某个过大的字段时(如text类型的字段),会使用defer,但是上面的演示代产生N+1的查询问题,在实际使用时千万要注意!

注意:上面的代码是个不太典型的 N+1查询的问题, 一般情况下 由外键查询产生的N+1问题比较多,即一条查询请求返回N条数据,当我们操作数据时,又会产生额外的请求。这就是N+1问题,所有的ORM框架都存在这样的问题。

2、only

同defer接口刚好相反, 如果只想获取到所有的title记录,就可以使用only,只获取title的内容,其他值在获取时会产生额外的查询。

这就是用来解决外键产生的N+1问题的方案。我们先来看看什么情况下会产生这个问题:

posts = Post.objects.all ()
for post in posts:  #产生数据库查询
    print (post.owner)  #产生额外的数据库查询

代码同上面类似,只是这里用的是owenr(是关联表)。它的解决方法就是用select_ related接口:

post = Post.objects.all() .select_related('category')
for post in posts: # 产生数据库查询,category数据也会一次性查询出来
    print (post.category)

当然,这个接口只能用来解决一对多的关联关系。对于多对多的关系,还得使用下面的接口。

针对多对多关系的数据,可以通过这个接口来避免N+1查询。比如,post和tag的关系可以通过这种方式来避免:

posts = Post.objects.all().prefetch_related('tag')
for post in posts:#产生两条查询语句,分别查询post和tag
    print(post.tag.al1())

5、extra()——实现复杂的where子句

函数原型:extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)

四、常用字段查询

  • exact

精确匹配。如果指定的值是 None,就会翻译成 SQL 中的 NULL (详情请查看 isnull )。

例如:

1

2

Entry.objects.get(id__exact=14)

Entry.objects.get(id__exact=None)

等价的 SQL:

1

2

SELECT ... WHERE id = 14;

SELECT ... WHERE id IS NULL;

  • iexact

忽略大小写的匹配。

例如:

Blog.objects.get(name__iexact='beatles blog')

Blog.objects.get(name__iexact=None)

等价于如下 SQL :

SELECT ... WHERE name ILIKE 'beatles blog';

SELECT ... WHERE name IS NULL;

  • contains

包含,用来进行相似查询。

  • icontains

同contains,只是忽略大小写。

  • exact

精确匹配。

  • iexact

同exact,忽略大小写。

  • __in

指定某个集合,比如Post.objects.filter(id__in=[1, 2, 3]) 相当于SELECT FROM table WHERE IN (1, 2, 3);。

  • gt

大于某个值。比如:Post.objects.filter(id__gt=1)

注意:是__gt

  • gte

大于等于某个值。

  • lt

小于某个值。

  • lte

小于等于某个值。

  • startswith

以某个字符串开头,与contains类似,只是会产生LIKE '<关键词>%'这样的SQL。

Entry.objects.filter(headline__startswith="What")

  • istartswith

同startswith, 忽略大小写。

Entry.objects.filter(headline__istartswith="What")

  • endswith

以某个字符串结尾。

Entry.objects.filter(headline__endswith="What")

  • iendswith

同endswith,忽略大小写。

Entry.objects.filter(headline__iendswith="What")

  • range

范围查询,多用于时间范围,如Post.objects.filter(created_time__range= ('2018-05-01','2018-06-01'))会产生这样的查询: SELECT .. . WHERE created_ time BETWEEN '2018-05-01' AND '2018-06-01' ;。

关于日期类的查询还有很多,比如date、year和month等,具体等需要时查文档即可。

这里你需要理解的是,Django之所以提供这么多的字段查询,其原因是通过ORM来操作数据库无法做到像SQL的条件查询那么灵活。

因此,这些查询条件都是用来匹配对应SQL语句的,这意味着,如果你知道某个查询在SQL中如何实现,可以对应来看Django提供的接口。

  •  isnull 

  • year,month, day

    对日期/时间字段精确匹配年分,年分用四位数字表示。

    例如:

    1

    Entry.objects.filter(pub_date__year=2005)

    等价于 SQL:

    1

    SELECT ... WHERE EXTRACT('year' FROM pub_date) = '2005';

  • search

  • regex

 

五、进阶查询

除了上面基础的查询语句外,Django还提供了其他封装,来满足更复杂的查询,比如 SELECT ... WHERE id = 1 OR id = 2 这样的查询,用上面的基础查询就无法满足。

1、F表达式

F表达式常用来执行数据库层面的计算,从而避免出现竞争状态。比如需要处理每篇文章的访问量,假设存在post.pv这样的字段,当有用户访问时,我们对其加1:

post = Post.objects.get(id=1)
post.pv = post.pv+1
post.save()

这在多线程的情况下会出现问题,其执行逻辑是先获取到当前的pv值,然后将其加1后赋值给post .pv.最后保存。

如果多个线程同时执行了post = Post.objects.get(id=1),那么每个线程里的post .pv值都是一样的, 执行完加1和保存之后,相当于只执行了一个加1,而不是多个。

这时通过F表达式就可以方便地解决这个问题:

from ajango.ab. models import F
post = Post.objects.get(id=1)
post.pv = F('pv') + 1
post.save():

这种方式最终会产生类似这样的SQL语句: UPDATE table SET pv = pv +1 WHERE ID = 1。 它在数据库层面执行原子性操作。

2、Q表达式

Q表达式就是用来解决前面提到的那个OR查询的,可以这么用:

from django.db.mode1s import Q
Post.objects.filter(Q(id=1) | Q(id=2))

或者进行AND查询:

Post.objects.filter(Q(id=1) & Q(id=2))

3、Count

用来做聚合查询,比如想要得到某个分类下有多少篇文章,简单的做法就是:

category = Category.objects.get(id=1)
posts_count = category.post_set.count()

但是如果想要把这个结果放到category上呢?通过category.post_count可以访问到:

from django.db.models import Count
categories = Category.objects.annotate(posts_count=Count('post'))
print(categories[0].posts_count)

这相当于给category动态增加了属性post_count,而这个属性的值来源于Count('post'),最后可以用int取整。

4、Sum

同Count类似,只是它是用来做合计的。比如想要统计所有数据字段的总和,可以这么做:

from django.db.models import Sum
Post.objects.all().aggregate(a=Sum('字段'))
#输出类似结果:{'a':487}为字典

python中对字典中键值对的获取:

for i in book:
    print(i)#键的获取
    print(book[i])#值的获取

上面演示了QuerySet的annotate和aggregate的用法,其中前者用来給QuerySet结果増加属性,后者只用来直接计算结果,这些聚合表达式都可以与它们结合使用。

除了Count和Sum外,还有Avg、Min和Max等表达式,均用来满足我们对SQL査洵的需求。

——————————————————————————————————

参考文档:https://www.cnblogs.com/JetpropelledSnake/p/9276763.html

https://www.cnblogs.com/JetpropelledSnake/p/9276457.html

发布了173 篇原创文章 · 获赞 326 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/William0318/article/details/104711159
今日推荐