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()
Related
I'm trying to create a form that allows a user to update their username or avatar. The problem that I am running into is that if I update the profile picture without changing the username, the django form validation if form.is_valid() will recognize the form as invalid, since the username already exists in the database. I'm using an update view, though I'm not sure I've implemented it correctly.
When I try to update the avatar without changing the username, I get a page that says "Form is invalid" from the line: HttpResponse("Form is invalid"). Is there a workaround to remove the form validation? I have tried removing if form.is_valid(): but received the error 'User_form' object has no attribute 'cleaned_data'.
I feel like there has to be an easy way around this that I have not been able to find, as so many sites allow you to update only one attribute at a time.
Views.py
model = User
form = User_form
fields = ['username', 'avatar']
template_name_suffix = '_update_form'
def update_profile(request, user_id):
if request.method == "POST":
form = User_form(request.POST, request.FILES)
if form.is_valid():
user = User.objects.get(pk=user_id)
username = form.cleaned_data['username']
avatar = form.cleaned_data['avatar']
if username != user.username:
user.username = username
if avatar != user.avatar:
if avatar:
user.avatar = avatar
user.save()
return redirect('index')
else:
return HttpResponse("Form is invalid")
models.py
class User(AbstractUser):
followed_by = models.ManyToManyField("User", blank=True, related_name="following")
avatar = models.ImageField(upload_to='profilepics/', verbose_name='Avatar', null=True, blank=True)
class User_form(ModelForm):
class Meta:
model = User
fields = ['username', 'avatar']
user_update_form.html
<form action="/profile/{{user.id}}/update" method="post" enctype='multipart/form-data'>
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Update">
</form>
You are doing too much work yourself. Django's ModelForms can not only create data, but update data as well. You pass the instance through the instance=… parameter:
from django.shortcuts import get_object_or_404
def update_profile(request, user_id):
user = get_object_or_404(User, pk=user_id)
if request.method == 'POST':
form = User_form(request.POST, request.FILES, instance=user)
if form.is_valid():
form.save()
return redirect('index')
else:
return HttpResponse('Form is invalid')
else:
form = User_form(instance=user)
return render(request, 'some_template.html', {'form': form})
For uniqness checks, it will exclude the instance when checking if the username already exists. So one can change the username, given the new username of course does not yet exists.
I have used the default path('',include("django.contrib.auth.urls")) in django to perform login,password reset operations for my project,I have thoroughly checked my signup form and the database,everything goes well with the registration part,but I am unable to authenticate all other users except the super user,what might be the reason for this issue?
myproject/urls.py:
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('home/', include('home.urls')),
path('accounts/',include('accounts.urls')),
path('',include("django.contrib.auth.urls"))
]
and in templates in registration directory my login form will look like
{% extends 'base.html' %}
{% block title %}Login{% endblock %}
{% block content %}
<h2>Login</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Login</button>
</form>
{% endblock %}
my signup view is:
class UserFormView(View):
form_class = RegForm
template_name = 'signup.html'
def get(self, request):
form = self.form_class()
return render(request, self.template_name, {'form': form})
def post(self, request):
form = self.form_class(request.POST)
if (form.is_valid()):
form.save()
return redirect('login')
return render(request, self.template_name, {'form': form})
And then then my form:
class RegForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput())
confirm_password=forms.CharField(widget=forms.PasswordInput())
class Meta:
model= User
fields=['first_name','last_name','username','email','date_joined','password','confirm_password']
def clean_password(self):
password=self.cleaned_data.get("password")
confirm_password = self.cleaned_data.get("confirm_password")
if(len(password)<8):
raise forms.ValidationError("The length of the password should be minimum 8 characters")
return password
def clean_email(self):
email=self.cleaned_data.get('email')
if(validate_email(email)==False):
raise forms.ValidationError("The Email Format is In Correct")
return email
def clean_confirm_password(self):
password = self.cleaned_data.get("password")
confirm_password = self.cleaned_data.get("confirm_password")
if (password != confirm_password):
raise forms.ValidationError('Password doesn\'t match')
As I said in comments you need to save the user like this:
def post(self, request):
form = self.form_class(request.POST)
if form.is_valid():
user = form.save(commit=False)
password = form.cleaned_data['password']
user.set_password(password)
user.save()
return redirect('login')
return render(request, self.template_name, {'form': form})
Django expects the password field of the User model to contained a hashed password. Your form stores the password in plaintext (which is a big security no-go).
I suggest you have a look at the source code of django.contrib.auth.forms.UserCreationForm on how to properly create a user.
Edit: my guess is that you can sign in with the superuser because you created it with the createsuperuser command.
This is because you incorrectly save the password. In django it perform a hashing over the password. You either use the django User password field( ref link https://docs.djangoproject.com/en/2.1/ref/contrib/auth/#django.contrib.auth.models.User.password) so your RegForm looks like
class RegForm(forms.ModelForm):
confirm_password=forms.CharField(widget=forms.PasswordInput())
class Meta:
model= User
fields=['first_name','last_name','username','email','date_joined','password','confirm_password']
def clean_password(self):
password=self.cleaned_data.get("password")
confirm_password = self.cleaned_data.get("confirm_password")
if(len(password)<8):
raise forms.ValidationError("The length of the password should be minimum 8 characters")
return password
def clean_email(self):
email=self.cleaned_data.get('email')
if(validate_email(email)==False):
raise forms.ValidationError("The Email Format is In Correct")
return email
def clean_confirm_password(self):
password = self.cleaned_data.get("password")
confirm_password = self.cleaned_data.get("confirm_password")
if (password != confirm_password):
raise forms.ValidationError('Password doesn\'t match')
Or
save the hashed value of input passwor in the post method. So the code look like
def post(self, request):
form = self.form_class(request.POST)
if (form.is_valid()):
user_form = form.save(commit=False)
user_form.set_password(request.POST.get('password'))
user_form.save()
return redirect('login')
return render(request, self.template_name, {'form': form})
I am trying to create a custom validator for my form by overwriting the clean() method, and for some reason validation errors are not being raised properly. Here's my code:
forms.py
from django import forms
from django.contrib.auth import get_user_model
class EmailForm(forms.Form):
email_field = forms.EmailField(label='E-mail address', max_length=128)
def clean(self):
cleaned_data = super(EmailForm, self).clean()
email = cleaned_data.get('email')
try:
u = get_user_model().objects.get(email=email)
raise forms.ValidationError("E-mail already in database.")
print('validation error raised')
except:
pass
views.py
from django.shortcuts import render
from django.contrib import messages
from .forms import EmailForm
def email_form(request):
if request.method == 'POST':
form = EmailForm(request.POST)
# If the form is valid...
if form.is_valid():
messages.add_message(request, messages.SUCCESS, "Kaboom! Your referral e-mail was sent. Hang tight - we'll be sure to let you know when your friend makes a purchase.")
else:
messages.add_message(request, messages.ERROR, 'Error!')
else:
form = EmailForm()
return render(request, 'template.html', {'form': form})
template.html
<form action="{% url 'form-process' %}" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
When I submit this form with input that should raise a validation error (i.e., an e-mail address that is already associated with a user in my database), I get the 'success' message rather than an error. The strangest part about it is that 'validation error raised' is printed in my console — but for some reason the form processes and form.is_valid() passes as True.
Can anyone tell me why form.is_valid doesn't output an error in the code below when a duplicate e-mail is entered?
It can't raise exception, because you catch is in except.
def clean(self):
cleaned_data = super(EmailForm, self).clean()
email = cleaned_data.get('email')
users = get_user_model().objects.filter(email=email)
if users.exists():
raise forms.ValidationError("E-mail already in database.")
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')
Can anyone spot what I'm doing wrong in the following example. Validating messages are not appearing in my template when incorrect details are entered such as a invalid email address. The template is loading and there are no errors.
I'm excepting validation messages to be printed on page, however for some reason this has suddenly stop working. As you can see from the code example below I'm passing the form in the context back to the template. this used to work and today just stopped.
view.py
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
# If form has passed all validation checks then continue to save member.
user = User.objects.create_user(
username=form.cleaned_data['username'],
email=form.cleaned_data['email'],
password=form.cleaned_data['password']
)
user.save()
#member = User.get_profile()
#member.name = form.cleaned_data['name']
#member.save()
member = Member(
user=user,
name=form.cleaned_data['name']
)
member.save()
# Save is done redirect member to logged in page.
return HttpResponseRedirect('/profile')
else:
# If form is NOT valid then show the registration page again.
form = RegistrationForm()
context = {'form':form}
return render_to_response('pageRegistration.html', context,context_instance=RequestContext(request))
form.py
class RegistrationForm(ModelForm):
username = forms.CharField(label=(u'User Name'))
email = forms.EmailField(label=(u'Email'))
password = forms.CharField(label=(u'Password'), widget=forms.PasswordInput(render_value=False))
passwordConfirm = forms.CharField(label=(u'Confirm Password'), widget=forms.PasswordInput(render_value=False))
class Meta:
model = Member
# Don't show user drop down.
exclude = ('user',)
def clean_username(self):
username = self.cleaned_data['username']
try:
User.objects.get(username=username)
except User.DoesNotExist:
return username
raise forms.ValidationError("Username already taken.")
def clean(self):
try:
cleaned_data = super(RegistrationForm, self).clean()
password = cleaned_data.get("password")
passwordConfirm = cleaned_data.get('passwordConfirm')
if password != passwordConfirm:
raise forms.ValidationError("Password does not match, try again.")
return cleaned_data
except:
raise forms.ValidationError("Error")
pageRegistration.html
<form action="" method="POST">
{% csrf_token %}
{% if forms.errors %}
<p>
correct some stuff
</p>
{% endif %}
{{form}}
<br>
<input type="submit" value="submit">
</form>
Since, the form is not validate in the else clause your form variable is overriden with a new form where it looses all of the errors
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
# If form has passed all validation checks then continue to save member.
user = User.objects.create_user(
username=form.cleaned_data['username'],
email=form.cleaned_data['email'],
password=form.cleaned_data['password']
)
user.save()
#member = User.get_profile()
#member.name = form.cleaned_data['name']
#member.save()
member = Member(
user=user,
name=form.cleaned_data['name']
)
member.save()
# Save is done redirect member to logged in page.
return HttpResponseRedirect('/profile')
return render_to_response('pageRegistration.html', context,context_instance=RequestContext(request))
Updated for the CBV world.
This is what was causing the equivalent error for me:
class CreateView(generic.CreateView): # or generic.UpdateView
def get_context_data(self, **kwargs):
context_data = super(CreateView, self).get_context_data(**kwargs)
# context_data['form'] = self.form_class # << this was the problematic override
Perhaps the page is returning a new form every time it reloads. Check if the
context variable in views.py contains 'form' : form