【问题标题】:Reversing namespaced URLs in Django: multiple instances of the same app在 Django 中反转命名空间的 URL:同一应用程序的多个实例
【发布时间】:2010-11-20 01:09:46
【问题描述】:

我使用 Django 已经有一段时间了(目前是 1.2 版),但最近才开始开发一个需要支持多个实例的应用程序。例如,项目urls.py 文件将包含它两次,在两个不同的命名空间下,如下所示:

urlpatterns = patterns('',
    (r'^instance1/', include('myapp.urls', namespace='instance1')),
    (r'^instance2/', include('myapp.urls', namespace='instance2')),
)

我一直很好,直到我意识到我需要弄清楚如何处理对reverse() 的所有内部调用(或对{% url %} 过滤器的模板调用)。例如,假设我在我的一个观点中正在做类似以下的事情:

return HttpResponseRedirect(reverse('view_name'))

或在我的模板之一中使用类似的东西:

<a href="{% url view_name %}">link text</a>

...其中view_namemyapp.urls 中包含的URL 模式的名称。由于我使用命名空间,这将引发错误:没有名为view_name 的视图。相反,我必须告诉它instance1:view_nameinstance2:view_name。但是动态地做这件事让我很难过。

我做了一些查看,它看起来像 current_app 参数,传递给 ContextRequestContext,旨在帮助解决这个问题,但根本不清楚如何动态传递 right 应用程序名称为current_app。那么告诉 Django 使用哪个命名空间的正确方法是什么?

编辑:我的用例是多次使用单个安装的应用程序。也就是说,它只存在于磁盘上一次,但会多次包含在项目的根目录urls.py 中(每次都在不同的命名空间下,如我上面的示例所示)。考虑到这一点,是否有任何好的方法来跟踪正在从哪个命名空间调用视图/模板,并在同一命名空间中使用 reverse(){% url %} ?我知道 Django 1.3 将提供一些额外的功能来帮助解决这个问题(即new and improved resolve()),但现在肯定有一个很好的方法来做到这一点......

【问题讨论】:

  • 我对@9​​87654322@ 的回答也适用于此。

标签: django namespaces


【解决方案1】:

自问题发布以来发生了很多变化,但对于未来的谷歌人(比如我自己)来说,指出 request 现在有命名空间可能会很有用(至少从 1.7 开始,如 this example 所示。

据我了解,我们应该能够简单地将 current_app 位置参数传递给 reverse/redirect 但我无法让它工作,所以我最终为此创建了一个帮助方法:

def __redirect(request, viewname, *args, **kwargs):
    to = viewname

    if callable(to):
        to = viewname.__module__ + '.' + viewname.__name__
    if request.resolver_match.namespace:
        to = '%s:%s' % (request.resolver_match.namespace, to)

    return redirect(
        to,
        *args,
        **kwargs
    )

我在这里使用redirect,但所有参数都传递给reverse,所以是一样的。

【讨论】:

    【解决方案2】:

    不是一个很好的解决方案,但由于您对命名空间和 URL 路径的初始部分使用相同的文本,您可以从 request.path (request.path.split('/')[1]) 中提取该元素并将其设置为 current_app请求上下文,或者只是将其用作视图中的命名空间。

    http://docs.djangoproject.com/en/dev/topics/http/urls/#url-namespaces 第 2 点。

    你可以这样做,例如在上下文处理器中(如果您想在模板中使用命名空间)。

    对于视图,您可以编写一个装饰器,为您的函数提供一个额外的 kwarg“命名空间”并将其用作:

    @feed_namespace
    def view1(request, *args, **kwargs):
        ns = kwargs['namespace']
    

    或者只是编写一个带有额外参数(请求)的 reverse_namespaced 函数,该函数从中获取命名空间,并使用它而不是 reverse。

    当然,如果您这样做,您将始终需要为此应用程序使用请求路径/命名空间

    【讨论】:

    • 谢谢,第一个想法(使用路径的一部分来获取命名空间)是我最终做的。如果我需要在 1.3 发布之前再次执行此操作,您的其他想法可能会很有帮助。
    【解决方案3】:

    有一个关于反转命名空间 url 的文档页面。

    http://docs.djangoproject.com/en/dev/topics/http/urls/#topics-http-reversing-url-namespaces

    reverse('instance1:myapp.urls.some_view')reverse('instance1:view_name') 应该可以工作,或者两者都可以 :) - 我自己从未尝试过。

    【讨论】:

    • 这些会起作用,真的——但那会硬编码应用程序,使其仅使用命名空间的特定值。我希望应用程序不关心它在用户的主 urls.py 文件中分配的名称空间:它应该自动使用“正确”的名称空间,根据调用视图的名称空间(或正在渲染的模板)进行调整.
    【解决方案4】:

    current_app 变量是您必须将自己设置为您喜欢的东西。

    我个人建议将其设置为 __name__.rsplit('.', 1)[0] 之类的东西,这样您就可以在 spam/views.py 中获得 spam

    但您可以将其定义为您喜欢的任何名称,只要您的应用名称与您在 urls 文件中定义的名称一致即可。

    【讨论】:

    • 嗯,也许我提到 current_app 是一个红鲱鱼。假设我使用相同的应用程序(相同的 views.py 文件,等等),但是两次导入相同的 app/urls.py 文件(项目的 urls.py 文件中的两个不同的包含,在两个命名空间下,就像在我的上面的示例),有没有办法自动让我的 reverse(){% url %} 调用坚持使用它们被调用的相同命名空间?
    • @peppergrower:据我所知没有。我自己选择将命名空间添加到reverse{% url %} 声明中。省去了一点麻烦。不过,将render_to_responsereverse 包装成包含您current_app 的版本将是微不足道的。这样的事情可能就足够了:reverse = lambda *a, **kw: original_reverse(current_app=YOUR_CURRENT_APP, *a, **kw)
    【解决方案5】:

    如果您不太依赖命名空间,则可以改为将 urls.py 编写为:

    urlpatterns = patterns('',
        (r'^(P<instance>/)', 'controllers.route_me'),
    )
    

    这将调用 controllers.route_me 函数并将请求以及字符串“instance”传递给它,您可以这样处理:

    # (in controllers.py)
    def route_me(request, instance):
        # you now have complete control, and do what you need to do with the 'instance' and 'request' vars
        pass
    

    【讨论】:

      猜你喜欢
      • 2018-08-20
      • 2017-03-13
      • 2013-10-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-23
      • 2013-11-09
      • 1970-01-01
      相关资源
      最近更新 更多