I am trying to create a custom signup form using HTML, CSS, Bootstrap as front end, and Django as backend, when I create the form, firstname, lastname, and email are appearing as required, but the password and confirm_password fields are not appearing as required, from forms.py, the widget's placeholder and class=form-control is also not being applied to it.
I asked ChatGPT, it is asking to clear Cache, switching to incognito or a different browser, I tried all things, and still it is appearing the same. I am providing all the files associated with the signup form I created.
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from django.db import models
class CustomUserManager(BaseUserManager):
def create_user(self, email, first_name, last_name=None, password=None, **extra_fields):
if not email:
raise ValueError('The Email field must be set.')
if not first_name:
raise ValueError('The First Name field must be set.')
if not password:
raise ValueError('Password cannot be left blank.')
email = self.normalize_email(email)
user = self.model(email=email, first_name=first_name, last_name=last_name, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
class CustomUser(AbstractBaseUser):
email = models.EmailField(unique=True)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50, blank=True, null=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
objects = CustomUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name']
def __str__(self):
return self.email
from django import forms
from django.contrib.auth.forms import UserCreationForm
from .models import CustomUser
class SignUpForm(UserCreationForm):
class Meta:
model = CustomUser
fields = ['first_name', 'last_name', 'email', 'password1', 'password2']
widgets = {
'first_name': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Enter your first name'}),
'last_name': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Enter your last name'}),
'email': forms.EmailInput(attrs={'class': 'form-control', 'placeholder': 'Enter your email'}),
'password1': forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': 'Enter password'}),
'password2': forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': 'Confirm password'}),
}
from django.shortcuts import render
from .forms import SignUpForm
def SignupView(request):
if request.method == 'POST':
form = SignUpForm(request.POST)
if form.is_valid():
user = form.save()
login(request, user)
return redirect('home')
else:
form = SignUpForm()
return render(request, 'signup.html', {'form': form})
def LoginView(request):
return render(request, 'login.html')
{% extends 'base.html' %}
{% block content %}
<!-- Top Section with Banner -->
<section class="container my-5 py-5 bg-light">
<div class="row mt-5 py-5">
<div class="col-12 text-center">
<h1 style="color:#333;">Join Us!</h1>
<p class="lead lh-lg" style="color:#555;">We'd love to hear from you!</p>
</div>
</div>
</section>
<!-- Sign Up Section -->
<section class="container my-5 py-5 bg-light">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<h5 class="card-header">Sign Up</h5>
<div class="card-body">
<form method="post">
{% csrf_token %}
{{ form.non_field_errors }}
<div class="mb-3 row">
<div class="col">
{{ form.first_name }}
</div>
<div class="col">
{{ form.last_name }}
</div>
</div>
<div class="mb-3">
{{ form.email }}
</div>
<div class="mb-3">
{{ form.password1 }}
</div>
<div class="mb-3">
{{ form.password2 }}
</div>
<button type="submit" class="btn btn-primary w-100">Sign Up</button>
<!-- Divider and Social Login Intro -->
<hr>
<p class="text-muted text-center">or sign up with</p>
<!-- Social Media Login Buttons -->
<div class="d-grid gap-2">
<button type="button" class="btn btn-primary btn-block mb-2" style="background-color: #3b5998;">
<i class="fab fa-facebook-f me-2"></i> Facebook
</button>
<button type="button" class="btn btn-primary btn-block" style="background-color: #db4437;">
<i class="fab fa-google me-2"></i> Google
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</section>
{% endblock %}
When the mentioned code is run I get the following output for signup.html
and following is the desired output that I want:
Notice the difference in the password and confirm password fields, that is the issue.
The BaseUserCreationForm
model form explicitly declares the password fields (has to, because they are not part of the model), and you can't use Meta.widgets to overwrite the widget of a form field that was explicitly declared. You can only overwrite fields that were implicitly added by the model form factory.
(yeah, I agree that is weird)
Fields defined declaratively are left as-is, therefore any customizations made to Meta attributes such as widgets, labels, help_texts, or error_messages are ignored; these only apply to fields that are generated automatically.
Links: django docs and a a related SO question
If you want to set your own widgets, you will need to declare the fields again:
class SignUpForm(UserCreationForm):
password1 = forms.CharField(
label=_("Password"),
strip=False,
widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': 'Enter password'}),
help_text=password_validation.password_validators_help_text_html(),
)
password2 = forms.CharField(
label=_("Password confirmation"),
widget=forms.PasswordInput(attrs={'class': 'form-control', 'placeholder': 'Confirm password'}),
strip=False,
help_text=_("Enter the same password as before, for verification."),
)
class Meta:
model = CustomUser
fields = ['first_name', 'last_name', 'email', 'password1', 'password2']
widgets = {
'first_name': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Enter your first name'}),
'last_name': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Enter your last name'}),
'email': forms.EmailInput(attrs={'class': 'form-control', 'placeholder': 'Enter your email'}),
}