I'm trying to auto generate a bunch of similar URLs and views based on Model data for an API. I generate a tree from the Models and have verified it is correct. However, when I try to use that tree to build nested URLs in nested namespaces, the URLs are built, but the namespaces are not.
Relevant code:
# urls.py
urlpatterns = patterns('',
url(r'^$', views.Index.as_view(), name='index'),
url(r'^api/', include(rest_api.generate_urls('api'),
namespace='api', app_name='app')),
)
# rest_api.py
def build_urls(tree, namespaces):
# Create the response function, which relies on data from tree
if condition1:
def response(request, **kwargs):
...
return build_response(data_from_tree)
elif condition2:
def response(request, **kwargs):
...
return build_response(data_from_tree)
else:
def response(request, **kwargs):
...
return build_not_found_response(data_from_tree)
urls = []
# response will present unique identifier, URL for each entry in Model
urls.append(url(r'^$', response, name='index'))
# Add any sub-paths before open-ended regex.
for path, node in tree.items():
urls.append(url(r'^{path}/'.format(path=path),
include(build_urls(node, namespaces + (path,)),
namespace=path, app_name='app')))
# response will query Model for appropriate entry & present data
urls.append(url(r"^(?P<name>[\w ']+)", response))
return patterns('', *urls)
def generate_urls(*namespaces):
return build_urls(tree, namespaces)
When I view the generated URLconf (via python manage.py show_urls
from django-extensions), none of the nested namespaces are there (cleaned for easy reading):
[riggs@dev app]$ python manage.py show_urls
/app/ app.views.Index index
/app/api/ app.rest_api.response api:index
/app/api/equipment/ app.rest_api.response api:index
/app/api/equipment/<name> app.rest_api.response
/app/api/materials/ app.rest_api.response api:index
/app/api/materials/<name> app.rest_api.response
/app/api/materials/raw/ app.rest_api.response api:index
/app/api/materials/raw/<name> app.rest_api.response
/app/api/materials/output1/ app.rest_api.response api:index
/app/api/materials/output1/<name> app.rest_api.response
/app/api/materials/output2/ app.rest_api.response api:index
/app/api/materials/output2/<name> app.rest_api.response
/app/api/recipe/ app.rest_api.response api:index
/app/api/recipe/stage1/ app.rest_api.response api:index
/app/api/recipe/stage1/<name> app.rest_api.response
/app/api/recipe/stage2/ app.rest_api.response api:index
/app/api/recipe/stage2/<name> app.rest_api.response
I don't understand why this isn't working.
Ultimately, I want these named views so I can reference them in URLs via django.core.urlresolvers.reverse
. Despite the lack of nesting, I was able to test reverse
by changing
urls.append(url(r'^$', response, name='index'))
to
urls.append(url(r'^$', response, name=namespaces[-1]))
which turned api:index
into api:equipment
, api:materials
, api:raw
, etc. However, when I test these names using
def response(request, **kwargs):
url = request.build_absolute_uri(
reverse(':'.join(('api', viewname)),
current_app=request.resolver_match.namespace))
...
return build_response(data_from_tree)
Django throws NoReverseMatch
with the message Reverse for 'equipment' with arguments '()' and keyword arguments '{}' not found. 0 pattern(s) tried: []
.
What am I doing wrong? Is django actually capable of doing what I want, here?
Turns out the above works great! show_urls
doesn't handle namespaces and so a different error was being masked by my assumption that the namespaces were wrong. Once I trusted they were correct, I was able to locate the offending code.