【问题标题】:In Django, How to Add Regional Prefix to Most Paths在 Django 中,如何为大多数路径添加区域前缀
【发布时间】:2016-12-24 13:42:24
【问题描述】:

我正在将电子商务网站转换为特定于区域(例如美国、欧盟)的网站,因此根据访问者将看到的内容,它基本上会感觉像是一个不同的网站,即使它实际上是一个网站(出于很多原因)。我网站上的大多数路径都将成为特定于区域的路径,方法是在路径开头添加区域前缀,例如“/us/”(如果它变得更容易,我可以转换所有路径)。

我的计划:

  • 中间件根据 1) 请求路径、2) 会话或 3) 根据 IP 地址的顺序猜测来识别区域,并在请求对象上设置。此外,一旦他们使用区域路径,它就会被存储为会话值。通过这种方式,区域上下文通过非区域特定的 URL 进行传递。

  • 必须更新特定于区域的 URL 模式以匹配区域,即使我已经在中间件中检测到该区域,因为逻辑比路径更复杂。尽管如此,出于下一个原因(反转),我必须将其作为参数并传递到我的所有视图中。此外,任何变成区域性的路径,都会将其先前的模式 301 重定向到其区域性路径。

  • 为了生成特定于区域的链接,我必须通过添加区域参数来更新对 reverse() 和 {% url %} 的许多调用。我希望这里有一些层可以自定义,以便在知道请求的情况下动态反转 URL。

我的主要问题是处理倒车的最佳方式(最后一个项目符号)。感觉像是做了很多不必要的工作。我愿意接受更好的方法来整体解决问题。

更新:

  • 我排除了子域,因为众所周知它们不利于 SEO 和转移权限。另外,我认为子域可能意味着完全不同的设置,而现在我将把它作为一个单独的 web 应用程序来管理。
  • 正如@RemcoGerlich 指出的那样,基本上我想添加LocaleMiddleware/i18n_patterns 在urlconf 和reversing 中添加的自动魔术行为。

【问题讨论】:

    标签: django routes url-routing region country


    【解决方案1】:

    我想出了几种可以做到这一点的方法(第四种是使用子域的奖励)。所有这些都假设有一个中间件可以检测区域并根据请求对其进行设置。

    1. 按照@RemcoGerlich 的提示,模仿 Django 如何处理 URL 的国际化。 LocaleMiddleware 检测语言并在该请求上设置活动语言(使用线程局部变量)。然后,使用该活动语言通过 i18n_patterns() 形成 URL,它实际上返回 LocaleRegexURLResolver(它是普通解析器的子类)而不是 url。我相信可以做类似的事情来支持其他类型的前缀。

    2. 更暴力的方法是再次将区域存储在请求中,而且再次存储在线程局部变量中,就像 Django 为活动语言所做的那样。更新 URL 以具有区域前缀的命名参数并添加到视图参数。实现自定义反向以添加区域参数。如果倾向于做坏事,可以对其进行猴子补丁以避免触及每个 reverseurl 模板引用。

    3. 使用基于区域的中间件set the request.urlconf,覆盖ROOT_URLCONF。这仅为此请求提供了一组完全不同的 URL。每个区域创建一个新的 URLconf,添加它们的前缀,然后包含基本 URLconf。无需捕获路径的区域部分或弄乱视图参数。反转 URL “正常工作”。

    4. 如果您想使用子域,而我没有使用,有一个名为 django-hosts 的 Django 应用程序,在此问题中引用:Django: Overwrite ROOT_URLCONF with request.urlconf in middleware

    对于我的应用程序,使用中间件覆盖 request.urlconf 是最简单、最优雅的解决方案。这是中间件的一个片段:

    # ... detect region first based on path, then session, and and maybe later IP address...
    # Then force the URLconf:
    if request.region == Region.EU:
        request.urlconf = "mysite.regional_urls.eu_urls"
    else:
        request.urlconf = "mysite.regional_urls.us_urls"
    

    我为每个区域创建了一个新的 URLconf,但它们是 DRY 单行:

    urlpatterns = create_patterns_for_region(Region.EU)
    

    这些引用了一个模板,该模板将我想成为区域性的 URL 与我想留下“裸露”的 URL 结合起来:

    from django.conf.urls import patterns, include, url
    
        def create_patterns_for_region(region):
            return patterns(
                '',
                # First match regional.
                url(r'^{}/'.format(region.short), include('mysite.regional_urls.regional_base_urls')),
                # Non-regional pages.
                url(r'', include('mysite.regional_urls.nonregional_base_urls')),
                # Any regional URL is missing.
                url(r'^{}/.*'.format(Region.REGION_PREFIX), error_views.Custom404.as_error_view()),
                # Attempt to map any non-regional URL to region for backward compatibility.
                url(r'.*', RegionRedirect.as_view()),
            )
    

    最后是向后兼容的重定向视图:

    class RegionRedirect(RedirectView):
        """ Map paths without region to regional versions for backward compatibility.
        """
        permanent = True
        query_string = True
    
        def get_redirect_url(self, *args, **kwargs):
            self.url = "/" + self.request.region.short + self.request.path
            return super(RegionRedirect, self).get_redirect_url(*args, **kwargs)
    

    确保更新缓存以包含区域。 ;)

    【讨论】:

      【解决方案2】:

      没有真正的答案,只有两个建议:

      1. 您不能使用子域吗?与中间件的想法相同,但使其独立于 URL 生成。

      2. Django 支持你的想法,但对于语言而不是区域 (docs here),也许你可以适应它,或者至少看看它如何解决你的问题。

      【讨论】:

      • 感谢 Remco。我主要排除了子域,因为众所周知它们对 SEO 不利。例如,我希望现有站点的域信用能够使我在新区域中受益。我得看看语言文档。
      • 是的,他们为语言所做的正是我想要做的。我可以在 urlconf 端使用 include docs.djangoproject.com/en/1.10/topics/http/urls/… 做同样的事情,但是,我看不到如何使反转工作?
      • 而且,即使包含也不提供从路径中剥离语言代码并且不将其作为参数发送给视图的自动行为。还有我想要的东西。
      • 再次感谢,这帮助我找到了我单独发布的几个答案。
      猜你喜欢
      • 2015-01-30
      • 2012-02-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-20
      • 1970-01-01
      • 2018-12-12
      • 2018-10-03
      相关资源
      最近更新 更多