Django URL名称空间 - 模板必须知道其命名空间?

4

我一直在摆弄我的第一个Django项目,该项目最初使用的是Django 1.6版本,最近刚刚升级到了Django 1.8。同时,我一直在学习《Django Patterns & Best Practices》以了解我应该如何更好地组织它。

我的项目有几个子应用程序和一个典型的主urls.py文件,其中包含类似以下内容的行:

 (r'', include('noc.apps.frontpage.urls')),

在每个应用程序中,我都使用应用程序名称作为URL名称的前缀,例如frontpage_edit_page,并在模板中使用{% url %}来引用视图之间的链接。

后来我了解到URL命名空间,认为可以简化我的URL名称。最初我理解的是,如果我在主urls.py中的每个include()中添加一个命名空间,那么一切都会像以前一样正常工作,因为所引用的URL名称都将由“本地”应用程序解析。但事实似乎并非如此。

在主urls.py中加入以下内容:

(r'', include('noc.apps.frontpage.urls', namespace='frontpage', app_name='frontpage')),

这是在应用程序 urls.py 中的内容:

 url(r'^frontpage/edit/(?P<slug>[A-Za-z0-9]+)$', views.edit_page, name='front_page_edit_page'),

在应用程序模板中使用 {% url 'front_page_edit_page' slug=page.slug %} 时,我得到了一个 NoReverseMatch 异常,没有尝试任何 URL。
我能找到的所有示例都是关于在 URL 命名空间前缀中添加前缀的 - frontpage:front_page_edit_page,但是 1)这如何改善以前的应用程序前缀? 2)你怎么可能有两个相同应用程序的实例,这应该是一个好处... 所以我认为这是用于在应用程序之间链接,而不是在应用程序内部。
那么我错过了什么? 我需要在我的视图函数中嵌入 app_name 或命名空间吗? 如果我确实在所有 URL 名称前缀中添加命名空间,甚至在应用程序内部,我会得到一个呈现的页面,但似乎违背了初衷。

命名空间是为了让您在不同的应用程序中使用相同的URL名称(例如,“index”,“create”,“thank_you”等)具有视图...您可以尝试通过使它们更长并以您的应用程序名称作为前缀来创建唯一名称作为应用程序作者,但您无法保证已创建唯一名称...使用命名空间,开发人员可以确保项目中跨应用程序的URL名称是唯一的。 - Anentropic
@Anentropic - 我也是这样理解的,但实际上并没有发生这种情况,因此才有了这个问题! - AnotherHowie
3个回答

8
你应该在URL标记中添加命名空间,这是正确的做法。
{% url 'frontpage:edit_page' slug='SLUG' %}

但更好的做法是将主项目的URL文件结构化,如下所示:

urlpatterns = patterns(
    '',
    url(r'^admin/', include(admin.site.urls)),  # NOQA
    url(r'frontpage', include('noc.apps.frontpage.urls', namespace='frontpage', app_name='frontpage')),

这样,您可以在主URL文件中为每个应用程序指定路径,避免重复;

urlpatterns = patterns(
    'noc.apps.frontpage.views',
    url(r'^edit/(?P<slug>[A-Za-z0-9]+)$', 'edit_page', name='edit_page'),

通过这个方法,你可以在所有应用中引入RESTful URL结构,以便你最终得到像下面这样的东西;RESTful是什么?它是一种Web API的设计风格和开发方式,通过使用HTTP来实现资源的状态转移。
urlpatterns = patterns(
    '',
    url(r'^admin/', include(admin.site.urls)),  # NOQA
    url(r'frontpage/', include('noc.apps.frontpage.urls', namespace='frontpage', app_name='frontpage')),
    url(r'contact/', include('noc.apps.contact.urls', namespace='contact', app_name='contact')),
    url(r'myapp/', include('noc.apps.myapp.urls', namespace='myapp', app_name='myapp')),

你的所有应用程序都可以遵循类似的结构;

urlpatterns = patterns(
    'noc.apps.contact.views',
    url(r'^$', 'index', name='index'),
    url(r'^add/$', 'add', name='add'),
    url(r'^edit/(?P<slug>[A-Za-z0-9]+)$', 'edit', name='edit'),

urlpatterns = patterns(
    'noc.apps.myapp.views',
    url(r'^$', 'index', name='index'),
    url(r'^add/$', 'add', name='add'),
    url(r'^edit/(?P<slug>[A-Za-z0-9]+)$', 'edit', name='edit'),

可以通过使用顶级命名空间来实现多个frontpage实例;

urlpatterns = patterns(
    '',
    url(r'^admin/', include(admin.site.urls)),  # NOQA
    url(r'frontpage/', include('noc.apps.frontpage.urls', namespace='frontpage1', app_name='frontpage')),
    url(r'frontpage/', include('noc.apps.frontpage.urls', namespace='frontpage2', app_name='frontpage')),

那么您应该能够按照以下方式针对顶层实例命名空间进行定位,然后是应用程序命名空间:
这样做的方式如下:
{% url 'frontpage1:frontpage:edit_page' slug='SLUG' %}
{% url 'frontpage2:frontpage:edit_page' slug='SLUG' %}

但是如果你想让你的模板链接更加通用,我相信你可以省略顶级命名空间,Django会自动解析到当前应用程序,并将其添加到请求对象中。这在反转命名空间URL末尾有详细说明。


也许我表达不够清晰,但那正是我所做的。我唯一还没做的就是简化名称,因为我无法使用原始名称使其正常工作。因此,如果将命名空间硬编码到模板中,您如何可能使用相同的应用程序两次,这是Django手册中的示例?(一个投票应用程序的两个实例)它比在所有URL名称前面加上'appname_'好在哪里?更重要的是,我猜想 - 即使在同一个应用程序中,您是否在url标签上指定命名空间前缀? - AnotherHowie
你需要对app_name进行硬编码(该名称在应用程序的两个实例中相同),但是namespace在两个实例中应该是不同的。然后,将当前的命名空间作为current_app参数传递,并且硬编码的app_name会解析为当前的namespace - knbk
然后,您将当前命名空间作为current_app参数传递。啊哈,我想这就是我缺少的东西。将其传递给什么?请求上下文?应用程序在哪里找到它被调用的命名空间? - AnotherHowie
另外,经过一夜的思考,我明白了你所说的URL前缀应该在顶级urls.py中的意思。在这种特殊情况下,主页应用程序提供(tada!)主页(^$) URL,所以我不能移动它,但我会为其他应用程序这样做。好建议。 - AnotherHowie
@AnotherHowie 对不起,我误解了你想要做的事情。实际上我从未尝试过这样做,但我会更新我的答案,并提供一个可能的解决方案,使用顶级命名空间来嵌套您的URL。 - markwalker_
我觉得我混淆了每个人,包括我自己,因为我使用了与应用程序名称相同的字符串作为命名空间...现在我明白你的意思了!我仍然认为这很奇怪,即可重用的应用程序(甚至可能不是你的)可以向其主机指定命名空间,但至少模板部分是有道理的 :-) - AnotherHowie

1

供任何人参考。

对我来说有效,Django 2.2.2

# project urls.py
# DjpollsConfig is my app config
from djpolls.apps import DjpollsConfig
djpolls_app_name = DjpollsConfig.name

urlpatterns = [
    path(djpolls_app_name, include('djpolls.urls', namespace='djpolls_app_name'))
]

# app urls.py
from django_proj.urls import djpolls_app_name
app_name = djpolls_app_name

# app template
{% url 'djpolls_app_name:detail' question.id %}

希望它有所帮助!

现在真正酷的是,如果app_name相同,您甚至不需要添加命名空间关键字参数。 - Bobort

0

试试这个

(r'', include('noc.apps.frontpage.urls', namespace = 'abc')),

然后在你的模板中:

{% url 'abc:frontpage:front_page_edit_page' slug=page.slug %}

1
这个问题仍然存在:有什么好处?现在模板中有一个硬编码的前缀,必须与代码中的前缀匹配。这就是我们开始时的情况,但没有冒号。 - AnotherHowie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接