How to call an auth view from another view? - django

I want to execute a custom logic block in a view before the PasswordResetView is called. This is what I am trying to do, which of course always fails regardless the code, I must be taking a non adequate route to achieving this. I need to save some information, do some internal notifications and only call the password reset view if a condition is met.
views.py
from django.contrib.auth.views import PasswordResetView
def user_password_reset(request):
# Do something here before loading the passwordresetview
# Logic here
return PasswordResetView.as_view(template_name = 'account/password_reset_form.html',
email_template_name='account/password_reset_email.html',
html_email_template_name='account/password_reset_email.html',
success_url = reverse_lazy('account:password_reset_done'))
Which is the standard way of achieving this?
Many thanks in advance.

You can call the function that is returned by the .as_view():
from django.contrib.auth.views import PasswordResetView
def user_password_reset(request):
# Do something here before loading the passwordresetview
# Logic here
return PasswordResetView.as_view(
template_name='account/password_reset_form.html',
email_template_name='account/password_reset_email.html',
html_email_template_name='account/password_reset_email.html',
success_url = reverse_lazy('account:password_reset_done')
)(request)
That being said, it might make more sense to just subclass the PasswordResetView, and for example overwrite the .dispatch(..) method.

Related

Django Redirect after Successful Login

I have a requirement that whenever I login or attempt to request a view that has a login_decorator then the next page be a page where I am required to ask the user to select a business entity (irreespective of the original view requested).
Let's say that the page is http://127.0.0.1:8999/APP/business_unit
To achieve this I configured the following in my settings.py
LOGIN_REDIRECT_URL='/APP/business_unit_selector'
Now when i try to access http://127.0.0.1:8999/APP/range_list
the page goes to http://127.0.0.1:8999/APP/login?next=/APP/range_list I was expecting that the next page after login be /APP/business_unit
but instead, the next page was /APP/range_list
The browser address bar has http://127.0.0.1:8999/APP/login?next=/APP/range_list
Is it possible to achieve what I am trying in Django?
LOGIN_REDIRECT_URL is used unly when next is unspecified. In your test request there is next=/APP/range_list - and that address is used to redirect user after login.
Probably the easiest and most effective solution is to make your own decorator, similar to login_required which redirects to /APP/business_unit_selector&next=<redirect_url> if unit is not selected, and apply it together with login_required. It is not the most efficient solution in terms of redirects number, but is quite clean, and doesn't mess up the login page.
You will also have to handle next parameter in your business_unit_selector view, if you like to achieve natural flow.
Your decorator should be something like
from django.contrib.auth.decorators import login_required
from django.core.urlresolvers import reverse
from django.shortcuts import redirect
from django.utils.http import urlquote
import functools
def business_unit_required(view):
#login_required # probably you want to incorporate it here
#functools.wraps(view)
def wrapped(request, *args, **kwargs):
if not 'selected_business_unit' in request.session:
return redirect('%s?next=%s' % (
reverse('business_unit_selector'),
urlquote(request.get_full_path())))
return view(request, *args, **kwargs)
return wrapped
The reason that http://127.0.0.1:8999/APP/login?next=/APP/range_list is redirecting you to range_list after logging in, is because with next= you are overriding what is specified in your settings file, LOGIN_REDIRECT_URL='/APP/business_unit_selector'.
If I understand correctly you need to user to choose a business entity after logging in.
A couple solutions that come to mind are as follows:
1.) Don't use a separate forms for login and business entity. Instead combine them.
Username
Password
Business Entity
2.) You can also specify in your view if the user doesn't have a buisness entity ResponseRedirect("/APP/business_unit_selector")
docs here

Django - Extra context in django-registration activation email

I'm using django-registration for a project of mine.
I'd like to add some extra contextual data to the template used for email activation.
Looking into the register view source, I cannot figure out how to do it.
Any idea ?
From what I remember, you need to write your own registration backend object (easier then is sounds) as well as your own profile model that inherits from RegistrationProfile and make the backend use your custom RegistrationProfile instead (This model is where the email templates are rendered and there is no way to extend the context, so they need to be overwritten)
A simple solution is to rewrite the send_activation_email
So instead of
registration_profile.send_activation_email(site)
I wrote this in my Users model
def send_activation_email(self, registration_profile):
ctx_dict = {
'activation_key': registration_profile.activation_key,
'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS,
'OTHER_CONTEXT': 'your own context'
}
subject = render_to_string('registration/activation_email_subject.txt',
ctx_dict)
subject = ''.join(subject.splitlines())
message = render_to_string('registration/activation_email.txt',
ctx_dict)
self.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)
And I call it like this
user.send_activation_email(registration_profile)
I don't get what it is your problem but the parameter is just in the code you link (the last one):
def register(request, backend, success_url=None, form_class=None,
disallowed_url='registration_disallowed',
template_name='registration/registration_form.html',
extra_context=None)
That means you can do it from wherever you are calling the method. Let's say your urls.py:
from registration.views import register
(...)
url(r'/registration/^$', register(extra_context={'value-1':'foo', 'value-2':'boo'})), name='registration_access')
That's in urls.py, where usually people ask more, but, of course, it could be from any other file you are calling the method.

