Extend Django all-auth signup to include a file and text field

In this tutorial, I will show you how we can extend the default all auth signup form to include an extra file field and a char field. I will also create two separate models for the file field and char field and link them to the custom user model via OneToOneField.

1) Install the Virtual Env

Let’s start by creating the virtual environment for our new django project.

In case you haven’t heard of the virtual environment it’s just the tool to keep the dependencies for different python projects separate by creating isolated Python environments.

Learn more about them here .

Now let’s install the virtual environment package for the python.

Open cmd/terminal and type the following command

pip install virtualenv

2) Set up New Django Project

Open the cmd/terminal, go to your desired directory.

cd Desktop

Create the new folder for our project.

mkdir new-folder
cd new-folder

Create the new virtual environment.

python -m venv myenv

myenv folder will be created inside the new-folder.

Now let’s activate our env.

myenv\Scripts\Activate

Install the Django and django-allauth into our environment.

pip install django
pip install django-allauth

Now let’s create a new django project and core app.

django-admin startproject MyProject
cd MyProject
python manage.py startapp core

At this point directory structure of our project should look like this.

diretory structure of the project

3) Edit Settings.py

Now let’s tell django about our newly installed django-allauth and core app.

Open MyProject/MyProject/settings.py

Add following to the INSTALLED_APPS list

    # All-Auth 
    'django.contrib.sites',
    'allauth',
    'allauth.account',

    # Our Apps
    'core',

So final installed apps should look like this.

# Application definition
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    # All-Auth 
    'django.contrib.sites',
    'allauth',
    'allauth.account',

    # Our Apps
    'core',
]

Also add some more settings at the end.

AUTHENTICATION_BACKENDS = [
    'django.contrib.auth.backends.ModelBackend',
    'allauth.account.auth_backends.AuthenticationBackend',
]

SITE_ID = 1

That’s it for the allauth settings.

Now to tell django where to save and look for user uploaded files. we have to add media routes inside our settings.py file

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

similarly, we have to tell django where to look for template files.

Add the below line to the templates dictionary inside the settings.py

os.path.join(BASE_DIR,"templates")

It should look like this.

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR,"templates")],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

4) Edit urls.py

Let’s add allauth authentication urls to our project.

Open MyProject/MyProject/urls.py

First import the path and include from django.urls.

from django.urls import path,include

then add the allauth urls to the urlpatterns list.

path('accounts/', include('allauth.urls')),

so final urls.py should look like this:

from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('accounts/', include('allauth.urls')),
]

5) Create a custom user model

Let’s create a custom user model that extends the default user model and add two extra fields to it.

open the MyProject/core/models.py

And add the following code to it.

from django.db import models
from django.contrib.auth.models import AbstractUser

class CustomUser(AbstractUser):
    pass


class DocumentModel(models.Model):
	user = models.OneToOneField(CustomUser,on_delete = models.CASCADE)
	document = models.FileField(upload_to='documents/')

	def __str__(self):
		return f"{self.user.username}'s Document"


class DescriptionModel(models.Model):
	user = models.OneToOneField(CustomUser,on_delete = models.CASCADE)
	description = models.CharField(max_length=100)

	def __str__(self):
		return f"{self.user.username}'s Description"

Code Explanation :

  • We first imported the models and AbstractUser from the Django.
  • Then we create our new custom user model that inherits from AbstractUser.
  • Then we create the DocumentModel that has one to one relation with the abstract user and has the document field.
  • Then we create the DescriptionModel that has one to one relation with the abstract user and has the description char field.

Lets add our newly created models to admin.py file

open MyProject/core/admin.py

and add the following code to it.

from django.contrib import admin
from .models import *

admin.site.register(CustomUser)
admin.site.register(DocumentModel)
admin.site.register(DescriptionModel)

Now to finally tell django about our custom user model add the following setting to MyProject/MyProject/settings.py

AUTH_USER_MODEL = 'core.CustomUser'

Now let’s finally run migrations

python manage.py makemigrations core
python manage.py core

6) Create A Custom SignUp Form

Let’s create a custom sign up form.

Create a new file named forms.py inside the MyProject/core folder. and add the following code to it.

from allauth.account.forms import SignupForm
from django import forms
from .models import DocumentModel,DescriptionModel

from django.core.validators import FileExtensionValidator

def validate_file_extension(value):
	"""
	Validator function to check uploaded file should be a pdf file
	"""
	return FileExtensionValidator(["pdf"])(value)


class SimpleSignupForm(SignupForm):
	# Our custom form inherits from the signup form

    description = forms.CharField(max_length=100) # Create a charfield

    # File Field with only pdf validator
    document = forms.FileField(validators=[validate_file_extension])

    def save(self, request):
    	# first call save of parent class
        user = super(SimpleSignupForm, self).save(request)

        # Now create new models
        DocumentModel.objects.create(user = user,document = self.cleaned_data['document'])
        DescriptionModel.objects.create(user = user,description = self.cleaned_data['description'])
        return user

Code Explanation :

  • First, we created a validator function that only allows files of pdf format only.
  • Then we defined our custom sign up form that inherits from the Allauth signup form
  • Then we defined our two custom fields(Description, file field).
  • Finally, we override the save method of parent class where we first call the parent save() method then create an instance of DocumentModel and DescriptionModel.

Now to tell all-auth that we have a custom sign up form add the following line to MyProject/MyProject/settings.py

ACCOUNT_FORMS = {'signup': 'core.forms.SimpleSignupForm'}

7) Edit Templates

If you run the server right now and head over localhost:8000/accounts/signup and fill the form. It will not accept the file. it’s because we have n’t specified ‘enctype’ inside our form.

So to accept the pdf files we have to edit our signup template.

First copy the templates folder from new-folder/myenv/Lib/site-packages/allauth/templates and paste it inside new-folder/Myproject.

Then, open the new-folder/MyProject/templates/account/signup.html and add the enctype to its form.

< form class="signup" id="signup_form" method="post" action="{% url 'account_signup' %}"  enctype="multipart/form-data">

Final sign up template will look like this

{% extends "account/base.html" %}

{% load i18n %}

{% block head_title %}{% trans "Signup" %}{% endblock %}

{% block content %}
< h1>{% trans "Sign Up" %}< /h1>

< p>{% blocktrans %}Already have an account? Then please < a href="{{ login_url }}">sign in < /a>.{% endblocktrans %} < /p>

< form class="signup" id="signup_form" method="post" action="{% url 'account_signup' %}"  enctype="multipart/form-data">
  {% csrf_token %}
  {{ form.as_p }}
  {% if redirect_field_value %}
  < input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
  {% endif %}
  < button type="submit">{% trans "Sign Up" %} »  < /button>
< /form>

{% endblock %}

That’s it. Finally run the server.

python manage.py runserver

Head over to http://localhost:8000/accounts/signup fill the form and click sign up.

You should see the user redirected to the profile page and pdf document being saved inside the new-folder/MyProject/media/documents/.

Leave a Reply