Django模型(2)

1. 执行原始SQL语句

Django提供了两种执行原始SQL语句的方法 :第一种是使用Manage.row()方法,但是使用Manage.row()方法只能使用原生的SQL查询不能执行删除、更新、增加操作,第二种是通过django.db.connection 连接数据库。再调用 connection.cursor() 获取光标对象。然后,调用 cursor.execute(sql, [params]) 执行SQL以及执行 cursor.fetchone()cursor.fetchall() 返回结果。

1.1 Manage.row()执行原生SQL查询

使用Manage.row()方法执行原始SQL查询,返回结果是 django.db.models.query.RawQuerySet 对象实例。这个 RawQuerySet 对象实例可以像普通的 QuerySet 一样。

举一个例子来说明。假设你有以下模型:

class Person(models.Model):
    first_name = models.CharField(max_length=20)
    last_name = models.CharField(max_length=20)
    birth_date = models.DateField(max_length=20)

然后可以执行自定义SQL,如下所示:

>>> for p in Person.objects.raw('SELECT * FROM myapp_person'):
...     print(p)python
John Smith
Jane Jones

当然,这个操作其实和Person.objects.all()是完全一样 , 但是, raw() 方法有很多其他的功能选择,使它变得非常强大。

1.1.1 索引查找

raw() 支持索引操作,所有如果只需要第一个查询结果,则可以这样编写代码:

>>> first_person = Person.objects.raw('SELECT * FROM myapp_person')[0]

但是,索引和切片不是在数据库级别执行的。如果你有大量的 Person 对象,在SQL级别限制查询更加有效:

>>> first_person = Person.objects.raw('SELECT * FROM myapp_person LIMIT 1')[0]

1.1.2 惰性模型字段

raw() 方法查询结果的字段是可以省略的:

>>> people = Person.objects.raw('SELECT id, first_name FROM myapp_person')

这个 Person 此查询返回的对象将是惰性的模型实例,这意味着查询中省略的字段将按需加载。例如::

>>> for p in Person.objects.raw('SELECT id, first_name FROM myapp_person'):
...     print(p.first_name, 
...           p.last_name) 
...
John Smith
Jane Jones

从表面上看,这个查询只查询到了idfirst_name。但是,实际上发出了3个查询。只有名字是由raw()查询检索的——最后两个名字在打印时都是按需检索的。

只有一个字段是不能忽略的-主键字段。Django使用主键来标识模型实例,因此它必须始终包含在原始查询中。安 InvalidQuery 如果忘记包含主键,将引发异常。

1.1.3 支持格式化传参

如果需要执行参数化查询,可以使用params参数个raw()方法传递参数

    lname = 'Doe'
    Person.objects.raw('SELECT * FROM myapp_person WHERE last_name = %s', [lname])

params 可以是参数列表或字典。你可以使用 %s 列表查询字符串中的占位符,或 %(key)s 字典的占位符。

1.2 直接执行自定义SQL

直接执行自定义SQL是通过django.db.connection 连接数据库。再调用 connection.cursor() 获取光标对象。然后,调用 cursor.execute(sql, [params]) 执行SQL以及执行 cursor.fetchone()cursor.fetchall() 返回结果。它的DB操作和Python DB API下规范下cursor对象常用接口是一样的(类似于pymysql操作mysql数据库),它比raw()更加强大,可以执行任意原生SQL语句。

# 使用django封装好的connection对象,会自动读取settings.py中数据库的配置信息
from django.db import connection

# 获取游标对象
cursor = connection.cursor()
# 拿到游标对象后执行sql语句
cursor.execute("select * from book")
# 获取所有的数据
rows = cursor.fetchall()
# 遍历查询到的数据
for row in rows:
    print(row)

2. 模型类的增删改查操作

2.1添加数据

模型类添加数据到数据库只需要实例化模型类对象,在实例化的过程中传递需要参数(需要添加的数据值),然后在实例化模型类之后,再调用模型的save方法,这样Django会自动的将这个模型转换成sql语句,然后存储到数据库中。示例代码如下:

class Book(models.Model):
    name = models.CharField(max_length=20,null=False)
    desc = models.CharField(max_length=100,name='description',db_column="description1")
    pub_date = models.DateTimeField(auto_now_add=True)

book = Book(name='三国演义',desc='三国英雄!')
book.save()

在没有调用save方法之前,后台是不会执行insert(插入)SQL语句, save() 方法是没有返回值的

Django提供了一个更加方便的方法create()方法,使用create()方法添加数据可以省略save步骤(实质上create()方法源码里帮我们调用了save方法):

book = Book.objects.create(name='三国演义',desc='三国英雄!')

objects属性,Model.objects实际上是一个 Manager对象实例, Django规定在模型类中至少有一个默认值 Manager,如果你不添加自己的 Manager,django将添加一个属性objects包含默认值Manager实例。如果你添加你自己的Manager实例属性,不显示默认属性。 Manager对象实例主要用于模型类的查询操作,比如我们修改默认属性为book_obj(实际开发一般不修改):

class Book(models.Model):
    name = models.CharField(max_length=20,null=False)
    desc = models.CharField(max_length=100,name='description',db_column="description1")
    pub_date = models.DateTimeField(auto_now_add=True)
    book_obj = models.Manager()

此时使用create()方法添加数据就变成了:

# 需要使用自定义的Manager属性
book = Book.book_obj.create(name='三国演义',desc='三国英雄!')

2.1.1 ForeignKey 和 ManyToManyField模型添加数据

更新一个 ForeignKey 字段的工作方式与保存普通字段完全相同——只需将正确类型的对象分配给相关字段即可。

class User(models.Model):
    username = models.CharField(max_length=20)
    password = models.CharField(max_length=100)


class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()

    author = models.ForeignKey("User",on_delete=models.CASCADE) 

以上使用ForeignKey来定义模型之间的关系。即在article的实例中可以通过author属性来操作对应的User模型。这样使用起来非常的方便。示例代码如下:

article = Article(title='abc',content='123')
author = User(username='张三',password='111111')
article.author = author
article.save()

# 修改article.author上的值
article.author.username = '李四'
article.save()

更新一个ManyToManyField工作方式有点不同——使用 add() 方法来向关系中添加记录:

class Article(models.Model):
     title = models.CharField(max_length=100)
     content = models.TextField()
     tags = models.ManyToManyField("Tag",related_name="articles")

 class Tag(models.Model):
     name = models.CharField(max_length=50)

以上使用ManyToManyField来定义模型之间的关系:

tag = Tag.objects.filter(name='娱乐').first()
article = Article(title='python入门',content='xxxxxxxxxxxxxxxxxx....')
article.tags.add(tag)

article必须是模型类对象,不能是QuerySet对象,被添加的对象(tag)也必须是模型类对象。

2.2 查询数据

Django使用Manager对象实例来执行模型类的查询操作,模型类中至少有一个默认值 Manager,模型类名.objects就是模型类默认的Manager对象实例, 查询一般就是使用filterexclude以及get三个方法来实现。我们可以在调用这些方法的时候传递不同的参数来实现查询需求。在ORM层面,这些查询条件都是使用field+__+condition的方式来使用的。

查找所有数据:

要查找Book这个模型对应的表下的所有数据。那么示例代码如下:

books = Book.objects.all()

以上将返回Book模型下的所有数据。

数据过滤:

在查找数据的时候,有时候需要对一些数据进行过滤。那么这时候需要调用objectsfilter方法。实例代码如下:

books = Book.objects.filter(name='三国演义')
> [<Book:三国演义>]

# 多个条件
books = Book.objects.filter(name='三国演义',desc='test')

调用filter,会将所有满足条件的模型对象都返回。

获取单个对象:

使用filter返回的是所有满足条件的结果集。有时候如果只需要返回第一个满足条件的对象。那么可以使用get方法。示例代码如下:

book = Book.objects.get(name='三国演义')
> <Book:三国演义>

当然,如果没有找到满足条件的对象,那么就会抛出一个异常。而filter在没有找到满足条件的数据的时候,是返回一个空的列表。

数据排序:

在之前的例子中,数据都是无序的。如果你想在查找数据的时候使用某个字段来进行排序,那么可以使用order_by方法来实现。示例代码如下:

books = Book.objects.order_by("pub_date")

以上代码在提取所有书籍的数据的时候,将会使用pub_date从小到大进行排序。如果想要进行倒序排序,那么可以在pub_date前面加一个负号。实例代码如下:

books = Book.objects.order_by("-pub_date")

2.2.1 查询条件

字段查找就是如何指定SQL的主要部分的WHERE条件。下面是常用的查询条件的使用:

  1. exact:使用精确的=进行查找。如果提供的是一个None,那么在SQL层面就是被解释为NULL。示例代码如下:
article = Article.objects.get(id__exact=14)
article = Article.objects.get(id__exact=None)

​ 以上的两个查找在翻译为SQL语句为如下:

select ... from article where id=14;
select ... from article where id IS NULL;

  1. iexact:使用like进行查找。示例代码如下:
article = Article.objects.filter(title__iexact='hello world')

​ 那么以上的查询就等价于以下的SQL语句:

select ... from article where title like 'hello world';

​ 注意上面这个sql语句,因为在MySQL中,没有一个叫做ilike的。所以exactiexact的区别实际上就是 LIKE=的区别,在大部分collation=utf8_general_ci情况下都是一样的(collation是用来对字符串比较的)。

  1. contains:大小写敏感,判断某个字段是否包含了某个数据。示例代码如下:
articles = Article.objects.filter(title__contains='hello')

​ 在翻译成SQL语句为如下:

select ... where title like binary '%hello%';

​ 要注意的是,在使用contains的时候,翻译成的sql语句左右两边是有百分号的,意味着使用的是模糊查询。 而exact翻译成sql语句左右两边是没有百分号的,意味着使用的是精确的查询。

  1. icontains:大小写不敏感的匹配查询。示例代码如下:
articles = Article.objects.filter(title__icontains='hello')

​ 在翻译成SQL语句为如下:

select ... where title like '%hello%';

  1. in:提取那些给定的field的值是否在给定的容器中。容器可以为listtuple或者任何一个可以迭代的对象,包括QuerySet对象。示例代码如下:
articles = Article.objects.filter(id__in=[1,2,3])

​ 以上代码在翻译成SQL语句为如下:

select ... where id in (1,3,4)

​ 当然也可以传递一个QuerySet对象进去。示例代码如下:

inner_qs = Article.objects.filter(title__contains='hello')
categories = Category.objects.filter(article__in=inner_qs)

​ 以上代码的意思是获取那些文章标题包含hello的所有分类。
​ 将翻译成以下SQL语句,示例代码如下:

select ...from category where article.id in (select id from article where title like '%hello%');

  1. gt:某个field的值要大于给定的值。示例代码如下:
articles = Article.objects.filter(id__gt=4)

​ 以上代码的意思是将所有id大于4的文章全部都找出来。
​ 将翻译成以下SQL语句:

select ... where id > 4;

  1. gte:类似于gt,是大于等于。

  2. lt:类似于gt是小于。

  3. lte:类似于lt,是小于等于。

  4. startswith:判断某个字段的值是否是以某个值开始的。大小写敏感。示例代码如下:

articles = Article.objects.filter(title__startswith='hello')

​ 以上代码的意思是提取所有标题以hello字符串开头的文章。
​ 将翻译成以下SQL语句:

select ... where title like 'hello%'

  1. istartswith:类似于startswith,但是大小写是不敏感的。

  2. endswith:判断某个字段的值是否以某个值结束。大小写敏感。示例代码如下:

articles = Article.objects.filter(title__endswith='world')

​ 以上代码的意思是提取所有标题以world结尾的文章。
​ 将翻译成以下SQL语句:

select ... where title like '%world';

  1. iendswith:类似于endswith,只不过大小写不敏感。

  2. range:判断某个field的值是否在给定的区间中。示例代码如下:

from django.utils.timezone import make_aware
from datetime import datetime
start_date = make_aware(datetime(year=2018,month=1,day=1))
end_date = make_aware(datetime(year=2018,month=3,day=29,hour=16))
articles = Article.objects.filter(pub_date__range=(start_date,end_date))

​ 以上代码的意思是提取所有发布时间在2018/1/12018/12/12之间的文章。
​ 将翻译成以下的SQL语句:

select ... from article where pub_time between '2018-01-01' and '2018-12-12'。

需要注意的是,以上提取数据,不会包含最后一个值。也就是不会包含2018/12/12的文章。
而且另外一个重点,因为我们在settings.py中指定了USE_TZ=True,并且设置了TIME_ZONE='Asia/Shanghai',因此我们在提取数据的时候要使用django.utils.timezone.make_aware先将datetime.datetimenavie时间转换为aware时间。make_aware会将指定的时间转换为TIME_ZONE中指定的时区的时间。

  1. date:针对某些date或者datetime类型的字段。可以指定date的范围。并且这个时间过滤,还可以使用链式调用。示例代码如下:
articles = Article.objects.filter(pub_date__date=date(2018,3,29))

​ 以上代码的意思是查找时间为2018/3/29这一天发表的所有文章。
​ 将翻译成以下的sql语句:

select ... WHERE DATE(CONVERT_TZ(`front_article`.`pub_date`, 'UTC', 'Asia/Shanghai')) = 2018-03-29

注意,因为默认情况下MySQL的表中是没有存储时区相关的信息的。因此我们需要下载一些时区表的文件,然后添加到Mysql的配置路径中。如果你用的是windows操作系统。那么在http://dev.mysql.com/downloads/timezones.html下载timezone_2018d_posix.zip - POSIX standard。然后将下载下来的所有文件拷贝到C:\ProgramData\MySQL\MySQL Server 5.7\Data\mysql中,如果提示文件名重复,那么选择覆盖即可。
如果用的是linux或者mac系统,那么在命令行中执行以下命令:mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -D mysql -u root -p,然后输入密码,从系统中加载时区文件更新到mysql中。

  1. year:根据年份进行查找。示例代码如下:
articles = Article.objects.filter(pub_date__year=2018)
articles = Article.objects.filter(pub_date__year__gte=2017)

以上的代码在翻译成SQL语句为如下:

select ... where pub_date between '2018-01-01' and '2018-12-31';
select ... where pub_date >= '2017-01-01';

  1. month:同year,根据月份进行查找。

  2. day:同year,根据日期进行查找。

  3. week_day:同year,根据星期几进行查找。1表示星期天,7表示星期六,2-6代表的是星期一到星期五。

  4. time:根据时间进行查找。示例代码如下:

