# Custom form in the Admin: how to make it look the same as Django's Admin forms?

**URL:** <https://forum.djangoproject.com/t/custom-form-in-the-admin-how-to-make-it-look-the-same-as-djangos-admin-forms/26899>\
**Category:** The Admin\
**Created:** [January 12, 2024, 8:50pm UTC](https://forum.djangoproject.com/t/custom-form-in-the-admin-how-to-make-it-look-the-same-as-djangos-admin-forms/26899 "2024-01-12T20:50:52Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![kevinrenskers](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kevinrenskers/32/28242_2.png) [@kevinrenskers](https://forum.djangoproject.com/u/kevinrenskers)\
**Post date:** [January 12, 2024, 8:50pm UTC](https://forum.djangoproject.com/t/custom-form-in-the-admin-how-to-make-it-look-the-same-as-djangos-admin-forms/26899/1 "2024-01-12T20:50:52Z")

</div>

I have a Django application which handles license codes, so there’s a model and a modeladmin, and that all works. But I want to add a custom page to the admin interface to generate a whole bunch of license codes and while everything is working, it looks really bad.

So first of all to get a new button in the top right, I have a custom `change_list.html` template for this specific model:

```html
{% extends "admin/change_list.html" %}

{% block object-tools-items %}
  {{ block.super }}
  <li><a href="generate/" class="addlink">Generate Codes</a></li>
{% endblock %}

```

This makes the extra button show up in the top right of the list of items. Clicking on that button opens a new page, which I created with this code:

```python
@admin.register(RedeemCode)
class RedeemCodeAdmin(admin.ModelAdmin):
    # [...the usual admin config...]

    def get_urls(self):
        return [
            path("generate/", self.admin_site.admin_view(self.generate_codes), name="generate-codes"),
        ] + super().get_urls()

    def generate_codes(self, request):
        class GenerateCodeForm(forms.Form):
            product = forms.ModelChoiceField(queryset=ProductVariant.objects.all())
            partner = forms.ModelChoiceField(queryset=Partner.objects.all())
            comment = forms.CharField()
            count = forms.IntegerField(min_value=1, initial=1)
            for_email = forms.CharField()
            export_csv = forms.BooleanField(required=False, label="Export generated codes to CSV")

        if request.method == "POST":
            form = GenerateCodeForm(request.POST)
            if form.is_valid():
                print(form.cleaned_data)
                return HttpResponseRedirect("/admin/shop/redeemcode/")

        context = dict(
            # Include common variables for rendering the admin template.
            self.admin_site.each_context(request),
            opts=RedeemCode._meta,
            title="Generate Codes",
            form=GenerateCodeForm(),
        )

        return TemplateResponse(request, "admin/shop/redeemcode/generate_codes.html", context)

```

And my `generate_codes.html` template:

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

{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% translate 'Home' %}</a>
&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
&rsaquo; {% translate 'Generate Codes' %}
</div>
{% endblock %}

{% block content %}
<form action="." method="post">
    {% csrf_token %}
    {{ form }}
    <input type="submit" value="Generate codes">
</form>
{% endblock %}

```

The result looks like this:

[![enter image description here](https://us1.discourse-cdn.com/flex026/uploads/djangoproject/original/3X/9/5/959f996742868697a38a3894e7edcd80bd7ae15a.png)](https://i.stack.imgur.com/F83KP.png)

It all works perfectly fine but that’s not very good looking. Compared to normal add/edit admin forms the labels don’t line up, text input fields are a lot smaller, the checkbox label is shown in front instead of after the checkbox, there’s no padding between the form and the submit button, etc.

So the question is how I can reuse the admin look and feel for a custom form on a custom page.

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [January 12, 2024, 9:00pm UTC](https://forum.djangoproject.com/t/custom-form-in-the-admin-how-to-make-it-look-the-same-as-djangos-admin-forms/26899/2 "2024-01-12T21:00:02Z")

</div>

That’s up to you to manage. You could look at how the various admin widgets are rendered and reuse _those_ templates. You can also fully customize your [form rendering](https://docs.djangoproject.com/en/5.0/topics/forms/#working-with-form-templates)

You could also take a look at [Crispy Forms](https://django-crispy-forms.readthedocs.io/en/latest/). That’s what we use and we’re really satisfied with it.

---

<div class="post-metadata">

**Author:** ![michalpokusa](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/michalpokusa/32/22964_2.png) [@michalpokusa](https://forum.djangoproject.com/u/michalpokusa)\
**Post date:** [October 10, 2024, 10:23pm UTC](https://forum.djangoproject.com/t/custom-form-in-the-admin-how-to-make-it-look-the-same-as-djangos-admin-forms/26899/3 "2024-10-10T22:23:00Z")

</div>

I know it has been a couple of months, but i encourage you to try [django-admin-action-forms](https://pypi.org/project/django-admin-action-forms/). I think it does exactly what you are looking for. The form page matches the Django admin style, without the need to manually extending templates etc.

Hope you find it useful! 🙂

---

<div class="post-metadata">

**Author:** ![kevinrenskers](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kevinrenskers/32/28242_2.png) [@kevinrenskers](https://forum.djangoproject.com/u/kevinrenskers)\
**Post date:** [October 12, 2024, 2:43pm UTC](https://forum.djangoproject.com/t/custom-form-in-the-admin-how-to-make-it-look-the-same-as-djangos-admin-forms/26899/4 "2024-10-12T14:43:33Z")

</div>

Sounds interesting, but isn’t this specifically (and only) for building admin actions? As in, actions that work on a selection of rows? I actually needed to create a form that’s not an admin action, not related to anything you first select, if that makes sense.

I will check out the project’s code to see how it matches the Django Admin styling, as that was the part I had problems with. Thanks!

---

<div class="post-metadata">

**Author:** ![michalpokusa](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/michalpokusa/32/22964_2.png) [@michalpokusa](https://forum.djangoproject.com/u/michalpokusa)\
**Post date:** [October 12, 2024, 3:07pm UTC](https://forum.djangoproject.com/t/custom-form-in-the-admin-how-to-make-it-look-the-same-as-djangos-admin-forms/26899/5 "2024-10-12T15:07:20Z")

</div>

I see what you mean. There is also a [django-no-queryset-admin-actions](https://pypi.org/project/django-no-queryset-admin-actions/) library, which provides a way to create actions without selecting any objects. From the code you provided, I believe you could add a button that would submit an empty form with action and csrf set, which would direct you to a action form page, the example is provided in README.

Both libs work together and are fully compatible with one another.

---

<div class="post-metadata">

**Author:** ![mountinash789](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/mountinash789/32/26602_2.png) [@mountinash789](https://forum.djangoproject.com/u/mountinash789)\
**Post date:** [January 30, 2025, 3:59pm UTC](https://forum.djangoproject.com/t/custom-form-in-the-admin-how-to-make-it-look-the-same-as-djangos-admin-forms/26899/6 "2025-01-30T15:59:52Z")

</div>

[kevinrenskers](https://forum.djangoproject.com/u/kevinrenskers) I had the same issue.  
I have managed to resolve it by doing this:

```auto
fieldsets = ((None, {"fields": ["user_1", "user_2"]}),)
context = dict(
    self.admin_site.each_context(request),
    opts=self.model._meta,
    title="Copy Permissions",
    form=helpers.AdminForm(
        CopyPermissionsForm(),
        list(fieldsets),
        {},
    )
)

```

I also have this in my html file.

```auto
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" href="{% static "admin/css/forms.css" %}">{% endblock %}

```
