メッセージフレームワーク

Web アプリケーションでは非常に一般的に、フォームやその他のユーザーインプットのプロセスの後、ユーザーに向けて一過性の通知メッセージ ("フラッシュメッセージ" とも言われます) を表示する必要があります。

Django は、anonymous および認証済みユーザーの両方に対して、Cookie とセッションをベースにしたメッセージングを完全にサポートしています。このメッセージフレームワークは、一時的に一つのリクエスト内にメッセージを保管し、その後のリクエスト (通常、直後のリクエスト) 内で表示するために、これらを検索することを可能にします。全てのメッセージは、優先順位 (例えば infowarning、ないし error) を決定づける特定の level でタグづけされます。

メッセージを有効にする

メッセージは、 ミドルウェア クラスと、それに対応する コンテクストプロセッサー を通して実行されます。

django-admin startproject によって生成されたデフォルトの settings.py は、メッセージ機能を有効にするために必要な設定を全て含んでいます:

  • 'django.contrib.messages' は、INSTALLED_APPS の中にあります。

  • MIDDLEWARE'django.contrib.sessions.middleware.SessionMiddleware''django.contrib.messages.middleware.MessageMiddleware' を含みます。

    デフォルトの storage backendsessions に依存します。そのため SessionMiddleware を有効にして、 MIDDLEWARE 内で MessageMiddleware より前に記述する必要があります。

  • TEMPLATES 設定で定義した DjangoTemplates バックエンドの 'context_processors' オプションは、'django.contrib.messages.context_processors.messages' を含みます。

メッセージを使いたくない場合は、INSTALLED_APPS から 'django.contrib.messages' を、MIDDLEWARE から MessageMiddleware 行を、そして TEMPLATES から messages コンテクストプロセッサーを削除できます。

メッセージエンジンを設定する

ストレージバックエンド

メッセージフレームワークは、一時的なメッセージを保管するために、異なるバックエンドを使うことができます。

Django は、django.contrib.messages の中で、以下の3つのビルトインのストレージクラスを提供しています:

class storage.session.SessionStorage

このクラスは、すべてのメッセージをリクエストのセッション内部に保管します。ゆえに、Django の contrib.sessions アプリケーションが必要となります。

class storage.cookie.CookieStorage

このクラスは、複数のリクエストにわたって通知を保持するために、Cookie (改ざんを防ぐため秘密のハッシュで署名されます) にメッセージデータを保管します。古いメッセージは Cookie データのサイズが 2048 バイトを超えると破棄されます。

Changed in Django 3.2:

Messages format was changed to the RFC 6265 compliant format.

class storage.fallback.FallbackStorage

このクラスは、まず CookieStorage を使い、単一の Cookie に合わないメッセージに対して SessionStorage を使います。Django の contrib.sessions アプリケーションも必要となります。

この動作は、可能な限りセッションへの書き込みを避けます。一般的なケースでは最高のパフォーマンスを提供するはずです。

FallbackStorage はデフォルトのストレージクラスです。もしあなたのニーズに合わないときは、 MESSAGE_STORAGE を完全な import パスに設定することで他のストレージクラスを選べます。例えば:

MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
class storage.base.BaseStorage

独自のストレージクラスに書き込むためには、 django.contrib.messages.storage.base 内の BaseStorage クラスをサブクラス化して、 _get_store メソッドを実装してください。

メッセージレベル

メッセージフレームワークは、Python ロギングモジュールに似た、設定可能なレベルアーキテクチャに基づきます。メッセージレベルは、タイプによるグループメッセージを可能にします。これにより、ビューやテンプレートの中で異なるフィルターおよび表示ができるようになります。

django.contrib.messages から直接インポートできるビルトインのレベルは以下の通りです:

定数 目的
DEBUG プロダクション環境では無視 (ないし削除) される、開発に関連したメッセージ
INFO ユーザーに何か情報を伝えるメッセージ
SUCCESS あるアクションが成功した、例えば "あなたのプロフィールは無事に更新されました"
WARNING 問題は起きなかったが、問題になり得る
ERROR あるアクションが成功 しなかった か、他の問題が発生した

MESSAGE_LEVEL 設定は、最小限の記録されたレベルを変更するため (もしくは リクエストごとに変更 するため)に使うことができます。これ以下のレベルのメッセージを追加しようとしても無視されます。