articles = Article.objects.filter(pub_date__time=datetime.time(12,12,12));

​ 以上的代码是获取每一天中12点12分12秒发表的所有文章。
​ 更多的关于时间的过滤,请参考Django官方文档

  1. isnull:根据值是否为空进行查找。示例代码如下:
articles = Article.objects.filter(pub_date__isnull=False)

以上的代码的意思是获取所有发布日期不为空的文章。
将来翻译成SQL语句如下:

select ... where pub_date is not null;

  1. regexiregex:大小写敏感和大小写不敏感的正则表达式。示例代码如下:
articles = Article.objects.filter(title__regex=r'^hello')

以上代码的意思是提取所有标题以hello字符串开头的文章。
将翻译成以下的SQL语句:

select ... where title regexp binary '^hello';

iregex是大小写不敏感的。

根据关联的表进行查询:

假如现在有两个ORM模型,一个是Article,一个是Category。代码如下:

class Category(models.Model):
    """文章分类表"""
    name = models.CharField(max_length=100)

class Article(models.Model):
    """文章表"""
    title = models.CharField(max_length=100,null=True)
    category = models.ForeignKey("Category",on_delete=models.CASCADE)

比如想要获取文章标题中包含"hello"的所有的分类。那么可以通过以下代码来实现:

categories = Category.object.filter(article__title__contains("hello"))

2.2.2 聚合函数

如果你用原生SQL,则可以使用聚合函数来提取数据。比如提取某个商品销售的数量,那么可以使用Count,如果想要知道商品销售的平均价格,那么可以使用Avg
聚合函数是通过aggregate方法来实现的。在讲解这些聚合函数的用法的时候,都是基于以下的模型对象来实现的。

  from django.db import models

 class Author(models.Model):
     """作者模型"""
     name = models.CharField(max_length=100)
     age = models.IntegerField()
     email = models.EmailField()

     class Meta:
         db_table = 'author'


 class Publisher(models.Model):
     """出版社模型"""
     name = models.CharField(max_length=300)

     class Meta:
         db_table = 'publisher'


 class Book(models.Model):
     """图书模型"""
     name = models.CharField(max_length=300)
     pages = models.IntegerField()
     price = models.FloatField()
     rating = models.FloatField()
     author = models.ForeignKey(Author,on_delete=models.CASCADE)
     publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)

     class Meta:
         db_table = 'book'


 class BookOrder(models.Model):
     """图书订单模型"""
     book = models.ForeignKey("Book",on_delete=models.CASCADE)
     price = models.FloatField()

     class Meta:
         db_table = 'book_order'

  1. Avg:求平均值。比如想要获取所有图书的价格平均值。那么可以使用以下代码实现。

     from django.db.models import Avg
     result = Book.objects.aggregate(Avg('price'))
     print(result)
    
    

    以上的打印结果是:

     {"price__avg":23.0}
    
    

    其中price__avg的结构是根据field__avg规则构成的。如果想要修改默认的名字,那么可以将Avg赋值给一个关键字参数。示例代码如下:

     from django.db.models import Avg
     result = Book.objects.aggregate(my_avg=Avg('price'))
     print(result)
    
    

    那么以上的结果打印为:

     {"my_avg":23}
    
    
  2. Count:获取指定的对象的个数。示例代码如下:

     from django.db.models import Count
     result = Book.objects.aggregate(book_num=Count('id'))
    
    

    以上的result将返回Book表中总共有多少本图书。
    Count类中,还有另外一个参数叫做distinct,默认是等于False,如果是等于True,那么将去掉那些重复的值。比如要获取作者表中所有的不重复的邮箱总共有多少个,那么可以通过以下代码来实现:

         from djang.db.models import Count
         result = Author.objects.aggregate(count=Count('email',distinct=True))
    
    
  3. MaxMin:获取指定对象的最大值和最小值。比如想要获取Author表中,最大的年龄和最小的年龄分别是多少。那么可以通过以下代码来实现:

     from django.db.models import Max,Min
     result = Author.objects.aggregate(Max('age'),Min('age'))
    
    

    如果最大的年龄是88,最小的年龄是18。那么以上的result将为:

     {"age__max":88,"age__min":18}
    
    
  4. Sum:求指定对象的总和。比如要求图书的销售总额。那么可以使用以下代码实现:

     from djang.db.models import Sum
     result = Book.objects.annotate(total=Sum("bookstore__price")).values("name","total")
    
    

    以上的代码annotate的意思是给Book表在查询的时候添加一个字段叫做total,这个字段的数据来源是从BookStore模型的price的总和而来。values方法是只提取nametotal两个字段的值。

  5. Variance:求指定对象的方差。

  6. StdDev:求指定对象的标准差。

2.2.3 F表达式和Q表达式

F表达式

F表达式是用来优化ORM操作数据库的。比如我们要将公司所有员工的薪水都增加1000元,如果按照正常的流程,应该是先从数据库中提取所有的员工工资到Python内存中,然后使用Python代码在员工工资的基础之上增加1000元,最后再保存到数据库中。这里面涉及的流程就是,首先从数据库中提取数据到Python内存中,然后在Python内存中做完运算,之后再保存到数据库中。示例代码如下:

employees = Employee.objects.all()
for employee in employees:
    employee.salary += 1000
    employee.save()

而我们的F表达式就可以优化这个流程,他可以不需要先把数据从数据库中提取出来,计算完成后再保存回去,他可以直接执行SQL语句,就将员工的工资增加1000元。示例代码如下:

from djang.db.models import F
Employee.object.update(salary=F("salary")+1000)

F表达式并不会马上从数据库中获取数据,而是在生成SQL语句的时候,动态的获取传给F表达式的值。

比如如果想要获取作者中,nameemail相同的作者数据。如果不使用F表达式,那么需要使用以下代码来完成:

    authors = Author.objects.all()
    for author in authors:
        if author.name == author.email:
            print(author)

如果使用F表达式,那么一行代码就可以搞定。示例代码如下:

    from django.db.models import F
    authors = Author.objects.filter(name=F("email"))

Q表达式

如果想要实现所有价格高于100元,并且评分达到9.0以上评分的图书。那么可以通过以下代码来实现:

books = Book.objects.filter(price__gte=100,rating__gte=9)

以上这个案例是一个并集查询,可以简单的通过传递多个条件进去来实现。
但是如果想要实现一些复杂的查询语句,比如要查询所有价格低于10元,或者是评分低于9分的图书。那就没有办法通过传递多个条件进去实现了。这时候就需要使用Q表达式来实现了。示例代码如下:

from django.db.models import Q
books = Book.objects.filter(Q(price__lte=10) | Q(rating__lte=9))

以上是进行或运算,当然还可以进行其他的运算,比如有&~(非)等。一些用Q表达式的例子如下:

from django.db.models import Q
# 获取id等于3的图书
books = Book.objects.filter(Q(id=3))
# 获取id等于3,或者名字中包含文字"记"的图书
books = Book.objects.filter(Q(id=3)|Q(name__contains("记")))
# 获取价格大于100,并且书名中包含"记"的图书
books = Book.objects.filter(Q(price__gte=100)&Q(name__contains("记")))
# 获取书名包含“记”,但是id不等于3的图书
books = Book.objects.filter(Q(name__contains='记') & ~Q(id=3))

2.3 QuerySet API

