浅谈Django QuerySet对象(模型.objects)的常用方法
准备工作:
新建一个项目,在项目中新家一个app,名字自取。将app添加值settings.py中,然后配置settings连接数据库。
在app中的models中新建模型:
from django.db import models
# Create your models here.
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'
执行makemigrations后在migrate。
然后手动向表中添加数据,例如我添加的信息:
然后配置urls,直至项目运行成功。
1.filter:
过滤,将满足条件的数据提取出来,返回一个新的QuerySet。
2. exclude:
排除,排除满足条件的数据,返回一个新的QuerySet。
例如,取出book中id大于等于2的图书,并且id不能等于3的图书。示例代码:
books = models.Book.objects.filter(id__gt=1).exclude(id=3)
我们也可以使用Q表达式来实现,
from django.db.models import Q
books = models.Book.objects.filter(id__gt=1).filter(~Q(id=3))
3. annotate:
给QuerySet中的每个对象都添加一个使用查询表达式(聚合函数、F表达式、Q表达式、Func表达式等)的新字段。
例如 给每本图书都添加一个字段叫author_name
from django.db.models import F
books = models.Book.objects.annotate(author_name = F('author__name'))
注意:
Book模型下面最开始是没有author_name这个属性的。只有一个author属性连接的一个外键.
author__name是book下的author属性下的name,即book这个表通过外键访问到author这个表中的name值。
执行完上述代码之后就在book中创建了一个新的属性author_name,但是只在内存中,不会映射到数据库中去。
4.order_by:
指定将查询的结果根据某个字段进行排序。如果要倒叙排序,那么可以在这个字段的前面加一个负号。
示例
def index2(request):
# 1. 将book中的price属性按照从小到大进行排序
# books = models.Book.objects.order_by('price')
# for book in books:
# print("%s-%s"%(book.name,book.price))
# 2. 将book中的price属性按照从大到小进行排序
books = models.Book.objects.order_by('-price')
for book in books:
print("%s-%s"%(book.name,book.price))
return HttpResponse('success')
修改对应注释代码哪呢个查看到效果。
需求:对价格进行排序,价格一样的就按照页数来进行排序,价格从小到大,页数从大到小。为了方便查看效果,可以先去数据库中修改数据。
def index2(request):
# 1. 将book中的price属性按照从小到大进行排序
# books = models.Book.objects.order_by('price')
# for book in books:
# print("%s-%s"%(book.name,book.price))
# 2. 将book中的price属性按照从大到小进行排序
# books = models.Book.objects.order_by('-price')
# for book in books:
# print("%s-%s"%(book.name,book.price))
# 3. 对价格进行排序,价格一样的就按照页数来进行排序,价格从小到大,页数从大到小。
books = models.Book.objects.order_by('price','-pages')
for book in books:
print("%s-%s-%s"%(book.name,book.price,book.pages))
return HttpResponse('success')
注意:
books = models.Book.objects.order_by('price','-pages')不等于books =
models.Book.objects.order_by('price').order_by('-pages')
如果使用多个order_by,会把前面排序的规则给打乱,只会使用最后面的一个排序方式。
order_by方法并没有改变数据库中的信息位置,只是我们将数据取出来进行了排序。
那么如果我们对数据库中的信息进行了排序,这样我们就不用每次取数据都需要进行一次排序了。应该怎样做呢?
我们只需要在模型中添加点代码就可以了。
例如:我们在Book者恶搞模型中对价格进行排序,价格一样的就按照页数来进行排序,价格从小到大,页数从大到小。
修改Book中的class Meta中的代码:
class Meta:
db_table = 'book_order'
ordering = ['price','-pages']
这样就对数据库中的信息进行了排序,我们在去数据
的时候也不用使用order_by来进行排序了。但是这样需要重新makegrations和migrate一下,这里就不做演示了。
需求:根据每本图书的销量来进行排序
from django.db.models import Q,F,Count
def index2(request):
# 1. 将book中的price属性按照从小到大进行排序
# books = models.Book.objects.order_by('price')
# for book in books:
# print("%s-%s"%(book.name,book.price))
# 2. 将book中的price属性按照从大到小进行排序
# books = models.Book.objects.order_by('-price')
# for book in books:
# print("%s-%s"%(book.name,book.price))
# 3. 对价格进行排序,价格一样的就按照页数来进行排序,价格从小到大,页数从大到小。
# books = models.Book.objects.order_by('price','-pages')
# for book in books:
# print("%s-%s-%s"%(book.name,book.price,book.pages))
# 4. 根据每本图书的销量来进行排序
results = models.Book.objects.annotate(sale_num=Count('bookorder__id')).order_by('sale_num')
for result in results:
print("%s-%s"%(result.name,result.sale_num))
return HttpResponse('success')
因为Book这个模型中没有sale_num这个属性,所以我们需要使用annotate这个方法来创建一个sale_num属性,然后使用Count方法进行赋值,然后使用order_by
进行排序。就实现了这个需求。
5. values:
用来指定在提取数据出来,需要提取哪些字段。默认情况下会把表中所有的字段全部都提取出来,可以使用values来进行指定,并且使用了values方法后,提取出的QuerySet中的数据类型不是模型,而是在values方法中指定的字段和值形成的字典。
需求: 只需要提取Book中的id 和 name
示例:
def index3(request):
# 1. 只需要提取Book中的id 和 name
books = models.Book.objects.values('id','name')
for book in books:
print(book)
return HttpResponse('success')
注意: 返回的是一个字典类型。字典的key就是属性名,value是属性值。
需求:提取Book中的name和author__name,并且字典中的key自己指定,不使用默认的。字典的key分别为bookName和authorNmae
def index3(request):
# 1. 只需要提取Book中的id 和 name
# books = models.Book.objects.values('id','name')
# for book in books:
# print(book)
# 需求:提取Book中的name和author__name,并且字典中的key自己指定,不使用默认的。
# 字典的key分别为`bookName`和`authorNmae`
books = models.Book.objects.values(bookName=F('name'),authorName=F('author__name'))
for book in books:
print(book)
return HttpResponse('success')
注意:
自己取名字不能取该模型的属性名,否则会报错。
如果在value中不传递任何参数,那么会获取这个模型所有的值。返回的还是一个字典。
6:values_list:
类似于values。只不过返回的QuerySet中,存储的不是字典,而是元组。操作和values是一样的,只是返回类型不一样。
注意:
当我们使用此方法只返回一个值的时候,那么这个元祖中只有一个值,我们可以添加一个参数flat=True,将元祖去掉,从而得到一个字符窜。只有当values_list中只有一个值的时候才能使用这个方法:
示例:
books = models.Book.objects.values_list('name',flat=True)
7. all:
获取这个ORM模型的QuerySet对象。即获取所有的数据。
获取Book中所有数据
示例:
books = models.Book.objects.all()
8.select_related:
在提取某个模型的数据的同时,也提前将相关联的数据提取出来。比如提取文章数据,可以使用select_related将author信息提取出来,以后再次使用article.author的时候就不需要再次去访问数据库了。可以减少数据库查询的次数。
def index4(request):
books = models.Book.objects.select_related('author')
for book in books:
print(book.author.name)
return HttpResponse('succrss')
注意:
select_related只能使用在设置了外键的模型中(即只能在一对多模型上,不能多对一,多对多等),比如我们只在Book设置了author外键和publisher外键。那么select_related里面只能传如这两个参数,而不能传入别的参数,如BookOrder,因为我们是在BookOrder中设置的外键连接到Book,并没有在Book中设置外键连接到BookOrder这个模型。
9. prefetch_related:
这个方法和select_related非常的类似,就是在访问多个表中的数据的时候,减少查询的次数。这个方法是为了解决多对一和多对多的关系的查询问题。
需求:从book中通过prefetch_related查询BookOrder中的信息。
示例代码
def index5(request):
books = models.Book.objects.prefetch_related("bookorder_set")
for book in books:
print('*'*30)
print(book.name)
orders = book.bookorder_set.all()
for order in orders:
print(order.id)
return HttpResponse('success')
prefetch_related方法也能办到select_related方法能办到的事情,只是select_related方法效率比prefetch_related方法效率高一点。所以能使用select_related方法的话就是用这个方法。但是这两种方法的执行效率都比传统的方法执行效率高。传统的方法就是先返回book对象,再通过book去查询对应的外键的相关信息。
10. defer:
在一些表中,可能存在很多的字段,但是一些字段的数据量可能是比较庞大的,而此时你又不需要,比如我们在获取文章列表的时候,文章的内容我们是不需要的,因此这时候我们就可以使用defer来过滤掉一些字段。这个字段跟values有点类似,只不过defer返回的不是字典,而是模型。
需求:过滤掉book 的name字段
def index6(request):
# 过滤掉book的name字段
books = models.Book.objects.defer('name')
for book in books:
print(book.id)
return HttpResponse('sucdess')
注意:
我们在使用defer过滤掉name字段之后,我们还是可以访问到name属性,是因为当我们访问name属性的时候,Django又去执行了一遍sql语句查询的代码。所以在我们开发的过程中,除非我们确定不会使用到此属性,否则不要去过滤它。
defer虽然能过滤字段,但是有些字段是不能过滤的,比如id,即使你过滤了,也会提取出来。
11. only:
跟defer类似,只不过defer是过滤掉指定的字段,而only是只提取指定的字段。
需求:只提取name属性
# 只提取name属性
books = models.Book.objects.only('name')
for book in books:
print(book.id,book.name)
注意: id这个字段我们是不能操作的,像上面一样,我们没有提取id属性,但是还是给我们提取出来了。所以id属性是一定会被提取出来的。
和defer一样,就算我们没有提取某个属性出来,我们还是可以访问到的,只是会重新执行一遍sql代码而已。
12. get:
获取满足条件的数据。这个函数只能返回一条数据,并且如果给的条件有多条数据,那么这个方法会抛出MultipleObjectsReturned错误,如果给的条件没有任何数据,那么就会抛出DoesNotExit错误。所以这个方法在获取数据的只能,只能有且只有一条。
获取id为1的数据
book = models.Book.objects.get(id=1)
13. create:
创建一条数据,并且保存到数据库中。这个方法相当于先用指定的模型创建一个对象,然后再调用这个对象的save方法。
publusher = models.Publisher.objects.create(name='知了出版社')
14. get_or_create:
根据某个条件进行查找,如果找到了那么就返回这条数据,如果没有查找到,那么就创建一个。
result = models.Publisher.objects.get_or_create(name='知了出版社')
print(result)
会返回一个元祖
查找的对象以及是否创建了这条数据。False就是没有创建这条数据。
15. bulk_create:
和create方法类似,只是这个方法可以一次性创建多个数据。
publusher =
models.Publisher.objects.bulk_create([models.Publisher(name='123出版社'),
models.Publisher(name='abc出版社'),])
16. count:
获取提取的数据的个数。如果想要知道总共有多少条数据,那么建议使用count,而不是使用len(articles)这种。因为count在底层是使用select
count(*)来实现的,这种方式比使用len函数更加的高效。
count = models.Book.objects.filter(name='xxx').count()
17. first和last:
返回QuerySet中的第一条和最后一条数据。如果为空则返回none。
18. aggregate:
使用聚合函数。
19. exists:
判断某个条件的数据是否存在。如果要判断某个条件的元素是否存在,那么建议使用exists,这比使用count或者直接判断QuerySet更有效得多。
示例代码如下:
# 最高效的判断值是否存在的方法
if Article.objects.filter(name='三国演义').exists():
print(True)
# 比上面的方法效率低一点
if Article.objects.filter(name='三国演义').count() > 0:
print(True)
# 还要比上面的效率低
if Article.objects.filter(name='三国演义'):
print(True)
20. distinct:
去除掉那些重复的数据。这个方法如果底层数据库用的是MySQL,那么不能传递任何的参数。
需求:提取所有销售的价格超过80元的图书,并且删掉那些重复的,那么可以使用distinct来帮我们实现,示例代码如下:
books = models.Book.objects.filter(bookorder__price__gte=80).distinct()
for book in books:
print(book.name)
并且distinct只会剔除那些完全相同的数据,如果有一个字段不相同,都不会剔除的。
如果在distinct之前使用了order_by,那么因为order_by会提取order_by中指定的字段,因此再使用distinct就会根据多个字段来进行唯一化,所以就不会把那些重复的数据删掉。
示例:
orders =
models.BookOrder.objects.order_by("pages").values("book_id").distinct()
21. update:
执行更新操作,在SQL底层走的也是update命令。比如要将所有图书的价格都提高10元。
book = models.Book.objects.update(price=F('price')+5)
22. delete:
删除所有满足条件的数据。删除数据的时候,要注意on_delete指定的处理方式。
例如删除作者id大于等于3的数据
result = models.Author.objects.get(id__gte=4).delete()
删除数据时一定要对你的表了如指掌,因为可能会牵连到很多其他数据。像在这个地方将这个作者删除了之后,那么这个作者对应的图书也将会被删除。
23. 切片操作:
有时候我们查找数据,有可能只需要其中的一部分。那么这时候可以使用切片操作来帮我们完成。QuerySet使用切片操作就跟列表使用切片操作是一样的。
# 获取1,2两条数据
books = models.Book.objects.all()[1:3]
for book in books:
print(book)
以上这篇浅谈Django
QuerySet对象(模型.objects)的常用方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。