Django parler TranslatableSlugMixin переводит с английского на другой язык, но после перевода не может быть переведен обратно возвращается 404

Я использую Django CMS с Django Parler и столкнулся с проблемой, которая сводит меня с ума, поэтому, если кто-нибудь сможет помочь, это будет очень цениться!

Итак, я создаю простое приложение для блога, в котором в качестве переводимого поля есть слаг. Вот упрощенная модель:

from parler.models import TranslatableModel, TranslatedFields

class Article(TranslatableModel):
    ...
    translations = TranslatedFields(
        ...
        slug = models.SlugField(_('slug'), max_length=255, blank=True, allow_unicode=True),
        meta = {'unique_together': (('language_code', 'slug'),)}
    )

    ...

    def get_absolute_url(self):
        return reverse('blog:article_detail', kwargs={'slug': self.slug})

Вот ссылки:

from django.conf.urls import include, url

from .views import ArticleDetailView

urlpatterns = [
    ...
    url(r'^(?P<slug>\w[-\w]*)/$', ArticleDetailView.as_view(), name='article_detail'),
]

И, наконец, вот мнение:

from django.views.generic import DetailView
from parler.views import TranslatableSlugMixin

from .models import Article

class ArticleDetailView(TranslatableSlugMixin, DetailView):
    model = Article
    template_name = 'blog/_article.html'

Я создал статью на английском, французском и немецком языках, с разными ссылками для каждого языка, давайте назовем их так:

/en/blog/english-slug
/fr/blog/french-slug
/de/blog/german-slug

Я могу перейти ко всем этим правильно, но в Django CMS вверху есть языковое меню, которое на английской странице показывает ссылки в виде:

/en/blog/english-slug
/fr/blog/english-slug
/de/blog/english-slug

Это нормально, так как именно это обрабатывает TranslatableSlugMixin в представлении (см. Здесь http://django-parler.readthedocs.io/en/latest/api/parler.views.html).

Поэтому, когда я нажимаю одну из ссылок (скажем, французскую), представление правильно находит правильную статью и перенаправляет меня на правильный URL-адрес. Итак, нажав:

/fr/blog/english-slug

Принял меня правильно, чтобы:

/fr/blog/french-slug

Но здесь все идет не так. Теперь я хочу вернуться на английскую страницу, которая отображается как:

/en/blog/french-slug

Но когда я нажимаю на ссылку, она переходит к 404. Это то же самое, если я перехожу на немецкий URL с французского. Однако, если я сразу перейду с английского на немецкий, это сработает.

Извините, я знаю, что объяснение сбивает с толку, но кажется, что перевод работает в одну сторону с базового / стандартного на другой язык, но не работает правильно при переключении между языками или обратно на базовый / по умолчанию.

Конечно, TranslatableSlugMixin разработан, чтобы это произошло?! Так я что-то здесь упускаю?

Любая помощь приветствуется. Рад предоставить больше информации, если это необходимо.

Спасибо

1 ответ

Решение

Итак, я выяснил, как сделать эту работу, и оказалось, что это сочетание вещей...

  1. Использование Django CMS по умолчанию было ошибкой:

    {% language_chooser "menu/language_chooser.html" %}
    

    Это приводит к URL-адресам, которые я описал выше:

    /en/blog/english-slug
    /fr/blog/english-slug
    /de/blog/english-slug
    

    Чтение документации Django Parler привело меня к использованию их языкового меню навигации:

    {% for lang_code, title in LANGUAGES %}
            {% get_language_info for lang_code as lang %}
            {% get_translated_url lang_code as tr_url %}
            {% if tr_url %}<li{% if lang_code == LANGUAGE_CODE %} class="is-selected"{% endif %}><a href="{{ tr_url }}" hreflang="{{ lang_code }}">{{ lang.name_local|capfirst }}</a></li>{% endif %}
    {% endfor %}
    

    Это приводит к URL-адресам, указывающим на правильное местоположение:

    /en/blog/english-slug
    /fr/blog/french-slug
    /de/blog/german-slug
    
  2. Чтобы сработала навигация Django Parler, мне нужно было обновить get_absolute_url() в модели, чтобы обрабатывать разные языки. Я сделал это следующим образом:

    from django.utils.translation import get_language
    
    from parler.models import TranslatableModel, TranslatedFields
    
    class Article(TranslatableModel):
    
        ...
    
        def get_absolute_url(self):
            language = get_language()
            if self.has_translation(language):
                slug = self.safe_translation_getter('slug', language_code=language)
                return reverse('blog:article_detail', kwargs={'slug': slug})
            # no translation so fallback to all article list
            return reverse('blog:article_list')
    

Уф! Это была головная боль! Надеюсь, это поможет кому-то еще в будущем!

PS Во время моего исследования я наткнулся на это приложение для блога, которое кажется действительно замечательным:

https://github.com/nephila/djangocms-blog

Это помогло мне добраться до сути этого кошмара!

ОБНОВИТЬ

Глядя на get_absolute_url() в djangocms-blog (ссылка выше), у них гораздо лучшее решение проблемы. Их реализация:

from django.utils.translation import get_language

from parler.models import TranslatableModel, TranslatedFields

class Article(TranslatableModel):

    ...

    def get_absolute_url(self, lang=None):
        if not lang or lang not in self.get_available_languages():
            lang = self.get_current_language()
        if not lang or lang not in self.get_available_languages():
            lang = get_language()
        with switch_language(self, lang):
            slug = self.safe_translation_getter('slug', language_code=lang, any_language=True)
            return reverse('blog:article_detail', kwargs={'slug': slug})

Спасибо Нефила, это спасло меня от многих ругательств и разочарований:)

Другие вопросы по тегам