views:

1345

answers:

5

I'm trying to get the hang of Django URL namespaces. But I can't find any examples or documentation.

Here is what I have tried.

urls.py:

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^foo/', include('sub_urls', namespace='foo', app_name='foo')),
    (r'^bar/', include('sub_urls', namespace='bar', app_name='bar')),            
)

sub_urls.py:

from django.conf.urls.defaults import patterns, url
from views import view1

urlpatterns = patterns('views',
    url(r'^(?P<view_id>\d+)/$', view1, name='view1')
)

views.py:

from django.shortcuts import render_to_response

def view1(request, view_id):
    return render_to_response('view1.html', locals())

In view1.html, {% url foo:view1 3 %} outputs /foo/3, and {% url bar:view1 3 %} outputs /bar/3. This holds true whether I browse to /foo/X or /bar/X.

What I want is to be able to browse to /foo/X or /bar/X, and have {% url view1 3 %} output either /foo/3 or /bar/3, respectively.

A: 

Here is one solution I came up with.

views.py:

from django.shortcuts import render_to_response
from django.template import RequestContext

def render_response_context(view, locals):
    request = locals["request"]
    app = "bar" if request.META["PATH_INFO"].lower().startswith("/bar") else "foo"
    return render_to_response(view, locals, 
        context_instance=RequestContext(request, current_app=app))

def view1(request, view_id):    
    return render_response_context('view1.html', locals())

view1.html:

{% load extras %}
{% namespace_url view1 3 %}

extras.py:

from django import template
from django.core.urlresolvers import reverse

register = template.Library()

@register.tag
def namespace_url(parser, token):
    tag_name, view_string, arg1 = token.split_contents()
    return NamespaceUrlNode(view_string, arg1)

class NamespaceUrlNode(template.Node):
    def __init__(self, view_string, arg1):
        self.view_string = view_string
        self.arg1 = arg1
    def render(self, context):
        return reverse("%s:%s" % (context.current_app, self.view_string), args=[self.arg1])

Basically I made sure to always pass the current_app context as either "foo" or "bar", which I calculate manually by looking at the request URL. Then I use a custom tag that resolves a URL based on current_app.

It's not very generic; "foo" and "bar" are hard-coded, and the tag can only take exactly one argument. Even with those issues fixed, this seems like a hack.

Chase Seibert
A: 

So

Suppose you are at the root url of your site

urlpatterns = patterns('',

Pattern's first argument is an empty string... because it's referring to root, now say you created an application called foo_app, foo_app has some views, you will do

(r'^foo/', include('foo_app.urls')),

Which tells Django that anything after /foo/ will be sent to file foo_app.urls Now foo_app has 2 views, Index and search.. your foo_app.urls would look something like

urlpatterns = patterns('foo_app.views',
(r'^index', 'index_view'),
(r'^search', 'search_view')

So! because the root urlconf connects "/foo/" to foo_app.urls, which then connects 'index' and 'search' to 2 views...

if i request as an example

http://localhost:8000/foo/index

it will will then process the request method 'index_view' in 'foo_app.views'

Post any other questions =D

PirosB3
This is correct, but doesn't address the question, which is how to have two versions of a view under two different URLs, and have the {% url %} tag generate the correct links based on which directory you're in, via namespaces.
Chase Seibert
A: 

I realize the solution below violates the DRY principal as you have to create essentially duplicate url config files for foo and bar, however I think it should work.

urls.py:

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^foo/', include('sub_urls_foo')),
    (r'^bar/', include('sub_urls_bar')),            
)

sub_urls_foo.py:

from django.conf.urls.defaults import patterns, url
from views import view1

urlpatterns = patterns('views',
    url(r'^(?P<view_id>\d+)/$', view1, 'view1_foo', {'namespace': 'view1_foo'})
)

sub_urls_bar.py:

from django.conf.urls.defaults import patterns, url
from views import view1

urlpatterns = patterns('views',
    url(r'^(?P<view_id>\d+)/$', view1, 'view1_bar', {'namespace': 'view1_bar'})
)

views.py:

from django.shortcuts import render_to_response

def view1(request, view_id, namespace):
    return render_to_response('view1.html', locals())

And then for the template use this:

{% url namespace 3 %}

I haven't tested the idea of using a variable in the name section of the {% url %} tag, but I think it should work.

Steven Potter
So you would need to use either {% url view1_bar 3 %} or {% url view1_foo 3 %}? What I really want is to be able to use {% url view1 3 %}, and have it pick foo or bar depending on which namespace matches the current URL.
Chase Seibert
Your right, my example will not work. I built it under the assumption that I could pass a template variable to the {% url %} tag. Which I have now read is not the case. So that leaves you two options. Either process the url in your view using reverse() and pass it in the context to template, or make a custom template tag.
Steven Potter
+1  A: 

I think that this isn't possible in django right now. Have a look at this message board post which references Ticket 11559. I think that you're trying to do the same thing - effectively pass an implicit parameter to the URL tag.

Also, assuming that sub_urls is from the same app both times you should make sure app_name is the same in both cases. You should only need to change namespace.

Mystic
A: 

There seems to be no direct way to do it. I would use a similiar solution as you introduced using a template tag, though I found a more generic way. I used the fact that you can pass optional parameters in your url conf, so you can keep track of the namespace:

#urls.py
from django.conf.urls import defaults

urlpatterns = defaults.patterns('',
    defaults.url(r'^foo/', include('sub_urls', namespace='foo', app_name='myapp'), 
    kwargs={'namespace':'foo'}),
    defaults.url(r'^bar/', include('sub_urls', namespace='bar', app_name='myapp'),
    kwargs={'namespace':'bar'}),      
)

That also violates the DRY principle, but not much though :)

Then in your view you get the namespace variable (sub_urls.py would be the same):

#views.py
from django import shortcuts

def myvew(request, namespace):
    context = dict(namespace=namespace)
    return shortcuts.render_to_response('mytemplate.html', context)

Later you just need a simple tag you pass your namespace variable and view name to:

#tags.py
from django import template
from django.core import urlresolvers

register = template.Library()

def namespace_url(namespace, view_name):
   return urlresolvers.reverse('%s:%s' % (namespace, view_name))
register.simple_tag(namespace_url)

and use it in the template (make sure to pass your view name as a string, and not as a template variable):

<!-- mytemplate.html -->
{% load tags %}
{% namespace_url namespace "view1"%}

Thanks for your hint btw.. I was looking for sth. like this.

Torsten