Search
Write a publication
Pull to refresh

Русские имена приложений и breadcrumbs в админке Django

Если вы используете Django админку, и хоть раз пробовали перевести имя приложения на русский язык, то для вас не станет сюрпризом отсутствие стандартного, предусмотренного разработчиками решения. В сети (в том числе и на хабре) можно найти информацию, посвященную этой проблемы.

Некоторые решения отлично работают, хоть и представляются слишком трудоемкими, другие ломают нативные джанговские тесты. Однако я не нашел ни одного простого решения, которое позволило бы изменить имя приложения в breadcrumbs.

Итак давайте посмотрим на внутренности админки и на то как можно решить нашу проблему.

image

Всего в джанго 29 шаблонов, использующих breadcrumbs, это легко проверить выполнив из корневого каталога фреймворка:

grep -R "{% block breadcrumbs %}" . | nl

Поэтому пришлось отказаться от переписывания стандарнтых шаблонов и обратить свое внимание на другие места, в котрых можно переопределить все и сразу.

Таким местом оказался модуль admin.py внутри приложения, потому что через его функцию admin.site.register проходят все модели которые будут отображаться в админке. Так что если обернуть ее в свой декоратор, то можно сразу в одном месте воздействовать на все зарегистрированные модели.

Все казалось просто – перегрузить несколько функций, атрибутов и дело сделано, но подвох оказался в том, что в разных шаблонах по разному определяется app_label. Так, например в app_index.html передается список app_list, у каждого элемента которого есть аттрибут name, в шаблон change_form.html передается сразу app_label. Поэтому в представленном вашему вниманию решении пришлось использовать несколько хаков.

Итак, от слов перейдем к коду:

from django.db.models.base import ModelBase
from django.core.urlresolvers import resolve

class AppLabelRenamer(object):
    ''' Переименовывает имя приложения и breadcrumbs в админке. '''

    def __init__(self, native_app_label, app_label):
        ''' self.module служит для отсеивания моделей других приложений. '''
        self.native_app_label = native_app_label
        self.app_label = app_label
        self.module = '.'.join([native_app_label, 'models'])

    class string_with_realoaded_title(str):
        ''' Класс для имени нашего приложения. Перегружает метод title,
            что впоследствии приводит к переименованию приложения. 
            Спасибо Ionel Maries Cristian '''
        def __new__(cls, value, title):
            instance = str.__new__(cls, value)
            instance._title = title
            return instance

        def title(self):
            return self._title

        __copy__ = lambda self: self
        __deepcopy__ = lambda self, memodict: self

    def rename_app_label(self, f):
        ''' Изменяет имя приложения и breadcrumbs '''
        app_label = self.app_label

        def rename_breadcrumbs(f):
            def wrap(self, *args, **kwargs):
                extra_context = kwargs.get('extra_context', {})
                extra_context['app_label'] = app_label
                kwargs['extra_context'] = extra_context
                return f(self, *args, **kwargs)
            return wrap

        def wrap(model_or_iterable, admin_class=None, **option):
            ''' '''
            if isinstance(model_or_iterable, ModelBase):
                model_or_iterable = [model_or_iterable]
            for model in model_or_iterable:
                if model.__module__ != self.module:
                    continue
                if admin_class is None:
                    admin_class = type(model.__name__+'Admin',
                                       (admin.ModelAdmin,), {})
                admin_class.add_view = rename_breadcrumbs(admin_class.add_view)
                admin_class.change_view = rename_breadcrumbs(admin_class.change_view)
                admin_class.changelist_view = rename_breadcrumbs(admin_class.changelist_view)
                model._meta.app_label = self.string_with_realoaded_title(
                                            self.native_app_label,
                                            self.app_label)
            return f(model, admin_class, **option)
        return wrap

    def rename_app_index(self, f):
        ''' Изменяет breadcrumb для имени приложения в app_index.html
            Здесь же можно изменить h1 заголовок, добавив
            в extra_content {'title': self.app_label} '''
        def wrap(request, app_label, extra_context=None):
            requested_app_label = resolve(request.path).kwargs.get('app_label', '')
            if requested_app_label and requested_app_label == self.native_app_label:
                app_label = self.string_with_realoaded_title(self.native_app_label,
                                                             self.app_label)
            else:
                app_label = requested_app_label
            return f(request, app_label, extra_context=None)
        return wrap

    def main(self):
        ''' Оборачивает стандартные функции нашими декораторами. '''
        admin.site.register = self.rename_app_label(admin.site.register)
        admin.site.app_index = self.rename_app_index(admin.site.app_index)

# Пример использования, где exampleapp имя нашего приложения.
# а Хабрахабр его перевод.
AppLabelRenamer(native_app_label=u'simplelms', app_label=u'Хабрахабр').main()



Осталось только вставить сорцы в admin.py вашего приложения, подставив свои аргументы (native_app_label и app_label) в конструктор класса и запустив main().

Класс string_with_realoaded_title взят отсюда.

Спасибо за внимание.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.