Django+Query

来源:互联网 发布:软件评分表 编辑:程序博客网 时间:2024/06/11 02:56

今天学习的是Django的Model Query,前一篇已经学习过Model了,讲述的主要是Django中是如何处理关系型数据的模型的,一对一,多对一,多对多等,这篇则主要是描述的查询,能够将数据存进去,还得取出来,Django给每一个Model自动提供了丰富的查询接口,而且能够进行关联查询,基本上,能够满足绝大多数的查询需求。

在Django的文档中,有一句话说的非常好:

A model class represents a database table, and an instance of that class represents a particular record in the database table.

Model类的实例代表的是数据库中的记录,我们在进行查询的时候,得到结果一般都是一个数据库记录集合,这对应到Django里,就是QuerySet,Django提供的查询接口全都封装在QuerySet中,比如filter(), order_by()等,这对应到SQL语句,就是SELECT语句。这里我们只对经常用到的QuerySet方法进行简单描述,起到一个勾起回忆的作用,至于更多的内容还请参考QuerySet详细的API:QuerySet API。

先构造几个有关联关系的Model:

<code class="hljs python" style="display: block; padding: 0.5em; color: rgb(248, 248, 242); font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; margin-top: 0px !important; background: rgb(35, 36, 31);"><span class="hljs-keyword" style="color: rgb(249, 38, 114); margin-top: 0px !important;">from</span> django.db <span class="hljs-keyword" style="color: rgb(249, 38, 114);">import</span> models<span class="hljs-class"><span class="hljs-keyword" style="color: rgb(102, 217, 239); margin-top: 0px !important;">class</span> <span class="hljs-title" style="color: rgb(102, 0, 102);">Blog</span><span class="hljs-params" style="color: rgb(102, 0, 102);">(models.Model)</span>:</span>    name = models.CharField(max_length=<span class="hljs-number" style="color: rgb(174, 129, 255);">100</span>)    tagline = models.TextField()    <span class="hljs-function" style="color: rgb(249, 38, 114);"><span class="hljs-keyword" style="color: rgb(102, 217, 239); margin-top: 0px !important;">def</span> <span class="hljs-title" style="color: rgb(166, 226, 46);">__unicode__</span><span class="hljs-params" style="color: rgb(248, 248, 242);">(self)</span>:</span>        <span class="hljs-keyword" style="color: rgb(249, 38, 114);">return</span> self.name<span class="hljs-class"><span class="hljs-keyword" style="color: rgb(102, 217, 239); margin-top: 0px !important;">class</span> <span class="hljs-title" style="color: rgb(102, 0, 102);">Author</span><span class="hljs-params" style="color: rgb(102, 0, 102);">(models.Model)</span>:</span>    name = models.CharField(max_length=<span class="hljs-number" style="color: rgb(174, 129, 255);">50</span>)    email = models.EmailField()    <span class="hljs-function" style="color: rgb(249, 38, 114);"><span class="hljs-keyword" style="color: rgb(102, 217, 239); margin-top: 0px !important;">def</span> <span class="hljs-title" style="color: rgb(166, 226, 46);">__unicode__</span><span class="hljs-params" style="color: rgb(248, 248, 242);">(self)</span>:</span>        <span class="hljs-keyword" style="color: rgb(249, 38, 114);">return</span> self.name<span class="hljs-class"><span class="hljs-keyword" style="color: rgb(102, 217, 239); margin-top: 0px !important;">class</span> <span class="hljs-title" style="color: rgb(102, 0, 102);">Entry</span><span class="hljs-params" style="color: rgb(102, 0, 102);">(models.Model)</span>:</span>    blog = models.ForeignKey(Blog)    headline = models.CharField(max_length=<span class="hljs-number" style="color: rgb(174, 129, 255);">255</span>)    body_text = models.TextField()    pub_date = models.DateField()    mod_date = models.DateField()    authors = models.ManyToManyField(Author)    n_comments = models.IntegerField()    n_pingbacks = models.IntegerField()    rating = models.IntegerField()    <span class="hljs-function" style="color: rgb(249, 38, 114);"><span class="hljs-keyword" style="color: rgb(102, 217, 239); margin-top: 0px !important;">def</span> <span class="hljs-title" style="color: rgb(166, 226, 46);">__unicode__</span><span class="hljs-params" style="color: rgb(248, 248, 242);">(self)</span>:</span>        <span class="hljs-keyword" style="color: rgb(249, 38, 114);">return</span> self.headline<span class="hljs-class"><span class="hljs-keyword" style="color: rgb(102, 217, 239); margin-top: 0px !important;">class</span> <span class="hljs-title" style="color: rgb(102, 0, 102);">EntryDetail</span><span class="hljs-params" style="color: rgb(102, 0, 102);">(models.Model)</span>:</span>    entry = models.OneToOneField(Entry)    details = models.TextField()</code>

这几个Model中,Blog和Entry是一对多的关系,Entry和Author是多对多的关系,Entry和EntryDetail是一对一的关系,分别通过ForeignKey, ManyToManyField, OneToOneField进行关联。现在我们来看看怎么对这些进行查询。

1. 首先来看最简单的,all(),一次获取所有的数据库记录:
<code class="hljs python" style="display: block; padding: 0.5em; color: rgb(248, 248, 242); font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; margin-top: 0px !important; background: rgb(35, 36, 31);"><span class="hljs-prompt" style="color: rgb(230, 219, 116); margin-top: 0px !important;">>>> </span>all_entries = Entry.objects.all()</code>

objects是什么呢?每一个Model都有一个Manager对象,Manager也是Model的查询接口,那它和QuerySet之间是什么关系呢?其实Manager可以看成是QuerySet的代理类,最开始的QuerySet对象就是通过Manager类来得到的,objects就是Manager对象在Model类中的属性名。至于为什么有Manager类,为什么QuerySet需要代理,我现在还不是很清楚,这些需要看Django的源码才能知道,现在我们先知道怎么用就可以了。

调用objects.all()方法,得到就是一个QuerySet对象,如:

<code class="hljs python" style="display: block; padding: 0.5em; color: rgb(248, 248, 242); font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; margin-top: 0px !important; background: rgb(35, 36, 31);">>>> type(Entry.objects.all())<class 'django.db.models.query.QuerySet'>>>> Entry.objects.all()[<Entry: headline1>, <Entry: headline2>]</code>
2. filters,过滤结果

即给Select语句加上Where查询条件,选出符合条件的子集,QuerySet中提供了两个方法:

  • filter(**kwargs)
    Returns a new QuerySet containing objects that match the given lookup parameters.
  • exclude(**kwargs)
    Returns a new QuerySet containing objects that do not match the given lookup parameters.

一个是过滤出符合条件的结果,一个是过滤出不符合套件的结果。注意,这里有一个很特殊的地方,就是这两方法的参数kwargs,有一定的约定,要符合:field__lookuptype=value这样的约束,field是Model的属性名,然后是两个下划线,然后是由Django定义的查询条件,比如exact, contains, startwith, gte, lt等,更多详细的内容,可以查看QuerySet API。下面给出两个例子:

<code class="hljs python" style="display: block; padding: 0.5em; color: rgb(248, 248, 242); font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; margin-top: 0px !important; background: rgb(35, 36, 31);"><span class="hljs-prompt" style="color: rgb(230, 219, 116); margin-top: 0px !important;">>>> </span>Blog.objects.filter(tagline__startswith=<span class="hljs-string" style="color: rgb(230, 219, 116);">"All"</span>)[<Blog: Beatles>]<span class="hljs-prompt" style="color: rgb(230, 219, 116);">>>> </span>Entry.objects.filter(pub_date__gte=<span class="hljs-string" style="color: rgb(230, 219, 116);">"2014-04-20"</span>)[<Entry: headline1>, <Entry: headline2>]</code>

需要注意的是,filter()和exclude()返回的结果仍然是一个QuerySet对象,所以,可以在一个语句中串联的使用filter,如:

<code class="hljs python" style="display: block; padding: 0.5em; color: rgb(248, 248, 242); font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; margin-top: 0px !important; background: rgb(35, 36, 31);"><span class="hljs-prompt" style="color: rgb(230, 219, 116); margin-top: 0px !important;">>>> </span>Entry.objects.filter(<span class="hljs-prompt" style="color: rgb(230, 219, 116);">... </span>    headline__startswith=<span class="hljs-string" style="color: rgb(230, 219, 116);">'What'</span><span class="hljs-prompt" style="color: rgb(230, 219, 116);">... </span>).exclude(<span class="hljs-prompt" style="color: rgb(230, 219, 116);">... </span>    pub_date__gte=datetime.date.today()<span class="hljs-prompt" style="color: rgb(230, 219, 116);">... </span>).filter(<span class="hljs-prompt" style="color: rgb(230, 219, 116);">... </span>    pub_date__gte=datetime(<span class="hljs-number" style="color: rgb(174, 129, 255);">2005</span>, <span class="hljs-number" style="color: rgb(174, 129, 255);">1</span>, <span class="hljs-number" style="color: rgb(174, 129, 255);">30</span>)<span class="hljs-prompt" style="color: rgb(230, 219, 116);">... </span>)</code>
3. 接下来,来说一说QuerySet的一些特性
  • 首先,QuerySet是各自独立的,上面例子中,使用了3个串联的filter方法,每一个filter都会得到一个QuerySet,这3个QuerySet之间并没有什么直接的关系,相互之间也没有影响。
  • 其次,是QuerySet有一个很重要的特性就是延迟加载,并不是每一次调用filter()方法就会去查询一次数据库,只有在真正需要数据的时候才会去查询数据库,比如:
    <code class="hljs perl" style="display: block; padding: 0.5em; color: rgb(248, 248, 242); font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; margin-top: 0px !important; background: rgb(35, 36, 31);">>>> <span class="hljs-keyword" style="color: rgb(249, 38, 114); margin-top: 0px !important;">q</span> = Entry.objects.filter(pub_date__gte=<span class="hljs-string" style="color: rgb(230, 219, 116);">"2014-04-20"</span>)>>> <span class="hljs-keyword" style="color: rgb(249, 38, 114);">q</span> = <span class="hljs-keyword" style="color: rgb(249, 38, 114);">q</span>.filter(mod_date__gte=<span class="hljs-string" style="color: rgb(230, 219, 116);">"2014-05-20"</span>)>>> <span class="hljs-string" style="color: rgb(230, 219, 116);">q[<Entry: headline1>, <Entry: headline2>]</span></code>
    前两个语句并没有去查数据库,第3个语句才会查了数据库,这种机制避免了不必要的数据库查询,减轻了数据库的压力。
  • 然后,就是QuerySet的缓存特性了。每一个QuerySet都有缓存,它第一次访问数据库时,会将结果缓存起来,这样当再次用到这个QuerySet时,就不用再次访问数据库了。比如:
    <code class="hljs python" style="display: block; padding: 0.5em; color: rgb(248, 248, 242); font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; margin-top: 0px !important; background: rgb(35, 36, 31);"><span class="hljs-prompt" style="color: rgb(230, 219, 116); margin-top: 0px !important;">>>> </span>print([e.headline <span class="hljs-keyword" style="color: rgb(249, 38, 114);">for</span> e <span class="hljs-keyword" style="color: rgb(249, 38, 114);">in</span> Entry.objects.all()])<span class="hljs-prompt" style="color: rgb(230, 219, 116);">>>> </span>print([e.pub_date <span class="hljs-keyword" style="color: rgb(249, 38, 114);">for</span> e <span class="hljs-keyword" style="color: rgb(249, 38, 114);">in</span> Entry.objects.all()])</code>
    这样就是访问了两次数据库,因为创建了两个不同的QuerySet对象,但是:
    <code class="hljs python" style="display: block; padding: 0.5em; color: rgb(248, 248, 242); font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; margin-top: 0px !important; background: rgb(35, 36, 31);"><span class="hljs-prompt" style="color: rgb(230, 219, 116); margin-top: 0px !important;">>>> </span>queryset = Entry.objects.all()<span class="hljs-prompt" style="color: rgb(230, 219, 116);">>>> </span>print([p.headline <span class="hljs-keyword" style="color: rgb(249, 38, 114);">for</span> p <span class="hljs-keyword" style="color: rgb(249, 38, 114);">in</span> queryset])<span class="hljs-prompt" style="color: rgb(230, 219, 116);">>>> </span>print([p.pub_date <span class="hljs-keyword" style="color: rgb(249, 38, 114);">for</span> p <span class="hljs-keyword" style="color: rgb(249, 38, 114);">in</span> queryset])</code>
    这样在执行第二个语句时,只访问了一次数据库,第3个语句就用的QuerySet缓存的数据了。
4. 直接得到一个Model对象:get()

filter()的返回结果总是得到一个QuerySet对象,QuerySet是Model对象集合,当你明确知道Model对象的集合只有一个时,可能就没必要用filter这么“麻烦”了,可以使用get()方法直接得到Model对象。如:

<code class="hljs python" style="display: block; padding: 0.5em; color: rgb(248, 248, 242); font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; margin-top: 0px !important; background: rgb(35, 36, 31);"><span class="hljs-prompt" style="color: rgb(230, 219, 116); margin-top: 0px !important;">>>> </span>one_entry = Entry.objects.get(pk=<span class="hljs-number" style="color: rgb(174, 129, 255);">1</span>)<span class="hljs-prompt" style="color: rgb(230, 219, 116);">>>> </span>type(one_entry)<<span class="hljs-class"><span class="hljs-keyword" style="color: rgb(102, 217, 239); margin-top: 0px !important;">class</span> '<span class="hljs-title" style="color: rgb(102, 0, 102);">query_test</span>.<span class="hljs-title" style="color: rgb(102, 0, 102);">models</span>.<span class="hljs-title" style="color: rgb(166, 226, 46); font-style: italic;">Entry</span>'></span></code>

需要注意的是,当get()查询的结果没有,会抛出 DoesNotExist 异常,或者是有多个时,会抛出MultipleObjectsReturned 异常。

5. limit and offset

当我们在做分页时,会用到SQL的limit和offset特性,从哪开始读取多少个记录。那这个在Django里是怎么用的呢?很简单,就是直接使用python中array-slicing的语法就行,如:

<code class="hljs xml" style="display: block; padding: 0.5em; color: rgb(248, 248, 242); font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; margin-top: 0px !important; background: rgb(35, 36, 31);">>>> Blog.objects.all()[<span class="hljs-tag" style="color: rgb(0, 102, 102); margin-top: 0px !important;"><<span class="hljs-title" style="color: rgb(249, 38, 114); margin-top: 0px !important;">Blog:</span> <span class="hljs-attribute" style="color: rgb(249, 38, 114);">Beatles</span>></span>, <span class="hljs-tag" style="color: rgb(0, 102, 102);"><<span class="hljs-title" style="color: rgb(249, 38, 114); margin-top: 0px !important;">Blog:</span> <span class="hljs-attribute" style="color: rgb(249, 38, 114);">blog2</span>></span>, <span class="hljs-tag" style="color: rgb(0, 102, 102);"><<span class="hljs-title" style="color: rgb(249, 38, 114); margin-top: 0px !important;">Blog:</span> <span class="hljs-attribute" style="color: rgb(249, 38, 114);">blog3</span>></span>]>>> Blog.objects.all()[0]<span class="hljs-tag" style="color: rgb(0, 102, 102);"><<span class="hljs-title" style="color: rgb(249, 38, 114); margin-top: 0px !important;">Blog:</span> <span class="hljs-attribute" style="color: rgb(249, 38, 114);">Beatles</span>></span>>>> Blog.objects.all()[1:3][<span class="hljs-tag" style="color: rgb(0, 102, 102);"><<span class="hljs-title" style="color: rgb(249, 38, 114); margin-top: 0px !important;">Blog:</span> <span class="hljs-attribute" style="color: rgb(249, 38, 114);">blog2</span>></span>, <span class="hljs-tag" style="color: rgb(0, 102, 102);"><<span class="hljs-title" style="color: rgb(249, 38, 114); margin-top: 0px !important;">Blog:</span> <span class="hljs-attribute" style="color: rgb(249, 38, 114);">blog3</span>></span>]</code>

第2个例子,通过[0]得到的就直接是一个Model对象,而第三个例子,通过[1:3]得到的是一个QuerySet对象,也就是说是一个Model对象的集合。第三个例子中,可以理解为limit等于2,offset等于1。


上面简单地介绍了一下Django提供的Model查询接口,虽然简单,但是基本上也就是我们平常经常用到的方法。下面我们再从一对一,多对一,多对多这三个方面,来看看怎么进行稍微复杂点的联合查询。

1. One-to-many relationships

一对多,从“多”的这一方,也就是ForeignKey所在的Model,向“一”的这一方查,比较容易,只需要指定属性名称就可以了,比如:

<code class="hljs python" style="display: block; padding: 0.5em; color: rgb(248, 248, 242); font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; margin-top: 0px !important; background: rgb(35, 36, 31);"><span class="hljs-prompt" style="color: rgb(230, 219, 116); margin-top: 0px !important;">>>> </span>e = Entry.objects.get(id=<span class="hljs-number" style="color: rgb(174, 129, 255);">2</span>)<span class="hljs-prompt" style="color: rgb(230, 219, 116);">>>> </span>e.blog</code>

