I have this model:
class User(AbstractUser):
REQUIRED_FIELDS = []
USERNAME_FIELD = 'email'
email = models.EmailField(
_('email address'),
max_length=150,
unique=True,
help_text=_('Required. 150 characters of fewer. Must be a valid email address.'),
error_messages={
'unique':_("A user with that email address already exists."),
},
)
this form class:
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = User
fields = ['username','email','password']
this view class:
class UserFormView(View):
form_class = UserForm
template_name = 'workoutcal/register.html'
def get(self, request):
print("Hi again")
form = self.form_class(None)
return render(request, self.template_name, {'form':form})
def post(self, request):
form = self.form_class(request.POST)
if form.is_valid():
user = form.save(commit=False)
username = form.cleaned_data['username']
password = form.cleaned_data['password']
user.set_password(password)
user.save()
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
return redirect('workoutcal:calendar')
return render(request, self.template_name, {'form': form})
and this url:
url(r'^register/$', views.UserFormView.as_view(), name='register')
So when I go to /workoutcal/register, I see this:
The "help text" is always shown in the browser. Is this default Django behaviour, or is it due to some error I have made?
Also, how do I make the text only show up if the user has entered bad data into the respective fields?
Yes if you are using {{ form }} in your template
It will always show help_text, label, errors and widget automatically
If you don't want that you need to render form manually by looping over fields
<form method="post" novalidate>
{% csrf_token %}
{{ form.non_field_errors }}
{% for hidden_field in form.hidden_fields %}
{{ hidden_field.errors }}
{{ hidden_field }}
{% endfor %}
{% for field in form.visible_fields %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
{{ field.help_text }} <!-- remove this line if you don't want to show it in your html.-->
</div>
{% endfor %}
<button type="submit">Submit</button>
</form>
For more read docs: https://docs.djangoproject.com/en/2.0/topics/forms/
Related
i have a problem with Django clean method because clean method of form doesn`t load error in template. Could someone help me ?
template.html
{% extends "index.html" %}
{% block header %}
<div id="container-register">
<div class="logo-register">Zarejestruj się</div>
<div class="register-form">
<form method="post">
{% csrf_token %}
{% for field in form %}
{{ field }} {{ field.errors }} <br>
{% endfor %}
<input type="submit" value="Zarejestruj">
</form>
</div>
</div>
{% endblock %}
view.py
class AddUserView(View):
template_name = 'add_user.html'
def get(self,request):
return render(request, self.template_name,{
'form': AddUserForm()
})
def post(self,request):
form = AddUserForm(request.POST)
if form.is_valid():
User.objects.create_user(
username=form.cleaned_data.get('username'),
email=form.cleaned_data.get('email'),
password=form.cleaned_data.get('password'),
first_name=form.cleaned_data.get('first_name'),
last_name=form.cleaned_data.get('last_name')
)
return redirect('/login')
else:
return render(request, self.template_name, context={
'form': AddUserForm()
})
forms.py
class AddUserForm(forms.Form):
username = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'Nazwa użytkownika'}), max_length=100)
password = forms.CharField(widget=forms.PasswordInput(attrs={'placeholder': 'Hasło'}), max_length=100)
password_repeat = forms.CharField(widget=forms.PasswordInput(attrs={'placeholder': 'Powtórz hasło'}),
max_length=100)
first_name = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'Imię'}), max_length=100)
last_name = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'Nazwisko'}), max_length=100)
email = forms.EmailField(widget=forms.TextInput(attrs={'placeholder': 'Email'}), max_length=100)
def clean_username(self):
if User.objects.filter(username=self.cleaned_data.get('username')).exists():
raise ValidationError('Ten login jest już zajęty')
return self.cleaned_data.get('username')
def clean_password_repeat(self):
if self.cleaned_data.get('password') != self.cleaned_data.get('password_repeat'):
raise ValidationError('Podane hasła różnią się od siebie!')
return self.cleaned_data.get('password_repeat')
I checked the page source to see if the errors class was added in the html file.
In your post method, you create a new form in the template context instead of reuse the existing with data and errors :
return render(request, self.template_name, context={
'form': form
})
I am (attempting to) implement the ability for a user to edit and update their email address on their profile page. I am getting no errors when doing this end to end but the new email is not being saved to the DB.
Everything seems to be working, even the redirect to the profile page in the edit_profile function, but the save() doesn't seem to be working, the users email doesn't update and when I am redirected back to the profile page, the email is still the current value.
Thanks!
Model:
class CustomUser(AbstractUser):
email = models.EmailField(_('email address'), unique=True)
is_pro = models.BooleanField(default=False)
is_golfer = models.BooleanField(default=False)
def __str__(self):
return self.email
Form
class EditProfileForm(forms.Form):
email = forms.EmailField(
label='', widget=forms.TextInput(attrs={'class': 'form-field'}))
View
#login_required
def edit_profile(request):
if request.method == "POST":
form = EditProfileForm(request.POST)
if form.is_valid():
email = form.cleaned_data["email"]
user = CustomUser.objects.get(id=request.user.id)
user.save()
return redirect("typeA", username=user.username)
else:
form = EditProfileForm()
return render(request, "registration/edit_profile.html", {'form': form})
URLS
urlpatterns = [
path('type_a_signup/', ASignUpView.as_view(), name='a_signup'),
path('type_b_signup/', BSignUpView.as_view(), name='b_signup'),
path('login/', LoginView.as_view(), name='login'),
path('password_reset', PasswordResetView.as_view(), name='password_reset'),
path('typea/<username>/', typeA, name='typeA'),
path('typeb/<username>/', typeB, name='typeB'),
path('login_success/', login_success, name='login_success'),
path('edit_profile/', edit_profile, name='edit_profile'),
]
Template
<div class="container">
<div class="form-container">
<h2>Edit profile</h2>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<div>
{{ form.email.label_tag }}
<input type="text" class="form-control {% if form.email.errors %}is-invalid{% endif %}" id="id_email"
name="email" value='{{ form.email.value|default:user.email }}'>
{% if form.email.errors %}
<div>{{ form.email.errors }}</div>
{% endif %}
</div>
<button type="submit">Submit</button>
</form>
<br>
</div>
You never set the email field of the object. You should set this with:
#login_required
def edit_profile(request):
if request.method == "POST":
form = EditProfileForm(request.POST)
if form.is_valid():
email = form.cleaned_data["email"]
user = request.user
user.email = email # 🖘 set the email field
user.save()
return redirect("typeA", username=user.username)
else:
form = EditProfileForm()
return render(request, "registration/edit_profile.html", {'form': form})
You should only redirect in case the form is successful. If it is not, Django will rerender the form with the errors.
I used custom form for register users. I want do validation for confirm password.
forms.py:
class RegistrationForm(UserCreationForm):
'''Register for new users'''
email = forms.EmailField(required=True)
first_name = forms.CharField(required=True)
last_name = forms.CharField(required=True)
class Meta:
model = get_user_model()
fields = {'username', 'password1', 'password2', 'email', 'first_name', 'last_name'}
def save(self, commit=True):
user = super(RegistrationForm, self).save(commit=False)
user.email = self.cleaned_data['email']
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
if commit:
user.save()
return user
template:
<div class="input-group register">
{{ form.password.errors }}
<label for="id_password1">Password: </label>
{{ form.password1 }}
</div>
<div class="input-group register">
{{ form.password.errors }}
<label for="id_password2">Confirm password: </label>
{{ form.password2 }}
</div>
views.py
def registration_view(request):
context = {}
context.update(csrf(request))
context['form'] = RegistrationForm()
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
form.save()
newuser = auth.authenticate(
username=form.cleaned_data['username'],
password=form.cleaned_data['password2']
)
auth.login(request, newuser)
return redirect('home')
else:
context['form'] = form
return render(request, '/registration.html', context)
How can I add validation for password(also confirm password)?
You inherit from UserCreationForm [GitHub]. This form already does that for you.
Indeed: the clean_password2 will validate that the two passwords are the same:
def clean_password2(self):
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError(
self.error_messages['password_mismatch'],
code='password_mismatch',
)
return password2
The _post_clean function will validate that the password is a valid password:
def _post_clean(self):
super()._post_clean()
# Validate the password after self.instance is updated with form data
# by super().
password = self.cleaned_data.get('password2')
if password:
try:
password_validation.validate_password(password, self.instance)
except forms.ValidationError as error:
self.add_error('password2', error)
Finally in the save() it will use .set_password() to set the password. This is necessary, since Django's User model will, like any good user model, hash the password.
def save(self, commit=True):
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
You thus should not interfere with these. You here only want to add first_name and last_name. So you can add that logic with:
class RegistrationForm(UserCreationForm):
'''Register for new users'''
email = forms.EmailField(required=True)
first_name = forms.CharField(required=True)
last_name = forms.CharField(required=True)
class Meta:
model = get_user_model()
fields = ['username', 'email', 'first_name', 'last_name']
That's all, since the ModelForm will take care of that.
Note that authenticate in your view is probably not necessary, since if you construct a user, it should authenticate. You can just login here:
def registration_view(request):
context = {}
context.update(csrf(request))
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
newuser = form.save()
auth.login(request, newuser)
return redirect('home')
else:
form = RegistrationForm()
context['form'] = form
return render(request, '/registration.html', context)
Try to use this in your register template
<form method="post" id="register-account-form">
{% csrf_token %}
{% for field in form %}
{% if field.help_text %}
<div class="notification error closeable">
<small style="color: grey">{{ field.help_text }}</small>
<a class="close" href="#"></a>
</div>
{% endif %}
{% for error in field.errors %}
<div class="notification error closeable">
<p style="color: red">{{ error }}</p>
<a class="close" href="#"></a>
</div>
{% endfor %}
{% endfor %}
<div class="input-with-icon-left">
<i class="icon-feather-user"></i>
{{ form.username }}
</div>
I find mistake
<div class="input-group register">
{{ form.password1.errors }}
<label for="id_password1">Пароль: </label>
{{ form.password1 }}
</div>
<div class="input-group register">
{{ form.password2.errors }}
<label for="id_password2">Подтвердите пароль: </label>
{{ form.password2 }}
</div>
How to authenticate user until he verify the email?
In my sign up process, I create the User, which means he can login even without verify email. Is there a build-in method to set a un-verified status on a User?
models.py
email_active = models.BooleanField(default=False,
verbose_name=u'Email active ')
views.py
username = request.POST.get("username")
password = request.POST.get("password")
user = authenticate(username=username, password=password)
if user is not None:
if user.email_active:
login(self.request, user)
return HttpResponse("login success")
else:
return HttpResponse("user email is not active")
else:
return HttpResponse("username or password error")
you can active email by send email with verify_code,user can use verify_code to active their email.
Demo:
models.py:
class EmailVerify(models.Model):
owner = models.ForeignKey(User,
on_delete=models.CASCADE,
verbose_name='owner')
verify_code = models.CharField(max_length=254,
null=True,
verbose_name='verify_code ')
def update(self):
self.verify_code = self.generate_key()
self.save()
return self.verify_code
def get_active_email_url(self, request):
from django.urls import reverse
url = '{}?active_code={}'.format(request.build_absolute_uri(reverse('user_login', args=())), self.verify_code)
return url
#staticmethod
def generate_key():
return binascii.hexlify(os.urandom(20)).decode()
class Meta:
verbose_name = 'EmailVerify'
utils.py :
def send_active_email(request, user):
current_site = get_current_site(request)
site_name = current_site.name
if EmailVerify.objects.filter(owner=user, category=0).exists():
verify = EmailVerify.objects.filter(owner=user).first()
else:
verify = EmailVerify.objects.create(owner=user)
verify.update()
title = u"{} active email".format(site_name)
message = "".join([
u"click this link can active your email:\n\n",
"{}\n\n".format(verify.get_active_email_url(request=request)),
])
try:
send_mail(title, message, settings.DEFAULT_FROM_EMAIL, [user.email])
message = "success"
except ConnectionRefusedError as e:
message = e.strerror
except Exception as e:
message = str(e)
return message
views.py
class LoginView(BaseContextMixin, FormView):
template_name = 'user/login.html'
form_class = LoginForm
success_url = reverse_lazy('index')
def get_context_data(self, **kwargs):
if 'form' not in kwargs:
kwargs['form'] = self.get_form()
if 'active_email' in self.request.GET:
active_email = self.request.GET.get('active_email')
try:
user = User.objects.get(email=active_email, email_active=False)
kwargs['message'] = send_active_email(self.request, user)
except (ObjectDoesNotExist, MultipleObjectsReturned):
kwargs['message'] = 'email not exist or actived'
if 'active_code' in self.request.GET:
active_code = self.request.GET.get('active_code')
try:
email_verify = EmailVerify.objects.get(verify_code=active_code)
email_verify.owner.email_active = True
email_verify.owner.save()
email_verify.delete()
kwargs['message'] = 'email {} actived'.format(email_verify.owner.email)
except ObjectDoesNotExist:
kwargs['message'] = 'unless link'
except MultipleObjectsReturned:
EmailVerify.objects.filter(verify_code=active_code).delete()
kwargs['message'] = 'error!'
return super(LoginView, self).get_context_data(**kwargs)
def get_form(self, form_class=None):
if form_class is None:
form_class = self.get_form_class()
return form_class(request=self.request, **self.get_form_kwargs())
forms.py:
class LoginForm(forms.Form):
active_email = None
username = forms.CharField(label='username')
password = forms.CharField(widget=forms.PasswordInput, label='password')
def __init__(self, request, *args, **kwargs):
super(LoginForm, self).__init__(*args, **kwargs)
self.request = request
self.fields['username'].widget.attrs.update({'class': 'form-control',
"placeholder": self.fields['username'].label})
self.fields['password'].widget.attrs.update({'class': 'form-control',
"placeholder": self.fields['password'].label})
def clean(self):
username = self.cleaned_data["username"]
password = self.cleaned_data["password"]
user = authenticate(username=username, password=password)
if user is not None:
if user.email_active:
login(self.request, user)
else:
self.active_email = user.email
self.add_error("username", "not active")
else:
self.add_error("username", "username or password error")
login.html:
{% extends "user/user_base.html" %}
{% block content %}
<div class="login-box">
<div class="login-logo">
{{ website_title|default_if_none:'' }}
</div>
<div class="login-box-body">
<form method="post" action="{% url 'user_login' %}" class="form">
{% csrf_token %}
{% for field in form %}
<div class="form-group no-margin {% if field.errors %} has-error {% endif %}">
<label class="control-label" for="{{ field.id_for_label }}">
<b>{{ field.label }}</b>
{% if message %}
{% ifequal field.name 'username' %}{{ message }}{% endifequal %}
{% endif %}
{% if field.errors %}
{{ field.errors.as_text }}
{% ifequal field.errors.as_text '* not active' %}
<a href="{% url 'user_login' %}?active_email={{ form.active_email }}">
send active emial
</a>
{% endifequal %}
{% endif %}
</label>
{{ field }}
</div>
{% endfor %}
<div class="row">
<div class="col-md-6">
<button id="btn_login" type="submit" style="width: 100%"
class="btn btn-raised btn-primary">Login
</button>
</div>
</div>
</form>
</div>
</div>
{% endblock %}
When I update the user profile via the view everything is saving to the db except the image. The forms are validating but image isn't being saved. I can log in the admin portal and successfully add an image to an existing instance. I assume my problem lies in my html template but I can't figure out what it is.
**Btw I've read multiple similiar post but none I believe addresses my issue.
form.py
class EditUserForm(forms.ModelForm):
template_name='/something/else'
class Meta:
model = User
fields = (
'email',
'first_name',
'last_name',
)
class EditProfileForm(forms.ModelForm):
template_name='/something/else'
class Meta:
model = UserProfile
fields = (
'description',
'city',
'website',
'phone',
'image',
)
views.py
#transaction.atomic
def edit_profile(request):
if request.method == 'POST':
form = EditUserForm(request.POST, instance=request.user)
form2 = EditProfileForm(request.POST, instance=request.user.userprofile)
if form.is_valid() and form2.is_valid():
form.save()
form2.save()
return redirect(reverse('accounts:view_profile'))
else:
form = EditUserForm(instance=request.user)
form2 = EditProfileForm(instance=request.user.userprofile)
args = {'form': form, 'form2':form2}
return render(request, 'accounts/edit_profile.html', args)
models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
description = models.CharField(max_length=100, default='')
city = models.CharField(max_length=100, default='')
website = models.URLField(default='')
phone = models.IntegerField(default=0)
image = models.ImageField(upload_to='profile_image', blank=True)
def __str__(self):
return self.user.username
edit_profile.html
<div class="container">
{% if form.errors %}
<ol>
{% for field in form %}
<H3 class="title">
<p class="error"> {% if field.errors %}<li>{{ field.errors|striptags }}</li>{% endif %}</p>
</H3>
{% endfor %}
</ol>
{% endif %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
{{ form2.as_p }}
<button type="submit">Submit</button>
</form>
<br>
</div>
If you are uploading files, you must instantiate the form with request.POST and request.FILES.
form2 = EditProfileForm(request.POST, request.FILES, instance=request.user.userprofile)
See the docs on file uploads for more info.