Django-allauth Customizing form templates and adding CSS

Hello Internet pRoGraMmeRs. Default Django-allauth forms are so basic, with no style, and simple. So let’s see how to customize django-allauth forms, and templates and yes we can add our CSS classes into the form and make it beautiful. Let’s jump into the code.

We are continuing the previous tutorial: Django-allauth email authentication tutorial

Configuring Django

The django-allauth package doesn’t offer any configuration to link CSS files in the <head /> or add classes to the inputs, so you have to use Django’s templating system to override allauth’s built-in templates.

So first create templates directory if you’ve not created one. And now if you created virtual environment then find lib directory. Just open it and goto python[version]/site-packages/allauth/templates now open it and copy account directory and copy into our main templates direcory. We just overriding the django-allauth templates. The easiest way to get the markup from allauth’s templates is to visit the allauth GitHub templates directory.

Okay now, your templates directory looks like this, here I created home.html and base.html from the previous tutorial. I don’t know what your directory looks like but you can see the account directory here, make changes like this,

django-allauth customizing templates, override default templates

templates directory

Static file setup

In order to add CSS, we need to set up some stuff. Now create a static directory in root and then css/main.css, like following

adding static file, css file

static file, css

In addition to using a static/ directory inside your apps, you can define a list of directories (STATICFILES_DIRS) in your settings file where Django will also look for static files. So open settings.py and add these lines of code

STATIC_URL = "static/"

STATICFILES_DIRS = [ BASE_DIR / "static"] # new

Now add some CSS. Open main.css add this

*{
    margin:0;
    padding:0;
    box-sizing:border-box;
}

body{
    font-family: 'Poppins', sans-serif;
    background: #f1f1f1;
}

main{
    max-width: 768px;
    width: 100%;
    margin: 0 auto;
    padding: 0 1rem;
}

.form-input{
    border: 1px solid #ccc;
    border-radius: 4px;
    padding: 0.5rem;
    font-size: 1rem;
    width: 100%;
    margin-bottom: 1rem;
}

.btn{
    background: #333;
    color: #fff;
    border: none;
    padding: 0.5rem 1rem;
    border-radius: 4px;
    font-size: 1rem;
    cursor: pointer;
}

.mt-3{
    margin-top: 1rem;
}

.wrapper{
    width: 100%;
    max-width: 400px;
    margin: 0 auto;
    margin-top: 2rem;
    background: #fff;
    padding: 1rem;
    border-radius: 4px;

}

li{
    list-style: none;
    padding: 1rem;
    border: 1px solid #ccc;
    border-radius: 4px;
    background-color: #ccc;
    margin:1rem 0;
}

open templates/base.html and make these changes,

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Django-allauth tutorial</title>
    <link rel="stylesheet" href="{% static 'css/main.css' %}">
</head>
<body>
    <main>
        {% if messages %}
            <div>
                <ul>
                {% for message in messages %}
                    <li>{{message}}</li>
                {% endfor %}
                </ul>
            </div>
        {% endif %}
        
        {% block content %}
        
        {% endblock content %}
    </main>
</body>
</html>

I copied the message snippet from account/base.html

run server and see the home page, you noticed some changes, and also open the login page and you can notice that CSS is not applied here because that page has its own base.html.

Customizing Django-allauth templates

See you have two options modify and static file in account/base.html or replace base.html with our base.html inside every template in the account directory.

Here I’m doing long way, I’m choosing the second option 🗿. Open every file inside account directory and replace account/base.html with base.html. for example, in login.html

<!-- templates/account/login.html -->

{% extends "base.html" %} <!-- this change only -->

{% load i18n %}
{% load account socialaccount %}

{% block head_title %}{% trans "Sign In" %}{% endblock %}

{% block content %}

<h1>{% trans "Sign In" %}</h1>

{% get_providers as socialaccount_providers %}

{% if socialaccount_providers %}
<p>{% blocktrans with site.name as site_name %}Please sign in with one
of your existing third party accounts. Or, <a href="{{ signup_url }}">sign up</a>
for a {{ site_name }} account and sign in below:{% endblocktrans %}</p>

<div class="socialaccount_ballot">

  <ul class="socialaccount_providers">
    {% include "socialaccount/snippets/provider_list.html" with process="login" %}
  </ul>

  <div class="login-or">{% trans 'or' %}</div>

</div>

{% include "socialaccount/snippets/login_extra.html" %}

{% else %}
<p>{% blocktrans %}If you have not created an account yet, then please
<a href="{{ signup_url }}">sign up</a> first.{% endblocktrans %}</p>
{% endif %}

<form class="login" method="POST" action="{% url 'account_login' %}">
  {% csrf_token %}
  {{ form.as_p }}
  {% if redirect_field_value %}
  <input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
  {% endif %}
  <a class="button secondaryAction" href="{% url 'account_reset_password' %}">{% trans "Forgot Password?" %}</a>
  <button class="primaryAction" type="submit">{% trans "Sign In" %}</button>
</form>

{% endblock %}

Okay change it and see the output, so you’re CSS is applied but still kinda ugly right?

login page modified in django-allauth

replaced base.html

Adding CSS to Django-allauth forms

You can easily add CSS to buttons and containers but the problem is when the form is generated with {{ form.as_p }}

Method 1

It’s very simple and kinda hacky, inspect all the elements generated by {{ form.as_p }} and write CSS for them. You can wrap all forms with a div with the same CSS class to specify CSS to them separately.

