Django模板中使用消息message框架

2019-06-13 14:37:40 浏览数 (1)

在web应用程序中,通常需要在处理表单或其他类型的用户输入之后向用户显示一次性通知消息(也称为“flash message”)。

为此,Django为匿名用户和经过身份验证的用户提供了对基于cookie和会话的消息传递的完全支持。messages框架允许您在一个请求中临时存储消息,并检索它们以在后续请求(通常是下一个请求)中显示。每条消息都有一个确定优先级的特定级别(例如,info、warning或error)。

启用消息

消息是通过中间件类和相应的上下文处理器实现的。

django-admin startproject创建的默认settings.py已经包含了启用消息功能所需的所有设置:

  • django.contrib.messagesINSTALLED_APPS中。
  • 中间件包含django.contrib.sessions.middleware.SessionMiddlewaredjango.contrib.messages.middleware.MessageMiddleware,默认存储后端依赖于会话。这就是为什么必须启用SessionMiddleware,并且在中间件中的MessageMiddleware之前。
  • 在模板设置中定义的DjangoTemplates的context_processor选项包含django.contrib.messages.context_processors.messages

如果你不想使用消息,你可以删除来自您的INSTALLED_APPSdjango.contrib.messages,来自MIDDLEWARE的MessageMiddleware,来自TEMPLATES的messages上下文处理器。

配置消息引擎

存储后端

消息框架可以使用不同的后端存储临时消息。

Django在django.contrib.messages中提供了三个内置的存储类:

  • class storage.session.SessionStorage

该类存储请求会话中的所有消息。因此它需要Django的contrib.sessions的应用程序。

  • class storage.cookie.CookieStorage

该类将消息数据存储在cookie中(使用秘密散列签名以防止操作),以便在请求之间持久化通知。如果cookie数据大小超过2048字节,则删除旧消息。

  • class storage.fallback.FallbackStorage

这个类首先使用CookieStorage,然后返回到使用SessionStorage来存储不能放入单个cookie的消息。它还需要Django的contrib.sessions的应用程序。

这种行为尽量避免向会话写入内容。它应该在一般情况下提供最好的性能。

FallbackStorage是默认的存储类。如果它不适合您的需要,您可以通过设置MESSAGE_STORAGE的完整导入路径来选择另一个存储类,例如:

代码语言:javascript复制
MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
  • class storage.base.BaseStorage

要编写自己的存储类,子类化django.contrib.messages.storage.baseBaseStorage,创建和实现_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_TAGSsetting设置为包含希望更改的级别的字典。由于这扩展了默认标签,只需要为您希望覆盖的级别提供标记:

代码语言:javascript复制
from django.contrib.messages import constants as messages
MESSAGE_TAGS = {
    messages.INFO: '',
    50: 'critical',
}

在视图和模板中使用消息

add_message(request, level, message, extra_tags='', fail_silently=False)

添加消息

要添加消息,直接使用message的add_messages方法

代码语言:javascript复制
from django.contrib import messages
messages.add_message(request, messages.INFO, 'Hello world.')

一些快捷方式提供了一种标准的方式来添加带有常用标记的消息(通常表示为消息的HTML类):

代码语言:javascript复制
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)

在您的模板中,使用如下内容:

代码语言:javascript复制
{% if messages %}
<ul class="messages">
    {% for message in messages %}
    <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
    {% endfor %}
</ul>
{% endif %}

如果您正在使用上下文处理器,您的模板应该使用RequestContext呈现。否则,确保消息对模板上下文可用。

即使您知道只有一条消息,您仍然应该遍历消息序列,否则消息存储将不会为下一个请求清除。

上下文处理器还提供了DEFAULT_MESSAGE_LEVELS变量,该变量是消息级别名称与其数值的映射:

