Redirect to Next after login in Django - django

When a user accesses a url which requires login. The view decorator redirects to the login page. after the user enters his username and password how can I redirect the user to the page he was trying to access ('next') ?
Views.py
def login_view(request):
template = 'pos/login.html'
form = LoginForm
if request.method == 'POST':
username = request.POST.get('username', '')
password = request.POST.get('password', '')
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
messages.success(request, "You have logged in!")
return redirect('home')
else:
messages.warning(request, "Your account is disabled!")
return redirect('/login')
else:
messages.warning(request, "The username or password are not valid!")
return redirect('/login')
context = {'form': form}
return render(request, template, context)
#login_required(redirect_field_name='next', login_url='/login')
def bar(request):
template = 'pos/bar.html'
drink = OrderItem.objects.filter(product__catgory__gt=1).order_by('-created')
context = {'drink': drink}
return render(request, template, context)
Login.html
<form action="/login" id="login_form" method="post" class="form-signin">
{% csrf_token %}
{{ form.as_p }}
<button class="btn btn-lg btn-primary btn-block" type="submit" value="login">Sign in</button>
<input type="hidden" name="next" value="{{next}}" />
</form>
url.py
url(r'^login', views.login_view, name='login'),
forms.py
class LoginForm(AuthenticationForm):
username = forms.CharField(label="Username", required=True, max_length=30,
widget=forms.TextInput(attrs={
'class': 'form-control',
'name': 'username'}))
password = forms.CharField(label="Password", required=True, max_length=30,
widget=forms.PasswordInput(attrs={
'class': 'form-control',
'name': 'password'}))

You can try:
return redirect(self.request.GET.get('next'))

The accepted answer does not check for the next parameter redirecting to an external site. For many applications that would be a security issue. Django has that functionality inbuilt in form of the django.utils.http.is_safe_url function. It can be used like this:
from django.shortcuts import redirect
from django.utils.http import url_has_allowed_host_and_scheme
from django.conf import settings
def redirect_after_login(request):
nxt = request.GET.get("next", None)
if nxt is None:
return redirect(settings.LOGIN_REDIRECT_URL)
elif not url_has_allowed_host_and_scheme(
url=nxt,
allowed_hosts={request.get_host()},
require_https=request.is_secure()):
return redirect(settings.LOGIN_REDIRECT_URL)
else:
return redirect(nxt)
def my_login_view(request):
# TODO: Check if its ok to login.
# Then either safely redirect og go to default startpage.
return redirect_after_login(request)

You can try by simply add this input field before submit button in accounts/login.html template
<input type="hidden" name="next" value="{{ request.GET.next }}"/>

Passing next to the login form and then the form passing that value on to view in a hidden input can be a bit convoluted.
As an alternative, it's possible to use django.core.cache here.
This way there is no need to pass anything extra to the form or to give the form an extra input field.
def login_view(request):
if request.method == 'GET':
cache.set('next', request.GET.get('next', None))
if request.method == 'POST':
# do your checks here
login(request, user)
next_url = cache.get('next')
if next_url:
cache.delete('next')
return HttpResponseRedirect(next_url)
return render(request, 'account/login.html')

This actually works for me quite nice:
from django.shortcuts import redirect
def login(request):
nxt = request.GET.get("next", None)
url = '/admin/login/'
if nxt is not None:
url += '?next=' + nxt
return redirect(url)
If previous URL contained next - call "login" URL and append the previous "next" to it.
Then, when you logged in - you'll continue with the page that was previously intended to be next.
In my project I made the following helper which works for Swagger login/logout:
def _redirect(request, url):
nxt = request.GET.get("next", None)
if nxt is not None:
url += '?next=' + nxt
return redirect(url)
def login(request):
return _redirect(request, '/admin/login/')
def logout(request):
return _redirect(request, '/admin/logout/')

Yes Arun Ghosh option is better but it leads to an exception in cases where there is no next value is found.
Hence I used this approach.
try: return redirect(request.GET.get('next')) except TypeError: return HttpResponseRedirect(reverse("default_app:url_name"))
OR
except Exception as e: return HttpResponseRedirect(reverse("default_app:url_name"))

path_redirect = request.get_full_path().split('?next=',1)
if '?next=' in request.get_full_path():# Redirecting After Login
return redirect(path_redirect[1])
else:
return redirect('index')

Related

Custom Django Password Reset - Link does not get invalidated

I know that the one time link for the password reset should be invalidated following the email password change procedure as given here enter link description here. Yet, with my below implementation, although the link can reset the password each time, it does not get invalidated. The link works each time. What could be the reason for this ?
(I also see the last login timestamp is also updated in admin pages for the particular user that I am changing the password for)
(forms.py)
from django.contrib.auth.forms import UserCreationForm, SetPasswordForm
from django.contrib.auth import get_user_model
class ResetPasswordForm(SetPasswordForm):
class Meta:
model = get_user_model()
fields = ('password1', 'password2')
(tokens.py)
from django.contrib.auth.tokens import PasswordResetTokenGenerator import six
class AccountActivationTokenGenerator(PasswordResetTokenGenerator):
def _make_hash_value(self, user, timestamp):
return (
six.text_type(user.pk) + six.text_type(timestamp) +
six.text_type(user.email_confirmed)
)
account_activation_token = AccountActivationTokenGenerator()
(views.py)
def activate_forgot_password(request, uidb64, token):
try:
uid = force_text(urlsafe_base64_decode(uidb64))
User = get_user_model()
user = User.objects.get(pk=uid)
except (TypeError, ValueError, OverflowError, User.DoesNotExist):
user = None
if user is not None and account_activation_token.check_token(user, token):
if request.method == 'POST':
form = ResetPasswordForm(user, request.POST)
if form.is_valid():
user = form.save(commit=False)
print(user.password)
user.save()
login(request, user, backend='mysite.signup.views.EmailBackend')
return redirect('home')
else:
form = ResetPasswordForm(user)
return render(request,
'change_password.html',
{'form': form,
'uidb64': uidb64,
'token': token})
return render(request, 'account_forgot_password_token_invalid.html')
(template.html)
<form id="ResetPasswordForm" method="post" action="{% url 'activate_forgot_password' uidb64=uidb64 token=token %}" validate>
{% csrf_token %}
.
.
.
<div class="form-group">
<button type="submit" id="btn-signup" class="btn btn-block btn-primary btn-lg">Change Password</button>
</div>
</form>
The fields in _make_hash_value method never got update, the link still valid until one of the field assigned updated or the token timeout, you can add more fields to make sure that will trigger change like user.last_login or even user.password

Django not accepting valid credentials

views.py. My default user login template, I have tried both methods listed below and don't know where I am makeing a mistake - I get an error that my credentials are incorrect every time
def signup(request):
if request.method == 'POST':
form = SignUpForm(request.POST)
if form.is_valid():
user = form.save()
user.refresh_from_db() # load the profile instance created by the signal
user.profile.university = form.cleaned_data.get('university')
user.save()
raw_password = form.cleaned_data.get('password1')
user = authenticate(username=user.username, password=raw_password)
#user_login(request, user)
return redirect('main:main_page')
else:
form = SignUpForm()
return render(request, 'signup.html', {'form': form})
def user_login(request):
'''
Using different method for getting username, tried this and didn't work either
if request.method == 'POST':
form = AuthenticationForm(request, data=request.POST)
if form.is_valid():
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
user = authenticate(username=username, password=password)
if user is not None:
login(request,user)
messages.info(request, "Successfully signed in")
return redirect('main:home')
else:
message = 'Sorry, the username or password you entered is not valid please try again.'
return render(request, 'login.html', {'message':message})
else:
message = 'Invalid'
return render(request, 'login.html', {'message':message})
else:
form=AuthenticationForm()
return render(request, 'login.html', {"form":form})
'''
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request,user)
return HttpResponseRedirect(reverse('index'))
else:
return HttpResponse("Your account was inactive.")
else:
message = 'Sorry, the username or password you entered is not valid please try again.'
return render(request, 'login.html', {'message':message})
else:
message = 'Request failed please try again.'
return render(request, 'login.html', {'message':message})
models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
first_name = models.CharField(max_length=100, blank=True)
last_name = models.CharField(max_length=100, blank=True)
email = models.EmailField(max_length=150)
bio = models.TextField()
university = models.CharField(max_length=30)
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def update_profile_signal(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
forms.py
class SignUpForm(UserCreationForm):
university = forms.CharField(max_length=30)
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email','university', 'password1', 'password2',)
app urls.py
app_name = 'main'
urlpatterns=[
path('signup/',views.signup,name='signup'),
path('user_login/',views.user_login,name='user_login'),
path('main_page/',views.main_page,name='main_page'),
]
login.html form
<form method="POST" action="{% url 'main:user_login' %}/" class="form-signin">
{%csrf_token%}
<div class="form-label-group">
<input type="text" name="username" id="inputText" class="form-control" placeholder="Username" required autofocus>
<br/>
</div>
<div class="form-label-group">
<input type="password" name="password" id="inputPassword" class="form-control" placeholder="Password" required>
</div>
<div class="custom-control custom-checkbox mb-3">
<input type="checkbox" class="custom-control-input" id="customCheck1">
<label class="custom-control-label" for="customCheck1">Remember password</label>
</div>
<input type="submit" class="form-control" name="" value="Login">
<hr class="my-4">
<p>Don't have account? Sign up here</p>
{% if message %}<p style="color: red;">{{message}}</p>{% endif %}
Forgot Password
</form>
I cannot figure out why my Django does not accept correct credentials. Which part of my code seems to be incorrect?
make sure to define the right user model to django settings, add
# AUTH_USER_MODEL = 'YOUR_APP_NAME.YOUR_CUSTOM_USER_MODEL'
to your django settings .like # AUTH_USER_MODEL = 'users.User'
and you need to write an abstract user model .
from django.contrib.auth.models import AbstractUser
class RightsSupport(models.Model):
class Meta:
managed = False # No database table creation or deletion \
# operations will be performed for this model.
permissions = (
("add_user", "Can Create User"),
("view_user", "Can View Single User"),
("change_user", "Can Change User"),
("list_user", "Can View User List"),
("add_plan", "Can Create Plan"),
("view_plan", "Can View Single Plan"),
("change_plan", "Can Change Plan"),
("list_plan", "Can View Plan List"),
)
class User(AbstractUser):
CREATED = 0
ACTIVE = 1
BANNED = 2
KICKED = 3
UPGRADE = 4
STS = (
(CREATED, 'Just Created'),
(ACTIVE, 'Activated'),
(BANNED, 'Disabled'),
(KICKED, 'Disabled'),
(UPGRADE, 'Active'),
)
status = models.IntegerField(choices=STS, default=CREATED, blank=True, null=True)
def __str__(self):
return self.username
also update your view, that code has a huge mistake on it . you forget to close docstring .
def signup(request):
if request.method == 'POST':
form = SignUpForm(request.POST)
if form.is_valid():
user = form.save()
user.refresh_from_db() # load the profile instance created by the signal
user.profile.university = form.cleaned_data.get('university')
user.save()
raw_password = form.cleaned_data.get('password1')
user = authenticate(username=user.username, password=raw_password)
#user_login(request, user)
return redirect('main:main_page')
else:
form = SignUpForm()
return render(request, 'signup.html', {'form': form})
def user_login(request):
'''
Using different method for getting username, tried this and didnt work either
'''
if request.method == 'POST':
form = AuthenticationForm(request, data=request.POST)
if form.is_valid():
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
user = authenticate(username=username, password=password)
if user is not None:
login(request,user)
messages.info(request, "Successfully signed in")
return redirect('main:home')
else:
message = 'Sorry, the username or password you entered is not valid please try again.'
return render(request, 'login.html', {'message':message})
else:
message = 'Invalid'
return render(request, 'login.html', {'message':message})
else:
form=AuthenticationForm()
return render(request, 'login.html', {"form":form})

user login redirect always to homepage

I am creating alogin view in which when user is not none then logged in it should go to same page before login , then also go to other url , but it always goes to the other url though it should go to first redirect
views.py
def login_page(request):
form = LoginForm(request.POST or None)
context = {
"form": form
}
next_get_url= request.GET.get('next')
next_post_url= request.POST.get('next')
redirect_path = next_get_url or next_post_url or None
if form.is_valid():
print(form.cleaned_data)
username = form.cleaned_data.get("username")
password = form.cleaned_data.get("password")
user = authenticate(request, username=username, password=password)
print(user)
if user is not None:
login(request, user)
if is_safe_url(redirect_path, request.get_host()):
return(redirect(redirect_path))
else:
return (redirect('/'))
else:
print ("Error")
return render(request, "accounts/login.html",context)
forms.py:
class LoginForm(forms.Form):
username = forms.CharField()
password = forms.CharField(widget=forms.PasswordInput)
i mean it always go to this case:
else:
return (redirect('/'))
whenever this is the case:
if is_safe_url(redirect_path, request.get_host()):
return(redirect(redirect_path))
all tries and solutions found did not solve this any help! and thanks in advance
Firstly, Django comes with a LoginView that handles the redirect for you. it would be better to use that than write your own view.
If you do write your own view, then you need to include your redirect_path in your context dictionary here is where Django does is, and then you need to include it as a hidden field in the login form (see the example template in the docs for LoginView:
<form ...>
...
<input type="submit" value="login">
<input type="hidden" name="next" value="{{ next }}">
</form>
You should use
redirect_path = next_get_url or next_post_url or "/"
because if you pass None to is_safe_url, it will return False as it only takes valid urls.
try a render methodes li this
def my_view(request):
username = request.POST['username']
password = request.POST['password']
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
# Redirect to a success page.
render(request , 'bootstrap/page_learn.html')
else:
# Return an 'invalid login' error message.
render(request , 'bootstrap/page_learn.html')

Django redirect not working when I submit my form

My redirect for my login page is not working correctly when I submit a form.
def login_page(request):
form = LoginForm(request.POST or None)
context = {
'form': form,
}
print(request.user.is_authenticated)
if form.is_valid():
username = form.cleaned_data.get("username")
password = form.cleaned_data.get("password")
user = authenticate(request, username=username, password=password)
if user is not None:
print(request.user.is_authenticated)
login(request, user)
# Redirect to a success page.
return redirect("login")
else:
# Return an 'invalid login' error message.
print("Error")
return render(request, "content/login.html", context)
I am expecting it to redirect to same page and print an output that lets me know the authentication worked. But this is what actually happens..
Page not found(404)
Request Method: GET
Request URL:http://127.0.0.1:8000/login/POST?username=stone&password=pass
Any idea as to what is going on?
You haven't shown your template, but it looks like you have action="POST" instead of method="POST" in your form tag.
Be sure, that your template.html looks like this:
<form method="post">
{% csrf_token %}
{{ form }}
</form>
def login_user(request):
if request.user.is_authenticated():
return redirect(reverse('homepage'))
form = LoginForm(request.POST or None)
if request.method == "POST":
if form.is_valid():
user = authenticate(username=form.cleaned_data['email'], password=form.cleaned_data['password'])
if user is not None:
login(request, user)
return redirect(reverse('homepage'))
else:
error_message = "* Password you entered is incorrect."
return render(request, "account/login.html",{
"form": form,
"error_message": error_message,
})
else:
return render(request, "account/login.html", {
"form": form,
})

Django Form class question

I'm making a form for user registration. Here's what my template looks like:
<h1>Register</h1>
<form action="/register/" method="post">
{{ form.as_p }}
<input type="submit" value="Register">
</form>
And here's my view:
from djangoproject1.authentication import forms
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
def main(request):
rform = forms.RegisterForm()
return render_to_response("authentication/index.html", {'form': rform})
def register(request):
if request.method == 'POST':
rform = forms.RegisterForm(request.POST)
if rform.is_valid():
print 'VALID!'
# do something
return HttpResponseRedirect("/register-success/")
else:
print 'INVALID!'
rform = forms.RegisterForm()
return render_to_response("authentication/index.html", {'form': rform})
I haven't gotten to the VALID part yet, I'm still working on the invalid part. Here is what my form looks like:
from django import forms
class RegisterForm(forms.Form):
username = forms.CharField(min_length=6,max_length=15)
password = forms.CharField(widget = forms.PasswordInput(),min_length=6,max_length=15)
confirm_password = forms.CharField(widget = forms.PasswordInput(),min_length=6,max_length=15)
phone_number = forms.RegexField('\d\d\d-\d\d\d-\d\d\d\d',error_message='Invalid format')
def clean_password(self):
password = self.cleaned_data['password']
confirm_password = self.cleaned_data['confirm_password']
if password != confirm_password:
raise forms.ValidationError("Passwords don't match")
return password
Username, password, phone number. Pretty straightforward. However, when I hit "Register" without filling in anything, I should get a bunch of errors but they don't appear anywhere. Is that supposed to happen automatically or am I missing something?
Thanks!
I think your problem is that in your else you're resetting your form to a new one, and the new form hasn't been validated. Try removing this line of code from your else
rform = forms.RegisterForm()