In which order to sort my middleware path match requirements?

I created a middleware which allows me to use a list of dictionaries to specify some access rules for any of my views. Each of these dictionaries looks like this:
REQUIREMENTS=(
{'viewname':'addtag',
'permissions':'can_add_tags'},
{'regex':re.compile(r'^somestart'),
'user_check':lambda request:request.user.username=='sam'}
)
In my middleware i then try to find out which of those requirements match the current request. To do so, i filter the complete REQUIREMENTS, and in the filter function i use this code to check if the path matches:
def process_request(self,request):
def path_matches(self,req):
path_matches = False
if (req.has_key('url') and req['url'] == request.path ) or\
(req.has_key('regex') and req['regex'].search(request.path)) or\
(req.has_key('viewname') and resolve(request.path).url_name==req['viewname']):
path_matches=True
return path_matches
requirements = filter(path_matches,REQUIREMENTS)
# now use the returned requirements to determine if a user
# matches the requirement and
My question now is: in which order should i use the checks? It's pretty clear that the check for the url is the fastest, so this has to be first. But then the question is if the regex search or django's url resolve function should follow first.
As i do not have any performance issues right now, this is more of an academic question. And if someone would have an all better solution to solve this, that would be even better.
edit:
To react to the given answers: what i'm trying to do is to create a possibility to restrict views of several external apps in a single file. So decorators are not an option, as long as i do not want to do something like this:
from ext_app1 import view1,view2
from ext_app2 import view3
#permission_required('can_do_stuff')
def view1_ext(*args,**kwargs):
return view1(args,kwargs)
which would lead to rewriting the url specifications each time i change permissions. I want to avoid that. Additionally my solution allows for a user_check function to do a check on a user like this:
def check_user(user):
if len(Item.objects.get(creator=user,datetime=today)) > 3:
return False
return True
That would be a simple way to, ie., restrict how many items a user can upload each day. (Ok, that would be possible with user_passes_test as well).
One more thing is that i sometimes want to check for a permission only if the request is a POST, or if the request contains a certain key:value pair (like 'action':'delete' should require the permission, while 'action':'change' should be allowed for anyone). This could be done with a custom decorator as well, but as soon as i would need a new check, i would need a new decorator.
If you're using Django's built in user authentication and permissions system (django.contrib.auth) then you should consider using the views decorators it provides instead of a middleware. These give you several advantages:
Code that changes together is in the same place, i.e. it's more likely that you'll need to change permissions for a specific view when you are changing the view's code than when you are changing other permissions.
You don't have to write much of the code yourself, which makes your project smaller and easier to maintain.
For simple situations you can use the login_required and permission_required decorators, and for a more complex condition the user_passes_test decorator allows you to check if the user passes any condition you care to specify.
The code looks something like this for a view function (example is lifted from the documentation):
from django.contrib.auth.decorators import permission_required
#permission_required('polls.can_vote')
def my_view(request):
...
If you're using class-based views then it looks a little different (again, this example is lifted from the documentation):
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic import TemplateView
class ProtectedView(TemplateView):
template_name = 'secret.html'
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(ProtectedView, self).dispatch(*args, **kwargs)
If you have a good reason not to use Django's permissions system, then you can still adopt a similar approach. The code for the django.contrib.auth decorators could easily be used as the basis of your own decorators.
You might be looking for user_passes_test decorator.
You can decorate the views you need instead of using a middleware.
The code will looks like this:
from django.contrib.auth.decorators import user_passes_test
#user_passes_test(lambda user: user.has_perm('model.can_add_tags') \
and user.username == 'sam')
def my_view(request):
...

django - test is_authenticated() in middleware instead of views

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.

Django: re-use login_required decorator inside other decorators

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