Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django message framework and login_required

Tags:

django

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.

like image 655
roflwaffle Avatar asked Apr 27 '10 18:04

roflwaffle


People also ask

What is message framework in Django?

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

How do I use Django message framework?

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."

How do I authenticate username and password in Django?

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."

How do I login as user in Django?

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 .


2 Answers

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 
like image 65
LvanderRee Avatar answered Oct 09 '22 04:10

LvanderRee


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.

like image 45
Daniel Roseman Avatar answered Oct 09 '22 04:10

Daniel Roseman