我们通常做查询操作的时候,都是通过模型名字.objects的方式进行操作。其实模型名字.objects是一个django.db.models.manager.Manager对象,而Manager这个类是一个“空壳”的类,他本身是没有任何的属性和方法的。他的方法全部都是通过Python动态添加的方式,从QuerySet类中拷贝过来的。示例图如下:

QuerySet和Manager.png

所以我们如果想要学习ORM模型的查找操作,必须首先要学会QuerySet上的一些API的使用。

2.3.1 返回新的QuerySet的方法

在使用QuerySet进行查找操作的时候,可以提供多种操作。比如过滤完后还要根据某个字段进行排序,那么这一系列的操作我们可以通过一个非常流畅的链式调用的方式进行。比如要从文章表中获取标题为123,并且提取后要将结果根据发布的时间进行排序,那么可以使用以下方式来完成:

articles = Article.objects.filter(title='123').order_by('create_time')

可以看到order_by方法是直接在filter执行后调用的。这说明filter返回的对象是一个拥有order_by方法的对象。而这个对象正是一个新的QuerySet对象。因此可以使用order_by方法。

那么以下将介绍在那些会返回新的QuerySet对象的方法。

  1. filter:将满足条件的数据提取出来,返回一个新的QuerySet。具体的filter可以提供什么条件查询。请见查询操作章节。

  2. exclude:排除满足条件的数据,返回一个新的QuerySet。示例代码如下:

     Article.objects.exclude(title__contains='hello')
    
    

    以上代码的意思是提取那些标题不包含hello的图书。

  3. annotate:给QuerySet中的每个对象都添加一个使用查询表达式(聚合函数、F表达式、Q表达式、Func表达式等)的新字段。示例代码如下:

     articles = Article.objects.annotate(author_name=F("author__name"))
    
    

    以上代码将在每个对象中都添加一个author__name的字段,用来显示这个文章的作者的年龄。

  4. order_by:指定将查询的结果根据某个字段进行排序。如果要倒叙排序,那么可以在这个字段的前面加一个负号。示例代码如下:

     # 根据创建的时间正序排序
     articles = Article.objects.order_by("create_time")
     # 根据创建的时间倒序排序
     articles = Article.objects.order_by("-create_time")
     # 根据作者的名字进行排序
     articles = Article.objects.order_by("author__name")
     # 首先根据创建的时间进行排序,如果时间相同,则根据作者的名字进行排序
     articles = Article.objects.order_by("create_time",'author__name')
    
    

    一定要注意的一点是,多个order_by,会把前面排序的规则给打乱,而使用后面的排序方式。比如以下代码:

     articles = Article.objects.order_by("create_time").order_by("author__name")
    
    

    他会根据作者的名字进行排序,而不是使用文章的创建时间。

  5. values:用来指定在提取数据出来,需要提取哪些字段。默认情况下会把表中所有的字段全部都提取出来,可以使用values来进行指定,并且使用了values方法后,提取出的QuerySet中的数据类型不是模型,而是在values方法中指定的字段和值形成的字典:

     articles = Article.objects.values("title",'content')
     for article in articles:
         print(article)
    
    

    以上打印出来的article是类似于{"title":"abc","content":"xxx"}的形式。
    如果在values中没有传递任何参数,那么将会返回这个恶模型中所有的属性。

  6. values_list:类似于values。只不过返回的QuerySet中,存储的不是字典,而是元组。示例代码如下:

     articles = Article.objects.values_list("id","title")
     print(articles)
    
    

    那么在打印articles后,结果为``等。
    如果在values_list中只有一个字段。那么你可以传递flat=True来将结果扁平化。示例代码如下:

     articles1 = Article.objects.values_list("title")
     >> <QuerySet [("abc",),("xxx",),...]>
     articles2 = Article.objects.values_list("title",flat=True)
     >> <QuerySet ["abc",'xxx',...]>
    
    
  7. all:获取这个ORM模型的QuerySet对象。

  8. select_related:在提取某个模型的数据的同时,也提前将相关联的数据提取出来。比如提取文章数据,可以使用select_relatedauthor信息提取出来,以后再次使用article.author的时候就不需要再次去访问数据库了。可以减少数据库查询的次数。示例代码如下:

     article = Article.objects.get(pk=1)
     >> article.author # 重新执行一次查询语句
     article = Article.objects.select_related("author").get(pk=2)
     >> article.author # 不需要重新执行查询语句了
    
    

    selected_related只能用在一对多或者一对一中,不能用在多对多或者多对一中。比如可以提前获取文章的作者,但是不能通过作者获取这个作者的文章,或者是通过某篇文章获取这个文章所有的标签。

  9. prefetch_related:这个方法和select_related非常的类似,就是在访问多个表中的数据的时候,减少查询的次数。这个方法是为了解决多对一多对多的关系的查询问题。比如要获取标题中带有hello字符串的文章以及他的所有标签,示例代码如下:

     from django.db import connection
     articles = Article.objects.prefetch_related("tag_set").filter(title__contains='hello')
     print(articles.query) # 通过这条命令查看在底层的SQL语句
     for article in articles:
         print("title:",article.title)
         print(article.tag_set.all())
    
     # 通过以下代码可以看出以上代码执行的sql语句
     for sql in connection.queries:
         print(sql)
    
    

    但是如果在使用article.tag_set的时候,如果又创建了一个新的QuerySet那么会把之前的SQL优化给破坏掉。比如以下代码:

     tags = Tag.obejcts.prefetch_related("articles")
     for tag in tags:
         articles = tag.articles.filter(title__contains='hello') #因为filter方法会重新生成一个QuerySet,因此会破坏掉之前的sql优化
    
     # 通过以下代码,我们可以看到在使用了filter的,他的sql查询会更多,而没有使用filter的,只有两次sql查询
     for sql in connection.queries:
         print(sql)
    
    

    那如果确实是想要在查询的时候指定过滤条件该如何做呢,这时候我们可以使用django.db.models.Prefetch来实现,Prefetch这个可以提前定义好queryset。示例代码如下:

     tags = Tag.objects.prefetch_related(Prefetch("articles",queryset=Article.objects.filter(title__contains='hello'))).all()
     for tag in tags:
         articles = tag.articles.all()
         for article in articles:
             print(article)
    
     for sql in connection.queries:
         print('='*30)
         print(sql)
    
    

    因为使用了Prefetch,即使在查询文章的时候使用了filter,也只会发生两次查询操作。

  10. defer:在一些表中,可能存在很多的字段,但是一些字段的数据量可能是比较庞大的,而此时你又不需要,比如我们在获取文章列表的时候,文章的内容我们是不需要的,因此这时候我们就可以使用defer来过滤掉一些字段。这个字段跟values有点类似,只不过defer返回的不是字典,而是模型。示例代码如下:

    articles = list(Article.objects.defer("title"))
    for sql in connection.queries:
        print('='*30)
        print(sql)
    
    

    在看以上代码的sql语句,你就可以看到,查找文章的字段,除了title,其他字段都查找出来了。当然,你也可以使用article.title来获取这个文章的标题,但是会重新执行一个查询的语句。示例代码如下:

    articles = list(Article.objects.defer("title"))
    for article in articles:
        # 因为在上面提取的时候过滤了title
        # 这个地方重新获取title,将重新向数据库中进行一次查找操作
        print(article.title)
    for sql in connection.queries:
        print('='*30)
        print(sql)
    
    

    defer虽然能过滤字段,但是有些字段是不能过滤的,比如id,即使你过滤了,也会提取出来。

  11. only:跟defer类似,只不过defer是过滤掉指定的字段,而only是只提取指定的字段。

  12. get:获取满足条件的数据。这个函数只能返回一条数据,并且如果给的条件有多条数据,那么这个方法会抛出MultipleObjectsReturned错误,如果给的条件没有任何数据,那么就会抛出DoesNotExit错误。所以这个方法在获取数据的只能,只能有且只有一条。

  13. create:创建一条数据,并且保存到数据库中。这个方法相当于先用指定的模型创建一个对象,然后再调用这个对象的save方法。示例代码如下:

    article = Article(title='abc')
    article.save()
    
    # 下面这行代码相当于以上两行代码
    article = Article.objects.create(title='abc')
    
    
  14. get_or_create:根据某个条件进行查找,如果找到了那么就返回这条数据,如果没有查找到,那么就创建一个。示例代码如下:

    obj,created= Category.objects.get_or_create(title='默认分类')
    
    

    如果有标题等于默认分类的分类,那么就会查找出来,如果没有,则会创建并且存储到数据库中。
    这个方法的返回值是一个元组,元组的第一个参数obj是这个对象,第二个参数created代表是否创建的。

  15. bulk_create:一次性创建多个数据。示例代码如下:

    Tag.objects.bulk_create([
        Tag(name='111'),
        Tag(name='222'),
    ])
    
    
  16. count:获取提取的数据的个数。如果想要知道总共有多少条数据,那么建议使用count,而不是使用len(articles)这种。因为count在底层是使用select count(*)来实现的,这种方式比使用len函数更加的高效。

  17. firstlast:返回QuerySet中的第一条和最后一条数据。

  18. aggregate:使用聚合函数。

  19. exists:判断某个条件的数据是否存在。如果要判断某个条件的元素是否存在,那么建议使用exists,这比使用count或者直接判断QuerySet更有效得多。示例代码如下:

    if Article.objects.filter(title__contains='hello').exists():
        print(True)
    比使用count更高效:
    if Article.objects.filter(title__contains='hello').count() > 0:
        print(True)
    也比直接判断QuerySet更高效:
    if Article.objects.filter(title__contains='hello'):
        print(True)
    
    
  20. distinct:去除掉那些重复的数据。这个方法如果底层数据库用的是MySQL,那么不能传递任何的参数。比如想要提取所有销售的价格超过80元的图书,并且删掉那些重复的,那么可以使用distinct来帮我们实现,示例代码如下:

    books = Book.objects.filter(bookorder__price__gte=80).distinct()
    
    

    需要注意的是,如果在distinct之前使用了order_by,那么因为order_by会提取order_by中指定的字段,因此再使用distinct就会根据多个字段来进行唯一化,所以就不会把那些重复的数据删掉。示例代码如下:

    orders = BookOrder.objects.order_by("create_time").values("book_id").distinct()
    
    

    那么以上代码因为使用了order_by,即使使用了distinct,也会把重复的book_id提取出来。

  21. update:执行更新操作,在SQL底层走的也是update命令。比如要将所有category为空的articlearticle字段都更新为默认的分类。示例代码如下:

    Article.objects.filter(category__isnull=True).update(category_id=3)
    
    

    注意这个方法走的是更新的逻辑。所以更新完成后保存到数据库中不会执行save方法,因此不会更新auto_now设置的字段。

  22. delete:删除所有满足条件的数据。删除数据的时候,要注意on_delete指定的处理方式。

  23. 切片操作:有时候我们查找数据,有可能只需要其中的一部分。那么这时候可以使用切片操作来帮我们完成。QuerySet使用切片操作就跟列表使用切片操作是一样的。示例代码如下:

    books = Book.objects.all()[1:3]
    for book in books:
        print(book)
    
    

    切片操作并不是把所有数据从数据库中提取出来再做切片操作。而是在数据库层面使用LIMIEOFFSET来帮我们完成。所以如果只需要取其中一部分的数据的时候,建议大家使用切片操作。

