views:

34

answers:

3

I am writing an application in Django, which uses [year]/[month]/[title-text] in the url to identitfy news items. To manage the items I have defined a number of urls, each starting with the above prefix.

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'),
)

I was wondering, if there is a mechanism in Django, which allows me to preprocess a given request to the views edit, show and save. It could parse the parameters e.g. year=2010, month=11, slug='this-is-a-title' and extract a model object out of them.

The benefit would be, that I could define my views as

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

instead of

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

What is the Django way of solving this? Or in a more generic way, is there some mechanism to implement request filters / preprocessors such as in JavaEE and Ruby on Rails?

+2  A: 

You need date based generic views and create/update/delete generic views maybe?

rebus
I have still much to read, until I get this completely. A filter does not seem to be that trivial in django.
nicore
Filter for filtering objects as in Model.objects.filter()?
rebus
+1  A: 

One way of doing this is to write a custom decorator. I tested this in one of my projects and it worked.

First, a custom decorator. This one will have to accept other arguments beside the function, so we declare another decorator to make it so.

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

Now the actual decorator:

@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

This decorator expects two additional arguments besides the function it is decorating. These are respectively the model class for which the instance is to be prepared and injected and the names of the attributes to be used to prepare the instance. In this case the decorator uses the attributes to get the instance from the database.

And now, a "generic" view making use of a show function.

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

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

And another:

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

In order for this to work the URL configuration parameters must contain the same key words as the attributes. Of course you can customize this by tweaking the decorator.

# 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'),
... 

Update

As @rebus correctly pointed out you also need to investigate Django's generic views.

Manoj Govindan
+1 nice beast :)
rebus
A: 

Django is python after all, so you can easily do this:

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