3

我正在使用messages向模板添加 Flash 消息(正如您所期望的那样)。

我遇到的问题是,如果您双击指向生成消息的页面的链接,则该消息会出现两次。

我正在使用该消息告诉用户我已将他们从他们期望去的地方重定向。他们不需要两次相同的消息。

我理解这里的逻辑,但想知道如何删除重复的消息。

  • 点击网址
  • 消息生成,保存在存储中
  • 在页面呈现之前再次单击 url
  • 生成第二条消息,保存在存储中
  • 响应添加存储中的所有消息
  • 呈现两条消息

最终我希望这是一个middleware这样它可以涵盖所有请求。

4

6 回答 6

7

遇到同样的问题并找到了另一个解决方案,使用自定义 MESSAGE_STORAGE:

from django.contrib.messages.storage.session import SessionStorage
from django.contrib.messages.storage.base import Message


class DedupMessageMixin(object):
    def __iter__(self):
        msgset = [tuple(m.__dict__.items())
                  for m in super(DedupMessageMixin, self).__iter__()]
        return iter([Message(**dict(m)) for m in set(msgset)])


class SessionDedupStorage(DedupMessageMixin, SessionStorage):
    pass


# in settings
MESSAGE_STORAGE = 'some.where.SessionDedupStorage'

这适用于也可以直接处理消息的代码,例如在视图中。由于它是一个 mixin,因此您可以轻松地将其重用于其他消息存储。

这是完全避免存储重复项的替代方法:

from django.contrib.messages.storage.session import SessionStorage
from itertools import chain


class DedupMessageMixin(object):
    def add(self, level, message, extra_tags):
        messages = chain(self._loaded_messages, self._queued_messages)
        for m in messages:
            if m.message == message:
                return
        return super(DedupMessageMixin, self).add(level, message, extra_tags)
于 2014-08-06T10:10:44.883 回答
6

我在中间件中遇到了同样的问题,但我更喜欢在我正在使用的 info 调用周围使用一个小包装器:

from django.contrib.messages import info
from django.contrib.messages import get_messages


def info_once_only(request, msg):
    """
    Just add the message once
    :param request:
    :param msg:
    :return:
    """
    if msg not in [m.message for m in get_messages(request)]:
        info(request, msg)


class PaymentsMiddleware(object):

    @staticmethod
    def process_request(request):
        """
        Put up a message letting a new user know that they are being dealt with.
        :param request:
        :return:
        """
        if hasattr(request, 'user'):
            user_profile = request.user.get_profile()
            if user_profile and user_profile.is_suspended:
                info_once_only(
                    request,
                    "Hi {username}, your account has been suspended, we'll be in touch shortly.".format(                           
                        username=request.user.username))
    return None
于 2014-12-12T00:35:49.333 回答
2

选择了自定义TEMPLATE_CONTEXT_PROCESSORS

将默认消息上下文处理器 ( 'django.contrib.messages.context_processors.messages',) 替换为简单的自定义版本:

from django.contrib.messages.api import get_messages


def messages(request):
    """Remove duplicate messages

    """
    messages = []
    unique_messages = []
    for m in get_messages(request):
        if m.message not in messages:
            messages.append(m.message)
            unique_messages.append(m)

    return {'messages': unique_messages}
于 2014-04-23T16:58:08.567 回答
1

对于那些使用Paul Whipp建议的人。

def info_once_only(request, msg):
    if msg not in [m.message for m in get_messages(request)]:
        info(request, msg)

请注意,迭代get_messages(request)会将每条消息标记为“待清除”,因此除了传入的消息之外,用户不会看到任何消息info_once_only(request, msg)

您应该设置storage.used = False为不清除所有其他消息。

def info_once_only(request, msg):
    storage = get_messages(request)
    if msg not in [m.message for m in storage]:
        info(request, msg)
    storage.used = False

请参阅官方文档中的相应部分。

于 2018-01-27T00:57:15.517 回答
0

我决定使用修改后的保罗的方法。我使用标准消息调用,但我导入:

from myapp.utils import Messages as messages

utils.py我的文件中:

from django.contrib.messages import (get_messages,
    error as _error, info as _info, success as _success, warning as _warning)

class Messages:
    @classmethod
    def error(cls, request, msg):
        cls._add_if_unique(request, msg, _error)

    @classmethod
    def info(cls, request, msg):
        cls._add_if_unique(request, msg, _info)

    @classmethod
    def success(cls, request, msg):
        cls._add_if_unique(request, msg, _success)

    @classmethod
    def warning(cls, request, msg):
        cls._add_if_unique(request, msg, _warning)

    @classmethod
    def _add_if_unique(cls, request, msg, msg_method):
        storage = get_messages(request)
        if msg not in [m.message for m in storage]:
            msg_method(request, msg)
        storage.used = False  # iterating over get_messages mark them as gone, so lets prevent this
于 2020-09-25T09:36:18.690 回答
0

受@rockingskier 的回答启发,但通过级别检查和代码糖得到了改进。

  1. 将此messages函数添加到您的上下文处理器中——您可能需要创建一个。

    # myproject/context_processors.py
    from django.contrib.messages import get_messages
    
    
    def messages(request):
        """
        Remove duplicate messages
        """
        current_messages = get_messages(request)
        unique_messages = {(m.level, m.message): m for m in current_messages}
        return {'messages': unique_messages.values()}
    
  2. 将上面的上下文处理器附加到项目模板设置中, django.contrib.messages.context_processors.messages. 例如:

    # myproject/settings.py
    TEMPLATES = [
        {
            # ...
            'OPTIONS': {
                'context_processors': [
                    # ...
                    'django.contrib.messages.context_processors.messages',
                    'myproject.context_processors.messages',  # Here
                ],
            },
        },
    ]
    
于 2022-03-03T15:07:43.613 回答