咱们一般作查询操做的时候,都是经过模型名字.objects的方式进行操做。其实模型名字.objects是一个django.db.models.manager.Manager对象,而Manager这个类是一个“空壳”的类,他自己是没有任何的属性和方法的。他的方法所有都是经过Python动态添加的方式,从QuerySet类中拷贝过来的。javascript
这个对象是django.db.models.manager.Manager
的对象,这个类是一个空壳类,他上面的全部方法都是从QuerySet
这个类上面拷贝过来的。所以咱们只要学会了QuerySet
,这个objects
也就知道该如何使用了。
Manager
源码解析:html
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中的一些方法拷贝出来
将知足条件的数据提取出来,返回一个新的QuerySet。具体的filter能够提供什么条件查询java
排除知足条件的数据,返回一个新的QuerySet。示例代码以下:python
Article.objects.exclude(title__contains='hello')
以上代码的意思是提取那些标题不包含hello的图书。ios
给QuerySet中的每一个对象都添加一个使用查询表达式(聚合函数、F表达式、Q表达式、Func表达式等)的新字段。示例代码以下:web
articles = Article.objects.annotate(author_name=F("author__name"))
以上代码将在每一个对象中都添加一个author__name的字段,用来显示这个文章的做者的年龄。sql
使用聚合函数。数据库
# 根据建立的时间正序排序 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
,会把前面排序的规则给打乱,而使用后面的排序方式。好比如下代码:django
articles = Article.objects.order_by("create_time").order_by("author__name")
他会根据做者的名字进行排序,而不是使用文章的建立时间。
固然,也能够在模型定义的在Meta
类中定义ordering
来指定默认的排序方式。示例代码以下:api
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
的返回值一样也是一个QuerySet
对象,可是这个QuetySet
中装的就不是模型了,而是一个一个的dict
字典.
若是咱们想要提取的是这个模型上关联的对象的属性,那么也是能够的,查找顺序跟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()
d跟values
是同样的做用.只不过这个方法返回的QuerySet
中,装的不是字典,而是元组,示例代码以下:
books = Book.objects.values_list("id","name")
那么以上代码的返回结果是:
(1,"西游记")
若是给values_list
只指定一个字段,那么咱们能够指定flat=True
,这样返回回来的结果就不在是一个元组,而是这个字段的值,示例代码以下:
books = Book.objects.values_list("name",flat=True)
那么以上返回的结果是:
`三国演义`
必定要注意的是,flat只能用在只有一个字段的状况下,不然就会报错.
查询模型下的全部数据,返回一个QuerySet
对象,这个QuerySet
对象没有通过任何的修改(好比过滤等)
在查找某个表的数据的时候,能够一次性把相关联的其余表的数据都提取出来,这样能够在之后访问相关联的表的数据的时候,不用再次查找数据库,能够节省一些开销.示例代码以下:
books = Book.objects.select_related("author","publisher") for book in books: print(book.author.name) # 由于在提取book的时候,使用了select_related,那么之后在访问book.author的时候,不会再次向数据库从新发起查询
注意:这个方法只能用在外键关联的对象上,对于那种多对多,或者多对一的状况不能使用它在实现,而应该使用prefetch_related
来实现.
这个方法相似与select_related
方法,也是用来查询语句的时候,提早将查找的数据提取出来.不过这个方法是用来解决多对多,非外键的的状况.这个方法会产生两个查询语句.因此,若是查询外键关联的模型就用select_related
,若是查询的是多对多或者非外键关联的状况,就用prefetch_related
.示例代码以下:
books = Book.objects.prefetch_related("bookorder_set")
须要注意的是:在使用prefetch_related
查找出来的boororder_set
,建议不要再对它进行任何操做,好比filter
,否则又会产生N多查询语句,影响查询的性能.好比如下的代码是不对的:
books = Book.objects.prefetch_related("bookorder_set") for book in books: print(book.name) # 这个地方若是对bookorder_set进行了操做,那么就又会产生新的sql语句,前面的prefetch_related就至关于白作了 oredrs = book.bookorder_set.fliter(price__gte=90) for order in orders: print(order.id)
那么若是确实是想要对预先查找的集合进行操做,那么咱们可使用django.db.models.Prefetch
来完成,示例代码以下:
# 先使用Prefetch把查找的条件写好,在放到prefetch_related中 from django.db.models import Prefetch prefetch = Prefetch("bookorder_set",queryset=Bookorder.objects.filter(price__gte=90)) books = Book.objects.prefetch_related(prefetch) for book in books: print(book.name) orders = book.bookorder_set.all() for order in orders: print(order.id)
这两个方法都会返回一个QuerySet
,而且这个QuerySet
中装的都是模型,而不是字段
1. defer
: 这个方法用来告诉ORM
,在查询某个模型的时候,过滤到某些字段.
2. only
: 这个方法用来告诉ONR
,在查询某个模型的时候,只提取某些字段.
注意: 使用defer
了的字段,之后在使用这个字段,会从新发起一次请求,所以要谨慎操做,only
也同理.示例代码以下:
articles = list(Article.objects.defer("title")) for article in articles: # 由于在上面提取的时候过滤了title # 这个地方从新获取title,将从新向数据库中进行一次查找操做 print(article.title) for sql in connection.queries: print('='*30) print(sql)
获取知足条件的数据,返回的是具体的模型。这个函数只能返回一条数据,而且若是给的条件有多条数据,那么这个方法会抛出MultipleObjectsReturned错误,若是给的条件没有任何数据,那么就会抛出DoesNotExit错误。因此这个方法在获取数据的只能,只能有且只有一条。
建立一条数据,而且保存到数据库中。这个方法至关于先用指定的模型建立一个对象,而后再调用这个对象的save方法。示例代码以下:
article = Article(title='abc') article.save() # 下面这行代码至关于以上两行代码 article = Article.objects.create(title='abc')
一次性建立多个数据,无论多少条数据,一条SQL语句解决
若是给定的条件有数据,那么就会把这个数据直接提取出来.若是给定的条件没有数据,那么就会先建立数据,而后再把数据返回回来.
count
:获取提取的数据的个数。若是想要知道总共有多少条数据,那么建议使用count
,而不是使用len(articles)
这种。由于count在底层是使用select count(*)
来实现的,这种方式比使用len函数更加的高效。
返回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
中指定的字段,所以再使用distinct
就会根据多个字段来进行惟一化,因此就不会把那些重复的数据删掉。示例代码以下:
orders = BookOrder.objects.order_by("create_time").values("book_id").distinct()
那么以上代码由于使用了order_by,即便使用了distinct,也会把重复的book_id提取出来。
一次性能够把全部的数据都更新完
update
:执行更新操做,在SQL底层走的也是update
命令。好比要将全部category为空的article的article字段都更新为默认的分类。示例代码以下:
Article.objects.filter(category__isnull=True).update(category_id=3)
注意这个方法走的是更新的逻辑。因此更新完成后保存到数据库中不会执行save
方法,所以不会更新auto_now
设置的字段。
delete
:删除全部知足条件的数据。删除数据的时候,要注意on_delete
指定的处理方式。
切片操做:有时候咱们查找数据,有可能只须要其中的一部分。那么这时候可使用切片操做来帮咱们完成。QuerySet使用切片操做就跟列表使用切片操做是同样的。示例代码以下:
books = Book.objects.all()[1:3] for book in books: print(book)
切片操做并非把全部数据从数据库中提取出来再作切片操做。而是在数据库层面使用LIMIE和OFFSET来帮咱们完成。因此若是只须要取其中一部分的数据的时候,建议你们使用切片操做。
生成一个QuerySet对象并不会立刻转换为SQL语句去执行。
好比咱们获取Book表下全部的图书:
books = Book.objects.all() print(connection.queries)
咱们能够看到在打印connection.quries的时候打印的是一个空的列表。说明上面的QuerySet并无真正的执行。
在如下状况下QuerySet会被转换为SQL语句执行:
迭代
:在遍历QuerySet对象的时候,会首先先执行这个SQL语句,而后再把这个结果返回进行迭代。好比如下代码就会转换为SQL语句:for book in Book.objects.all(): print(book)
使用步长
作切片
操做:QuerySet能够相似于列表同样作切片操做。作切片操做自己不会执行SQL语句,可是若是若是在作切片操做的时候提供了步长,那么就会立马执行SQL语句。须要注意的是,作切片后不能再执行filter方法,不然会报错。
调用len函数
:调用len函数用来获取QuerySet中总共有多少条数据也会执行SQL语句。
调用list函数
:调用list函数用来将一个QuerySet对象转换为list对象也会立马执行SQL语句。
判断
:若是对某个QuerySet进行判断,也会立马执行SQL语句。