【问题标题】:Django apps resolving to the wrong namespaceDjango 应用程序解析到错误的命名空间
【发布时间】:2016-08-22 13:49:27
【问题描述】:

在我的项目中,我有三个应用程序,“abc”、“xyz”和“common”。 Common 不是一个真正的应用程序,因为它只是存储由两个应用程序继承和扩展的模板、模型和视图。

项目级 urls.py 看起来像这样,并正确地将请求重定向到相应的应用程序:

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^abc/', include('abc.urls')),
    url(r'^xyz/', include('xyz.urls')),
]

两个应用程序的 url.py 文件看起来像这样;唯一的区别是用 XYZ 替换 ABC 的每个实例:

from django.conf.urls import url
from views import ABCAlertList as AlertList
from views import ABCEventList as EventList
from views import ABCEventDetail as EventDetail
from views import ABCEventReplay as EventReplay
from views import ABCUploadView as UploadView

urlpatterns = [
   url(r'^$', AlertList.as_view(), name='view_alerts'),
   url(r'^entities/(?P<uid>\w+)/$', EventList.as_view(), name='view_uid'),
   url(r'^entities/(?P<uid>\w+)/replay/$', EventReplay.as_view(), name='view_replay'),
   url(r'^entities/(?P<uid>\w+)/event/(?P<eid>\w+)/$', EventDetail.as_view(), name='view_event'),

   url(r'^upload/$', UploadView.as_view(), name='upload_file'),
]

同样,两个应用程序之间的所有视图都是通用的,因此它们中的任何一个都没有特定于应用程序的内容。两个应用程序都使用同一个通用模板中的同一行:

<a href="{% url 'view_uid' alert.uid %}">

现在,问题来了:

App ABC 在顶级页面上运行良好。但是它呈现的 url 越过该点指向错误的应用程序。

例如,我会在

http://localhost:8888/abc/ 

并且该页面上的网址呈现为

http://localhost:8888/xyz/entities/262b3bce18e71c5459a41e1e6d52a946ab47e88f/

什么给了?看起来 Django 正在读取错误的应用程序的 urls.py。

【问题讨论】:

  • 两个 urls 文件中的名称是否相同。尝试在 ABC 应用程序中将 view_uid 更改为 abc_view_uid。和 XYZ 应用程序中的 xyz_view_uid。
  • 当您说 /abc/ 页面上的 URL 时,您是指指向应用程序其他部分的链接吗?因为如果是这样,这听起来可能只是一个简单的错字。假设您的 ABC 和 XYZ urls.py 是适当的类型,我不明白为什么 Django 会创建错误的 URL。

标签: python django


【解决方案1】:

Django 无法仅通过视图名称和参数来区分 abc/ 和 xyz/ 下的 URL。由于反转会以相反的顺序遍历模式,xyz/ 下的模式将始终首先匹配,因此使用reverse() 或{% url %} 标记生成的所有链接都将指向xyz 应用程序。

您需要为每个模式指定一个唯一名称,或使用 URL 命名空间。在 Django 1.9+ 中,您应该设置 app_name 属性:

app_name = 'abc'
urlpatterns = [
   url(r'^$', AlertList.as_view(), name='view_alerts'),
   url(r'^entities/(?P<uid>\w+)/$', EventList.as_view(), name='view_uid'),
   url(r'^entities/(?P<uid>\w+)/replay/$', EventReplay.as_view(), name='view_replay'),
   url(r'^entities/(?P<uid>\w+)/event/(?P<eid>\w+)/$', EventDetail.as_view(), name='view_event'),

   url(r'^upload/$', UploadView.as_view(), name='upload_file'),
]

在 Django 1.8 中,您需要将 namespace 参数传递给 include():

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^abc/', include('abc.urls', namespace='abc')),
    url(r'^xyz/', include('xyz.urls', namespace='xyz')),
]

然后您可以通过传递正确的命名空间来反转 url:

<a href="{% url 'abc:view_uid' alert.uid %}">

如果您需要为两个应用使用相同的模板或函数,则需要将两个应用的应用命名空间设置为相同,但使用不同的实例命名空间。

在 1.9+ 中,这意味着使用相同的 app_name 属性,但传递不同的 namespace 参数:

# myapp/urls.py
app_name = 'common_app_name'
urlpatterns = [
    # app urls
]

# myproject/urls.py
urlpatterns = [
    url(r'^abc/', include('abc.urls', namespace='abc')),
    url(r'^xyz/', include('xyz.urls', namespace='xyz')),
]

在模板中,您需要使用应用程序命名空间来反转 url。当前的实例命名空间会被自动考虑在内。在调用reverse() 时,您需要传递当前命名空间:

reverse('common_app_name:view_alerts', current_app=request.resolver_match.namespace)

在 Django 1.8 中,您没有 app_name 属性,您需要将其作为参数传递给 include():

urlpatterns = [
    url(r'^abc/', include('abc.urls', namespace='abc', app_name='common_app_name')),
    url(r'^xyz/', include('xyz.urls', namespace='xyz', app_name='common_app_name')),
]

Django 1.8 也不会在调用{% url %} 标记时自动使用当前实例命名空间。您需要为此设置request.current_app 属性:

def my_view(request):
    request.current_app = request.resolver_match.namespace
    ...

【讨论】:

  • 谢谢,这就是我害怕的。回到绘图板...:\
  • ...也许不是。鉴于这些限制,我想我会绕过 URL 调度程序并手动编译它们。
  • @Ivan 也许我的更新会对您的具体情况有所帮助?除此之外,我不知道你的确切问题是什么,所以我无法帮助你。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-03-13
  • 1970-01-01
  • 1970-01-01
  • 2013-02-07
  • 1970-01-01
  • 1970-01-01
  • 2013-05-24
相关资源
最近更新 更多