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

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

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

メッセージを有効にする

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

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

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

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

    デフォルトの ストレージバックエンドsessions に依存します。そのため 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 バイトを超えると破棄されます。

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 をテンプレートコンテキストに対して有効にしてください。

メッセージが一つしかないとわかっている場合でも、 messages 配列をイテレートする必要があります。そうしないとメッセージストレージは次のリクエストのためにクリアされないからです。

コンテキストプロセッサーは、メッセージレベルの名前と数値を対応付ける 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 で返すことができます。

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

Message クラス

class Message

テンプレート内のメッセージのリストをループさせると、 Message クラスのインスタンスが得られます。これらのインスタンスにはいくつかの属性しかありません:

  • 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 だけを使い、独自のレベルに依存しないことが推奨されます。

リクエストごとの最小記録レベルを変更する

最小記録レベルは、set_level メソッドを使用してリクエストごとに設定できます。

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)

同様に、現在の有効レベルは get_level を使って取得できます。

from django.contrib import messages

current_level = messages.get_level(request)

最小記録レベルが機能する仕組みについての詳細は、上記の Message levels を参照してください。

追加のメッセージタグを追加する

メッセージタグをより直接的に制御したい場合は、追加メソッドのいずれかに、追加のタグを含む文字列を任意で指定できます:

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

追加タグは、そのレベルのデフォルトタグの前に追加され、スペースで区切られます。

メッセージフレームワークが無効になっているときのエラーを隠す

再利用可能なアプリ (または他のコード) を書いており、メッセージング機能を含めたいが、利用者に無理に有効化させたくない場合は、任意の add_message ファミリーのメソッドに fail_silently=True という追加のキーワード引数を渡すことができます。たとえば:

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

注釈

fail_silently=True を設定すると、メッセージフレームワークが無効になっており、add_message メソッドのいずれかを使用しようとした際に発生するはずの MessageFailure を隠します。それ以外の理由で発生する失敗は隠されません。

クラスベースのビューにメッセージを追加する

class views.SuccessMessageMixin

FormView をベースにしたクラスに、成功メッセージ属性を追加します。

get_success_message(cleaned_data)

cleaned_data はフォームからのクリーンなデータであり、文字列フォーマットに使用されます。

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"

form からのクリーン済みデータは、 %(field_name)s 構文を使用して文字列補完に利用できます。ModelForms の場合、保存された object からフィールドにアクセスが必要な場合は、 get_success_message() メソッドをオーバーライドしてください。

ModelForms 用の views.py の例:

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,
        )

メッセージの有効期限

メッセージは、ストレージインスタンスがイテレートされる際にクリアされるようにマークされています (そして、応答が処理される際にもクリアされます)。

メッセージがクリアされるのを避けるために、イテレートの後にメッセージストレージを False に設定できます:

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

並列リクエストの動作

クッキー (そしてセッション) の仕組みから、同じクライアントが複数の要求を並行して行い、メッセージを設定または取得する場合、クッキーやセッションを使用するバックエンドの動作は定義されていません。たとえば、クライアントが1つのウィンドウ (またはタブ) でメッセージを作成する要求を開始し、別のウィンドウで未読メッセージを取得する要求を開始するとき、最初のウィンドウがリダイレクトされる前に、そのメッセージが最初ではなく、代わりに2番目のウィンドウに表示されることがあります。

要するに、同じクライアントからの複数の同時リクエストが関わると、メッセージがそれらを作成した同じウィンドウに必ず届くわけではなく、場合によっては全く届かないことがあります。ほとんどのアプリケーションでは通常問題とならず、HTML5では各ウィンドウ/タブが独自の閲覧コンテキストを持つため、これは問題にならなくなります。

設定

いくつかの 設定 を使用して、メッセージの動作を制御できます。

Cookiesを使用するバックエンドでは、Cookieの設定はセッションCookieの設定から取得されます。

テスト

New in Django 5.0.

このモジュールは、HttpResponse に添付されたメッセージをテストするためのカスタマイズされたテストアサーションメソッドを提供しています。

このアサーションを利用するには、クラス階層に MessagesTestMixin を追加してください。

from django.contrib.messages.test import MessagesTestMixin
from django.test import TestCase


class MsgTestCase(MessagesTestMixin, TestCase):
    pass

次に、テストで MsgTestCase を継承してください。

MessagesTestMixin.assertMessages(response, expected_messages, ordered=True)

response に追加された messagesexpected_messages と一致することをアサートします。

expected_messagesMessage オブジェクトのリストです。

デフォルトでは、比較は順序に依存します。これを無効にするには、 ordered 引数を False に設定します。

Back to Top