Method 2

In order to specify the class in fields we need to create a custom form, how so? First create forms.py inside your app. If you are following from the previous tutorial we are creating forms.py inside core directory,

let’s override the form,

from allauth.account.forms import LoginForm
from django import forms

class CustomLoginForm(LoginForm):
    def __init__(self, *args, **kwargs):
        super(CustomLoginForm, self).__init__(*args, **kwargs)
        self.fields['login'].widget = forms.TextInput(attrs={'class': 'form-input', 'placeholder': 'Username'})
        self.fields['password'].widget = forms.PasswordInput(attrs={'class': 'form-input', 'placeholder': 'Password'})
        self.fields['login'].label = ''
        self.fields['password'].label = ''

You have to modify all forms like this. Here the all forms are listed: https://django-allauth.readthedocs.io/en/latest/forms.html

I also modified login.html

{% extends "base.html" %}

{% load i18n %}
{% load account socialaccount %}

{% block head_title %}{% trans "Sign In" %}{% endblock %}


  {% block content %}
  <div class="wrapper">


  <h1>{% trans "Sign In" %}</h1>

  {% get_providers as socialaccount_providers %}

  {% if socialaccount_providers %}
  <p>{% blocktrans with site.name as site_name %}Please sign in with one
  of your existing third party accounts. Or, <a href="{{ signup_url }}">sign up</a>
  for a {{ site_name }} account and sign in below:{% endblocktrans %}</p>

  <div class="socialaccount_ballot">

    <ul class="socialaccount_providers">
      {% include "socialaccount/snippets/provider_list.html" with process="login" %}
    </ul>

    <div class="login-or">{% trans 'or' %}</div>

  </div>

  {% include "socialaccount/snippets/login_extra.html" %}

  {% else %}
  <p>{% blocktrans %}If you have not created an account yet, then please
  <a href="{{ signup_url }}">sign up</a> first.{% endblocktrans %}</p>
  {% endif %}

  <form class="login" method="POST" action="{% url 'account_login' %}">
    {% csrf_token %}
    {{ form.as_p }}
    {% if redirect_field_value %}
    <input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
    {% endif %}
    <a class="button secondaryAction" href="{% url 'account_reset_password' %}">{% trans "Forgot Password?" %}</a>

    <div class="mt-3"><button class="primaryAction btn" type="submit">{% trans "Sign In" %}</button></div>
  </form>

</div>

  {% endblock %}

Open login page, it look like this.

Login page after adding css in django-allauth forms

Login page after adding css

Ummm yaa I’m not good in CSS, add your own and yes if you’re using any CSS framework like bootstrap, now you can add classes like this.

Method 3

Specify every form fields seperataly like {{ form.fieldname.errors }} in order to wrap those fields with div element. like this,

{% extends "base.html" %} 
{% load i18n %} 
{% load account socialaccount %} 

{%block head_title %}{% trans "Sign In" %}{% endblock %} 

{% block content %}

  <div class="account-form">
    <h1>{% trans "Login" %}</h1>

    {% get_providers as socialaccount_providers %} {% if socialaccount_providers %}
    <p>
      {% blocktrans with site.name as site_name %}Please sign in with one of your
      existing third party accounts. Or, <a href="{{ signup_url }}">sign up</a> for
      a {{ site_name }} account and sign in below:{% endblocktrans %}
    </p>

    <div class="socialaccount_ballot">
      <ul class="socialaccount_providers">
        {% include "socialaccount/snippets/provider_list.html" with process="login"%}
      </ul>

      <div class="login-or">{% trans 'or' %}</div>
    </div>

    {% include "socialaccount/snippets/login_extra.html" %} {% else %}
    <p>
      {% blocktrans %}If you have not created an account yet, then please
      <a href="{{ signup_url }}">sign up</a> first.{% endblocktrans %}
    </p>
    {% endif %}

    <form method="POST" action="{% url 'account_login' %}">
      {% csrf_token %} 
        
        {{ form.login }}
        <span class="text-error">{{ form.login.errors }}</span>

        {{ form.password }}
        <span class="text-error">{{ form.password.errors }}</span>
        
        <div style="display:flex; margin-bottom:10px; align-items:center">
            {{ form.remember}} &nbsp;
            <span style="margin-top:3px">Remember me</span>
        </div>
        <span class="text-error">{{ form.password.errors }}</span>

        {% if form.errors %}
        <ul class="text-error">
          {% for field in form %}
              {% for error in field.errors %}
                
                <li>{{ error|escape }}</li>
                  
              {% endfor %}
          {% endfor %}
          {% for error in form.non_field_errors %}
            
                <li>{{ error|escape }}</li>
              
          {% endfor %}
        </ul>
        {% endif %}

     

      {% if redirect_field_value %}
      
      <input
        type="hidden"
        name="{{ redirect_field_name }}"
        value="{{ redirect_field_value }}"
      />
      {% endif %}

      <button class="btn btn-primary" type="submit">{% trans "Login" %}</button>
      <a class="button secondaryAction" href="{% url 'account_reset_password' %}"
        >{% trans "Forgot Password?" %}</a
      >
    </form>
  </div>
{% endblock content%}

This code is not from this project, I copied from my another project where I used bootstrap and this method is works better with bootstrap.

official doc: https://django-allauth.readthedocs.io/

Okey hApPY coding (:

Previous tutorial: Django-allauth authentication tutorial

You may also like,