More fields in the User Model

In the course " Mastering Django - AJAX, Class Based Views, Forms" there is a signup page which asks for username and password. But the User model has first_name, last_name and email additional fields.

  1. How do we make use of the first_name, last_name and email in the form ? Will this automatically work ?
form_class = UserCreationForm
username, password, first_name, last_name, email= form.cleaned_data.get('username'), form.cleaned_data.get('password1'), form.cleaned_data.get('first_name'), form.cleaned_data.get('last_name'), form.cleaned_data.get('email')
user = authenticate(username=username, password=password, first_name=first_name, last_name=last_name, email=email)

How will signup.html form know to add the remaining fields?

  1. What if I want more custom fields in the user model to be stored in the database with custom validation ?

1. If u want to use custom creation form, the best way is to create such. In forms.py, where u add all needed fields.

forms.py:

class CustomUserCreationForm(UserCreationForm):`
    class Meta:
        model = User
        # show those fields in signup form
        fields = ['username', 'password1', 'password2', 'first_name", "last_name", "email"]

your view could look like below:

views.py

class SignUpUser(View):
    def get(self, request, *args, **kwargs):
        return render(request, 'accounts/signup.html', {'form': CustomUserCreationForm()})

    def post(self, request, *args, **kwargs):
        if request.POST['password1'] == request.POST['password2']:
            try:
                user = User.objects.create_user(request.POST['username'], password=request.POST['password1'], first_name=request.POST['first_name'], last_name=request.POST['last_name'], email=request.POST['email'])
                login(request, user)
                return redirect('home')
            except IntegrityError:
                username = request.POST['username']
                form = CustomUserCreationForm(initial={
                    'username': username,
                })
                context = {
                    'error': 'User ' + username + ' already exists!',
                    'form': form,
                }
                return render(request, 'accounts/signup.html', context)
        else:
            form = CustomUserCreationForm(initial={
                'username': request.POST['username'],
            })

            context = {
                'error': 'Passwords did not match!!!',
                'form': form
            }
            return render(request, 'accounts/signup.html', context)

and finally your html template could look like below:

signup.html:

{% extends 'myapp/base.html' %}
{% load static %}
{% block content %}
   <form method="POST" action="">{% csrf_token %}
     {{ form.as_p }}
     <button class="btn btn-primary" role="button" type="submit">
       Save
     </button>
{% endblock %}

So, in such way u can customize UserCreationForm and add first and last names to it.

2. How to add more custom fields to the User model aka customize User model. Imo the best way is to extend Django User with OneToOne relation (more here: Customizing authentication in Django | Django documentation | Django). Take a look for the first example:

models.py:

from django.contrib.auth.models import User 

class UserType(models.TextChoices):
    STUDENT = 'STUD', 'Student'
    TEACHER = 'TEACH', 'Teacher'
    ADMINISTRATOR = 'ADMIN', 'Administrator'
 
# class below extends Django User. There is additional field - user_type
class MyUser(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    user_type = models.CharField(
        max_length=5,
        choices=UserType.choices,
        default=UserType.STUDENT,
    )

part of html template could look like below::

signupuser.html:

...
<form method="POST">
   {% csrf_token %}
   <div class="form-group">
     <label for="username">Username</label>
     <input type="text" name="username" class="form-control" id="username" aria-describedby="usernameHelp">
     <small id="usernameHelp" class="form-text text-muted">Your username must be unique. We'll let you know if someone has taken it already.</small>
   </div>
  <div class="form-group">
     <label for="user-type">Who are you?</label>
     <! -- select with values from UserType model -- >
     <select name="user-type" id="user-type">
        <option value ="STUD">Student</option>
        <option value ="TEACH">Teacher</option>
        <option value ="ADMIN">Administrator</option>
      </select>
   </div>
   <div class="form-group">
      <label for="password1">Password</label>
      <input type="password" name="password1" class="form-control" id="password1">
   </div>
   <div class="form-group">
      <label for="password2">Confirm Password</label>
      <input type="password" name="password2" class="form-control" id="password2">
   </div>
   <button type="submit" class="btn btn-primary">Sign Up</button>
</form>
...

and finally views.py:

views.py:

def signupuser(request):
    if request.method == 'GET':
        return render(request, 'accounts/signupuser.html', )
    else:
        if request.POST['password1'] == request.POST['password2']:
            print(request.POST)
            try:
                user = User.objects.create_user(request.POST['username'], password=request.POST['password1'])
                MyUser(user_id=user.id, user_type=request.POST['user-type']).save()
                login(request, user)
                return redirect('home')

effects:


db

...

Take a look second example - extending Django User by additional profile_picture. Both examples use extending Django User concept to add custom fields:

models.py:

from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
 
class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    profile_picture = models.ImageField(upload_to='images/media/', verbose_name="avatar", null=True, blank=True)

    # below we define signals so our Profile model will be automatically created/updated when we create/update User instances.
    @receiver(post_save, sender=User)
    def create_user_profile(sender, instance, created, **kwargs):
        if created:
            Profile.objects.create(user=instance)
     
    @receiver(post_save, sender=User)
    def save_user_profile(sender, instance, **kwargs):
        instance.profile.save()

Simply we hooked create_user_profile and save_user_profile methods to the django User model. whenever a save event occurs our Profile model will be updated/created. more about signals u can read here: https://docs.djangoproject.com/en/3.1/ref/signals .

forms.py:

from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
 
 
class CustomUserCreationForm(UserCreationForm):
    # adding extra field for profile picture
    profile_picture = forms.ImageField()
 
    class Meta:
        model = User
        # show those fields in signup form
        fields = ['username', 'password1', 'password2', 'profile_picture']

We overrided default UserCreationForm by adding extra field. All behaviours of UserCreationForm stay untouched - plus of inheritance :slight_smile:

views.py:

def signup_user(request):
    if request.method == 'GET':
        # here we render site with our extended creation form
        return render(request, 'accounts/signup.html', {'form': CustomUserCreationForm()})
    else:
        # here we deal with data from the form after submit
        if request.POST['password1'] == request.POST['password2']:
            try:
                # we create user as usual
                user = User.objects.create_user(request.POST['username'], password=request.POST['password1'])
                # here we grab profile picture and connect with user
                user.profile.profile_picture = request.FILES['profile_picture']
                login(request, user)
                return redirect('currenttodos')
            except IntegrityError:
                username = request.POST['username']
                form = CustomUserCreationForm(initial={
                    'username': username,
                })
                context = {
                    'error': 'User ' + username + ' already exists!',
                    'form': form,
                }
                return render(request, 'accounts/signup.html', context)
        else:
            form = CustomUserCreationForm(initial={
                'username': request.POST['username'],
            })
 
            context = {
                'error': 'Passwords did not match!!!',
                'form': form
            }
            return render(request, 'accounts/signup.html', context)

signup.html:

{% extends 'nyapp/base.html' %}
{% load static %}
{% block content %}
  <form method="POST" action="" enctype="multipart/form-data">{% csrf_token %}
    {{ form.as_p }}
    <button class="btn btn-primary" role="button" type="submit">
      Save
    </button>
{% endblock %}

hope above examples show how to add custom fields to User model in Django. Last thing u ask is validation - own validation. Imo the best way is to use widgets - Widgets | Django documentation | Django

Wow! Amazing answer @croolic. Thanks for sharing