如何在Django中编写请求过滤器/预处理器

时间:2010-09-11 10:49:08

标签: django filter django-urls

我正在Django中编写一个应用程序,它在url中使用[year]/[month]/[title-text]来识别新闻项目。为了管理项目,我已经定义了许多网址,每个网址都以上面的前缀开头。

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中?

3 个答案:

答案 0 :(得分:2)

答案 1 :(得分: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的通用视图。

答案 2 :(得分:0)

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

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...