Kevin Renskers
Kevin Renskers

Reputation: 5892

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

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:

{% 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:

enter image description here

Clicking on that button opens a new page, which I created with this code:

@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:

{% 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

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.

Upvotes: 0

Views: 60

Answers (1)

binaryEcon
binaryEcon

Reputation: 523

One year later, but what about this (tested with Django 5.1.1):

{% extends "admin/base_site.html" %}

{# added static to load #}
{% load i18n admin_urls static %}

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


{% block content %}
    <form action="." method="post">
        {% csrf_token %}

        {# generate form #}
        <div>
            <fieldset class="module aligned">
                {% for field in form %}
                    <div class="form-row">
                        <div>
                            <div class="flex-container">
                                {{ field.label_tag  }}
                                {{ field }}
                            </div>
                        </div>
                    </div>
                {% endfor %}
            </fieldset>
        </div>
        <input type="submit" value="Generate codes">
    </form>
{% endblock %}

Upvotes: 1

Related Questions