Skip to content

Instantly share code, notes, and snippets.

@agusmakmun
Last active November 13, 2024 03:59
Show Gist options
  • Save agusmakmun/e1254b90405db6d4fd984165f1e5c63f to your computer and use it in GitHub Desktop.
Save agusmakmun/e1254b90405db6d4fd984165f1e5c63f to your computer and use it in GitHub Desktop.
Modify the default Django admin login form without modifying the entire models to new AdminSite

For example we want to modify the default login form with only add new reCAPTCHA feature. Ya, because previously I have an issue about brute forcing password to this page. So, we should add it reCAPTCHA to fix the fuln issue.

Option 1

We can just overwrite the default admin site from django.

  1. myapp/forms.py
from django import forms
from django.conf import settings
from django.contrib.auth.forms import AuthenticationForm

from captcha.fields import ReCaptchaField
from captcha.widgets import ReCaptchaV2Checkbox


class AuthAdminForm(AuthenticationForm):

    if settings.DEBUG:
        captcha = ReCaptchaField(widget=ReCaptchaV2Checkbox(
            attrs={
                'data-theme': 'light',
                'data-size': 'normal',
                # 'style': ('transform:scale(1.057);-webkit-transform:scale(1.057);'
                #           'transform-origin:0 0;-webkit-transform-origin:0 0;')
            }
        ))
  1. templates/myapp/admin/login.html

The point in this template is, we just add a new reCAPTCHA field. Here is the default: https://github.com/django/django/blob/main/django/contrib/admin/templates/admin/login.html

{% extends "admin/base_site.html" %}
{% load i18n static %}

{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/login.css" %}">
{{ form.media }}
{% endblock %}

{% block bodyclass %}{{ block.super }} login{% endblock %}

{% block usertools %}{% endblock %}

{% block nav-global %}{% endblock %}

{% block nav-sidebar %}{% endblock %}

{% block content_title %}{% endblock %}

{% block breadcrumbs %}{% endblock %}

{% block content %}
{% if form.errors and not form.non_field_errors %}
<p class="errornote">
{% if form.errors.items|length == 1 %}{% translate "Please correct the error below." %}{% else %}{% translate "Please correct the errors below." %}{% endif %}
</p>
{% endif %}

{% if form.non_field_errors %}
{% for error in form.non_field_errors %}
<p class="errornote">
    {{ error }}
</p>
{% endfor %}
{% endif %}

<div id="content-main">

{% if user.is_authenticated %}
<p class="errornote">
{% blocktranslate trimmed %}
    You are authenticated as {{ username }}, but are not authorized to
    access this page. Would you like to login to a different account?
{% endblocktranslate %}
</p>
{% endif %}

<form action="{{ app_path }}" method="post" id="login-form">{% csrf_token %}
  <div class="form-row">
    {{ form.username.errors }}
    {{ form.username.label_tag }} {{ form.username }}
  </div>
  <div class="form-row">
    {{ form.password.errors }}
    {{ form.password.label_tag }} {{ form.password }}
    <input type="hidden" name="next" value="{{ next }}">
  </div>
  <!-- start: this is the point --->
  <div class="form-row">
    {{ form.captcha.errors }}
    {{ form.captcha.label_tag }} {{ form.captcha }}
  </div>
  <!-- end: this is the point --->
  {% url 'admin_password_reset' as password_reset_url %}
  {% if password_reset_url %}
  <div class="password-reset-link">
    <a href="{{ password_reset_url }}">{% translate 'Forgotten your password or username?' %}</a>
  </div>
  {% endif %}
  <div class="submit-row">
    <input type="submit" value="{% translate 'Log in' %}">
  </div>
</form>

</div>
{% endblock %}
  1. myproject/urls.py
from django.contrib import admin
from django.urls import include, path

from myapp.forms import AuthAdminForm

admin.autodiscover()
admin.site.login_form = AuthAdminForm
admin.site.login_template = 'account/admin/login.html'

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

Just it.


Option 2

Overwrite the /admin/login/ url with new views.

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

from myapp.views import AuthAdminView

urlpatterns = [
    path('admin/login', AuthAdminView.as_view(), name='login'),
    path('admin/', admin.site.urls),
    ...
]
@ahmhesham
Copy link

I get this error

AttributeError at /admin/login/
'MyLoginForm' object has no attribute 'get_user'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment