Django message framework and login_required

前端 未结 5 1069
不知归路
不知归路 2021-02-02 10:34

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 certai

相关标签:
5条回答
  • 2021-02-02 11:18

    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.

    0 讨论(0)
  • 2021-02-02 11:22

    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
    
    0 讨论(0)
  • 2021-02-02 11:27

    I know it's old but it can still help others

    I think to redirect an unconnected user to the login page and at the same time send a message you can proceed as follows:

    from django.contrib import messages
    
    def views(request):
        if request.user.is_anonymous:
            messages.add_message(request, messages.INFO,
                                 'You must be logged in')
            return redirect('your_login_views')
        else:
            # do smothing
            return redirect(request, 'your_page.html')
    
    0 讨论(0)
  • 2021-02-02 11:33

    Try using this: http://code.google.com/p/django-session-messages/

    or use Django 1.2 (currently in Beta) and the messages framework: http://docs.djangoproject.com/en/dev/ref/contrib/messages/#ref-contrib-messages

    0 讨论(0)
  • 2021-02-02 11:40

    This is an old question but I still have it 10 years later. Here is the solution I came up with. It's kind of hacky but it's only 7 lines of code including the decorator. You use 2 functions.

    The first is mapped to the URL path. It checks to see if the user is logged in. If the user is not logged in, it sets a message. Regardless of the user's login state, it returns with a call to the second function.

    The second function does what the standard view would have done but it has the decorator on it.

    def content(request):
        if request.user.is_anonymous:
            messages.warning(request, 'You must log in to view the course content.')
        return content2(request)
    
    @login_required
    def content2(request):
        return render(request, 'path/template.html')
    

    I am assuming that your log in template already displays messages.

    Like I said, it's a bit of a hack but it works really well.

    I am using Django version 3.0.6

    0 讨论(0)
提交回复
热议问题