メッセージタグ

メッセージタグは、メッセージレベルの表現文字列に加えて、ビューの中で直接付与される追加のタグです (詳しくは、以下の 追加のメッセージタグを付与する をご覧ください )。タグは文字列で保管され、スペースによって区切られます。典型的には、メッセージタグは、メッセージタイプに基いてメッセージのスタイルをカスタマイズするために、CSS のクラスとして利用されます。デフォルトでは、それぞれのレベルは、レベル定数を小文字にした、単一のタグを持っています。

レベル定数 タグ
DEBUG debug
INFO info
SUCCESS success
WARNING warning
ERROR error

メッセージレベル (ビルトインないしカスタムのいずれか) のデフォルトのタグを変更するためには、 MESSAGE_TAGS 設定を、あなたが変更したいと思うレベルを含むディクショナリにセットしてください。これでデフォルトのタグを拡張するので、あなたがすべきことはオーバーライドしようとするレベルに対してタグを提供することだけです:

from django.contrib.messages import constants as messages
MESSAGE_TAGS = {
    messages.INFO: '',
    50: 'critical',
}

ビューとテンプレートでメッセージを使う

add_message(request, level, message, extra_tags='', fail_silently=False)[ソース]

メッセージを付与する

メッセージを付与するには、以下を呼び出してください:

from django.contrib import messages
messages.add_message(request, messages.INFO, 'Hello world.')

いくつかのショートカットメソッドは、一般的に使われるタグ (通常、メッセージのHTMLクラスとして表されます) とともにメッセージを付与する標準的な方法を提供します:

messages.debug(request, '%s SQL statements were executed.' % count)
messages.info(request, 'Three credits remain in your account.')
messages.success(request, 'Profile details updated.')
messages.warning(request, 'Your account expires in three days.')
messages.error(request, 'Document deleted.')

メッセージを表示する

get_messages(request)[ソース]

あなたのテンプレート内で、以下のようなものを使ってください:

{% if messages %}
<ul class="messages">
    {% for message in messages %}
    <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
    {% endfor %}
</ul>
{% endif %}

コンテクストプロセッサーを使っている場合、テンプレートは RequestContext でレンダリングされる必要があります。もしくは、 messages をテンプレートコンテクストに対して有効にしてください。

Even if you know there is only one message, you should still iterate over the messages sequence, because otherwise the message storage will not be cleared for the next request.

コンテクストプロセッサーは、メッセージレベルの名前と数値を対応させた DEFAULT_MESSAGE_LEVELS 変数も提供します。

{% if messages %}
<ul class="messages">
    {% for message in messages %}
    <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>
        {% if message.level == DEFAULT_MESSAGE_LEVELS.ERROR %}Important: {% endif %}
        {{ message }}
    </li>
    {% endfor %}
</ul>
{% endif %}

テンプレート以外ではget_messages() を使うことができます:

from django.contrib.messages import get_messages

storage = get_messages(request)
for message in storage:
    do_something_with_the_message(message)

例えば、 すべてのメッセージを取得して、TemplateResponseMixin の代わりに JSONResponseMixin の中にreturnすることができます。

get_messages() は設定されたストレージバックエンドのインスタンスを返します。

Message クラス

class storage.base.Message

When you loop over the list of messages in a template, what you get are instances of the Message class. They have only a few attributes:

  • message: メッセージの実際のテキストです。
  • level: メッセージのタイプを説明する数値です (詳細は上述の message levels セクション)。
  • tags: スペースによって区切られた、全てのメッセージのタグを結合する文字列 (extra_tagslevel_tag) です。
  • extra_tags: スペースによって区切られた、メッセージのカスタムタグを含む文字列です。空がデフォルトです。
  • level_tag: レベルを表す文字列です。デフォルトでは定数名の小文字バージョンですが、MESSAGE_TAGS 設定によって変更することができます。

独自のメッセージレベルを作成する

メッセージレベルは実際には単なる整数なので、独自のレベル定数を定義して、よりカスタマイズされたユーザーフィードバックを作成することができます。例えば:

CRITICAL = 50

def my_view(request):
    messages.add_message(request, CRITICAL, 'A serious error occurred.')

