Django学习小记[3] —— Query

今天学习的是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:

from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def __unicode__(self):
        return self.name


class Author(models.Model):
    name = models.CharField(max_length=50)
    email = models.EmailField()

    def __unicode__(self):
        return self.name


class Entry(models.Model):
    blog = models.ForeignKey(Blog)
    headline = models.CharField(max_length=255)
    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()

    def __unicode__(self):
        return self.headline


class EntryDetail(models.Model):
    entry = models.OneToOneField(Entry)
    details = models.TextField()

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

1. 首先来看最简单的,all(),一次获取所有的数据库记录:
>>> all_entries = Entry.objects.all()

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

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

>>> type(Entry.objects.all())
<class 'django.db.models.query.QuerySet'>
>>> Entry.objects.all()
[<Entry: headline1>, <Entry: headline2>]
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。下面给出两个例子:

>>> Blog.objects.filter(tagline__startswith="All")
[<Blog: Beatles>]
>>> Entry.objects.filter(pub_date__gte="2014-04-20")
[<Entry: headline1>, <Entry: headline2>]

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

>>> Entry.objects.filter(
...     headline__startswith='What'
... ).exclude(
...     pub_date__gte=datetime.date.today()
... ).filter(
...     pub_date__gte=datetime(2005, 1, 30)
... )
3. 接下来,来说一说QuerySet的一些特性
  • 首先,QuerySet是各自独立的,上面例子中,使用了3个串联的filter方法,每一个filter都会得到一个QuerySet,这3个QuerySet之间并没有什么直接的关系,相互之间也没有影响。
  • 其次,是QuerySet有一个很重要的特性就是延迟加载,并不是每一次调用filter()方法就会去查询一次数据库,只有在真正需要数据的时候才会去查询数据库,比如:
    >>> q = Entry.objects.filter(pub_date__gte="2014-04-20")
    >>> q = q.filter(mod_date__gte="2014-05-20")
    >>> q
    [<Entry: headline1>, <Entry: headline2>]
    
    前两个语句并没有去查数据库,第3个语句才会查了数据库,这种机制避免了不必要的数据库查询,减轻了数据库的压力。
  • 然后,就是QuerySet的缓存特性了。每一个QuerySet都有缓存,它第一次访问数据库时,会将结果缓存起来,这样当再次用到这个QuerySet时,就不用再次访问数据库了。比如:
    >>> print([e.headline for e in Entry.objects.all()])
    >>> print([e.pub_date for e in Entry.objects.all()])
    
    这样就是访问了两次数据库,因为创建了两个不同的QuerySet对象,但是:
    >>> queryset = Entry.objects.all()
    >>> print([p.headline for p in queryset])
    >>> print([p.pub_date for p in queryset])
    
    这样在执行第二个语句时,只访问了一次数据库,第3个语句就用的QuerySet缓存的数据了。
4. 直接得到一个Model对象:get()

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

>>> one_entry = Entry.objects.get(pk=1)
>>> type(one_entry)
<class 'query_test.models.Entry'>

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

5. limit and offset

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

>>> Blog.objects.all()
[<Blog: Beatles>, <Blog: blog2>, <Blog: blog3>]
>>> Blog.objects.all()[0]
<Blog: Beatles>
>>> Blog.objects.all()[1:3]
[<Blog: blog2>, <Blog: blog3>]

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


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

1. One-to-many relationships

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

>>> e = Entry.objects.get(id=2)
>>> e.blog

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

>>> b = Blog.objects.get(id=1)
>>> b.entry_set.all() # Returns all Entry objects related to Blog.

# b.entry_set is a Manager that returns QuerySets.
>>> b.entry_set.filter(headline__contains='Lennon')
>>> b.entry_set.count()
2. Many-to-many relationships

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

e = Entry.objects.get(id=3)
e.authors.all() # Returns all Author objects for this Entry.
e.authors.count()
e.authors.filter(name__contains='John')

a = Author.objects.get(id=5)
a.entry_set.all() # Returns all Entry objects for this Author.

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

3. One-to-one relationships

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

ed = EntryDetail.objects.get(id=2)
ed.entry # Returns the related Entry object.

e = Entry.objects.get(id=2)
e.entrydetail # returns the related EntryDetail object

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

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

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

猜你喜欢

转载自blog.csdn.net/hackerain/article/details/39838559