2.3.1 不返回QuerySet的方法

以下方法不使用缓存,它们每次被调用时都会查询数据库。

get()

  • get(**kwargs)

返回与给定查找参数匹配的对象,该对象应采用中描述的格式。

get()如果找到多个对象。就会抛出 MultipleObjectsReturned 异常,该异常是模型类的属性。

get()如果没有为给定参数找到对象,则出现DoesNotExist异常。此异常是模型类的属性。例子:

Entry.objects.get(id='foo') # raises Entry.DoesNotExist

这个DoesNotExist异常继承自django.core.exceptions.ObjectDoesNotExist,这样您可以针对多个 DoesNotExist例外情况。例子:

from django.core.exceptions import ObjectDoesNotExist
try:
    e = Entry.objects.get(id=3)
    b = Blog.objects.get(id=1)
except ObjectDoesNotExist:
    print("Either the entry or blog doesn't exist.")

如果希望查询集返回一行,则可以使用 get() 不带任何参数返回该行的对象::

entry = Entry.objects.filter(...).exclude(...).get()

create()

  • create(**kwargs)

一种方便的方法,用于创建一个对象并将其全部保存在一个步骤中。因此:

p = Person.objects.create(first_name="Bruce", last_name="Springsteen")

和:

p = Person(first_name="Bruce", last_name="Springsteen")
p.save(force_insert=True)

是等价的。

这个 force_insert 参数在其他地方有文档记录,但这意味着总是会创建一个新对象。通常你不需要担心这个。但是,如果模型包含您设置的手动主键值,并且该值已存在于数据库中,则调用 create() 将失败 IntegrityError 因为主键必须是唯一的。如果您使用的是手动主键,请准备好处理异常。

get_or_create()

  • get_or_create(defaults=None, **kwargs)

一种方便的查找给定对象的方法 kwargs (如果模型具有所有字段的默认值,则可能为空),如有必要,请创建一个字段。

返回的元组 (object, created) 在哪里 object 是检索或创建的对象,并且 created 是一个布尔值,指定是否创建了新对象。

这意味着这是一个到样板文件代码的快捷方式。例如::

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)},
)

传递给的任何关键字参数 get_or_create() - 除了 一个可选的调用 defaults -将用于 get() 调用。如果找到一个对象, get_or_create() 返回该对象的元组,然后 False .

可以通过链接为检索到的对象指定更复杂的条件 get_or_create() 具有 filter() 并使用 Q objects . 例如,要检索罗伯特或鲍勃马利(如果两者都存在),并创建后者,否则:

from django.db.models import Q

obj, created = Person.objects.filter(
    Q(first_name='Bob') | Q(first_name='Robert'),
).get_or_create(last_name='Marley', defaults={'first_name': 'Bob'})

如果找到多个对象, get_or_create() 加薪 MultipleObjectsReturned . 如果对象是 not 发现, get_or_create() 将实例化并保存新对象,返回新对象的元组 True . 将根据以下算法大致创建新对象:

params = {k: v for k, v in kwargs.items() if '__' not in k}
params.update({k: v() if callable(v) else v for k, v in defaults.items()})
obj = self.model(**params)
obj.save()

