Django模型层(2)

时间:2021-06-20 16:17:01

<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>9 Django 模型层(2) - Yuan先生 - 博客园</title>
<link type="text/css" rel="stylesheet" href="/bundles/blog-common.css?v=-hy83QNg62d4qYibixJzxMJkbf1P9fTBlqv7SK5zVL01"/>
<link id="MainCss" type="text/css" rel="stylesheet" href="/skins/summerGarden/bundle-summerGarden.css?v=R6EW1cwbYc7SqZ5y0CMKPNjYaFnIdEGDIwRo4NL-lHw1"/>
<link type="text/css" rel="stylesheet" href="/blog/customcss/262677.css?v=OnOP1mIcZaQtSlUQQ3R9nRxA03w%3d"/>
<link id="mobile-style" media="only screen and (max-width: 767px)" type="text/css" rel="stylesheet" href="/skins/summerGarden/bundle-summerGarden-mobile.css?v=9eIhgMh0fONJ_oTulhhwN-Ot1dq2yL8Q7MlDslRvpnA1"/>
<link title="RSS" type="application/rss+xml" rel="alternate" href="http://www.cnblogs.com/yuanchenqi/rss"/>
<link title="RSD" type="application/rsd+xml" rel="EditURI" href="http://www.cnblogs.com/yuanchenqi/rsd.xml"/>
<link type="application/wlwmanifest+xml" rel="wlwmanifest" href="http://www.cnblogs.com/yuanchenqi/wlwmanifest.xml"/>
<script src="//common.cnblogs.com/scripts/jquery-2.2.0.min.js"></script>
<script type="text/javascript">var currentBlogApp = 'yuanchenqi', cb_enable_mathjax=false;var isLogined=true;</script>
<script src="/bundles/blog-common.js?v=d16NGD79qD3qnJt25hXDZ2sGoojamz2W5Rl4vT0CGVg1" type="text/javascript"></script>
</head>
<body>
<a name="top"></a>

<!--done-->
<div id="home">
<div id="header">
<div id="blogTitle">
<a id="lnkBlogLogo" href="http://www.cnblogs.com/yuanchenqi/"><img id="blogLogo" src="/Skins/custom/images/logo.gif" alt="返回主页" /></a>

<!--done-->
<h1><a id="Header1_HeaderTitle" class="headermaintitle" href="http://www.cnblogs.com/yuanchenqi/">Yuan先生</a></h1>
<h2></h2>

</div><!--end: blogTitle 博客的标题和副标题 -->
<div id="navigator">

<ul id="navList">
<li></li>
<li></li>
<li><a href="http://news.cnblogs.com/">新闻</a></li>
<li></li>
<li></li>
<li><a id="blog_nav_admin" class="menu" rel="nofollow" href="https://i.cnblogs.com/">管理</a></li>
<li>
</li>
</ul>

<div class="blogStats">

</div><!--end: blogStats -->
</div><!--end: navigator 博客导航栏 -->
</div><!--end: header 头部 -->

<div id="main">
<div id="mainContent">
<div class="forFlow">

<div id="post_detail">
<!--done-->
<div id="topics">
<div class = "post">
<h1 class = "postTitle">
<a id="cb_post_title_url" class="postTitle2" href="https://www.cnblogs.com/yuanchenqi/articles/8963244.html">9 Django 模型层(2)</a>
</h1>
<div class="clear"></div>
<div class="postBody">
<div id="cnblogs_post_body" class="blogpost-body"><h1>多表操作</h1>
<h2>创建模型</h2>
<p>实例:我们来假定下面这些概念,字段和关系</p>
<p>作者模型:一个作者有姓名和年龄。</p>
<p>作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)</p>
<p>出版商模型:出版商有名称,所在城市以及email。</p>
<p>书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。</p>
<p>模型建立如下:</p>
<div class="cnblogs_code">
<pre><span style="color: #0000ff;">from</span> django.db <span style="color: #0000ff;">import</span><span style="color: #000000;"> models

</span><span style="color: #008000;">#</span><span style="color: #008000;"> Create your models here.</span>

<span style="color: #0000ff;">class</span><span style="color: #000000;"> Author(models.Model):
nid </span>= models.AutoField(primary_key=<span style="color: #000000;">True)
name</span>=models.CharField( max_length=32<span style="color: #000000;">)
age</span>=<span style="color: #000000;">models.IntegerField()

