I'm using the Django Message Framework to show messages to users as well as the @login_required
decorator on one of my views. So if a user tries to access a certain view without being logged in, they get kicked to the login page. How would I go about adding an error message to the login page saying "In order to do ... you must be logged in". I can't add it in the view like you normally would because the non-logged in user would never get to there.
The messages framework allows you to temporarily store messages in one request and retrieve them for display in a subsequent request (usually the next one). Every message is tagged with a specific level that determines its priority (e.g., info , warning , or error ).
Import messages from django. contrib at the top of the file then go to the view function where you wish to add the message(s). In this case, it's a contact view that needs a Django success message and a Django error message. Add a success message just before the return redirect ("main:homepage") stating "Message sent."
from django. contrib. auth import authenticate user = authenticate(username='john', password='secret') if user is not None: if user. is_active: print "You provided a correct username and password!" else: print "Your account has been disabled!" else: print "Your username and password were incorrect."
Django by default will look within a templates folder called registration for auth templates. The login template is called login. html . Create a new directory called templates and within it another directory called registration .
It took me a while to figure out a nice way of doing this, but I think I have an implementation, based on the answer of Daniel Roseman
First thing I did was creating a decorator that sets messages when a user is not logged in, exactly like login_required.
So I wrote login_required_message:
try: from functools import wraps except ImportError: from django.utils.functional import wraps # Python 2.4 fallback. from django.utils.decorators import available_attrs from django.contrib import messages default_message = "Please log in, in order to see the requested page." def user_passes_test(test_func, message=default_message): """ Decorator for views that checks that the user passes the given test, setting a message in case of no success. The test should be a callable that takes the user object and returns True if the user passes. """ def decorator(view_func): @wraps(view_func, assigned=available_attrs(view_func)) def _wrapped_view(request, *args, **kwargs): if not test_func(request.user): messages.error(request, message) return view_func(request, *args, **kwargs) return _wrapped_view return decorator def login_required_message(function=None, message=default_message): """ Decorator for views that checks that the user is logged in, redirecting to the log-in page if necessary. """ actual_decorator = user_passes_test( lambda u: u.is_authenticated, #fixed by removing () message=message, ) if function: return actual_decorator(function) return actual_decorator
With this implementation you can now annotate your view methods like this:
from decorators import login_required_message from django.contrib.auth.decorators import login_required @login_required_message(message="You should be logged in, in order to see the index!") @login_required def index(request): pass
Now first the message will be set, then the redirect will be performed.
However I actually don't want to add the login_required_message decorator everywhere. It would be much nicer to have only one decorator. So lets chain them (simply add this to your decorator.py file after login_required_message):
from django.contrib.auth import REDIRECT_FIELD_NAME from django.contrib.auth.decorators import login_required def login_required_message_and_redirect(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None, message=default_message): if function: return login_required_message( login_required(function, redirect_field_name, login_url), message ) return lambda deferred_function: login_required_message_and_redirect(deferred_function, redirect_field_name, login_url, message)
It took me a while to figure out this last line; but lambda's to the rescue!
Now you can replace the two decorators with only login_required_message_and_redirect: Almost there! Since actually I want to use this new login_required_message-method everywhere, I add a monkey-patch for login_required and it is used everywhere (again add to the bottom of the decorators.py file)!
from django.contrib.auth import decorators setattr(decorators, 'login_required', login_required_message_and_redirect)
which allows me to call:
# a message will appear, since login_required is monkey patched @login_required def logout(request): pass # or customize the message per view @login_required(message="You should be logged in message! Available after monkey patch") def index(request): pass
There's not any obvious way. The only thing that springs to mind is to write your own version of the decorator that puts a message into the session before redirecting, then get the login template to display the message from the session.
You'd need to use the code in django.contrib.auth.decorators
, in particular the user_passes_test
function - the bit to add the message would have to go before return HttpResponseRedirect
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With