在英语中,这意味着从任何不包含双下划线(表示非精确查找)的非“默认值”关键字参数开始。然后添加 defaults ,根据需要重写任何键,并将结果用作模型类的关键字参数。如果在 defaults ,评估它们。如上所述,这是对所用算法的简化,但它包含了所有相关的细节。内部实现有比这更多的错误检查,并处理一些额外的边缘条件;如果您感兴趣,请阅读代码。

如果您有一个名为 defaults 并希望在 get_or_create() 只是使用 'defaults__exact' ,像这样::

Foo.objects.get_or_create(defaults__exact='bar', defaults={'defaults': 'baz'})

这个 get_or_create() 方法的错误行为与 create() 当您使用手动指定的主键时。如果需要创建一个对象并且该键已经存在于数据库中,则 IntegrityError 将被提升。

这个方法是原子的,假定正确的用法、正确的数据库配置和底层数据库的正确行为。但是,如果在数据库级别不强制 kwargs 用于 get_or_create 调用(见) uniqueunique_together ,该方法容易出现竞争条件,导致多行同时插入相同参数。

如果您使用的是MySQL,请确保使用 READ COMMITTED 隔离级别而不是 REPEATABLE READ (默认),否则您可能会看到 get_or_create 将提高 IntegrityError 但是这个物体不会出现在后面 get() 打电话。

最后,一句关于使用的话 get_or_create() 在Django视图中。请确保只在 POST 除非你有充分的理由不这样做。 GET 请求不应该对数据有任何影响。相反,使用 POST 每当对页面的请求对数据有副作用时。更多,请参见 Safe methods 在HTTP规范中。

警告

你可以使用 get_or_create() 通过 ManyToManyField 属性和反向关系。在这种情况下,您将在该关系的上下文中限制查询。如果你不经常使用它,可能会导致一些完整性问题。

以下型号:

class Chapter(models.Model):
    title = models.CharField(max_length=255, unique=True)

class Book(models.Model):
    title = models.CharField(max_length=256)
    chapters = models.ManyToManyField(Chapter)

你可以使用 get_or_create() 通过书籍的章节字段,但它只在该书的上下文中提取:

>>> book = Book.objects.create(title="Ulysses")
>>> book.chapters.get_or_create(title="Telemachus")
(<Chapter: Telemachus>, True)
>>> book.chapters.get_or_create(title="Telemachus")
(<Chapter: Telemachus>, False)
>>> Chapter.objects.create(title="Chapter 1")
<Chapter: Chapter 1>
>>> book.chapters.get_or_create(title="Chapter 1")
# Raises IntegrityError

这是因为它试图通过《尤利西斯》这本书获得或创造“第1章”,但它不能做到任何一个:关系不能获取那一章,因为它与那本书无关,但也不能创造它,因为 title 字段应该是唯一的。

update_or_create()

  • update_or_create(defaults=None, **kwargs)

用给定的 kwargs ,必要时创建新的。这个 defaults 是用于更新对象的(字段、值)对的字典。中的值 defaults 可以是可调用的。

返回的元组 (object, created) 在哪里 object 是创建或更新的对象,并且 created 是一个布尔值,指定是否创建了新对象。

这个 update_or_create 方法尝试根据给定的 kwargs . 如果找到匹配项,它将更新 defaults 字典。

这意味着这是一个到样板文件代码的快捷方式。例如::

defaults = {'first_name': 'Bob'}
try:
    obj = Person.objects.get(first_name='John', last_name='Lennon')
    for key, value in defaults.items():
        setattr(obj, key, value)
    obj.save()
except Person.DoesNotExist:
    new_values = {'first_name': 'John', 'last_name': 'Lennon'}
    new_values.update(defaults)
    obj = Person(**new_values)
    obj.save()

随着模型中字段数的增加,这种模式变得相当复杂。上面的例子可以用重写 update_or_create() 像这样::

obj, created = Person.objects.update_or_create(
    first_name='John', last_name='Lennon',
    defaults={'first_name': 'Bob'},
)

有关如何传入名称的详细描述 kwargs 已解决,请参阅 get_or_create() .

如上所述 get_or_create() ,此方法容易出现竞争条件,如果在数据库级别不强制唯一性,则可能导致同时插入多行。

喜欢 get_or_create()create() ,如果正在使用手动指定的主键,并且需要创建一个对象,但该键已存在于数据库中,则 IntegrityError 提高了。

bulk_create()

  • bulk_create(objs, batch_size=None, ignore_conflicts=False)

此方法以有效的方式将提供的对象列表插入数据库(通常只有1个查询,不管有多少个对象)::

>>> Entry.objects.bulk_create([
...     Entry(headline='This is a test'),
...     Entry(headline='This is only a test'),
... ])

不过,这有许多警告:

  • 模型的 save() 将不调用方法,并且 pre_savepost_save 不会发送信号。

  • 它不适用于多表继承方案中的子模型。

  • 如果模型的主键是 AutoField 它不检索和设置主键属性,因为 save() 是的,除非数据库后端支持它(目前是PostgreSQL)。

  • 它不适用于多对多关系。

  • 它铸造 objs 到一个列表,该列表完全评估 objs 如果是发电机。强制转换允许检查所有对象,以便可以首先插入具有手动设置的主键的任何对象。如果要批量插入对象而不同时评估整个生成器,则可以使用此技术,只要对象没有任何手动设置的主键::

    from itertools import islice
    
    batch_size = 100
    objs = (Entry(headline='Test %s' % i) for i in range(1000))
    while True:
        batch = list(islice(objs, batch_size))
        if not batch:
            break
        Entry.objects.bulk_create(batch, batch_size)
    
    

这个 batch_size 参数控制在单个查询中创建的对象数。默认值是在一个批处理中创建所有对象,但sqlite除外,其中默认值是每个查询最多使用999个变量。

在支持它的数据库(除了Oracle)上,设置 ignore_conflicts 参数到 True 告诉数据库忽略插入任何失败约束的行(如重复的唯一值)的失败。启用此参数将禁用在每个模型实例上设置主键(如果数据库通常支持)。

Changed in Django 2.2:

这个 ignore_conflicts 已添加参数。

bulk_update()

New in Django 2.2:

  • bulk_update(objs, fields, batch_size=None)

此方法可以有效地更新所提供模型实例上的给定字段,通常只需一个查询:

>>> objs = [
...    Entry.objects.create(headline='Entry 1'),
...    Entry.objects.create(headline='Entry 2'),
... ]
>>> objs[0].headline = 'This is entry 1'
>>> objs[1].headline = 'This is entry 2'
>>> Entry.objects.bulk_update(objs, ['headline'])

QuerySet.update() 用于保存更改,因此这比遍历模型列表和调用 save() 但有几点需要注意:

  • 无法更新模型的主键。
  • 每个模型 save() 未调用方法,并且 pre_savepost_save 没有发送信号。
  • 如果更新大量行中的大量列,则生成的SQL可能非常大。通过指定合适的 batch_size .
  • 更新在多表继承祖先上定义的字段将对每个祖先产生额外的查询。
  • 如果 objs 包含重复项,只更新第一个。

这个 batch_size 参数控制单个查询中保存的对象数。默认情况下,更新一批中的所有对象,除了对查询中使用的变量数量有限制的sqlite和oracle。

count()

  • count()

返回一个整数,该整数表示数据库中与 QuerySet .

例子::

# Returns the total number of entries in the database.
Entry.objects.count()

# Returns the number of entries whose headline contains 'Lennon'
Entry.objects.filter(headline__contains='Lennon').count()

A count() 调用执行 SELECT COUNT(*) 在幕后,所以你应该经常使用 count() 而不是将所有记录加载到Python对象中并调用 len() 在结果上(除非您无论如何都需要将对象加载到内存中,在这种情况下 len() 会更快)。

请注意,如果需要 QuerySet 并且还从中检索模型实例(例如,通过对其进行迭代),使用它可能更有效 len(queryset) 这不会导致额外的数据库查询 count() 会。

in_bulk()

  • in_bulk(id_list=None, field_name='pk')

获取字段值列表 (id_listfield_name 对于这些值,并返回将每个值映射到具有给定字段值的对象实例的字典。如果 id_list 未提供,将返回查询集中的所有对象。 field_name 必须是唯一字段,并且默认为主键。

例子::

>>> 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>}

如果你通过 in_bulk() 一个空列表,你会得到一本空字典。

iterator()

  • iterator(chunk_size=2000)

评估 QuerySet (通过执行查询)并返回迭代器(请参见 PEP 234 )在结果上。一 QuerySet 通常在内部缓存其结果,以便重复的计算不会导致额外的查询。相反, iterator() 将直接读取结果,而不在 QuerySet 级别(在内部,默认迭代器调用 iterator() 并缓存返回值)。对于一个 QuerySet 它返回大量只需要访问一次的对象,这可以导致更好的性能和显著的内存减少。

注意使用 iterator() 在一 QuerySet 已评估的将强制它重新评估,重复查询。

此外,使用 iterator() 前因 prefetch_related() 调用将被忽略,因为这两个优化在一起没有意义。

根据数据库后端的不同,查询结果将同时加载,或者使用服务器端光标从数据库中进行流式传输。

使用服务器端光标

甲骨文与 PostgreSQL 使用服务器端游标从数据库流式传输结果,而不将整个结果集加载到内存中。

Oracle数据库驱动程序始终使用服务器端游标。

对于服务器端光标, chunk_size 参数指定要在数据库驱动程序级别缓存的结果数。获取更大的块会减少数据库驱动程序和数据库之间的往返次数,但会牺牲内存。

在PostgreSQL上,只有在 DISABLE_SERVER_SIDE_CURSORS 设置是 False . 读 事务池和服务器端游标 如果您使用的是在事务池模式下配置的连接池。禁用服务器端游标时,行为与不支持服务器端游标的数据库相同。

没有服务器端光标

MySQL不支持流式结果,因此python数据库驱动程序将整个结果集加载到内存中。然后,数据库适配器使用 fetchmany() 方法定义于 PEP 249 .

sqlite可以使用 fetchmany() 但是,由于sqlite不提供连接内查询之间的隔离,因此在写入要循环访问的表时要小心。见 使用时隔离 QuerySet.iterator() 更多信息。

这个 chunk_size 参数控制从数据库驱动程序中检索的批次django的大小。更大的批处理减少了与数据库驱动程序通信的开销,但代价是内存消耗略有增加。

默认值为 chunk_size ,2000,来自 a calculation on the psycopg mailing list

假设有10-20列的行与文本和数字数据混合,2000将获取少于100kb的数据,这似乎是在传输的行数和过早退出循环时丢弃的数据之间的一个很好的折衷。

Changed in Django 2.2:

添加了对sqlite上的结果流的支持。

latest()

  • latest(*fields)

基于给定字段返回表中的最新对象。

此示例返回最新的 Entry 在表格上,根据 pub_date 领域:

Entry.objects.latest('pub_date')

您还可以根据几个字段选择最新的。例如,选择 Entry 最早的 expire_date 当两个条目相同时 pub_date ::

Entry.objects.latest('pub_date', '-expire_date')

负面签到 '-expire_date' 排序方法 expire_date 在里面 下降的 秩序。自从 latest() 获取最后一个结果, Entry 最早的 expire_date 被选中。

如果你的模型 Meta 指定 get_latest_by ,您可以省略 earliest()latest() . 中指定的字段 get_latest_by 默认情况下将使用。

喜欢 get()earliest()latest() 提升 DoesNotExist 如果没有具有给定参数的对象。

注意 earliest()latest() 纯粹为了方便和可读性而存在。

earliest()latest() 可以返回日期为空的实例。

由于将排序委托给数据库,因此如果使用不同的数据库,允许空值的字段的结果的排序可能会有所不同。例如,PostgreSQL和MySQL对空值进行排序,就像它们高于非空值一样,而SQLite则相反。

您可能希望筛选出空值::

Entry.objects.filter(pub_date__isnull=False).latest('pub_date')

earliest()

  • earliest(*fields)

以其他方式工作 latest() 但方向改变了。

first()

  • first()

返回与查询集匹配的第一个对象,或者 None 如果没有匹配的对象。如果 QuerySet 未定义排序,则按主键自动排序查询集。这可能会影响聚合结果,如中所述 与默认排序或 order_by() .

例子::

p = Article.objects.order_by('title', 'pub_date').first()

注意 first() 是一种方便的方法,下面的代码示例等效于上面的示例:

try:
    p = Article.objects.order_by('title', 'pub_date')[0]
except IndexError:
    p = None

last()

  • last()

作品像 first() ,但返回查询集中的最后一个对象。

aggregate()

  • aggregate(*args, **kwargs)

返回计算的聚合值(平均值、和等)的字典 QuerySet . 每个参数 aggregate() 指定将包含在返回的字典中的值。

Django提供的聚合函数在 Aggregation Functions 下面。因为集料也是 query expressions 可以将聚合与其他聚合或值组合以创建复杂聚合。

使用关键字参数指定的聚合将使用关键字作为批注的名称。匿名参数将根据聚合函数的名称和要聚合的模型字段为其生成一个名称。复杂聚合不能使用匿名参数,必须将关键字参数指定为别名。

例如,当您处理博客条目时,您可能希望知道贡献博客条目的作者数:

>>> from django.db.models import Count
>>> q = Blog.objects.aggregate(Count('entry'))
{'entry__count': 16}

通过使用关键字参数指定聚合函数,可以控制返回的聚合值的名称::

>>> q = Blog.objects.aggregate(number_of_entries=Count('entry'))
{'number_of_entries': 16}

有关聚合的深入讨论,请参见 the topic guide on Aggregation .

exists()

  • exists()

返回 True 如果 QuerySet 包含任何结果,以及 False 如果不是。它试图以最简单和最快的方式执行查询,但是 does 执行与普通查询几乎相同的查询 QuerySet 查询。

exists() 对于与中的两个对象成员关系相关的搜索很有用 QuerySet 以及任何对象的存在 QuerySet 尤其是在大 QuerySet .

找到具有唯一字段的模型(例如 primary_key )是的成员 QuerySet 是::

entry = Entry.objects.get(pk=123)
if some_queryset.filter(pk=entry.pk).exists():
    print("Entry contained in queryset")

这将比以下更快,后者需要对整个查询集进行计算和迭代:

if entry in some_queryset:
   print("Entry contained in QuerySet")

要查找查询集是否包含任何项:

if some_queryset.exists():
    print("There is at least one object in some_queryset")

速度比:

if some_queryset:
    print("There is at least one object in some_queryset")

…但不是很大程度上(因此需要一个大型的查询集来提高效率)。

