模型.objects:
这个对象是django.db.models.manager.Manager
的对象,这个类是一个空壳类,他上面的所有方法都是从QuerySet
这个类上面拷贝过来的。因此我们只要学会了QuerySet
,这个objects
也就知道该如何使用了。
Manager
源码解析:
class_name = "BaseManagerFromQuerySet"
class_dict = {
'_queryset_class': QuerySet
}
class_dict.update(cls._get_queryset_methods(QuerySet))
# type动态的时候创建类
# 第一个参数是用来指定创建的类的名字。创建的类名是:BaseManagerFromQuerySet
# 第二个参数是用来指定这个类的父类。
# 第三个参数是用来指定这个类的一些属性和方法
return type(class_name,(cls,),class_dict)
_get_queryset_methods:这个方法就是将QuerySet中的一些方法拷贝出来
filter/exclude/annotate:过滤/排除满足条件的/给模型添加新的字段。
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")
他会根据作者的名字进行排序,而不是使用文章的创建时间。
当然,也可以在模型定义的在Meta
类中定义ordering
来指定默认的排序方式。示例代码如下:
class Meta:
db_table = 'book_order'
ordering = ['create_time','-price']
还可以根据annotate
定义的字段进行排序。比如要实现图书的销量进行排序,那么示例代码如下:
books = Book.objects.annotate(order_nums=Count("bookorder")).order_by("-order_nums")
for book in books:
print('%s/%s'%(book.name,book.order_nums))
values:
有时候我们在表中查找数据的时候,并不是想把所有的字段都提取出来。我们有可能只想要其中的几个字段,这时候就可以使用values
来实现,需要几个字段,就把这几个字段的名字传递到这个方法中,示例代码如下:
books = Book.objects.values('id', 'name')
values
的返回值同样是一个QuerySet
对象,但其中不在是模型,而是字典。
如果我们想要提取的是这个模型上关联的对象属性,那么也是可以的,查找顺序跟filter
的用法是一样的,示例代码如下:
books = Book.objects.values('id', 'name', 'author__name')
以上将会提取author
的name
字段,如果我们不想使用这个名字。那么可以使用关键字 参数,示例代码如下:
books = Book.objects.values("id", "name", "author_name=F('author__name')")
注意:自定义的名字,不能和模型本身拥有的字段一样,比如以上author_name
取名author
就会报错,因为Book
上本身就有author
字段名字。
在values
中,也可以使用聚合函数来形成一个新的字段,比如我想要获取每本图书的销量,那么示例代码如下:
books = Book.objects.values('id', 'name', order_nums=Count('bookorder'))
如果调用values
方法时,没有传递任何参数,那么会获取这个模型上的所有的字段以及对应的值形成的字典,示例代码如下:
books = Book.objects.values()
那么books
中的值如下:
{'id': 1, 'name': '三国演义', 'pages': 987, 'price': 108.0, 'rating': 3.9, 'author_id': 3, 'publisher_id': 1}
values_list
跟values
是一样的作用,只不过这个方法返回的QuerySet
中,装的不是字典,而是元组,示例代码如下:
books = Book.objects.values_list('id', 'name')
那么以上代码返回的结果时:
(1, '三国i演义')
如果给values_list
只指定一个字段,那我们可以指定flat=True
,这样返回回来的结果就不在是一个元组,而是这个字段的值,示例代码如下:
books = Book.objects.values_list('name', flat=True)
那么以上返回的结果是:
'三国演义'
一点要注意的是,flat只能用在一个字段的情况下,否则就会报错。
all:
这个方法简单的返回一个QuerySet
对象,这个QuerySet
对象没有经过任何的修改(比如:过滤等)
select_related:
在查找某个表的数据的时候,可以一次性把相关联的其他表的数据都提取出来,这样可以在以后访问相关联的表的数据的时候,不用再次查询数据库,可以节省一些开销。示例代码如下:
books = Book.objects.select_related("author", ''publisher")
for book in books:
print(book.author.name)
# 因为在提取book时后,使用了select_related,
# 那么以后再访问book.author的时候,不会再次项数据库重新发起查询
注意:这个方法只能用在外键关联的对象上,对于那种多对多、或者多对一的情况,不能使用它来实现,而应该使用prefetch_related
来实现。
prefetch_related:
这个方法类似于select_related
方法,也是用来在查询语句的时候,提前将指定的数据找出来,只不过这个方法用来解决多对多,或者多对一的情况,这个方法产生两个查询语句。所以,若果在这个方法中查询使用外键关联的模型时,也会产生两个查询语句,因此如果查询的是外键关联的模型建议使用select_related
方法。在查询多对多或者多对一的关联对象的时候,在使用模型怎么访问这个多对多,那么就在这个方法中传递什么字符串,比如要获取图书上的所有订单,那么示例代码如下:
books = Book.objects.preftch_related(''bookorder_set")
需要注意的是:在使用preftch_related
查找出来的bookorder_ser
,建议不要再对他进行任何操作,不如filter
,不然又会产生N多查询语句,比如以下代码是不对的:
books = Book.objects.preftch_related(''bookorder_set")
for book in books:
print('='*30)
print(book.name)
orders = book.bookorder_set.filter(price__gte=90)
for order in orders:
print(order.id)
那么如果确实像要对预先查找的集合进行操作,那么我们可以使用django.db.models.Prefetch
来完成,示例代码如下:
form django.db.models import Prefetch
# 先使用Prefetch把查找的条件写好,再放到prefetch_related中
prefetch = Prefetch("bookorder_set", queryset=BookOrder.objects.filter(price__gte=90))
books = Book.objects.preftch_related(prefetch)
for book in books:
print('='*30)
print(book.name)
orders = book.bookorder_set.all()
for order in orders:
print(order.id)
defer和only:
这个方法都会返回一个QuerySet
对象,并且这个QuerySet
中装的都是模型,而不是字典。
1、defer
: 这个方法用来告诉ORM
, 在查询某个模型的时候,过滤掉某些字段。
2、only
: 这个方法用来告诉ORM
,在查询某个模型的时候,只提取某几个字段。
注意:使用defer
或者only
,如果没有提取这个字段,那么后面使用这个字段,会重新发起一次请求,因此要谨慎使用。
get:
获取满足条件的值,这个方法给定的条件只能匹配到一条数据,如果匹配到多条数据或者没有数据,它都会报错,因此使用时,都是根据主键为条件来匹配的。
create:
可以创建一条数据,并且将这条数据保存到数据库中,以下代码是等价的。
publisher = Publisher(name='xxxx出版社')
publisher.save()
# 等价于下面这句
Publisher.objects.create(name='xxxx出版社')
get_or_create:
如果给定的条件有数据,那么就会把这个数据给提取出来,如果给定的条件没有数据,就会先创建数据,然后把数据给返回回来。
bulk_creat:
一次性创建多个数据,示例代码如下:
publisher = Publisher.objects.bulk_create([
Publisher(name='123出版社'),
Publisher(name='456出版社')
])
count:
获取提取的数据个数,如果想要知道总共有多少条数据,那么建议使用count,而不是使用len(books)这种,因为Count在底层是使用select count(*)
来实现的,这种方式比使用len函数更加高效。
first 和 last
返回QuerySet
中第一和最后一条数据
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)
distinct:
去掉那些重复的数据,这个方法如果底层数据库使用的是MySQL,那么不能传递任何参数,比如想要提取所有销售价格超过80元的图书,并且删除掉那些重复的,那么可以使用distinct来帮我们实现,示例代码如下:
books = Book.objects.filter(bookorder__price__gte=80).distinct()
需要注意的是,如果distinct之前使用了order_by,那么order_by会提取order_by中指定的字段,因此,再使用distinct1就会根据多个字段来进行唯一化,就不会把那些重复的数据删除掉,示例代码如下:
orders = BookOrder.objects.order_by('create_time').values('book_id').distinct()
# 那么以上代码因为使用了order_by,即使使用了distinct,也会把重复的book_id提取出来
update和delete:
一次性可以把所有的数据都更新,
一次性可以把满足条件的数据都删除掉,但需要注意的删除数据的时候,要注意on_delete指定的处理方式。
切片操作:
有时候我们查找数据,有可能只需要其中的一部分,那么这时候可以使用切片操作来帮助我们完成。QuerySet使用切片操作就跟列表使用切片操作一样,示例代码如下:
books = Book.objects.all()[1:3]
for book in books:
print(book)
切片操作并不是把所有的数据从数据库中提取出来再做切片操作,而是在数据库层面使用LIMIT和OFFSET来帮助我们完成。
什么时候 Django 会将 QuerySet 转换为 SQL 去执行:
1、迭代
2、使用步长做切片操作
3、调用len函数
4、调用list函数:将一个QuerySet对象转换为list对象也会立即执行SQL语句。
5、判断:如果对某个QuerySet进行片段,也会立马执行 SQL 语句。