Всем по сообщению

Original author: Jeff Croft
  • Translation
В сегодняшних веб-приложениях часто требуется выдать посетителю некоторое оповещение. Будь то «Ваш комментарий сохранён и ожидает модерации» или же «Благодарим за проявленный интерес, мы обязательно вышлем Вам приглашение, как только откроемся», эти небольшие сообщения появляются то тут, то там постоянно, так что весьма здорово иметь удобный интерфейс для отображения их пользователю.

Идущее в поставке с Django приложение аутентификации и авторизации (django.contrib.auth) всегда включало базовый функционал для отображения всплывающих сообщений пользователю, но он имел несколько досадных недостатков. В Django 1.2 же теперь есть совершенно новый фреймворк для таких сообщений, написанный в первую очередь Тобаясом Макналти (Tobias McNulty).


Так что же было не так со старым способом?


Поскольку старая система сообщений является частью django.contrib.auth, вы должны подключить это приложение, чтобы воспользоваться малой его частью, отвечающей за оповещения. Но бывает, что вам не нужны аутентификация и авторизация, но нужны эти маленькие сообщения. Или, возможно, вы используете своё собственное приложение аутентификации. Или же, наоборот, вы используете django.contrib.auth, но вам не нужны сообщения. В Django 1.2 у вас наконец появилась возможность для всего этого.

Кроме того, старая система хранит сообщения в базе данных. И хоть это и нормально для многих проектов, всё-таки это означает, что каждый просмотр любой странички сопровождается дополнительным запросом к БД. А он попросту не нужен в большинстве случаев. В Django 1.2 появилась возможность хранить сообщения вне базы данных для небольшого повышения производительности.

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

Наконец, система оповещений в Django 1.1 жёстко привязывает каждое сообщение к конкретному пользователю, что означает, что вы не можете показывать такие сообщения анонимным пользователям. В Django 1.2 появилась и возможность показывать оповещения вне зависимости от того, залогинен пользователь или нет.


Итак, приступим.


Новая система уже подключена по умолчанию, если вы создаёте проект при помощи django-admin.py startproject. Если же у вас уже есть старый проект, или вы создали проект каким-либо другим способом, включить систему очень просто. Всего три шага:
  1. Добавьте 'django.contrib.messages.middleware.MessageMiddleware' в список MIDDLEWARE_CLASSES в файле настроек.
  2. Добавьте 'django.contrib.messages.context_processors.messages' в список TEMPLATE_CONTEXT_PROCESSORS в файле настроек.
  3. Добавьте 'django.contrib.messages' в список INSTALLED_APPS всё в том же файле настроек.

Добавление оповещений


Добавление оповещений пользователям в новом фреймворке также элементарно, особенно если вам достаточно одного из пяти предопределённых типов сообщений (DEBUG, INFO, SUCCESS, WARNING, ERROR — об этом чуть позже).

Итак, что же вам нужно сделать:
  1. from django.contrib import messages
  2. messages.success(request, "Skadoosh! You've updated your profile!")
Точно так же и для других типов сообщений:
  1. messages.info(request, 'Yo! There are new comments on your photo!')
  2. messages.error(request, 'Doh! Something went wrong.')
  3. messages.debug(request, 'Bam! %s objects were modified.' % modified_count)
  4. messages.warning(request, 'Uh-oh. Your account expires in %s days.' % expiration_days)
Поскольку сообщения прикрепляются к объекту request, вам понадобится доступ к нему, но он у вас уже есть во всех вьюшках.

Если вы переходите со старой версии, вам понадобится выполнить всего два несложных пункта:
  1. Во всех файлах views.py, где имеется создание оповещений, добавьте в начале следующую строку:
    from django.contrib import messages
  2. Замените везде
    request.user.message_set.create(message=message)
    на вызов одного из методов нового API, например
    messages.error(request, message)

Отображение сообщений


Для вывода сообщений в шаблоне используйте нечто вроде следующих строк:
  1. {% if messages %}
  2.     <ul class="messages">
  3.     {% for message in messages %}
  4.         <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>
  5.             {{ message }}
  6.         </li>
  7.     {% endfor %}
  8.     </ul>
  9. {% endif %}
Как правило имеет смысл поместить эти строки в ваш базовый шаблон, так чтобы все шаблоны, наследующие его, отображали оповещения.

Код практически идентичен тому, что вы уже использовали в Django 1.1, но вы должны были заметить новое свойство tags. Django 1.2 даёт каждому оповещению тип со строковым представлением для использования в шаблонах. В данном случае мы выводим это значение в качестве имени CSS класса, которое может быть использовано для индивидуальной стилизации различных типов сообщений, например:
  1. .messages li.error { background-color: red; }
  2. .messages li.success { background-color: green; }
Если вы просто хотите показывать пользователю сообщения, можете дальше не читать. На самом деле больше ничего и не надо. Всё легко и просто.


Механизмы хранения сообщений