独自のメッセージレベルを作成するときには、既存のレベルを上書きしないように気をつけてください。ビルトインのレベルで使われている値は以下の通りです:

レベル定数
DEBUG 10
INFO 20
SUCCESS 25
WARNING 30
ERROR 40

HTML や CSS の中で独自のレベルを指定する必要がある場合は、MESSAGE_TAGS 設定を通してマップを提供する必要があります。

注釈

再利用可能なアプリケーションを作成している場合には、ビルトインの message levels だけを使い、独自のレベルに依存しないことが推奨されます。

Changing the minimum recorded level per-request

The minimum recorded level can be set per request via the set_level method:

from django.contrib import messages

# Change the messages level to ensure the debug message is added.
messages.set_level(request, messages.DEBUG)
messages.debug(request, 'Test message...')

# In another request, record only messages with a level of WARNING and higher
messages.set_level(request, messages.WARNING)
messages.success(request, 'Your profile was updated.') # ignored
messages.warning(request, 'Your account is about to expire.') # recorded

# Set the messages level back to default.
messages.set_level(request, None)

Similarly, the current effective level can be retrieved with get_level:

from django.contrib import messages
current_level = messages.get_level(request)

For more information on how the minimum recorded level functions, see Message levels above.

Adding extra message tags

For more direct control over message tags, you can optionally provide a string containing extra tags to any of the add methods:

messages.add_message(request, messages.INFO, 'Over 9000!', extra_tags='dragonball')
messages.error(request, 'Email box full', extra_tags='email')

Extra tags are added before the default tag for that level and are space separated.

Failing silently when the message framework is disabled

If you're writing a reusable app (or other piece of code) and want to include messaging functionality, but don't want to require your users to enable it if they don't want to, you may pass an additional keyword argument fail_silently=True to any of the add_message family of methods. For example:

messages.add_message(
    request, messages.SUCCESS, 'Profile details updated.',
    fail_silently=True,
)
messages.info(request, 'Hello world.', fail_silently=True)

注釈

Setting fail_silently=True only hides the MessageFailure that would otherwise occur when the messages framework disabled and one attempts to use one of the add_message family of methods. It does not hide failures that may occur for other reasons.

Adding messages in class-based views

class views.SuccessMessageMixin

Adds a success message attribute to FormView based classes

get_success_message(cleaned_data)

cleaned_data is the cleaned data from the form which is used for string formatting

Example views.py:

from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.edit import CreateView
from myapp.models import Author

class AuthorCreateView(SuccessMessageMixin, CreateView):
    model = Author
    success_url = '/success/'
    success_message = "%(name)s was created successfully"

The cleaned data from the form is available for string interpolation using the %(field_name)s syntax. For ModelForms, if you need access to fields from the saved object override the get_success_message() method.

Example views.py for ModelForms:

from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.edit import CreateView
from myapp.models import ComplicatedModel

class ComplicatedCreateView(SuccessMessageMixin, CreateView):
    model = ComplicatedModel
    success_url = '/success/'
    success_message = "%(calculated_field)s was created successfully"

    def get_success_message(self, cleaned_data):
        return self.success_message % dict(
            cleaned_data,
            calculated_field=self.object.calculated_field,
        )

Expiration of messages

The messages are marked to be cleared when the storage instance is iterated (and cleared when the response is processed).

To avoid the messages being cleared, you can set the messages storage to False after iterating:

storage = messages.get_messages(request)
for message in storage:
    do_something_with(message)
storage.used = False

Behavior of parallel requests

Due to the way cookies (and hence sessions) work, the behavior of any backends that make use of cookies or sessions is undefined when the same client makes multiple requests that set or get messages in parallel. For example, if a client initiates a request that creates a message in one window (or tab) and then another that fetches any uniterated messages in another window, before the first window redirects, the message may appear in the second window instead of the first window where it may be expected.

In short, when multiple simultaneous requests from the same client are involved, messages are not guaranteed to be delivered to the same window that created them nor, in some cases, at all. Note that this is typically not a problem in most applications and will become a non-issue in HTML5, where each window/tab will have its own browsing context.

設定

A few settings give you control over message behavior:

For backends that use cookies, the settings for the cookie are taken from the session cookie settings: