I am having troubles with the redirect to the same page user was on after logging in, no matter how hard I tried and read all the questions here. I think it is high time I showed folks out here my code so if you can point out mistake(s). Thanks.
My login url (in base.html template):
This is present on every page. Changes to logout once the user is logged in. I read that I have to pass a param for example 'next' like this:
Login
but,
*The {{request.path}} is always empty. *
login view:
def mylogin(request):
"""login view"""
try:
redirect_to = request.GET.get('next', '/')
except ValueError:
redirect_to = "/"
errors = ''
t = loader.get_template('login.html')
# check for POST data
if request.method == 'POST':
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
# Redirect to a success page.
return HttpResponseRedirect(redirect_to)
else:
c = Context({
'errors':'disabled',
})
return HttpResponse("disabled")
else:
c = Context({
'errors':'incorrect',
})
return HttpResponse(t.render(c))
else:
c = Context({
'errors':None,
})
return HttpResponse(t.render(c))
The login form, in login.html template
<form method="post" action="?next={{ redirect_to }}" >
<p><label for="username">Username</p>
<p></label><input type="text" name="username" value="" id="username" /></p>
<p><label for="password">Password</label></p>
<p><input type="password" name="password" value="" id="password" /></p>
<p><input type="submit" value="Login"></p>
</form>
You're not passing request to the template context, so naturally it's empty. The easiest way to get it in there is to use a RequestContext, which uses the built-in context processors to add variables to the context.
The usual way to do this is to use render_to_response with the extra parameter context_instance=RequestContext(request). For some reason, you're rendering all your templates the long way, so you'll need to create that requestcontext yourself.
You'll also need to add django.core.context_processors.request to the CONTEXT_PROCESSORS tuple ins settings.py.
Related
I have problem similar to this one: Django form data lost on making login required on post
I want answer to be added if user is logged in otherwise redirect user to login page, let him login and then add his answer. My problem is that I lose content of the form when redirecting to login page.
These are my views:
def question(request, question_id):
question = get_object_or_404(Question, pk=question_id)
form = AnswerForm()
return render(request, 'questions/question.html', {'question': question, 'form' : form},)
#login_required
def answer(request, question_id):
question = get_object_or_404(Question, pk=question_id)
form = AnswerForm(request.POST) if request.method == 'POST' else AnswerForm()
if form.is_valid():
answer = form.save(commit=False)
answer.author = request.user
answer.question = question
answer.save()
return HttpResponseRedirect(reverse('question', args=(question.id,)))
Form:
<form method="POST" action="{% url 'answer' question.id %}">
{% csrf_token %}
{{form.as_p}}
<input type="submit" value="Odpowiedz">
</form>
Form in login template:
<form method="post" action="{% url 'login' %}">
{% csrf_token %}
{{ form.as_p }}
<input type="hidden" name="next" value="{{request.GET.next}}" />
<input type="submit" value="login" />
</form>
I don't know if something more than url can be passed by using next.
#login_required decorator automatically adds a next GET parameter to the LOGIN_URL, so that user is redirected back to the same page the login was triggered from. Right now, you're triggering it from the answer view, so user will come back directly to answer view. POST Data is lost whenever a redirect occurs, so there's no way you can get the data except for temporarily saving it in cookies or redirecting the user to question page after login.
Implementation 1
POST Data is not saved but the user gets redirected to QuestionView
from django.shortcuts import redirect, reverse
...
...
def answer(request, question_id):
if not request.user.is_authenticated:
return redirect("%s?next=%s" % (settings.LOGIN_URL, reverse('question-view-name'))
...
...
Implementation 2
POST Data is saved to the session and then retrieved after login. I haven't tried it and it required you to serialize POST data to string. Also, requires django.contrib.sessions in INSTALLED_APPS and django.contrib.sessions.middleware.SessionMiddleware in your MIDDLEWARE. Checkout https://data-flair.training/blogs/django-sessions/ for how to set, create and delete session variables.
from django.shortcuts import redirect, reverse
...
...
def answer(request, question_id):
if not request.user.is_authenticated:
request.session['form_data'] = YourSerializerFunctionToSerializeFormData(request.POST)
# must retuern a string, use json.dumps() to convert a dictionary to string
return redirect("%s?next=%s" % (settings.LOGIN_URL, reverse('answer-view-name', args=[question_id]))
data = None
if request.method == 'GET':
if 'form_data' in request.session:
data = request.session['form_data']
del request.session['form_data'] # Delete the key once done
else:
data = request.POST
...
form = AnswerForm(data) if data else AnswerForm()
...
...
Hope it helps.
This question has been answered earlier but those solutions are not working for me.
My setup have Django 1.9 and MongoDB as backend. I have custom user model.
I have a webpage where I am uploading files. that flow is working fine. Now I have to make that page login protected.
When I am trying to access https://localhost/upload it is redirecting me to https://localhost/login_page?next=/upload
I am giving correct credentials and I can see in django logs that it is redrecting me to /upload but again pointing me back to login page.
models.py
class Uploadedfiles(models.Model):
docfile = models.FileField(upload_to='uploads')
views.py
from django.contrib.auth.decorators import login_required
import os
#login_required()
def uploadfiles(request):
if request.method == 'POST':
form = UploadForm(request.POST, request.FILES)
if form.is_valid():
newdoc = Uploadedfiles(docfile=request.FILES['docfile'])
newdoc.save()
return HttpResponseRedirect(reverse('upload'))
else:
form = UploadForm() # A empty, unbound form
ufiles = Uploadedfiles.objects.all()
return render(
request,
'upload.html',
{'ufiles': ufiles, 'form': form}
login_page.html
<div>
{% if next %}
<form action="/login/?next={{ request.path }}" method="post" >
{%else%}
<form action="/login_page/" method="post" >
{% endif %}
{% csrf_token %}
<p><b>Login</b></p>
<input type="email" name="email" placeholder="Email" required="">
<input type="password" name="password" placeholder="Password" id="new_password">
<span id="message"></span>
<button type="submit" name="Login"><b>Login</b></button>
</form>
</div>
login view
def LoginPage(request,*args, **kwargs):
msg = "Please provide details"
next = request.POST.get('next', request.GET.get('next', ''))
if 'email' in request.POST and 'password' in request.POST:
password=request.POST['password']
email=request.POST['email']
payload = { "password" : password, "email":email}
response = requests.post('https://localhost/login',data=payload)
code=response.status_code
if '200'==code or 200==code:
msg="Your are login successfully"
print(msg)
return HttpResponseRedirect("/upload")
else:
msg=response[msg]
response = render(request,'login_page.html',{'msg':msg})
else:
response = render(request,'login_page.html',{})
return response
urls.py
urlpatterns = [
url(r'^login$', Login.as_view(), name=r"login"), #This is REST API
url(r'^login_page/$', LoginPage, name=r"loginpage")
]
Please someone help me..
EDIT 1 : adding custom user model
from mongoengine import fields, Document
class User(Document):
id = fields.IntField(primary_key=True)
username = fields.StringField(required=True)
email = fields.EmailField()
name = fields.StringField()
password = fields.StringField(required=True)
A simple solution would be to use LOGIN_REDIRECT_URL
LOGIN_REDIRECT_URL = '/upload'
I have a registration method in views.py
def register_page(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
user = User.objects.create_user(username=form.cleaned_data['username'],
password=form.cleaned_data['password1'],
email=form.cleaned_data['email'])
if request.POST.get("admin", ""):
content_type = ContentType.objects.get_for_model(User)
permission = Permission.objects.get(codename='admin_can_manage_users')
user.user_permissions.add(permission)
new_user = authenticate(username=form.cleaned_data['username'], password=form.cleaned_data['password1'])
login(request, new_user)
return redirect('payment_list')
form = RegistrationForm()
variables = RequestContext(request, {'form': form})
return render_to_response('registration/register.html',variables)
I also have a register form with standart fields and additional manually-added checkboxes. I want to check if one checkbox is checked and if so add permissions to users.
<form method="post" action=".">
{{ form.as_p }}
<input type="radio" class='radio-button' name="regular" id="regular"/><label for="regular">User</label>
<input type="radio" class='radio-button' name="manager" id="manager"/><label for="manager">Manager</label>
<input type="radio" class='radio-button' name="admin" id="admin"/><label for="admin">admin</label>
<input type="submit" value="register" />
</form>
Now permissions aren't assigned automatically. Previously I assigned this permission to first user and it worked but when I tried to assign it to another user got nothing.
Did you save the user?
user.user_permissions.add(permission)
user.save()
You can also turn the logs on and see what queries django does to database like this
The error was because of the namespace: my permission was in auth namespace. Just changed to another (another model) and it works perfectly.
I make login/password form:
model:
class LoginForm(forms.Form):
username = forms.CharField(max_length=100)
password = forms.CharField(widget=forms.PasswordInput(render_value=False),max_length=100)
view:
def login_view(request):
if request.method == 'POST':
username = request.POST['email']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None and user.is_active:
login(request, user)
return HttpResponseRedirect("/n1.html")# Redirect to a success page.
return HttpResponseRedirect("/login")
form=LoginForm()
return render(request, 'enter.html', {'login_form': LoginForm})
urls:
(r'^login/$', login_view),
template:
{% if form.errors %}
<p>Something is wrong</p>
{% endif %}
<form class="form-signin" action="" method="post">
<h2 class="form-signin-heading">Login</h2>
{% csrf_token %}
<input class="input-block-level" type="text" name="email" value="" id="email" placeholder="Email">
<input class="input-block-level" type="password" name="password" value="" id="username" placeholder="Password">
<button class="btn btn-large btn-primary" type="submit">Login</button>
<input type="hidden" name="next" value="{{next|escape}}" />
</form>
I use redirect to login page then login or password is wrong, but I want to make error message in this case. Why construction {% if form.errors %} doesn't work? Thx!
Because the form has no idea an error occurred.
When you construct the form here:
form=LoginForm()
You're constructing it without passing it any information. It doesn't know anything about the POST the user just did, or that the the login failed, or that the password was missing, or whatever the error was.
Here's what my login forms look like:
class LoginForm(forms.Form):
username = forms.CharField(max_length=255, required=True)
password = forms.CharField(widget=forms.PasswordInput, required=True)
def clean(self):
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
user = authenticate(username=username, password=password)
if not user or not user.is_active:
raise forms.ValidationError("Sorry, that login was invalid. Please try again.")
return self.cleaned_data
def login(self, request):
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
user = authenticate(username=username, password=password)
return user
We override the form's clean method, so that if the form passes validation we can check the user's credentials. We also put a login method on the form object itself, to make our view cleaner.
Next, in our view, we want to do this:
def login_view(request):
form = LoginForm(request.POST or None)
if request.POST and form.is_valid():
user = form.login(request)
if user:
login(request, user)
return HttpResponseRedirect("/n1.html")# Redirect to a success page.
return render(request, 'enter.html', {'login_form': form })
We instantiate the form and hand it the request.POST to check against.
If we have a POST, we check if the form is valid. The form's "clean" method will get called, and check the user credentials for us.
If the credentials fail, we raise an error, which we need to show in our template.
Errors raised by the form (but not attached to a field) are stored in non_field_errors, which can be displayed like so:
{% if form.non_field_errors %}
<ul class='form-errors'>
{% for error in form.non_field_errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
When you redirect, I'm not sure you send the context of your page. So when you redirect to the /login/ page, django tinks it's a new form which is loaded.
I have a simple signup form (in signup.html)
<form action="adduser" method="post">
{% csrf_token %}
Email Address: <input type="email" name="email" required autocomplete="on" placeholder="fr#star.com"/><br/>
Username: <input type="text" name="username" maxlength=25 required placeholder="JoyfulSophia"/><br/>
Password: <input type="password" name="password" maxlength=30 required placeholder="**********" /><br/>
<br/>
<input type="submit" value="Send" /> <input type="reset">
</form>
This redirects to the addUser view:
def adduser(request):
u = User.objects.create_user(request.POST['username'], request.POST['email'], password=request.POST['password'])
u.save()
a = Accounts(user=u)
p = Passwords(user=u)
a.save()
p.save()
return HttpResponseRedirect(reverse('OmniCloud_App.views.profile', args=(u.id,)))
Here is the profile:
#login_required
def profile(request, User_id):
u = get_object_or_404(User, pk=User_id)
a = get_object_or_404(Accounts, pk=User_id)
return render_to_response('profile.html', context_instance=RequestContext(request))
So they wouldn't be signed in, but that's okay because we can send you over to /accounts/login?next=/7/ since they are user 7 (Problems Ahead!)
def login(request):
username = request.POST['username']
password = request.POST['password']
user = auth.authenticate(username=username, password=password)
if user is not None and user.is_active:
auth.login(request, user)
return HttpResponseRedirect("/account/profile/")
else:
return HttpResponseRedirect("/account/invalid/")
The request doesn't contain anything called username, but the one which was submitted to the addUser form does, so how can I shoot that bro over to login? I could have it parse the url (which contains the next=USER_ID) but that wouldn't work for someone that just types in base_url/login, and the user_id won't be part of the url forever. So what's a brother to do?
Post data exists only for one request. If you want to use it later you should save it somewhere else.
You could login the user right after registration, in adduser view, he just entered his username and password, he doesn't have to do it again.
And login view is a little off. This is just a "POST" part of the view. You need to check and see if it's GET request and if it is return template with form containing username and password fields and with target url that points to the same view. Something like this:
def login(request):
if request.method == 'GET':
return render_to_response('login.html',
{ 'form': LoginForm() },
context_instance=RequestContext(request))
elif request.method == 'POST':
form = LoginForm(request.POST)
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
user = auth.authenticate(username=username, password=password)
if user is not None and user.is_active:
auth.login(request, user)
return HttpResponseRedirect("/account/profile")
else:
return HttpResponseRedirect("/account/invalid/")
Where login.html is something like this:
{% extends "base_site.html" %}
{% block content %}
<form method="post" target="{% url login_view %}">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Login" />
</form>
{% endblock content %}
Also, you could return user to the same login form if username and password didn't match and add some message!
This is just from the top of my head, didn't try it, but it should work!
There is an extensible user-registration application for Django called django-registration that offers you a lot of functionality for creating and registering users. Setting it up is very simple, you can find a very good doc here