Django предоставляет несколько бэкендов для хранения сообщений, и очень легко вы можете создать свой собственный. LegacyFallbackStorage используется по умолчанию и подойдёт для большинства проектов, однако, есть несколько причин, по которым вы захотите сменить бэкенд:
  • django.contrib.messages.storage.session.SessionStorage. Этот бэкенд хранит все сообщения в сессии. Таким образом, требуется подключить приложение django.contrib.sessions (скорее всего оно уже активировано в вашем проекте, т.к. оно используется по умолчанию). Поскольку сессии по умолчанию хранятся в базе данных, использование этого бэкенда всё ещё требует запроса к БД при обращении к сообщениям (например, при вызове {% if messages %} в шаблоне).
  • django.contrib.messages.storage.cookie.CookieStorage хранит сообщения в куках. Таким образом, запрос к базе не требуется, что влечёт большую производительность. Однако, имеется один недостаток: максимальная длина куки — 4096 байт, так что сообщения длиннее 4 КБ доставлены не будут.
  • django.contrib.messages.storage.fallback.FallbackStorage: этот механизм сперва использует CookieStorage, но в случае, когда текст не помещается в куку, обращается к SessionStorage.
  • django.contrib.messages.storage.user_messages.LegacyFallbackStorage. Предоставлен для обратной совместимости с Django 1.1 и более ранними версиями. Работает точно так же, как FallbackStorage, но также получает сообщения из старой системы оповещений — django.contrib.auth. Однако, как и сама система из django.contrib.auth, этот механизм объявлен устаревшим и будет убран из Django 1.4. Пока этот механизм используется по умолчанию, но как только его уберут, на его место встанет FallbackStorage.

Типы сообщений и тэги


Как было упомянуто ранее, Django предоставляет 5 встроенных типов сообщений. Каждый тип — это целое число. Вы без проблем можете расширить или изменить существующие типы. По умолчанию мы имеем:
  • DEBUG: 10
  • INFO: 20
  • SUCCESS: 25
  • WARNING: 30
  • ERROR: 40
Чтобы добавить свой собственный тип, объявите константу и вызовите метод add_message() для создания сообщения нового типа:
  1. CRITICAL = 50
  2. messages.add_message(request, CRITICAL, 'OH NOES! A critical error occured.')
Конечно, вы захотите использовать эту информацию в HTML и CSS, так что вам потребуется добавить настройку MESSAGE_TAGS в ваш файл settings.py, чтобы задать строковое представление вашему новому типу сообщений:
  1. MESSAGE_TAGS = {50: 'critical'}

В заключение


Новая система сообщений, включенная в Django 1.2, — это не очень сложная часть фреймворка, но она предоставляет функционал, без которого нельзя представить современное веб-приложение, и делает это очень элегантно и просто. Кроме того, она полностью совместима с предыдушими версиями, так что вам не надо беспокоиться о том, что старый код или сторонние приложения сломаются после перехода на 1.2. Но не забывайте, что прослойка совместимости будет удалена в 1.4, запланированной где-то на вторую половину 2027-го года.

Шутка. Наслаждайтесь Django 1.2! Это действительно классное обновление нашего любимого фреймворка.

--
Перевод подготовлен в Vim.
Share post

Comments 12

    +1
    спасибо за перевод) буду использовать.
      +1
      спасибо. упустил как-то эту новинку
        +2
        Уже заюзал эту систему
          +2
          Когда уже наконец выдет 1.2?
          ps: не знал, что с помощью вима можно переводить тексты, это очень по-гиковски! :)
            0
            По поводу релиза:
            It's difficult to know exactly how much work is left before we do the final ticket cull, but our first-cut revised estimate is for an RC1 release around March 22, with a final release around March 29.
            отсюда

            Вольный перевод:
            Точно не известно, но мы планируем отрелизиться около 29-го марта.


            По поводу vim'а: ну, переводил-то я сам, просто в виме можно сделать :vsp (vertical split), что весьма удобно при переводе. Ну, и удобства редактирования текста у него не отнять.
            +1
            Спасибо, теперь и мои приложения станут лучше
              +1
              спасибо за подробное описание, раньше юзал django-notify, очень похожий механизм.
                +1
                Было бы здорово, если бы кто-нибудь еще написал про обновление, касающееся форм и про особенности миграции на 1.2 в связи с этим.
                  0
                  попробовал в 1.2 beta 1
                  не заработало буду смотреть код…
                    0
                    Проверил на trunk'е — работает.

                    Вы подключили мидлварь, аппликейшн? Передаёте context_instance=RequestContext(request) в рендер шаблона?
                      0
                      все подключил по документации,
                      использую return direct_to_template(request,…
                      + при попытке messages.set_level(request, messages.DEBUG)
                      наблюдаю 'module' object has no attribute 'set_level'

                      Django Version: 1.2 beta 1

                      AUTHENTICATION_BACKENDS
                      ('django.contrib.auth.backends.ModelBackend',)

                      INSTALLED_APPS
                      ['djangoappengine', 'django.contrib.messages', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'xxx', 'contact']

                      MESSAGE_STORAGE
                      'django.contrib.messages.storage.session.SessionStorage'

                      MIDDLEWARE_CLASSES
                      ('django.middleware.common.CommonMiddleware', 'django.middleware.http.ConditionalGetMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.locale.LocaleMiddleware')

                      TEMPLATE_CONTEXT_PROCESSORS
                      ('django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages')

                        0
                        Да, с set_level косяк. Надо добавить в файл contrib.messages.api строчку:

                         __all__ = (
                            'add_message', 'get_messages',

                            'get_level', 'set_level',

                            'debug', 'info', 'success', 'warning', 'error',
                         )
                        

                        Уже отрепортил: code.djangoproject.com/ticket/13170

                        А вообще работает и на beta 1 (скачал, проверил).

                  Only users with full accounts can post comments. Log in, please.