如果从“一”的这一方向“多”的那一方查,那么“多”的一方是“一”的一方的一个集合,Django会自动为“一”的这一方创建一个叫做"Foo_set"的Manager,它就是“多”的这一方的集合,可以直接通过这个Manager对关联的Model进行增删查改操作。比如:

<code class="hljs python" style="display: block; padding: 0.5em; color: rgb(248, 248, 242); font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; margin-top: 0px !important; background: rgb(35, 36, 31);"><span class="hljs-prompt" style="color: rgb(230, 219, 116); margin-top: 0px !important;">>>> </span>b = Blog.objects.get(id=<span class="hljs-number" style="color: rgb(174, 129, 255);">1</span>)<span class="hljs-prompt" style="color: rgb(230, 219, 116);">>>> </span>b.entry_set.all() <span class="hljs-comment" style="color: rgb(117, 113, 94);"># Returns all Entry objects related to Blog.</span><span class="hljs-comment" style="color: rgb(117, 113, 94);"># b.entry_set is a Manager that returns QuerySets.</span><span class="hljs-prompt" style="color: rgb(230, 219, 116);">>>> </span>b.entry_set.filter(headline__contains=<span class="hljs-string" style="color: rgb(230, 219, 116);">'Lennon'</span>)<span class="hljs-prompt" style="color: rgb(230, 219, 116);">>>> </span>b.entry_set.count()</code>
2. Many-to-many relationships

多对多,其实和一对多非常的像,定义ManyToManyField的Model访问和它关联的Model时,直接访问该Model的名字就可以了,反过来,就需要加上“_set”了,如:

<code class="hljs javascript" style="display: block; padding: 0.5em; color: rgb(248, 248, 242); font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; margin-top: 0px !important; background: rgb(35, 36, 31);">e = Entry.objects.get(id=<span class="hljs-number" style="color: rgb(174, 129, 255); margin-top: 0px !important;">3</span>)e.authors.all() # Returns all Author objects <span class="hljs-keyword" style="color: rgb(249, 38, 114);">for</span> <span class="hljs-keyword" style="color: rgb(249, 38, 114);">this</span> Entry.e.authors.count()e.authors.filter(name__contains=<span class="hljs-string" style="color: rgb(230, 219, 116);">'John'</span>)a = Author.objects.get(id=<span class="hljs-number" style="color: rgb(174, 129, 255);">5</span>)a.entry_set.all() # Returns all Entry objects <span class="hljs-keyword" style="color: rgb(249, 38, 114);">for</span> <span class="hljs-keyword" style="color: rgb(249, 38, 114);">this</span> Author.</code>

是不是和一对多非常的像?

3. One-to-one relationships

一对一,和前两个也是非常像的,唯一不同的地方,还是在“反过来”,也就是由没有定义OneToOneField的那一方向定义了OneToOneField的那一方进行的查询,如:

<code class="hljs makefile" style="display: block; padding: 0.5em; color: rgb(248, 248, 242); font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; margin-top: 0px !important; background: rgb(35, 36, 31);"><span class="hljs-constant" style="color: rgb(102, 217, 239); margin-top: 0px !important;">ed</span> = EntryDetail.objects.get(id=2)ed.entry <span class="hljs-comment" style="color: rgb(117, 113, 94);"># Returns the related Entry object.</span><span class="hljs-constant" style="color: rgb(102, 217, 239);">e</span> = Entry.objects.get(id=2)e.entrydetail <span class="hljs-comment" style="color: rgb(117, 113, 94);"># returns the related EntryDetail object</span></code>

好,最后说一点,在没有定义xxxToxxxField的那一方,是怎么知道都是有谁和它关联呢?也就是是在什么时候,它的Model里面多了一个xxx_set属性呢?答案就是任何一个Model呗加载的时候,Django会遍历INSTALLED_APPS中的所有APP的所有Model,根据xxxToxxxField来对应的给关联类对象创建xxx_set这样的Manager属性。

这样虽然有些暴力,但是使得Django程序看上去更简洁,没有无用的重复的代码了。

好,Model Query就介绍到这里,这里只是一个入门的流水而已,更加丰富的内容,还需要移步到Django的文档中去:

  • https://docs.djangoproject.com/en/1.6/topics/db/queries/
  • https://docs.djangoproject.com/en/1.6/ref/models/querysets/
0 0
原创粉丝点击