2

Django beginner here.

I have been using the inbuilt login_required decorator. I want to override it for certain users who's referral urls match a certain pattern (e.g. all users originating from /buy_and_sell/).

My purpose is to show a special login page to just these users, and a generic one to everyone else.

I've been looking at various examples of writing custom decorators (e.g. here, here, here and here). But I find the definitions hard to grasp for a beginner. Can someone give me a layman's understanding (and preferably illustrative example) of how I can solve my problem?

Sophia111
  • 167
  • 2
  • 4
  • 13

2 Answers2

2

There is user_passes_test decorator included in Django.You do not have to make your own decorator.

from django.contrib.auth.decorators import user_passes_test

def check_special_user(user):
    return user.filter(is_special=True)

# if not the special user it will redirect to another login url , otherwise process the view
@user_passes_test(check_special_user,login_url='/login/') 
def my_view(request):
   pass
    ...

Need Request in decorator

To do that make a clone version of user_passes_test in your project or app and make change as follow,

def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
    """
    Decorator for views that checks that the user passes the given test,
    redirecting to the log-in page if necessary. 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 test_func(request.user):  # change this line to request instead of request.user
                return view_func(request, *args, **kwargs)
            path = request.build_absolute_uri()
            resolved_login_url = resolve_url(login_url or settings.LOGIN_URL)
            # If the login url is the same scheme and net location then just
            # use the path as the "next" url.
            login_scheme, login_netloc = urlparse(resolved_login_url)[:2]
            current_scheme, current_netloc = urlparse(path)[:2]
            if ((not login_scheme or login_scheme == current_scheme) and
                    (not login_netloc or login_netloc == current_netloc)):
                path = request.get_full_path()
            from django.contrib.auth.views import redirect_to_login
            return redirect_to_login(
                path, resolved_login_url, redirect_field_name)
        return _wrapped_view
    return decorator

change test_func(request.user) to test_func(request) and you will get whole request in your decorator function.

Edit: In url.py ,

url (
    r'^your-url$',
    user_passes_test(check_special_user, login_url='/login/')(
        my_view
    ),
    name='my_view'
)
Aniket Pawar
  • 2,641
  • 19
  • 32
  • Question: how would I get the referral url in `check_special_user`? i dont have `request` there. – Sophia111 Jul 26 '17 at 17:36
  • No request can not be used in user_passes_test method decorator. What you exactly trying to do? – Aniket Pawar Jul 26 '17 at 17:42
  • Like I mentioned in my question, I'm trying to show a special login page to **unauth users** who originated from certain urls in my app (e.g. `/buy_and_sell/`). All other unauth users would be shown a generic login page. I need to look at the contents of `request.META.get('HTTP_REFERER')` for that. – Sophia111 Jul 26 '17 at 17:45
  • 'trying to show a special login page to users who originated from certain urls' - Are you making any changes in user table for this ? like the user is originated now. – Aniket Pawar Jul 26 '17 at 17:50
  • No, users are unauthenticated during this flow, I change nothing in user table. Just want to show special login page to this special set of unauth users, and a generic login page to all other unauth users. – Sophia111 Jul 26 '17 at 17:52
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/150218/discussion-between-aniket-and-sophia111). – Aniket Pawar Jul 26 '17 at 17:52
1

Here the best answer to understand python decorators : How to make a chain of function decorators?

You can use login_url argument of login_required :

@login_required(login_url='some_url)

Another way is to create a custom decorator, an example from the documentation of Django :

from django.contrib.auth.decorators import user_passes_test

def email_check(user):
    return user.email.endswith('@example.com')

@user_passes_test(email_check)
def my_view(request):
    ...
Augustin Laville
  • 476
  • 5
  • 14