问题
I'm using messages
to add flash messages to the template (just as you'd expect).
The problem I have is that if you double click a link to a page that generates a message then the message appears twice.
I am using the message to tell the user I have redirected them from where they were expecting to go. They don;t need the same message twice.
I understand the logic here but am wondering how I can remove duplicated messages.
- click url
- message generated, saved in storage
- click url again before page renders
- second message generated, saved in storage
- response adds all messages from storage
- renders with two messages
Ultimately I would like this to be a middleware
so it can cover off all requests.
回答1:
Ran into the same problem and found another solution, using a custom 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'
This will work fine with code that would also play with messages directly, say in a view for example. Since it's a mixin, you can easily reuse it for other message storages.
Here is an alternative to avoid storing duplicates at all:
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)
回答2:
I had the same problem in middleware but preferred a little wrapper around the info call that I was using:
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
回答3:
Opted for a custom TEMPLATE_CONTEXT_PROCESSORS
.
Replace the default messages context processor ('django.contrib.messages.context_processors.messages',
) with a simple custom version:
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}
回答4:
For those using Paul Whipp's suggestion.
def info_once_only(request, msg):
if msg not in [m.message for m in get_messages(request)]:
info(request, msg)
Note that iterating over get_messages(request)
will mark each message as 'to be cleared', so the user won't see any messages except the one passed into info_once_only(request, msg)
.
You should set storage.used = False
so that all the other messages aren't cleared.
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
See the corresponding section in the official documentation.
来源:https://stackoverflow.com/questions/23249807/django-remove-duplicate-messages-from-storage