在Django中,如何为大多数路径添加区域前缀
In Django, How to Add Regional Prefix to Most Paths
我正在将电子商务网站转换为特定区域(例如,美国、欧盟),因此根据访问者将看到的内容,访问者基本上会觉得它是一个不同的网站,即使它实际上是一个站点(出于多种原因)。我网站上的大部分路径都将成为特定于区域的路径,方法是在路径开头加上区域前缀,例如“/us/”(我可以转换所有路径,但如果它使它变得非常容易的话)。
我的计划:
中间件根据 1) 请求路径、2) 会话或 3) 基于 IP 地址的顺序猜测来识别区域,并在请求对象上设置。此外,一旦他们使用区域路径,它就会存储为会话值。通过这种方式,区域上下文被传送到非区域特定的 URLs。
区域特定的 URL 模式必须更新以匹配区域,即使我已经在中间件中检测到该区域,因为逻辑比只是路径。尽管如此,出于下一个原因(反转),我必须将其作为参数并传递到我的所有视图中。此外,任何正在成为区域性的路径都会将其先前的模式 301 重定向到它们的区域路径。
为了生成区域特定的链接,我必须通过添加区域参数来更新对 reverse() 和 {% url %} 的许多调用。我希望这里有一些层,我可以自定义以根据请求的知识动态反转 URLs。
我的主要问题是处理反转的最佳方式(最后一个项目符号)。感觉像是做了很多不必要的工作。我愿意接受更好的方法来整体解决问题。
更新:
- 我排除了子域,因为众所周知它们不利于 SEO 和转移权限。另外,我认为子域可能意味着完全不同的设置,而现在我将把它作为一个单独的 webapp 来管理。
- 正如@RemcoGerlich 指出的那样,基本上我想添加 LocaleMiddleware/i18n_patterns 在 urlconf 和反向中添加的自动行为。
没有真正的答案,只有两个建议:
你不能使用子域吗?与中间件的想法相同,但使其独立于 URL 代。
Django 支持您的想法,但对于语言而不是区域 (docs here),也许您可以调整它,或者至少看看它如何解决您的问题。
我想出了几种方法(第四种是使用子域的好处)。所有人都假设有一个中间件可以检测区域并根据请求进行设置。
按照@RemcoGerlich 的提示,模仿 Django 如何处理 URL 的国际化。 LocaleMiddleware
检测语言并根据该请求设置活动语言(使用线程局部变量)。然后,该活动语言用于通过 i18n_patterns() 形成 URL,实际上 returns 一个 LocaleRegexURLResolver
(它是普通解析器的子类)而不是 url。我相信可以做类似的事情来支持其他类型的前缀。
一种更暴力的方法是再次将区域不仅存储在请求中,而且再次存储在线程局部变量中,就像 Django 对活动语言所做的那样。更新 URL 以具有区域前缀的命名参数并添加到视图参数。实施自定义反转以添加区域参数。如果倾向于作恶,这可以通过猴子修补来避免触及每个 reverse
和 url
模板引用。
使用中间件set the request.urlconf
基于区域,覆盖ROOT_URLCONF
。这仅为该请求提供了一组完全不同的 URL。为每个区域创建一个新的 URLconf,添加它们的前缀,然后包含基本 URLconf。无需捕获路径的区域部分或弄乱视图参数。反转 URL "just works".
如果你想使用子域,我没有,有一个名为 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)
确保更新缓存以包含区域。 ;)
我正在将电子商务网站转换为特定区域(例如,美国、欧盟),因此根据访问者将看到的内容,访问者基本上会觉得它是一个不同的网站,即使它实际上是一个站点(出于多种原因)。我网站上的大部分路径都将成为特定于区域的路径,方法是在路径开头加上区域前缀,例如“/us/”(我可以转换所有路径,但如果它使它变得非常容易的话)。
我的计划:
中间件根据 1) 请求路径、2) 会话或 3) 基于 IP 地址的顺序猜测来识别区域,并在请求对象上设置。此外,一旦他们使用区域路径,它就会存储为会话值。通过这种方式,区域上下文被传送到非区域特定的 URLs。
区域特定的 URL 模式必须更新以匹配区域,即使我已经在中间件中检测到该区域,因为逻辑比只是路径。尽管如此,出于下一个原因(反转),我必须将其作为参数并传递到我的所有视图中。此外,任何正在成为区域性的路径都会将其先前的模式 301 重定向到它们的区域路径。
为了生成区域特定的链接,我必须通过添加区域参数来更新对 reverse() 和 {% url %} 的许多调用。我希望这里有一些层,我可以自定义以根据请求的知识动态反转 URLs。
我的主要问题是处理反转的最佳方式(最后一个项目符号)。感觉像是做了很多不必要的工作。我愿意接受更好的方法来整体解决问题。
更新:
- 我排除了子域,因为众所周知它们不利于 SEO 和转移权限。另外,我认为子域可能意味着完全不同的设置,而现在我将把它作为一个单独的 webapp 来管理。
- 正如@RemcoGerlich 指出的那样,基本上我想添加 LocaleMiddleware/i18n_patterns 在 urlconf 和反向中添加的自动行为。
没有真正的答案,只有两个建议:
你不能使用子域吗?与中间件的想法相同,但使其独立于 URL 代。
Django 支持您的想法,但对于语言而不是区域 (docs here),也许您可以调整它,或者至少看看它如何解决您的问题。
我想出了几种方法(第四种是使用子域的好处)。所有人都假设有一个中间件可以检测区域并根据请求进行设置。
按照@RemcoGerlich 的提示,模仿 Django 如何处理 URL 的国际化。
LocaleMiddleware
检测语言并根据该请求设置活动语言(使用线程局部变量)。然后,该活动语言用于通过 i18n_patterns() 形成 URL,实际上 returns 一个LocaleRegexURLResolver
(它是普通解析器的子类)而不是 url。我相信可以做类似的事情来支持其他类型的前缀。一种更暴力的方法是再次将区域不仅存储在请求中,而且再次存储在线程局部变量中,就像 Django 对活动语言所做的那样。更新 URL 以具有区域前缀的命名参数并添加到视图参数。实施自定义反转以添加区域参数。如果倾向于作恶,这可以通过猴子修补来避免触及每个
reverse
和url
模板引用。使用中间件set the
request.urlconf
基于区域,覆盖ROOT_URLCONF
。这仅为该请求提供了一组完全不同的 URL。为每个区域创建一个新的 URLconf,添加它们的前缀,然后包含基本 URLconf。无需捕获路径的区域部分或弄乱视图参数。反转 URL "just works".如果你想使用子域,我没有,有一个名为
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)
确保更新缓存以包含区域。 ;)