代码语言:javascript复制
{% 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()

代码语言:javascript复制
from django.contrib.messages import get_messages

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

例如,可以获取所有消息,以JSONResponseMixin而不是TemplateResponseMixin返回它们。

get_messages()将返回配置的存储后端实例。

更多内容参看:https://docs.djangoproject.com/zh-hans/2.1/ref/contrib/messages/

消息类

class storage.base.Message

当您在模板中循环消息列表时,得到的是消息类的实例。这是一个非常简单的对象,只有几个属性:

  • 消息:消息的实际文本。
  • level:描述消息类型的整数(参见上面的message levels部分)。
  • 标记:由空格分隔的所有消息标记(extra_tags和level_tag)组合而成的字符串。
  • extra_tags:一个字符串,包含此消息的自定义标记,用空格分隔。默认为空。
  • level_tag:级别的字符串表示形式。默认情况下,它是相关常量名称的小写版本,但如果需要,可以通过使用MESSAGE_TAGS设置来更改。

创建自定义消息级别

消息级别不仅仅是整数,所以可以定义自己的级别常量,并使用它们创建更多定制的用户反馈,例如:

代码语言:javascript复制
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设置提供映射。

如果要创建可重用的应用程序,建议只使用内置的消息级别,而不依赖任何自定义级别。

更改每个请求的最低记录级别

可以通过set_level方法为每个请求设置最低记录级别:

代码语言:javascript复制
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检索当前有效级别:

代码语言:javascript复制
from django.contrib import messages
current_level = messages.get_level(request)

有关最低记录级别函数的更多信息,请参见上面的消息级别。

添加额外的消息标签

为了对消息标记进行更直接的控制,您可以选择为任何add方法提供包含额外标记的字符串:

代码语言:javascript复制
messages.add_message(request, messages.INFO, 'Over 9000!', extra_tags='dragonball')
messages.error(request, 'Email box full', extra_tags='email')

在该级别的默认标记之前添加额外的标记,并将它们分隔开来。

在禁用消息框架时静默失败

如果您正在编写一个可重用的应用程序(或其他代码片段),并且希望包含消息传递功能,但是如果用户不希望启用它,您可能会向add_message方法家族传递一个额外的关键字参数fail_silent =True。例如:

代码语言:javascript复制
messages.add_message(
    request, messages.SUCCESS, 'Profile details updated.',
    fail_silently=True,
)
messages.info(request, 'Hello world.', fail_silently=True)

设置fail_silent =True只会隐藏当消息框架被禁用并试图使用add_message方法家族之一时发生的MessageFailure。它不会隐藏由于其他原因可能发生的失败。

在基于类的视图中添加消息

class views.SuccessMessageMixin

success_message属性

向基于FormView的类添加一个success消息属性

get_success_message(cleaned_data)cleaned_data是从用于字符串格式化的表单中清除的数据,例如

代码语言:javascript复制
from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.edit import CreateView
from myapp.models import Author

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

通过使用%(field_name)的语法,可以使用从表单中清理出来的数据进行字符串插值。

get_success_message(self, cleaned_data)方法

对于ModelForms,如果需要从保存的对象访问字段,请覆盖get_success_message()方法。例如:

代码语言:javascript复制
from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.edit import CreateView
from myapp.models import ComplicatedModel

class ComplicatedCreate(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

代码语言:javascript复制
storage = messages.get_messages(request)
for message in storage:
    do_something_with(message)
storage.used = False

使用实例

创建消息分类显示模板

当消息的类型不同时,显示不同的样式,创建message-alert-content.html文件

代码语言:javascript复制
{% if messages %}
    {% for message in messages %}
        {% if message.level == DEFAULT_MESSAGE_LEVELS.INFO %}
            <div class="alert alert-info alert-dismissable">
                <button aria-hidden="true" data-dismiss="alert" class="close" type="button">×</button>
                【信息】:{{ message }}
            </div>
        {% endif %}

        {% if message.level == DEFAULT_MESSAGE_LEVELS.SUCCESS %}
            <div class="alert alert-success alert-dismissable">
                <button aria-hidden="true" data-dismiss="alert" class="close" type="button">×</button>
                【成功】:{{ message }}
            </div>
        {% endif %}

        {% if message.level == DEFAULT_MESSAGE_LEVELS.WARNING %}
            <div class="alert alert-warning alert-dismissable">
                <button aria-hidden="true" data-dismiss="alert" class="close" type="button">×</button>
                【警告】:{{ message }}
            </div>
        {% endif %}

        {% if message.level == DEFAULT_MESSAGE_LEVELS.ERROR %}
            <div class="alert alert-danger alert-dismissable">
                <button aria-hidden="true" data-dismiss="alert" class="close" type="button">×</button>
                【错误】:{{ message }}
            </div>
        {% endif %}
    {% endfor %}
{% endif %}

UpdateView中使用message

form_valid定义消息添加

基于类的视图可以定义在验证表单的函数中

代码语言:javascript复制
@method_decorator(login_required, name='dispatch')
class BlogNoticeUpdate(UpdateView):
    model = BlogNotice
    template_name = 'rearend/blog-notice-edit.html'
    form_class = BlogNoticeForm
    context_object_name = 'blog_notice'
    # success_url = reverse_lazy('blog_admin:blog_notice')  # 这儿不能使用reverse

    def get_success_url(self):  # 自定义跳转链接
        return reverse_lazy('blog_admin:blog_notice', kwargs=self.kwargs)

    def form_valid(self, form):
        messages.success(self.request, '更新成功,该页面显示更新后的内容!')
        return super(BlogNoticeUpdate, self).form_valid(form)

当然可以直接添加类方法,需要继承xxxMessageMixin父类

模板中包含消息模块

代码语言:javascript复制
<div class="ibox-content">
    <!--消息弹出-->
    {% include 'rearend/includes/message-alert-content.html' %}

    <form class="form-horizontal" method="post" action=".">
        <div class="form-group">
            <label for="id_blogger" class="col-sm-2 control-label">博主:</label>
            <div class="col-sm-10">
                <input type="text" name="blogger" value="{{ form.blogger.value }}" maxlength="10" required="" id="id_blogger" class="form-control">
            </div>
        </div>
        <div class="form-group">
            <label for="id_title" class="col-sm-2 control-label">公告标题:</label>
            <div class="col-sm-10">
                <input type="text" name="title" value="{{ form.title.value }}" maxlength="20" required="" id="id_title" class="form-control">
            </div>
        </div>
        <div class="form-group">
            <label for="id_content" class="col-sm-2 control-label">公告:</label>
            <div class="col-sm-10">
                <textarea name="content" cols="40" rows="10" id="id_content" class="form-control">{{ form.content.value }}</textarea>
            </div>
        </div>
        {% csrf_token %}
        <div class="hr-line-dashed"></div>
        <div class="form-group">
            <div class="col-sm-4 col-sm-offset-2">
                <button class="btn btn-primary" type="submit">保存内容</button>
            </div>
        </div>
    </form>
</div>

image.png

函数中使用message

直接在相应的位置添加

代码语言:javascript复制
messages.success(request, '删除专题成功!')

messages.error(request, 'xxx')  # 使用消息框架

##主要用在view.login函数,不管登录是否成功,都会设置message变量,然后在login.html显示

代码语言:javascript复制
from django.contrib import messages#需要导的包

##提供两个函数

代码语言:javascript复制
messages.add_message(request,messages.INFO,'要显示的字符串')
messages.get_messages(request)

默认类型有:(message.INFO)

  • DEBUG
  • INFO
  • SUCCESS
  • WARNING
  • ERROR 也可以在setting.py中自定义自己的等级标签 附上我的代码
  • view.login_2
代码语言:javascript复制
def login_2(request):
    if request.method == 'POST':
        login_form = LoginForm(request.POST)
        if login_form.is_valid():
            login_name=request.POST['username'].strip()
            login_password = request.POST['password']
            user = authenticate(username=login_name,password=login_password)
            if user is not None:
                if user.is_active:
                    auth.login(request,user)#把此用户的数据存入session中
                    print "success"
                    messages.add_message(request,messages.SUCCESS,'成功登陆了')#使用了django的信息显示框架 message framework
                    return HttpResponseRedirect('/userinfo/')
                else:
                    messages.add_message(request,messages.WARNING,"账号尚未启动")
            else:
                messages.add_message(request,messages.WARNING,"登陆失败")
        else:
            messages.add_message(request,messages.INFO,"请检查输入的字段内容")
    else:
        login_form = LoginForm()

    template = get_template('one/agriculture/login.html')
    request_context = RequestContext(request)
    request_context.push(locals())
    html = template.render(request_context)
    return HttpResponse(html)
  • login.html
代码语言:javascript复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{% for message in messages %}
    <div class='alert alert-{{message.tags}}'>{{ message }}</div>
{% endfor %}
<!--主要是结合message framework 用-->

<p>登陆我的农商</p>
<form action="/login_2/" method="post">
    {% csrf_token %}
        <table>
            {{ login_form.as_table }}
        </table>
    <input type="submit" value="登陆"  />

</form>
</body>
</html>
  • 截图

0 人点赞