В Django, как добавить региональный префикс к большинству путей

Я преобразовываю сайт электронной коммерции в регион (например, США, ЕС), чтобы посетители воспринимали его как другой сайт в зависимости от контента, который они увидят, хотя на самом деле это один сайт (для многих причины). Большинство путей на моем сайте будут привязаны к региону благодаря префиксу региона в начале пути, например, «/us/» (однако я мог бы преобразовать все, если бы это значительно упростило задачу).

Мой план:

  • ПО промежуточного слоя идентифицирует регион на основе 1) пути запроса, 2) сеанса или 3) угадывания на основе IP-адреса в указанном порядке и устанавливается в объекте запроса. Кроме того, как только они используют региональный путь, он сохраняется как значение сеанса. Таким образом, контекст региона переносится по URL-адресам, не зависящим от региона.

  • Шаблоны URL, которые зависят от региона, должны быть обновлены, чтобы соответствовать региону, хотя я уже обнаружил регион в промежуточном программном обеспечении, поскольку логика была более сложной, чем просто путь. Тем не менее, я должен сделать его параметром и передать во все свои представления по следующей причине (реверсирование). Кроме того, любой путь, который становится региональным, будет иметь свои предыдущие шаблоны 301, перенаправляющие на свои региональные пути.

  • Чтобы генерировать ссылки, зависящие от региона, мне приходится обновлять многие вызовы reverse() и {% url %}, добавляя аргумент региона. Я бы хотел, чтобы здесь был какой-то слой, который я мог бы настроить для динамического изменения URL-адресов со знанием запроса.

Мой основной вопрос - лучший способ справиться с реверсом (последняя пуля). Такое ощущение, что много ненужной работы. Я открыт для лучших способов решения проблемы в целом.

Обновления:

  • Я исключил поддомены, потому что известно, что они вредны для SEO и передачи полномочий. Кроме того, я думаю, что поддомены могут подразумевать совершенно разные настройки, тогда как сейчас я буду управлять этим как одним веб-приложением.
  • Как указывает @RemcoGerlich, в основном я хочу добавить автоматическое поведение, которое LocaleMiddleware/i18n_patterns добавляет как в urlconf, так и в обратном порядке.

person John Lehmann    schedule 24.12.2016    source источник


Ответы (2)


Я придумал несколько способов, как это можно сделать (четвертый — бонус с использованием поддоменов). Все предполагают промежуточное ПО, которое определяет регион и устанавливает его по запросу.

  1. Следуя совету @RemcoGerlich, подражайте тому, как Django обрабатывает интернационализацию URL-адресов. LocaleMiddleware определяет язык и устанавливает активный язык для этого запроса (с помощью локальной переменной потока). Затем этот активный язык используется для формирования URL-адресов с помощью i18n_patterns(), который фактически возвращает LocaleRegexURLResolver (который является подклассом обычного преобразователя) вместо URL-адресов. Я считаю, что нечто подобное можно сделать для поддержки других типов префиксов.

  2. Более грубый подход состоит в том, чтобы снова сохранить регион не только в запросе, но и снова в локальной переменной потока, как это делает Django для активного языка. Обновите URL-адреса, чтобы они имели именованный аргумент для префикса региона, и добавьте аргументы просмотра. Реализуйте пользовательский реверс, чтобы добавить параметр региона. Если вы склонны делать зло, это может быть исправлено, чтобы не касаться каждой отдельной ссылки на шаблоны reverse и url.

  3. Используйте промежуточное ПО, чтобы установить request.urlconf< /a> в зависимости от региона, чтобы переопределить ROOT_URLCONF. Это предоставляет совершенно другой набор URL-адресов только для этого запроса. Создайте по одной новой конфигурации URL для каждого региона, добавьте свой префикс, а затем включите базовую конфигурацию URL. Не нужно захватывать региональную часть пути или возиться с параметрами представления. Изменение URL-адресов «просто работает».

  4. Если вы хотите использовать поддомены, чего я не делал, есть приложение Django под названием django-hosts, как указано в этом вопросе: Django: перезапишите ROOT_URLCONF с помощью request.urlconf в промежуточном программном обеспечении.

Для моего приложения переопределение 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"

Я создал по одной новой конфигурации URL для каждого региона, но это СУХИЕ однострочники:

urlpatterns = create_patterns_for_region(Region.EU)

Они ссылались на шаблон, который сочетал в себе как 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)

Обязательно обновите кеширование, чтобы включить регион. ;)

person John Lehmann    schedule 26.12.2016

Нет реального ответа, только два предложения:

  1. Вы не можете использовать поддомены? Та же идея с промежуточным программным обеспечением, но делает его независимым от генерации URL.

  2. Django поддерживает вашу идею, но для языков вместо регионов (docs здесь), возможно, вы сможете адаптировать это или хотя бы посмотреть, как это решает вашу проблему.

person RemcoGerlich    schedule 24.12.2016
comment
Спасибо Ремко. В основном я исключил поддомены, потому что известно, что они вредны для SEO. Например, я хочу, чтобы кредит домена для моего существующего сайта приносил мне пользу в новых регионах. Мне нужно посмотреть документацию по языку. - person John Lehmann; 24.12.2016
comment
Да, то, что они делают для языков, это именно то, что я пытаюсь сделать. Я могу сделать то же самое на стороне urlconf, используя include https://docs.djangoproject.com/en/1.10/topics/http/urls/#Include-other-urlconfs однако я не понимаю, как заставить работать реверсирование? - person John Lehmann; 24.12.2016
comment
И даже включение не обеспечивает автоматического волшебного поведения, когда код языка удаляется из пути и не отправляется в качестве аргумента представлениям. Тоже что-то хочу. - person John Lehmann; 24.12.2016
comment
Еще раз спасибо, это помогло мне найти несколько ответов, которые я разместил отдельно. - person John Lehmann; 26.12.2016