Change Overriden Django Views Return URL - django

I want to change the return url of a django-allauth page.
I know I could override the entire function in views.py and just change the return url at the bottom, but doesn't seem ideal as it could cause issues if associated code in the django-allauth package gets changed by the packages authors.
Is there a better way to do this?
Thank you.

How I ended up doing this:
from allauth.account.views import PasswordChangeView
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse
class CustomPasswordChangeView(LoginRequiredMixin, PasswordChangeView):
def get_success_url(self):
if [...]:
success_url = reverse([...], kwargs={'username': self.request.user.username})
else:
success_url = reverse([...], kwargs={'username': self.request.user.username})
return success_url
custom_password_change = login_required(CustomPasswordChangeView.as_view())

Related

How to handle redirect for login_required in htmx partial view?

I have two views.
class IndexView(TemplateView):
template_name = 'index.html'
#require_POST
#login_required
def partial_view(request):
return render(request, 'partials/stuff.html')
I want the index page to be "public" but if user takes action (which triggers partial view), they should be redirected to LOGIN_URL, if not logged in.
The problem is that my partial view will return the entire LOGIN_URL page. So there's a page within a page now.
Is it possible to redirect the "parent" page when using partial views?
I didn't manage to make "post-login redirection" work but my solution is good enough for my needs.
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.conf import settings
from django.contrib.auth.decorators import login_required as django_login_required
from django.http import HttpResponse
from functools import wraps
from django.shortcuts import resolve_url
def login_required(function=None, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
#wraps(function)
def wrapper(request, *args, **kwargs):
if not request.user.is_authenticated and request.htmx:
resolved_login_url = resolve_url(login_url or settings.LOGIN_URL)
return HttpResponse(status=204, headers={'HX-Redirect': resolved_login_url})
return django_login_required(
function=function,
login_url=login_url,
redirect_field_name=redirect_field_name
)(request, *args, **kwargs)
return wrapper

how can i overide django allauth signup success_url

I'm working on a project using allauth and i'm using customer user model and i wan the newly registered user to be redirected to a different page (say profile form page) which will be totally different from the login_redirect_url, I have tried it this way
any idea how i can make this work pls?
from django.shortcuts import get_object_or_404, redirect, render
from allauth.account.views import LogoutView
from django.urls import reverse_lazy
from allauth.account.views import SignupView
from django.views.generic import TemplateView
from .models import CustomUser
class Signup(SignupView):
success_url = reverse_lazy('business:company_profile')
def get_success_url(self):
return self.success_url
I am not sure there is way to override SignUp redirection since when you sign up in the application, you also sign in, which will use the login_redirect_url.
If you overrode login_redirect_url (documentation) you can update your logic and redirect the user to his profile if some fields are missing/empty?
def get_login_redirect_url(self, request):
if not request.user.your_custom_field:
path = "/accounts/{username}/"
return path.format(username=request.user.username)
else
return "/"
You could also implement another logic by adding a bool is_first_visit on your CustomerUser model (with default=True) and set it to False after his first visit.
Is the code that you proposed not working? What errors does it produce?
On first glance, the view that you've proposed should work. You would just have to make sure it's being used in "urls.py".

Redirect anonymous users to log in (don't show them anything)

Django 1.9.6.
I want to absolutely disable the whole website from viewing by anonymous users. Anonymous users will always be redirected to login page.
I have created a general view. The problem is that subclasses of GeneralView may not just render a template but perform some calculations or just be of different kinds: DetailView, ListView etc.
class GeneralView(View):
def get(self, request, template):
if not request.user.is_authenticated() and request.user.is_active:
return redirect("auth_login")
else:
return render(request, template)
If I try to inherit, the code gets clumsy:
class HomePageView(GeneralView):
def get(self, request, template):
super().get(self, request)
Well, what can I do here? I get error message that my get method doesn't return HttpResponse.
I can rewrite get method of the superclass to return status code. Then check it in the subclass. But this seems to be garbage.
In other words I'm lost in clouds of inheritance. Could you give me a kick here how always to redirect anonymous users to login page, whereas let logged in users see everything.
Thank you in advance.
You could use the UserPassesTestMixin for this.
from django.core.urlresolvers import reverse_lazy
class GeneralView(UserPassesTestMixin, View):
def test_func(self):
# I assume you missed out the brackets in your question and actually wanted 'if not (request.user.is_authenticated() and request.user.is_active)'
return request.user.is_authenticated() and request.user.is_active
login_url = reverse_lazy('auth_login')
The mixin overrides dispatch, so you won't have to call super() when you override get() in your view.
class HomePageView(GeneralView):
def get(self, request):
...
I think your error for get method not returning belongs to not putting a return statement. in fact, in get method of the child class you should do:
class HomePageView(GeneralView):
def get(self, request, template):
return super().get(self, request)
That should solve the error
If you have lots of views and you do not want to touch any one you can just use Middleware for this issue. Try code below:
import traceback
from django.contrib.auth.decorators import login_required
class RejectAnonymousUsersMiddleware(object):
def process_view(self, request, view_func, view_args, view_kwargs):
current_route_name = resolve(request.path_info).url_name
if current_route_name in settings.AUTH_EXEMPT_ROUTES:
return
if request.user.is_authenticated:
return
return login_required(view_func)(request, *view_args, **view_kwargs)
Cautions:
You must add this middleware to the bottommost of middleware section
of settings.py
You should put this variable in settings.py
AUTH_EXEMPT_ROUTES = ('register', 'login', 'forgot-password')
New versions of Django provides the #login_required decorator. If an anonymous user tries to access the view, the system redirects to the login page.
from django.contrib.auth.decorators import login_required
#login_required
def my_view(request):
...
It can be used in function views, as shown above, or generic views (using #method_decorator, usually in dispatch method)
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic import TemplateView
#method_decorator(login_required, name='dispatch')
class ProtectedView(TemplateView):
template_name = 'secret.html'

reverse for success_url on Django Class Based View complain about circular import

When using method-based view, redirecting with reverse didn't complain about this and can still find the root url conf. But, in class-based views, it complain:
ImproperlyConfigured at /blog/new-post/
The included urlconf 'blog.urls' does not appear to have any
patterns in it. If you see valid patterns in the file then the
issue is probably caused by a circular import.
My class is defined like this:
class BlogCreateView(generic.CreateView):
form_class = Blog
template_name = 'blog/new-post.html'
success_url = reverse('blog:list-post')
How to properly use reverse for success_url in class-based views? Thanks.
PS: And I'm interested in why it's need to restart runserver after this error (not like an error like TemplateDoesNotExists which is no need to restart runserver)
Using reverse in your method works because reverse is called when the view is run.
def my_view(request):
url = reverse('blog:list-post')
...
If you overrride get_success_url, then you can still use reverse, because get_success_url calls reverse when the view is run.
class BlogCreateView(generic.CreateView):
...
def get_success_url(self):
return reverse('blog:list-post')
However, you can't use reverse with success_url, because then reverse is called when the module is imported, before the urls have been loaded.
Overriding get_success_url is one option, but the easiest fix is to use reverse_lazy instead of reverse.
from django.urls import reverse_lazy
# from django.core.urlresolvers import reverse_lazy # old import for Django < 1.10
class BlogCreateView(generic.CreateView):
...
success_url = reverse_lazy('blog:list-post')
To answer your final question about restarting runserver, the ImproperlyConfigured error is different from TemplateDoesNotExists because it occurs when the Django application is loaded.
Try using reverse_lazy instead of reverse in your CBV. Its a lazily evaluated version of reverse. It won't execute until the value is needed.
from django.core.urlresolvers import reverse_lazy
class BlogCreateView(generic.CreateView):
form_class = Blog
template_name = 'blog/new-post.html'
success_url = reverse_lazy('blog:list-post')

Django: "How to get the slug from url() before passing it to MyClass.as_view()?" or "How to check if registration is open?"

I want to check if a user is allowed to register for a specific event. I thought in order to save code I could do it like the login_required decorator right between the url() and MyClass.as_view(). But how do I get that slug? Or is this solution totally wrong? (I unfortunatelly can't use the user_passes_test because I don't want to test someting on the user but on the url.)
So I tryed this:
views.py
from registration.models import Event
from django.utils import timezone
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
def reg_is_open(event_slug):
"""
Return True if registration is open.
"""
event = get_object_or_404(Event, slug=event_slug)
if event.open_date <= timezone.now() and event.cut_off >= timezone.now():
return True
def allow_view(cls, **initkwargs):
"""
Check weather registration is open and user is logged in.
Returns to registration start page if registration is closed.
"""
slug = initkwargs.get('event') # Does not work!
if not reg_is_open(slug):
return HttpResponseRedirect(reverse('registration:event_index', args=slug))
return login_required(cls.as_view(**initkwargs))
# Also works when I remove **initkwargs. That means that what I'm looking for just passes...
urls.py
from django.conf.urls import patterns, url, include
from registration import views
event_patterns = patterns('',
url(r'^person/$', views.allow_view(views.PersonList), name='person_list'),
# instead of
# url(r'^person/$', login_required(views.PersonList.as_view()), name='person_list'),
# ...
urlpatterns = patterns('',
url(r'^(?P<event>[-a-zA-Z0-9_]+)/$', views.EventDetails.as_view(), name='event_index'),
url(r'^(?P<event>[-a-zA-Z0-9_]+)/', include(event_patterns)),
# ...
If I understand correctly, your view is a DetailView of the event, correct? In that case, use something along the lines of:
class EventDetailView(DetailView):
model = Event
def dispatch(self, request, *args, **kwargs):
if self.request.user not in self.object.registered_users.all():
return HttpResponseForbidden()
else:
return super(EventDetailView, self).dispatch(request, *args, **kwargs)
This assumes that you have a M2M key between User and Event models, called registered_users on the Event side; change the code to fit your situation.