</span><span style="color: #008000;">#</span><span style="color: #008000;"> 与AuthorDetail建立一对一的关系</span>
authorDetail=models.OneToOneField(to=<span style="color: #800000;">"</span><span style="color: #800000;">AuthorDetail</span><span style="color: #800000;">"</span>,on_delete=<span style="color: #000000;">models.CASCADE)<br />
</span><span style="color: #0000ff;">class</span><span style="color: #000000;"> AuthorDetail(models.Model):

nid </span>= models.AutoField(primary_key=<span style="color: #000000;">True)
birthday</span>=<span style="color: #000000;">models.DateField()
telephone</span>=<span style="color: #000000;">models.BigIntegerField()
addr</span>=models.CharField( max_length=64<span style="color: #000000;">)

</span><span style="color: #0000ff;">class</span><span style="color: #000000;"> Publish(models.Model):
nid </span>= models.AutoField(primary_key=<span style="color: #000000;">True)
name</span>=models.CharField( max_length=32<span style="color: #000000;">)
city</span>=models.CharField( max_length=32<span style="color: #000000;">)
email</span>=<span style="color: #000000;">models.EmailField()

</span><span style="color: #0000ff;">class</span><span style="color: #000000;"> Book(models.Model):

nid </span>= models.AutoField(primary_key=<span style="color: #000000;">True)
title </span>= models.CharField( max_length=32<span style="color: #000000;">)
publishDate</span>=<span style="color: #000000;">models.DateField()
price</span>=models.DecimalField(max_digits=5,decimal_places=2<span style="color: #000000;">)

</span><span style="color: #008000;">#</span><span style="color: #008000;"> 与Publish建立一对多的关系,外键字段建立在多的一方</span>
publish=models.ForeignKey(to=<span style="color: #800000;">"</span><span style="color: #800000;">Publish</span><span style="color: #800000;">"</span>,to_field=<span style="color: #800000;">"</span><span style="color: #800000;">nid</span><span style="color: #800000;">"</span>,on_delete=<span style="color: #000000;">models.CASCADE)
</span><span style="color: #008000;">#</span><span style="color: #008000;"> 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表</span>
authors=models.ManyToManyField(to=<span style="color: #800000;">'</span><span style="color: #800000;">Author</span><span style="color: #800000;">'</span>,)</pre>
</div>
<p>&nbsp;生成表如下:</p>
<p><img src="https://images2018.cnblogs.com/blog/877318/201805/877318-20180501223306119-1804705285.png" alt="" width="415" height="105" /></p>
<p><img src="https://images2018.cnblogs.com/blog/877318/201805/877318-20180501223412676-377958198.png" alt="" width="404" height="100" /></p>
<p><img src="https://images2018.cnblogs.com/blog/877318/201805/877318-20180501223511240-492522724.png" alt="" width="476" height="100" /></p>
<p><img src="https://images2018.cnblogs.com/blog/877318/201805/877318-20180501223620025-185160553.png" alt="" width="350" height="99" /></p>
<p><img src="https://images2018.cnblogs.com/blog/877318/201805/877318-20180501223701796-763894415.png" alt="" width="377" height="100" /></p>
<p>&nbsp;</p>
<p>注意事项:</p>
<ul>
<li>&nbsp;表的名称<code>myapp_modelName</code>,是根据 模型中的元数据自动生成的,也可以覆写为别的名称  </li>
<li><code><span style="font-family: 'PingFang SC', 'Helvetica Neue', Helvetica, Arial, sans-serif;">&nbsp;</span>id</code>&nbsp;字段是自动添加的</li>
<li>&nbsp;对于外键字段,Django 会在字段名上添加<tt class="docutils literal">"_id"</tt>&nbsp;来创建数据库中的列名</li>
<li>&nbsp;这个例子中的<code>CREATE TABLE</code>&nbsp;SQL 语句使用PostgreSQL 语法格式,要注意的是Django 会根据settings&nbsp;中指定的数据库类型来使用相应的SQL 语句。</li>
<li>&nbsp;定义好模型之后,你需要告诉Django _使用_这些模型。你要做的就是修改配置文件中的INSTALL_APPSZ中设置,在其中添加<code>models.py</code>所在应用的名称。</li>
<li class="line" data-line="91">外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None 。</li>
</ul>
<h2 class="line" data-line="91">添加表纪录&nbsp;</h2>
<p>操作前先简单的录入一些数据:</p>
<p>publish表:</p>
<p><img src="https://images2018.cnblogs.com/blog/877318/201805/877318-20180501231939514-1159349232.png" alt="" width="341" height="65" /></p>
<p>author表:</p>
<p><img src="https://images2018.cnblogs.com/blog/877318/201805/877318-20180501232010534-1683544746.png" alt="" width="281" height="63" /></p>
<p>authordetail表:</p>
<p><img src="https://images2018.cnblogs.com/blog/877318/201805/877318-20180501232217201-492441826.png" alt="" width="311" height="65" /></p>
<h3>一对多</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;">方式1:
publish_obj=Publish.objects.get(nid=1)
book_obj=Book.objects.create(title="金瓶眉",publishDate="2012-12-12",price=100,publish=publish_obj)

方式2:
book_obj=Book.objects.create(title="金瓶眉",publishDate="2012-12-12",price=100,publish_id=1)  </pre>
</div>
<p class="line" data-line="91"><img src="https://images2018.cnblogs.com/blog/877318/201805/877318-20180501231012070-1410608284.png" alt="" width="353" height="48" /></p>
<p class="line" data-line="91"><span style="color: #ff0000;">核心:book_obj.publish与book_obj.publish_id是什么?&nbsp;</span></p>
<h3>多对多</h3>
<div class="cnblogs_code">
<pre><span style="color: #008000;"> #</span><span style="color: #008000;"> 当前生成的书籍对象</span>
book_obj=Book.objects.create(title=<span style="color: #800000;">"</span><span style="color: #800000;">追风筝的人</span><span style="color: #800000;">"</span>,price=200,publishDate=<span style="color: #800000;">"</span><span style="color: #800000;">2012-11-12</span><span style="color: #800000;">"</span>,publish_id=1<span style="color: #000000;">)
</span><span style="color: #008000;">#</span><span style="color: #008000;"> 为书籍绑定的做作者对象</span>
yuan=Author.objects.filter(name=<span style="color: #800000;">"</span><span style="color: #800000;">yuan</span><span style="color: #800000;">"</span>).first() <span style="color: #008000;">#</span><span style="color: #008000;"> 在Author表中主键为2的纪录</span>
egon=Author.objects.filter(name=<span style="color: #800000;">"</span><span style="color: #800000;">alex</span><span style="color: #800000;">"</span>).first() <span style="color: #008000;">#</span><span style="color: #008000;"> 在Author表中主键为1的纪录</span>

<span style="color: #008000;">#</span><span style="color: #008000;"> 绑定多对多关系,即向关系表book_authors中添加纪录</span>
book_obj.authors.add(yuan,egon) <span style="color: #008000;">#</span><span style="color: #008000;"> 将某些特定的 model 对象添加到被关联对象集合中。 ======= book_obj.authors.add(*[])</span></pre>
</div>
<p>数据库表纪录生成如下:</p>
<p>book表&nbsp;</p>
<p><img src="https://images2018.cnblogs.com/blog/877318/201805/877318-20180501233728425-1500453543.png" alt="" width="448" height="63" /></p>
<p>book_authors表</p>
<p><img src="https://images2018.cnblogs.com/blog/877318/201805/877318-20180501233939850-1362764638.png" alt="" width="190" height="58" /></p>
<p><span style="color: #ff0000;">核心:book_obj.authors.all()是什么?</span></p>
<p>多对多关系其它常用API:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;">book_obj.authors.remove() # 将某个特定的对象从被关联对象集合中去除。 ====== book_obj.authors.remove(*[])
book_obj.authors.clear() #清空被关联对象集合
book_obj.authors.set() #先清空再设置  </pre>
</div>
<p><a href="http://www.cnblogs.com/yuanchenqi/articles/8978167.html" target="_blank">more</a></p>
<h2>基于对象的跨表查询</h2>
<h3>一对多查询(Publish 与 Book)</h3>
<p>正向查询(按字段:publish):</p>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;"> # 查询主键为1的书籍的出版社所在的城市
book_obj=Book.objects.filter(pk=1).first()
# book_obj.publish 是主键为1的书籍对象关联的出版社对象
print(book_obj.publish.city)  </pre>
</div>
<p>反向查询(按表名:book_set):</p>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;"> publish=Publish.objects.get(name="苹果出版社")
#publish.book_set.all() : 与苹果出版社关联的所有书籍对象集合
book_list=publish.book_set.all()
for book_obj in book_list:
print(book_obj.title)</pre>
</div>
<h3>一对一查询(Author 与 AuthorDetail)</h3>
<p>正向查询(按字段:authorDetail):</p>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;"> egon=Author.objects.filter(name="egon").first()
print(egon.authorDetail.telephone)</pre>
</div>
<p>反向查询(按表名:author):</p>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;"># 查询所有住址在北京的作者的姓名

authorDetail_list=AuthorDetail.objects.filter(addr="beijing")
for obj in authorDetail_list:
print(obj.author.name)</pre>
</div>
<h3>多对多查询 (Author 与 Book)</h3>
<p>正向查询(按字段:authors):</p>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;"># 金瓶眉所有作者的名字以及手机号

book_obj=Book.objects.filter(title="金瓶眉").first()
authors=book_obj.authors.all()
for author_obj in authors:
print(author_obj.name,author_obj.authorDetail.telephone)</pre>
</div>
<p>反向查询(按表名:book_set):</p>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;"># 查询egon出过的所有书籍的名字

author_obj=Author.objects.get(name="egon")
book_list=author_obj.book_set.all() #与egon作者相关的所有书籍
for book_obj in book_list:
print(book_obj.title)</pre>
</div>
<p><strong>注意:</strong></p>
<p>你可以通过在 ForeignKey() 和ManyToManyField的定义中设置 related_name 的值来覆写 FOO_set 的名称。例如,如果 Article model 中做一下更改:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;">publish = ForeignKey(Book, related_name='bookList')</pre>
</div>
<p>那么接下来就会如我们看到这般:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;"> # 查询 人民出版社出版过的所有书籍

publish=Publish.objects.get(name="人民出版社")
book_list=publish.bookList.all() # 与人民出版社关联的所有书籍对象集合</pre>
</div>
<h2>基于双下划线的跨表查询&nbsp;</h2>
<p>Django 还提供了一种直观而高效的方式在查询(lookups)中表示关联关系,它能自动确认 SQL JOIN 联系。要做跨关系查询,就使用两个下划线来链接模型(model)间关联字段的名称,直到最终链接到你想要的 model 为止。</p>
<p><span style="color: #ff0000;">关键点:正向查询按字段,反向查询按表名。</span></p>
<h3>一对多查询</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;"># 练习1: 查询苹果出版社出版过的所有书籍的名字与价格(一对多)

# 正向查询 按字段:publish

queryResult=Book.objects
            .filter(publish__name="苹果出版社")
            .values_list("title","price")

# 反向查询 按表名:book

queryResult=Publish.objects
              .filter(name="苹果出版社")
              .values_list("book__title","book__price")</pre>
</div>
<h3>多对多查询  </h3>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;"># 练习2: 查询alex出过的所有书籍的名字(多对多)

# 正向查询 按字段:authors:
queryResult=Book.objects
            .filter(authors__name="yuan")
            .values_list("title")

# 反向查询 按表名:book
queryResult=Author.objects
              .filter(name="yuan")
              .values_list("book__title","book__price")</pre>
</div>
<h3>混合使用</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;"># 练习3: 查询人民出版社出版过的所有书籍的名字以及作者的姓名

# 正向查询
queryResult=Book.objects
            .filter(publish__name="人民出版社")
            .values_list("title","authors__name")
# 反向查询
queryResult=Publish.objects
              .filter(name="人民出版社")
              .values_list("book__title","book__authors__age","book__authors__name")

# 练习4: 手机号以151开头的作者出版过的所有书籍名称以及出版社名称

queryResult=Book.objects
            .filter(authors__authorDetail__telephone__regex="151")
            .values_list("title","publish__name")</pre>
</div>
<p>注意:</p>
<p>反向查询时,如果定义了related_name ,则用related_name替换表名,例如:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;">publish = ForeignKey(Blog, related_name='bookList')
</pre>
</div>
<p class="brush:python;collapse:true;;gutter:true;">练习1: 查询人民出版社出版过的所有书籍的名字与价格(一对多)</p>
<div class="cnblogs_Highlighter">
<pre class="brush:python;collapse:true;;gutter:true;"> #反向查询 不再按表名:book,而是related_name:bookList

queryResult=Publish.objects
              .filter(name="人民出版社")
              .values_list("bookList__title","bookList__price")&nbsp;</pre>
</div>
<h2>聚合查询与分组查询</h2>
<h3><tt class="descname">聚合</tt></h3>
<p><tt class="descname">aggregate</tt>(*args,&nbsp;**kwargs)</p>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;"># 计算所有图书的平均价格
&gt;&gt;&gt; from django.db.models import Avg
&gt;&gt;&gt; Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35}
</pre>
</div>
<p><span id="yiyi-17" class="yiyi-st"><tt class="docutils literal"><span class="pre">aggregate()</span></tt>是<tt class="docutils literal"><span class="pre">QuerySet</span></tt>&nbsp;的一个终止子句,意思是说,它返回一个包含一些键值对的字典。<span id="yiyi-18" class="yiyi-st">键的名称是聚合值的标识符,<span id="yiyi-19" class="yiyi-st">值是计算出来的聚合值。<span id="yiyi-20" class="yiyi-st">键的名称是按照字段和聚合函数的名称自动生成出来的。<span id="yiyi-21" class="yiyi-st">如果你想要为聚合值指定一个名称,可以向聚合子句提供它。</span></span></span></span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;">&gt;&gt;&gt; Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 34.35}
</pre>
</div>
<p><span id="yiyi-22" class="yiyi-st">如果你希望生成不止一个聚合,你可以向<tt class="docutils literal"><span class="pre">aggregate()</span></tt>子句中添加另一个参数。<span id="yiyi-23" class="yiyi-st">所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询:</span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;">&gt;&gt;&gt; from django.db.models import Avg, Max, Min
&gt;&gt;&gt; Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}
</pre>
</div>
<h3>分组</h3>
<div class="cnblogs_code">
<pre><span style="color: #008000;">#</span><span style="color: #008000;">##################################--单表分组查询--#######################################################</span>
<span style="color: #000000;">

查询每一个部门名称以及对应的员工数

emp:

id name age salary dep
</span>1 alex 12 2000<span style="color: #000000;"> 销售部
</span>2 egon 22 3000<span style="color: #000000;"> 人事部
</span>3 wen 22 5000<span style="color: #000000;"> 人事部

sql语句:
select dep,Count(</span>*) <span style="color: #0000ff;">from</span><span style="color: #000000;"> emp group by dep;

ORM:
emp.objects.all().values(</span><span style="color: #800000;">"</span><span style="color: #800000;">dep</span><span style="color: #800000;">"</span>).annotate(Count(<span style="color: #800000;">"</span><span style="color: #800000;">id</span><span style="color: #800000;">"</span><span style="color: #000000;">)

</span><span style="color: #008000;">#</span><span style="color: #008000;">##################################--多表分组查询--#######################################################</span>
<span style="color: #000000;">

多表分组查询:

查询每一个部门名称以及对应的员工数

emp:

id name age salary dep_id
</span>1 alex 12 2000 1
2 egon 22 3000 2
3 wen 22 5000 2<span style="color: #000000;">

dep

id name
</span>1<span style="color: #000000;"> 销售部
</span>2<span style="color: #000000;"> 人事部

emp-dep:

id name age salary dep_id id name
</span>1 alex 12 2000 1 1<span style="color: #000000;"> 销售部
</span>2 egon 22 3000 2 2<span style="color: #000000;"> 人事部
</span>3 wen 22 5000 2 2<span style="color: #000000;"> 人事部

sql语句:

select dep.name,Count(</span>*) <span style="color: #0000ff;">from</span> emp left join dep on emp.dep_id=<span style="color: #000000;">dep.id group by emp.dep_id
select dep.name,Count(</span>*) <span style="color: #0000ff;">from</span> emp left join dep on emp.dep_id=<span style="color: #000000;">dep.id group by dep.id,dep.name

ORM:
dep.objetcs.all().annotate(c</span>=Count(<span style="color: #800000;">"</span><span style="color: #800000;">emp</span><span style="color: #800000;">"</span>)).values(<span style="color: #800000;">"</span><span style="color: #800000;">name</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">c</span><span style="color: #800000;">"</span>)</pre>
</div>
<p>annotate()为<span style="color: #ff0000;">调用的<tt class="docutils literal">QuerySet</tt></span>中每一个对象都生成一个独立的统计值(统计方法用聚合函数)。 </p>
<p>(1) 练习:统计每一本书的作者个数</p>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;"> bookList=Book.objects.annotate(authorsNum=Count('authors'))
for book_obj in bookList:
print(book_obj.title,book_obj.authorsNum)</pre>
</div>
<div class="cnblogs_code" onclick="cnblogs_code_show('bbb30d65-6d99-45e6-8057-f2ad0c5699c6')"><img id="code_img_closed_bbb30d65-6d99-45e6-8057-f2ad0c5699c6" class="code_img_closed" src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt="" /><img id="code_img_opened_bbb30d65-6d99-45e6-8057-f2ad0c5699c6" class="code_img_opened" style="display: none;" onclick="cnblogs_code_hide('bbb30d65-6d99-45e6-8057-f2ad0c5699c6',event)" src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" alt="" />
<div id="cnblogs_code_open_bbb30d65-6d99-45e6-8057-f2ad0c5699c6" class="cnblogs_code_hide">
<pre><span style="color: #000000;">SELECT
</span><span style="color: #800000;">"</span><span style="color: #800000;">app01_book</span><span style="color: #800000;">"</span>.<span style="color: #800000;">"</span><span style="color: #800000;">nid</span><span style="color: #800000;">"</span><span style="color: #000000;">,
</span><span style="color: #800000;">"</span><span style="color: #800000;">app01_book</span><span style="color: #800000;">"</span>.<span style="color: #800000;">"</span><span style="color: #800000;">title</span><span style="color: #800000;">"</span><span style="color: #000000;">,
</span><span style="color: #800000;">"</span><span style="color: #800000;">app01_book</span><span style="color: #800000;">"</span>.<span style="color: #800000;">"</span><span style="color: #800000;">publishDate</span><span style="color: #800000;">"</span><span style="color: #000000;">,
</span><span style="color: #800000;">"</span><span style="color: #800000;">app01_book</span><span style="color: #800000;">"</span>.<span style="color: #800000;">"</span><span style="color: #800000;">price</span><span style="color: #800000;">"</span><span style="color: #000000;">,
</span><span style="color: #800000;">"</span><span style="color: #800000;">app01_book</span><span style="color: #800000;">"</span>.<span style="color: #800000;">"</span><span style="color: #800000;">pageNum</span><span style="color: #800000;">"</span><span style="color: #000000;">,
</span><span style="color: #800000;">"</span><span style="color: #800000;">app01_book</span><span style="color: #800000;">"</span>.<span style="color: #800000;">"</span><span style="color: #800000;">publish_id</span><span style="color: #800000;">"</span><span style="color: #000000;">,
COUNT(</span><span style="color: #800000;">"</span><span style="color: #800000;">app01_book_authors</span><span style="color: #800000;">"</span>.<span style="color: #800000;">"</span><span style="color: #800000;">author_id</span><span style="color: #800000;">"</span>) AS <span style="color: #800000;">"</span><span style="color: #800000;">authorsNum</span><span style="color: #800000;">"</span><span style="color: #000000;">
FROM </span><span style="color: #800000;">"</span><span style="color: #800000;">app01_book</span><span style="color: #800000;">"</span> LEFT OUTER JOIN <span style="color: #800000;">"</span><span style="color: #800000;">app01_book_authors</span><span style="color: #800000;">"</span><span style="color: #000000;">
ON (</span><span style="color: #800000;">"</span><span style="color: #800000;">app01_book</span><span style="color: #800000;">"</span>.<span style="color: #800000;">"</span><span style="color: #800000;">nid</span><span style="color: #800000;">"</span> = <span style="color: #800000;">"</span><span style="color: #800000;">app01_book_authors</span><span style="color: #800000;">"</span>.<span style="color: #800000;">"</span><span style="color: #800000;">book_id</span><span style="color: #800000;">"</span><span style="color: #000000;">)
GROUP BY
</span><span style="color: #800000;">"</span><span style="color: #800000;">app01_book</span><span style="color: #800000;">"</span>.<span style="color: #800000;">"</span><span style="color: #800000;">nid</span><span style="color: #800000;">"</span><span style="color: #000000;">,
</span><span style="color: #800000;">"</span><span style="color: #800000;">app01_book</span><span style="color: #800000;">"</span>.<span style="color: #800000;">"</span><span style="color: #800000;">title</span><span style="color: #800000;">"</span><span style="color: #000000;">,
</span><span style="color: #800000;">"</span><span style="color: #800000;">app01_book</span><span style="color: #800000;">"</span>.<span style="color: #800000;">"</span><span style="color: #800000;">publishDate</span><span style="color: #800000;">"</span><span style="color: #000000;">,
</span><span style="color: #800000;">"</span><span style="color: #800000;">app01_book</span><span style="color: #800000;">"</span>.<span style="color: #800000;">"</span><span style="color: #800000;">price</span><span style="color: #800000;">"</span><span style="color: #000000;">,
</span><span style="color: #800000;">"</span><span style="color: #800000;">app01_book</span><span style="color: #800000;">"</span>.<span style="color: #800000;">"</span><span style="color: #800000;">pageNum</span><span style="color: #800000;">"</span><span style="color: #000000;">,
</span><span style="color: #800000;">"</span><span style="color: #800000;">app01_book</span><span style="color: #800000;">"</span>.<span style="color: #800000;">"</span><span style="color: #800000;">publish_id</span><span style="color: #800000;">"</span></pre>
</div>
<span class="cnblogs_code_collapse">sql</span></div>
<p>(2) 如果想对所查询对象的关联对象进行聚合:</p>
<p class="brush:python;gutter:true;">练习:统计每一个出版社的最便宜的书</p>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;"> publishList=Publish.objects.annotate(MinPrice=Min("book__price"))
for publish_obj in publishList:
print(publish_obj.name,publish_obj.MinPrice)
</pre>
</div>
<p>annotate的返回值是querySet,如果不想遍历对象,可以用上valuelist:</p>
<div class="cnblogs_code">
<pre>queryResult=<span style="color: #000000;"> Publish.objects
            .annotate(MinPrice</span>=Min(<span style="color: #800000;">"</span><span style="color: #800000;">book__price</span><span style="color: #800000;">"</span><span style="color: #000000;">))
            .values_list(</span><span style="color: #800000;">"</span><span style="color: #800000;">name</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">MinPrice</span><span style="color: #800000;">"</span><span style="color: #000000;">)
</span><span style="color: #0000ff;">print</span>(queryResult)</pre>
</div>
<p>方式2: </p>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;">queryResult=Book.objects.values("publish__name").annotate(MinPrice=Min('price')) # 思考: if 有一个出版社没有出版过书会怎样?</pre>
</div>
<p>(3) 统计每一本以py开头的书籍的作者个数:</p>
<div class="cnblogs_code">
<pre> queryResult=<span style="color: #000000;">Book.objects
           .filter(title__startswith</span>=<span style="color: #800000;">"</span><span style="color: #800000;">Py</span><span style="color: #800000;">"</span><span style="color: #000000;">)
           .annotate(num_authors</span>=Count(<span style="color: #800000;">'</span><span style="color: #800000;">authors</span><span style="color: #800000;">'</span>))</pre>
</div>
<p>(4) 统计不止一个作者的图书:</p>
<div class="cnblogs_code">
<pre>queryResult=<span style="color: #000000;">Book.objects
          .annotate(num_authors</span>=Count(<span style="color: #800000;">'</span><span style="color: #800000;">authors</span><span style="color: #800000;">'</span><span style="color: #000000;">))
          .filter(num_authors__gt</span>=1)</pre>
</div>
<p>(5)&nbsp;根据一本图书作者数量的多少对查询集&nbsp;<tt class="docutils literal">QuerySet</tt>进行排序:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;">Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors')
</pre>
</div>
<p>(6) 查询各个作者出的书的总价格:</p>
<div class="cnblogs_code">
<pre><span style="color: #008000;">#</span><span style="color: #008000;"> 按author表的所有字段 group by</span>
queryResult=Author.objects<br />              .annotate(SumPrice=Sum(<span style="color: #800000;">"</span><span style="color: #800000;">book__price</span><span style="color: #800000;">"</span>))<br />              .values_list(<span style="color: #800000;">"</span><span style="color: #800000;">name</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">SumPrice</span><span style="color: #800000;">"</span><span style="color: #000000;">)
</span><span style="color: #0000ff;">print</span><span style="color: #000000;">(queryResult)</span><span style="color: #008000;"><br /></span></pre>
</div>
<h2>F查询与Q查询</h2>
<h3>F查询</h3>
<p>在上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢?</p>
<p>Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。</p>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;"> # 查询评论数大于收藏数的书籍

from django.db.models import F
Book.objects.filter(commnetNum__lt=F('keepNum'))</pre>
</div>
<p>Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。</p>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;"># 查询评论数大于收藏数2倍的书籍
Book.objects.filter(commnetNum__lt=F('keepNum')*2)</pre>
</div>
<p>修改操作也可以使用F函数,比如将每一本书的价格提高30元:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;">Book.objects.all().update(price=F("price")+30) </pre>
</div>
<h3>Q查询</h3>
<p><span id="yiyi-212" class="yiyi-st"><tt class="xref py py-meth docutils literal"><span class="pre">filter()</span></tt>&nbsp;等方法中的关键字参数查询都是一起进行&ldquo;AND&rdquo; 的。&nbsp;<span id="yiyi-213" class="yiyi-st">如果你需要执行更复杂的查询(例如<tt class="docutils literal"><span class="pre">OR</span></tt>&nbsp;语句),你可以使用<tt class="xref py py-class docutils literal"><span class="pre">Q&nbsp;<span class="pre">对象</span></span></tt>。</span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;">from django.db.models import Q
Q(title__startswith='Py')
</pre>
</div>
<p><span id="yiyi-217" class="yiyi-st"><tt class="docutils literal"><span class="pre">Q</span></tt>&nbsp;对象可以使用<tt class="docutils literal"><span class="pre">&amp;</span></tt>&nbsp;和<tt class="docutils literal"><span class="pre">|</span></tt>&nbsp;操作符组合起来。<span id="yiyi-218" class="yiyi-st">当一个操作符在两个<tt class="docutils literal"><span class="pre">Q</span></tt>&nbsp;对象上使用时,它产生一个新的<tt class="docutils literal"><span class="pre">Q</span></tt>&nbsp;对象。</span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;">bookList=Book.objects.filter(Q(authors__name="yuan")|Q(authors__name="egon"))
</pre>
</div>
<p>等同于下面的SQL&nbsp;<tt class="docutils literal"><span class="pre">WHERE</span></tt>&nbsp;子句:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;">WHERE name ="yuan" OR name ="egon"
</pre>
</div>
<p><span id="yiyi-221" class="yiyi-st">你可以组合<tt class="docutils literal"><span class="pre">&amp;</span></tt>&nbsp;和<tt class="docutils literal"><span class="pre">|</span></tt>&nbsp;&nbsp;操作符以及使用括号进行分组来编写任意复杂的<tt class="docutils literal"><span class="pre">Q</span></tt>&nbsp;对象。<span id="yiyi-222" class="yiyi-st">同时,<tt class="docutils literal"><span class="pre">Q</span></tt>&nbsp;对象可以使用<tt class="docutils literal"><span class="pre">~</span></tt>&nbsp;操作符取反,这允许组合正常的查询和取反(<tt class="docutils literal"><span class="pre">NOT</span></tt>) 查询:</span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;"> bookList=Book.objects.filter(Q(authors__name="yuan") &amp; ~Q(publishDate__year=2017)).values_list("title")
</pre>
</div>
<p><span id="yiyi-227" class="yiyi-st">查询函数可以混合使用<tt class="docutils literal"><span class="pre">Q 对象</span></tt>和关键字参数。<span id="yiyi-228" class="yiyi-st">所有提供给查询函数的参数(关键字参数或<tt class="docutils literal"><span class="pre">Q</span></tt>&nbsp;对象)都将"AND&rdquo;在一起。<span id="yiyi-229" class="yiyi-st">但是,如果出现<tt class="docutils literal"><span class="pre">Q</span></tt>&nbsp;对象,它必须位于所有关键字参数的前面。<span id="yiyi-230" class="yiyi-st">例如:</span></span></span></span></p>
<div class="cnblogs_Highlighter">
<pre class="brush:python;gutter:true;"> bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017),
title__icontains="python"
)
</pre>
</div>
<p>  </p>
<p>&nbsp;</p></div><div id="MySignature"></div>
<div class="clear"></div>
<div id="blog_post_info_block">
<div id="BlogPostCategory"></div>
<div id="EntryTag"></div>
<div id="blog_post_info">
</div>
<div class="clear"></div>
<div id="post_next_prev"></div>
</div>

</div>
<div class = "postDesc">posted @ <span id="post-date">2018-04-27 16:44</span> <a href='http://www.cnblogs.com/yuanchenqi/'>Yuan先生</a> 阅读(<span id="post_view_count">...</span>) 评论(<span id="post_comment_count">...</span>) &nbsp;<a href="https://i.cnblogs.com/EditArticles.aspx?postid=8963244" rel="nofollow">编辑</a> <a href="#" onclick="AddToWz(8963244);return false;">收藏</a></div>
</div>
<script type="text/javascript">var allowComments=false,cb_blogId=262677,cb_entryId=8963244,cb_blogApp=currentBlogApp,cb_blogUserGuid='6d0ad0ce-3cb7-e511-9fc1-ac853d9f53cc',cb_entryCreatedDate='2018/4/27 16:44:00';loadViewCount(cb_entryId);var cb_postType=2;</script>

</div><!--end: topics 文章、评论容器-->
</div><a name="!comments"></a><div id="blog-comments-placeholder"></div><script type="text/javascript">var commentManager = new blogCommentManager();commentManager.renderComments(0);</script>
<div id='comment_form' class='commentform'>
<a name='commentform'></a>
<div id='divCommentShow'></div>
<div id='comment_nav'><span id='span_refresh_tips'></span><a href='javascript:void(0);' onclick='return RefreshCommentList();' id='lnk_RefreshComments' runat='server' clientidmode='Static'>刷新评论</a><a href='#' onclick='return RefreshPage();'>刷新页面</a><a href='#top'>返回顶部</a></div>
<div id='comment_form_container'></div>
<div class='ad_text_commentbox' id='ad_text_under_commentbox'></div>
<div id='ad_t2'></div>
<div id='opt_under_post'></div>
<div id='cnblogs_c1' class='c_ad_block'></div>
<div id='under_post_news'></div>
<div id='cnblogs_c2' class='c_ad_block'></div>
<div id='under_post_kb'></div>
<div id='HistoryToday' class='c_ad_block'></div>
<script type='text/javascript'>
fixPostBody();
setTimeout(function () { incrementViewCount(cb_entryId); }, 50);
deliverAdT2();
deliverAdC1();
deliverAdC2();
loadNewsAndKb();
loadBlogSignature();
LoadPostInfoBlock(cb_blogId, cb_entryId, cb_blogApp, cb_blogUserGuid);
GetPrevNextPost(cb_entryId, cb_blogId, cb_entryCreatedDate, cb_postType);
loadOptUnderPost();
GetHistoryToday(cb_blogId, cb_blogApp, cb_entryCreatedDate);
</script>
</div>

</div><!--end: forFlow -->
</div><!--end: mainContent 主体内容容器-->

<div id="sideBar">
<div id="sideBarMain">

<div id="calendar"><div id="blog-calendar" style="display:none"></div><script type="text/javascript">loadBlogDefaultCalendar();</script></div>

<DIV id="leftcontentcontainer">
<div id="blog-sidecolumn"></div><script type="text/javascript">loadBlogSideColumn();</script>
</DIV>

</div><!--end: sideBarMain -->
</div><!--end: sideBar 侧边栏容器 -->
<div class="clear"></div>
</div><!--end: main -->
<div class="clear"></div>
<div id="footer">

<!--done-->
Copyright &copy;2018 Yuan先生
</div><!--end: footer -->
</div><!--end: home 自定义的最大容器 -->
<!--PageEndHtml Block Begin-->
<script language="javascript" type="text/javascript">
//生成目录索引列表
function GenerateContentList()
{
var jquery_h1_list = $('#cnblogs_post_body h1').not('.catListTitle').not('#blogTitle h1');//如果你的章节标题不是h4,只需要将这里的h4换掉即可
if(jquery_h1_list.length>0)
{
var content = '<a name="_labelTop"></a>';
content += '<div id="navCategory">';
content += '<p style="font-size:25px;color:#EE1289;"><i>知识预览</i></p>';
content += '<ul id="sortChapter">';
for(var i =0;i<jquery_h1_list.length;i++)
{
var go_to_top = '<div style="text-align: right"><a href="#_labelTop">回到顶部</a><a name="_label' + i + '"></a></div>';
$(jquery_h1_list[i]).before(go_to_top);
var li_content = '<li><a href="#_label' + i + '"style="color: #003366;text-decoration:none;font-size:18px">' + $(jquery_h1_list[i]).text() + '</a></li>';
content += li_content;
}
content += '</ul>';
content += '</div>';
if($('#cnblogs_post_body').length != 0 )
{
$($('#cnblogs_post_body')[0]).prepend(content);
}
}
}
GenerateContentList();

$('#Header1_HeaderTitle').attr('href','https://i.cnblogs.com/EditArticles.aspx')

</script>
<!--PageEndHtml Block End-->
</body>
</html>