回到顶部

Contents

Django admin: How to insert Inline between fields

The positioning of the TabularInline, it would seem to be a simple task. Most of Django developers face this challenge when the size of ModelAdmin becomes bigger than the several "screens" and ordering becomes critical for the usability.

Problem

For example let's create an app called with two models: and .sample_appModelAImage

models.py

from django.db import models
from django.utils.translation import ugettext_lazy as _


class ModelA(models.Model):
    title = models.CharField(
        verbose_name=_('title'),
        max_length=255,
        blank=True,
        null=True,
    )

    description = models.TextField(
        verbose_name=_('Description'),
        blank=True,
        null=True,
    )

    def __str__(self):
        return self.title


class Image(models.Model):
    model_a = models.ForeignKey(
        'ModelA',
        verbose_name='model A'
    )
    image = models.ImageField(
        upload_to='images/'
    )

Now let's create simple TabularInline and try to insert this in the following order:

  1. title
  2. ImageInline
  3. description

admin.py

from django.contrib import admin

from .models import ModelA, Image


class ImageInline(admin.TabularInline):
    model = Image


class ModelAAdmin(admin.ModelAdmin):
    fields = (
        'title',
        ImageInline,
        'description'
    )

admin.site.register(ModelA, ModelAAdmin)

As you can guess we can't do this and will get an error:

TypeError at /admin/sample_app/modela/add/
sequence item 0: expected str instance, MediaDefiningClass found
....

Solution

In all variations of this problem at StackOverflow, we can find an abstract answer like: "customize ", but for some reason, I can't find any of the ready to use snippets, so here is my own.change_form.html

admin.py

To start let's define the class variable in the , and set that we want to see that inline after the field "title" and put to the appropriate list.insert_afterImageInlineImageInline

from django.contrib import admin

from .models import ModelA, Image


class ImageInline(admin.TabularInline):
    model = Image
    insert_after = 'title'


class ModelAAdmin(admin.ModelAdmin):
    fields = (
        'title',
        'description'
    )
    inlines = [
        ImageInline,
    ]
    change_form_template = 'admin/custom/change_form.html'

    class Media:
        css = {
            'all': (
                'css/admin.css',
            )
        }


admin.site.register(ModelA, ModelAAdmin)

Now let's create our own template for the editing form using the basic template. We need to customize cycles that are responsible for the rendering inlines:

templates/admin/custom/change_form.html

{% extends "admin/change_form.html" %}
{% load i18n admin_urls static admin_modify %}
{% block field_sets %}
    {% for fieldset in adminform %}
        {#  Custom fieldsets template #}
        {% include "admin/custom/fieldset.html" with inline_admin_formsets=inline_admin_formsets %}
    {% endfor %}
{% endblock %}

{# Filter inlines that where already rendered to avoid duplication #}
{% block inline_field_sets %}
    {% for inline_admin_formset in inline_admin_formsets %}
        {% if not inline_admin_formset.opts.insert_after %}
            {% include inline_admin_formset.opts.template %}
        {% endif %}
    {% endfor %}
{% endblock %}

And update the fieldsets template which will be rendering the inlines.

templates/admin/custom/fieldset.html

<fieldset class="module aligned {{ fieldset.classes }}">
    {% if fieldset.name %}<h2>{{ fieldset.name }}</h2>{% endif %}
    {% if fieldset.description %}
        <div class="description">{{ fieldset.description|safe }}</div>
    {% endif %}
    {% for line in fieldset %}
        <div class="form-row{% if line.fields|length_is:'1' and line.errors %} errors{% endif %}{% if not line.has_visible_field %} hidden{% endif %}{% for field in line %}{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% endfor %}">
            {% if line.fields|length_is:'1' %}{{ line.errors }}{% endif %}
            {% for field in line %}
                <div{% if not line.fields|length_is:'1' %}
                    class="field-box{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% if not field.is_readonly and field.errors %} errors{% endif %}{% if field.field.is_hidden %} hidden{% endif %}"{% elif field.is_checkbox %}
                    class="checkbox-row"{% endif %}>
                    {% if not line.fields|length_is:'1' and not field.is_readonly %}{{ field.errors }}{% endif %}
                    {% if field.is_checkbox %}
                        {{ field.field }}{{ field.label_tag }}
                    {% else %}
                        {{ field.label_tag }}
                        {% if field.is_readonly %}
                            <div class="readonly">{{ field.contents }}</div>
                        {% else %}
                            {{ field.field }}
                        {% endif %}
                    {% endif %}
                    {% if field.field.help_text %}
                        <div class="help">{{ field.field.help_text|safe }}</div>
                    {% endif %}
                </div>
                {# Insert inlines after the field #}
                {% for inline_admin_formset in inline_admin_formsets %}
                        {% if inline_admin_formset.opts.insert_after == field.field.name %}
                            {% include inline_admin_formset.opts.template %}
                    {% endif %}
                {% endfor %}
            {% endfor %}
        </div>
    {% endfor %}
</fieldset>

After that we can "enjoy" of the following:

Still only need to fix some CSS, to protect the perfectionists from an eye bleeding:

static/css/admin.css

.form-row .inline-group {
    margin-top: 30px;
}

Result

Here is the final result of our work and the whole project that you can "touch" on the Github.

English is not my mother tongue so if you have found any mistakes or know how to improve this text please contact me directly.

If I've saved a little of your time please share this post on the social networks. Thanks!

from: Django admin: How to insert Inline between fields - Anton Linevych's blog (linevi.ch)


^_^
请喝咖啡 ×

文章部分资料可能来源于网络,如有侵权请告知删除。谢谢!

前一篇: 安卓手机摄像头变成电脑(windows)的摄像头(DroidCam 摄像头)
下一篇: 理财计算器
captcha