你如何使用 Django URL 命名空间?
Posted
技术标签:
【中文标题】你如何使用 Django URL 命名空间?【英文标题】:How do you use Django URL namespaces? 【发布时间】:2010-12-27 12:29:41 【问题描述】:我正在尝试掌握Django URL namespaces 的窍门。但我找不到任何示例或文档。
这是我尝试过的。
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())
在 view1.html 中,% url foo:view1 3 % 输出 /foo/3,% url bar:view1 3 % 输出 /bar/3。无论我浏览到 /foo/X 还是 /bar/X,这都是正确的。
我想要的是能够浏览到 /foo/X 或 /bar/X,并让 % url view1 3 % 分别输出 /foo/3 或 /bar/3。
【问题讨论】:
我这样做的原因是我可以让两个命名空间使用不同的 CSS 文件。 IE。在 /foo 下浏览会包含 foo.css,/bar 会包含 bar.css。 我知道,拥有相同内容的不同网址肯定会让您的网站在搜索引擎上受到惩罚。我不知道这是否对您来说是一个问题,但如果是,我建议您以其他方式解决这个问题。也许你可以在客户端设置一个cookie,让他们以其他方式选择css样式。 我最终设置了基于不同子域的 CSS。本地测试很痛苦,需要主机文件条目。 请注意,命名空间是为 disambiguation 制作的:即使视图 names 冲突(和通过命名空间前缀这样做)。您在上面要求的内容可能被视为'歧义':根据上下文,故意使用单个视图名称来引用两个不同视图对象中的任何一个。混合这两个目标对我来说听起来并不像pythonic。 【参考方案1】:似乎没有直接的方法可以做到这一点。我会使用与您介绍的使用模板标签类似的解决方案,尽管我找到了一种更通用的方法。我使用了您可以在 url conf 中传递可选参数的事实,因此您可以跟踪命名空间:
#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'),
)
这也违反了 DRY 原则,但并不多:)
然后在您的视图中,您将获得命名空间变量(sub_urls.py 将是相同的):
#views.py
from django import shortcuts
def myvew(request, namespace):
context = dict(namespace=namespace)
return shortcuts.render_to_response('mytemplate.html', context)
稍后你只需要一个简单的标签,将你的命名空间变量和视图名称传递给:
#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, args=args, kwargs=kwargs)))
register.simple_tag(namespace_url)
并在模板中使用它(确保将视图名称作为字符串传递,而不是作为模板变量传递):
<!-- mytemplate.html -->
% load tags %
% namespace_url namespace "view1"%
顺便说一句,谢谢您的提示。我正在寻找某事。像这样。
【讨论】:
我看到这是一个非常古老的帖子,但是,dict(namespace=namespace) 在计算上比 'namespace' : namespace 更昂贵。使用“dis”找出原因! 对namespace_url函数的小改动:def namespace_url(namespace, view_name, *args, **kwargs): return urlresolvers.reverse('%s:%s' % (namespace, view_name), args= args, kwargs=kwargs) 将允许您支持 url 参数 谢谢。不错的补充。将其添加到答案中。 我认为在添加kwargs
之后,在给定的情况下,可以而且应该从include()
调用中删除namespace
和app_name
参数。那么,再次合理干燥。【参考方案2】:
我认为这在 django 中是不可能的。看看这个引用Ticket 11559 的message board post。我认为您正在尝试做同样的事情 - 有效地将隐式参数传递给 URL 标记。
另外,假设sub_urls
两次来自同一个应用程序,您应该确保app_name
在两种情况下都相同。您应该只需要更改命名空间。
【讨论】:
【参考方案3】:我意识到下面的解决方案违反了 DRY 原则,因为您必须为 foo 和 bar 创建本质上重复的 url 配置文件,但是我认为它应该可以工作。
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())
然后对于模板使用这个:
% url namespace 3 %
我还没有测试在 % url % 标签的名称部分使用变量的想法,但我认为它应该可行。
【讨论】:
所以你需要使用 % url view1_bar 3 % 还是 % url view1_foo 3 %?我真正想要的是能够使用 % url view1 3 %,并让它根据与当前 URL 匹配的命名空间来选择 foo 或 bar。 你说得对,我的例子行不通。我在假设我可以将模板变量传递给 % url % 标记的情况下构建它。我现在读到的情况并非如此。因此,您有两个选择。使用 reverse() 处理视图中的 url 并将其在上下文中传递给模板,或者制作自定义模板标签。【参考方案4】:这是我想出的一种解决方案。
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])
基本上,我确保始终将 current_app 上下文作为“foo”或“bar”传递,这是我通过查看请求 URL 手动计算的。然后我使用自定义标签来解析基于 current_app 的 URL。
它不是很通用; "foo" 和 "bar" 是硬编码的,标签只能接受一个参数。即使解决了这些问题,这似乎也是一种 hack。
【讨论】:
以上是关于你如何使用 Django URL 命名空间?的主要内容,如果未能解决你的问题,请参考以下文章
如何在可重用的应用程序中将命名空间 url 与 django 一起使用
如何在 Django 命名空间可重用应用程序中进行反向 URL 搜索