Django-allauth Template Customization
TLDR: My notes on how to customize various templates used by django-allauth.
django-allauth
provides default templates for many scenarios such as authentication, registration, password reset, etc. These templates can be found at https://github.com/pennersr/django-allauth/tree/main/allauth/templates. You can see that there are quite a few of them. So in this notes, I just focus on login
template used for username/password authentication. The approach to customize other templates will be similar.
As of this writing Jan 2024, the current implementation has the following structure:
In Django, DIRS is searched before APP_DIRS, so to override any of the allauth’s default templates, you just need to create your overrides in the same folder structure and adds the overrides to DIRS.
Example 1: to override login.html I would need to do the followings:
- create a new login.html in
BASE_DIR / 'templates' / 'account' / 'login.html'
- adds BASE_DIR / ‘templates’ to settings.py like so
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
TEMPLATES = [
{
# ...
"DIRS": [
BASE_DIR / "templates"
],
"APP_DIRS": True,
# ...
},
]
Why does the folder structure start with BASE_DIR/ 'templates'
? That’s because allauth’s current implementation looks up login.html in [root]/templates/account
(reference the diagram above)
Example 2: to override button, I would need to do the followings:
- create button.html in
BASE_DIR/'templates'/'allauth'/'elements'/'button.html'
- also make sure
BASE_DIR/'templates
is listed in settings.py > TEMPLATES > DIRS
Example 3: to override the entrance layout, I would need to do the followings
- create entrance.html in
BASE_DIR/'allauth'/'layout'/'entrance.html'
- also make sure
BASE_DIR/'templates
is listed in settings.py > TEMPLATES > DIRS
Override plus Reusing built-in templates
You can mix and match your custom templates with built-in templates. By this I mean in your override, you can still reference built-in templates.
For example, if you decide to override login.html so that you can extend your own base template, you can still reference the built-in form
element if you choose to do so.
Element
Initially when I was looking at allauth’s source, I was very confused by the use of element
, slot
, and attrs
. For example:
- This is how
fields
element is used
{% element form form=form method="post" action=login_url tags="entrance,login" %}
{% slot body %}
{% csrf_token %}
{% element fields form=form unlabeled=True %}
{% endelement %}
{% if redirect_field_value %}
<input type="hidden"
name="{{ redirect_field_name }}"
value="{{ redirect_field_value }}" />
{% endif %}
{% endslot %}
{% slot actions %}
{% element button type="submit" tags="prominent,login" %}
{% trans "Sign In" %}
{% endelement %}
{% endslot %}
{% endelement %}
- This is how it’s defined:
<form method="{{ attrs.method }}" action="{{ attrs.action }}">
{% slot body %}
{% endslot %}
{% slot actions %}
{% endslot %}
</form>
For anyone like me who’s more experienced with js frameworks such as React, the above snippets are equivalent to the following
// how to use form
<FormComponent
form={form}
method={"post"}
action={login_url}
tags={"entrance, login"}
>
<BodyComponent />
<ActionComponent />
</FormComponent>;
// how the FormComponent is defined
type Props = {
form: DjangoForm,
method: "post" | "get",
action: string,
tags: string,
};
const FormComponent = (attrs: Props) => {
const method = attrs.method;
const action = attrs.action;
// ..
};
I guess the most confusing thing to me was the attrs
naming. As a n00b, at a first glance I thought it references some context variable passed to the template. But no, it’s just “props” passed by the template that uses the element. In hindsight, it’s kind of obvious :)
Customize template per layout
allauth
also has another trick up its sleeve for overriding elements such as form, h1 etc. per layout.
For each element in allauth/elements/
that is referenced in another template that extends a layout from allauth/layouts
, you can create an override by creating a file named <element>__<layout>.html
.
For example, you can override h1
element used in login.html
, by creating h1__entrance.html
.
The allauth’s code that implements this feature is in https://github.com/pennersr/django-allauth/blob/main/allauth/templatetags/allauth.py#L74. This looks for layout information in extends
tags, or {%include%}
with page_layout
. There’s a line that also looks for layout information using layout_context
key, but I was not able to find any places where such key has been used. The code comment mentions it’s supposed to be used in {%element%}
tag.
Layout
Currently there are 2 built-in layouts:
- entrance.html
- manage.html
In the context of regular accounts, i.e. username/password:
- Entrance layout is extended by authentication pages (signin, signout).
- Manage layout is extended by password and email management pages that allow you to reset your password or update your email addresses.
Hopefully you find these notes useful as you work on customizing allauth’s UI!