2010-09-11 111 views
2

我在Django中编写了一个应用程序,它使用URL中的[year]/[month]/[title-text]来标识新闻项目。要管理这些项目,我已经定义了许多网址,每个网址都以上面的前缀开头。如何在Django中编写请求过滤器/预处理器

urlpatterns = patterns('msite.views', 
    (r'^(?P<year>[\d]{4})/(?P<month>[\d]{1,2})/(?P<slug>[\w]+)/edit/$', 'edit'), 
    (r'^(?P<year>[\d]{4})/(?P<month>[\d]{1,2})/(?P<slug>[\w]+)/$', 'show'), 
    (r'^(?P<year>[\d]{4})/(?P<month>[\d]{1,2})/(?P<slug>[\w]+)/save$', 'save'), 
) 

我在想,如果有在Django的机制,这让我预处理给定的请求的意见editshowsave。它可以解析参数,例如year=2010, month=11, slug='this-is-a-title'并从中提取模型对象。

的好处是,我能确定我的看法

def show(news_item): 
    '''does some stuff with the news item, doesn't have to care 
     about how to extract the item from request data''' 
    ... 

,而不是

def show(year, month, slug): 
    '''extract the model instance manually inside this method''' 
    ... 

什么是解决这个的Django的方法是什么? 或者更通用的方式,有没有一些机制来实现请求过滤器/预处理器,例如在JavaEE和Ruby on Rails中?

回答

2
+0

我还有很多阅读,直到我得到这个完全。在Django中,过滤器似乎不是那么简单。 – nre 2010-09-13 13:20:40

+0

如在Model.objects.filter()中一样过滤对象? – 2010-09-13 13:31:13

+0

很确定他在谈论一个方法,它在发送到实际的请求处理程序之前过滤(预处理)请求。 Java/Spring中的同义词将是一个拦截器......在Rails中它将是before_filter。 – threejeez 2013-06-04 14:56:28

1

这样做的一种方法是编写一个自定义装饰器。我在我的一个项目中测试了它,并且它工作正常。

首先,一个自定义装饰器。这一个将不得不接受函数旁边的其他参数,所以我们声明另一个装饰器来做到这一点。

decorator_with_arguments = lambda decorator: lambda * args, **kwargs: lambda func: decorator(func, *args, **kwargs) 

现在实际的装饰:

@decorator_with_arguments 
def parse_args_and_create_instance(function, klass, attr_names): 
    def _function(request, *args, **kwargs): 
     model_attributes_and_values = dict() 
     for name in attr_names: 
      value = kwargs.get(name, None) 
      if value: model_attributes_and_values[name] = value 

     model_instance = klass.objects.get(**model_attributes_and_values) 
     return function(model_instance) 
    return _function 

这个装饰需要两个额外的参数,除了它的装饰功能。这些分别是要为其准备和注入实例的模型类以及用于准备实例的属性的名称。在这种情况下,装饰器使用来自数据库实例的属性get

现在,使用show函数的“通用”视图。

def show(model_instance): 
    return HttpResponse(model_instance.some_attribute) 

show_order = parse_args_and_create_instance(Order, ['order_id'])(show) 

而另:

show_customer = parse_args_and_create_instance(Customer, ['id'])(show) 

为了这个工作的URL配置参数必须包含相同的关键词作为属性。当然你可以通过调整装饰器来定制这个。

# urls.py 
... 
url(r'^order/(?P<order_id>\d+)/$', 'show_order', {}, name = 'show_order'), 
url(r'^customer/(?P<id>\d+)/$', 'show_customer', {}, name = 'show_customer'), 
... 

更新

由于@rebus正确pointed out还需要调查Django的通用视图。

+0

+1不错的野兽:) – 2010-09-11 11:52:34

0

Django是蟒蛇毕竟,所以你可以很容易地做到这一点:

def get_item(*args, **kwargs): 
    year = kwargs['year'] 
    month = kwargs['month'] 
    slug = kwargs['slug'] 
    # return item based on year, month, slug... 

def show(request, *args, **kwargs): 
    item = get_item(request, *args, **kwargs) 
    # rest of your logic using item 
    # return HttpResponse...