在Django中,如何为大多数路径添加区域前缀

In Django, How to Add Regional Prefix to Most Paths

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

我的计划:

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

更新:

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

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

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

我想出了几种方法(第四种是使用子域的好处)。所有人都假设有一个中间件可以检测区域并根据请求进行设置。

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

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

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

  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,但它们是单行的:

urlpatterns = create_patterns_for_region(Region.EU)

这些引用了一个模板,该模板结合了我想成为区域性的 URL 和我想保留的 URL "bare":

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)

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