In django, I want to do a particular action just before login_required() is called for a view. One hack or easy way out is to just have that url point to a view where login is not required and do that particular action (action: setting some things in the request.session) and then redirect back to this main view where login is required.
How should I go about it, if there's some option to cater to such things in django?
Is there a better or simpler way than to write my own decorator to do this?
Decorator is the best way, because it make code easy to understand and as for security reason, user can block redirections so you'll need to think about how to avoid this.
This arcticle seems usefull How to make a chain of function decorators?, because I think you'll need to pass request variable and also place your decorator to process checking first.
Just use the user_passes_test decorator instead and do your custom stuff before manually validating that they are authenticated. See: https://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.decorators.user_passes_test
Your question doesn't give much detail, but going by your example (setting some request variables), it sounds like you need Request Middleware. Take a look at the builtin django.contrib.auth.middleware.AuthenticationMiddleware for an example:
class AuthenticationMiddleware(object):
def process_request(self, request):
assert hasattr(request, 'session'), "The Django authentication middleware requires session middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.sessions.middleware.SessionMiddleware'."
request.user = SimpleLazyObject(lambda: get_user(request))
You'll need to define a class with the process_request method (as shown above). The process_request method will be called for each request, and passed a request instance. Within the method, you can check for request.user, and if the user is authenticated, and set whatever variables you want for the current request.
Writing your own decorator is the best way to approach this. Anywhere that you have #login_required, just add your own decorator in addition to the login_required decorator.
Or, you could call the login_required function from your decorator.
Related
What is #csrf_exempt, and why should we use this in our views.py? Also, are there any alternatives to it?
Normally when you make a request via a form you want the form being submitted to your view to originate from your website and not come from some other domain. To ensure that this happens, you can put a csrf token in your form for your view to recognize. If you add #csrf_exempt to the top of your view, then you are basically telling the view that it doesn't need the token. This is a security exemption that you should take seriously.
The decorator marks a view as being exempt from the protection ensured by the middleware. Example:
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
def my_view(request):
return HttpResponse('Hello world')
You should not have to use this unless you know exactly why. A good example where it is used is to build a webhook, that will receive informations from another site via a POST request. You then must be able to receive data even if it has no csrf token, but will be replaced by another system of security. For example, if you use stripe for the subscriptions of your clients, you need to know if a client unsubscribed his account. The webhook will be the way to inform your site, and then cut the access to your service for the unsubscribed client.
I am little bit confused on process_request and process_view.
Process request is something that u want to pass on view with request. Example can be taken from request.user.
Then what does process_view does ? Is it for executing any view initially befor calling any url ? Like initially I want to show home view but this can be done from url too.
Can anyone give me example when to use process_view ?
Thank you
process_request is called before Django determines which view should handle the request (hence, its only parameter is the request).
process_view is called after Django determines which view will handle the request, but before that view is called. It will have access to the request object, along with the view that will handle it and the parameters that will be passed to that view.
Whenever you need to know the view that will be used for a request, you can use process_view. A good example for this is Django's CSRF Middleware process_view, which will not enforce CSRF protection if a csrf_exempt decorator is present on the view the request is meant for:
def process_view(self, request, callback, callback_args, callback_kwargs):
[...]
if getattr(callback, 'csrf_exempt', False):
return None
[...]
Adrian Ghiuta have written a very good answer. I just want to add few points to that.
process_request is called before doing the url matching and process_view is called after url matching but before invoking that view.
We can use the process_request to change the url itself and thus invoke a different view. This point helped me understand these, so I thought to answer it, maybe it'll someone else too.
I love django's #login_required decorator, but there's one thing I can't figure out how to make it do.
If an unauthenticated user tries visits a #login_required page (e.g. "/private-stuff/"), I want to kick them back to the home page (e.g. "/home/"). But I don't want to append a "?next=" argument to the url. In other words, I just want to redirect to "/home/", not "/home/?next=/private-stuff/".
How can I do that? Is there a better way than just writing my own decorator?
Isn't it just simply as:
#decorators.login_required(redirect_field_name=None)
Well, there's two ways that I can think of. First, would be the "correct" way, in the sense that you're not breaking any functionality, only adding new functionality: create your own login_required decorator. The problem though is that Django has really tucked the redirect after login functionality away, and it requires a lot of parts. The login_required decorator is really just a wrapper around the user_passes_test decorator, which in turn calls the redirect_to_login view, and it's that view that adds the next param to the querystring. In your custom decorator, you can roll all or some of this functionality straight into the decorator, but you'll need to reference all three for the necessary code.
The other, and far simpler option, is to create some middleware to remove the querystring if it's set:
from django.conf import settings
from django.http import HttpResponseRedirect
class RemoveNextMiddleware(object):
def process_request(self, request):
if request.path == settings.LOGIN_URL and request.GET.has_key('next'):
return HttpResponseRedirect(settings.LOGIN_URL)
And, then add the import path to that middleware to MIDDLEWARE_CLASSES. Remember that in the request phase, middleware is processed first to last or top-down, in other words. This should come relatively early in the request phase, but you may need to play around a bit with it to see what can and can't come before it.
The only real problem with this method is that it "breaks" the next redirect functionality, and not in a very intuitive way, if a later developer inherits your codebase along with a mandate to allow the redirect, it might be a bit flummoxing.
login(request, user)
if request.POST['next']:
return redirect(request.POST['next'])
else:
msg = u"Welcome..."
return render_to_response('members/welcome.html', {'msg':msg},
context_instance=RequestContext(request))
Most of my views require login. Is there a way to specify that all views or a subset of views must have the user is_authenticated() = True before proceeding. If not, the user is redirected to a login page?
https://docs.djangoproject.com/en/1.4/topics/auth/#the-login-required-decorator
from django.contrib.auth.decorators import login_required
#login_required
def my_view(request):
...
If you want so, there are some rules that you must follow,
request.user is set on AuthenticationMiddleware, so placing your custom middleware before that will fail.
you can only use process_view method, which is executed before your view is called
But, somehow; you must make a kind of list like login free views, but that is not likely because checking the list everytime you create a new view might be problem. On the other hand, login_required decorator works just like that and it is easier to use... So you must think twice before doing a such thing and consider if you really need something like that.
Here is the doc for Custom Middlewares and here is the django defalut middleware classes which is important because order of the middelware classes is really matters.
According to one of the comments in https://stackoverflow.com/a/8715790/210481, which I agree with, we should avoid multiple decorators if one depends on the other.
So, in the example, if we have a decorator "active_required" for active users, we should not have to use both active_required and login_required on the same view.
We should have "login_required" decorator "called" somehow inside the "active_required" one.
Is it possible to do it with the standard "login_required" decorator that comes with django?
My requirements are:
1) if the user is not authenticated, I should redirect him to LOGIN_URL
2) if the user is authenticated (passed login_required), but is not active, I should redirect him to a page to "re-activate" his account
3) if the user is authenticated and active, the user can access the view
Thanks in advance
When thinking about your question, I found it easier to create a simple active_required decorator first. This is very easy, because we can use the the user_passes_test function in django.contrib.auth.decorators.
The question then changes to "how do I combine the login_required and active_required into one decorator?". We need to define a function which:
takes a view function as it's argument
applies both decorators to it to create a new view function
returns the new view function
Putting it all together, you have the following:
from django.contrib.auth.decorators import user_passes_test, login_required
active_required = user_passes_test(lambda u: u.is_active, login_url=REACTIVATE_URL)
def active_and_login_required(view_func):
decorated_view_func = login_required(active_required(view_func))
return decorated_view_func