此外,如果 some_queryset 尚未评估,但您知道它将在某个时间点,然后使用 some_queryset.exists() 将比简单地使用 bool(some_queryset) ,它检索结果,然后检查是否返回了任何结果。

update()

  • update(**kwargs)

对指定的字段执行SQL更新查询,并返回匹配的行数(如果某些行已有新值,则可能不等于更新的行数)。

例如,要关闭2010年发布的所有日志的评论,可以执行以下操作:

>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False)

(这假设 Entry 模型有字段 pub_datecomments_on

您可以更新多个字段-数量没有限制。例如,这里我们更新 comments_onheadline 领域::

>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False, headline='This is old')

这个 update() 方法被立即应用,并且对 QuerySet 它只能更新模型主表中的列,而不能更新相关模型上的列。您不能这样做,例如:

>>> Entry.objects.update(blog__name='foo') # Won't work!

基于相关字段的筛选仍然是可能的,但是:

>>> Entry.objects.filter(blog__id=1).update(comments_on=True)

你不能调用 update() 在一 QuerySet 已经取了一个切片或不能再过滤的。

这个 update() 方法返回受影响的行数::

>>> Entry.objects.filter(id=64).update(comments_on=True)
1

>>> Entry.objects.filter(slug='nonexistent-slug').update(comments_on=True)
0

>>> Entry.objects.filter(pub_date__year=2010).update(comments_on=False)
132

如果您只是更新一个记录,而不需要对模型对象做任何事情,那么最有效的方法是调用 update() 而不是将模型对象加载到内存中。例如,不要这样做:

e = Entry.objects.get(id=10)
e.comments_on = False
e.save()

…这样做:

Entry.objects.filter(id=10).update(comments_on=False)

使用 update() 还可以防止在加载对象和调用之间的短时间内,数据库中的某些内容可能发生变化的争用情况。 save() .

最后,要意识到 update() 在SQL级别进行更新,因此不调用任何 save() 方法,它也不会发出 pre_savepost_save 信号(是调用的结果 Model.save() )如果要为具有自定义 save() 方法,循环它们并调用 save() ,像这样:

for e in Entry.objects.filter(pub_date__year=2010):
    e.comments_on = False
    e.save()

delete()

  • delete()

对中的所有行执行SQL删除查询 QuerySet 并返回删除的对象数和一个字典,其中包含每个对象类型的删除数。

这个 delete() 立即应用。你不能调用 delete() 在一 QuerySet 已经取了一个切片或不能再过滤的。

例如,删除特定日志中的所有条目:

>>> b = Blog.objects.get(pk=1)

# Delete all the entries belonging to this Blog.
>>> Entry.objects.filter(blog=b).delete()
(4, {'weblog.Entry': 2, 'weblog.Entry_authors': 2})

默认情况下,Django的 ForeignKey 模拟SQL约束 ON DELETE CASCADE -换言之,任何具有指向要删除对象的外键的对象都将与它们一起删除。例如::

>>> blogs = Blog.objects.all()

# This will delete all Blogs and all of their Entry objects.
>>> blogs.delete()
(5, {'weblog.Blog': 1, 'weblog.Entry': 2, 'weblog.Entry_authors': 2})

这个级联行为可以通过 on_delete 论据 ForeignKey .

这个 delete() 方法执行批量删除,不调用任何 delete() 模型上的方法。但是,它确实会发出 pre_deletepost_delete 所有已删除对象的信号(包括级联删除)。

Django需要将对象提取到内存中以发送信号并处理级联。但是,如果没有级联和信号,那么Django可能会采用快速路径,删除对象而不提取到内存中。对于较大的删除,这可能会导致内存使用显著减少。执行的查询量也可以减少。

设置为 on_delete DO_NOTHING 不要阻止删除时采用快速路径。

注意,在对象删除中生成的查询是一个可更改的实现细节。

as_manager()

  • classmethod as_manager()

返回的实例的类方法 Manager 一份 QuerySet 的方法。见 使用创建经理 QuerySet 方法 了解更多详细信息。

explain()

  • explain(format=None, **options)

返回 QuerySet 的执行计划,其中详细说明了数据库将如何执行查询,包括将要使用的任何索引或联接。了解这些详细信息可能有助于提高慢速查询的性能。

例如,使用PostgreSQL时:

>>> print(Blog.objects.filter(title='My Blog').explain())
Seq Scan on blog  (cost=0.00..35.50 rows=10 width=12)
  Filter: (title = 'My Blog'::bpchar)

数据库之间的输出差异很大。

explain() 所有内置数据库后端都支持,但Oracle除外,因为实现并不简单。

这个 format 参数从数据库的默认值(通常基于文本)更改输出格式。PostgreSQL支持 'TEXT''JSON''YAML''XML' . MySQL支持 'TEXT' (也称为 'TRADITIONAL''JSON' .

一些数据库接受可以返回有关查询的更多信息的标志。将这些标志作为关键字参数传递。例如,使用PostgreSQL时:

>>> print(Blog.objects.filter(title='My Blog').explain(verbose=True))
Seq Scan on public.blog  (cost=0.00..35.50 rows=10 width=12) (actual time=0.004..0.004 rows=10 loops=1)
  Output: id, title
  Filter: (blog.title = 'My Blog'::bpchar)
Planning time: 0.064 ms
Execution time: 0.058 ms

在某些数据库上,标记可能会导致执行查询,这可能会对数据库产生不利影响。例如,PostgreSQL的 ANALYZE 如果存在触发器或调用函数,则标志可能导致对数据的更改,即使对于 SELECT 查询。

2.3.3 QuerySet惰性执行和缓存

生成一个QuerySet对象并不会马上转换为SQL语句去执行。
比如我们获取Book表下所有的图书:

books = Book.objects.all()
print(connection.queries)

我们可以看到在打印connection.quries的时候打印的是一个空的列表。说明上面的QuerySet并没有真正的执行。
在以下情况下QuerySet会被转换为SQL语句执行:

  1. 迭代:在遍历QuerySet对象的时候,会首先先执行这个SQL语句,然后再把这个结果返回进行迭代。比如以下代码就会转换为SQL语句:

     for book in Book.objects.all():
         print(book)
    
    
  2. 使用步长做切片操作:QuerySet可以类似于列表一样做切片操作。做切片操作本身不会执行SQL语句,但是如果如果在做切片操作的时候提供了步长,那么就会立马执行SQL语句。需要注意的是,做切片后不能再执行filter方法,否则会报错。

  3. 调用len函数:调用len函数用来获取QuerySet中总共有多少条数据也会执行SQL语句。

  4. 调用list函数:调用list函数用来将一个QuerySet对象转换为list对象也会立马执行SQL语句。

  5. 判断:如果对某个QuerySet进行判断,也会立马执行SQL语句。

2.4 删除数据

在查找到数据后,便可以进行删除了。删除数据非常简单,只需要调用这个对象的delete方法即可。实例代码如下:

book = Book.objects.get(name='三国演义')
book.delete()

2.5 修改数据

在查找到数据后,便可以进行修改了。修改的方式非常简单,只需要将查找出来的对象的某个属性进行修改,然后再调用这个对象的save方法便可以进行修改。示例代码如下:

from datetime import datetime
book = Book.objects.get(name='三国演义')
book.pub_date = datetime.now()
book.save()

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容