VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > Python基础教程 >
  • 建立一个更高级别的查询 API:正确使用Django ORM 的方式(2)

 

注意:这里可以写成request.user.todo_set.filter(is_done=False, priority=1)。但是这里只是一个实验。

为什么这样写不好呢?

首先,代码冗长。七行代码才能完成,正式的项目中,将会更加复杂。

其次,泄露实现细节。比如代码中的is_done是BooleanField,如果改变了他的类型,代码就不能用了。

然后就是,意图不清晰,很难理解。

最后,使用中会有重复。例:你需要写一行命令,通过cron,每周发送给所有用户一个todo list,这时候你就需要复制-粘贴着七行代码。这不符合DRY(do not repeat yourself)

让我们大胆的猜测一下:直接使用低等级的ORM代码是反模式的。

如何改进呢?

使用 Managers 和 QuerySets

首先,让我们先了解一下概念。

Django 有两个关系密切的与表级别操作相关的构图:managers 和 querysets

manager(django.db.models.manager.Manager的一个实例)被描述成 “通过查询数据库提供给Django的插件”。Manager是表级别功能的通往ORM大门。每一个model都有一个默认的manager,叫做objects。

Quesyset (django.db.models.query.QuerySet) 是“数据库中objects的集合”。本质上是一个SELECT查询,也可以使用过滤,排序等(filtered,ordered),来限制或者修改查询到的数据。用来 创建或操纵 django.db.models.sql.query.Query实例,然后通过数据库后端在真正的SQL中查询。

 

啊?你还不明白?

随着你慢慢深入的了解ORM,你就会明白Manager和QuerySet之间的区别了。

人们会被所熟知的Manager接口搞糊涂,因为他并不是看上去那样。

Manager接口就是个谎言。

QuerySet方法是可链接的。每一次调用QuerySet的方法(如:filter)都会返回一个复制的queryset等待下一次的调用。这也是Django ORM 流畅之美的一部分。

但是当Model.objects 是一个 Manager时,就出现问题了。我们需要调用objects作为开始,然后链接到结果的QuerySet上去。

那么Django又是如何解决呢?

接口的谎言由此暴露,所有的QuerySet 方法基于Manager。在这个方法中,通过

让我们立刻回到todo list ,解决query接口的问题。Django推荐的方法是自定义Manager子类,并加在models中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
self.get_query_set()的代理,重新创建一个QuerySet。
class Manager(object):
 
    # SNIP some housekeeping stuff..
 
    def get_query_set(self):
        return QuerySet(self.model, using=self._db)
 
    def all(self):
        return self.get_query_set()
 
    def count(self):
        return self.get_query_set().count()
 
    def filter(self,*args,**kwargs):
        return self.get_query_set().filter(*args,**kwargs)
 
    # and so on for 100+ lines...

 

你也可以在model中增加多个managers,或者重新定义objects,也可以维持单个的manager,增加自定义方法。

 

下面让我们实验一下这几种方法:

 

方法1:多managers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class IncompleteTodoManager(models.Manager):
    def get_query_set(self):
        return super(TodoManager,self).get_query_set().filter(is_done=False)
 
class HighPriorityTodoManager(models.Manager):
    def get_query_set(self):
        return super(TodoManager,self).get_query_set().filter(priority=1)
 
class Todo(models.Model):
    content= models.CharField(max_length=100)
    # other fields go here..
 
    objects= models.Manager()# the default manager
 
    # attach our custom managers:
    incomplete= models.IncompleteTodoManager()
    high_priority= models.HighPriorityTodoManager()

相关教程