Aziz Alfoudari
Aziz Alfoudari

Reputation: 5263

Django - Add an extra field to an inline form in admin interface

Suppose I have the following in admin.py:

class ImageInline(admin.TabularInline):
    model = Image

class ObjectAdmin(admin.ModelAdmin):
    inlines = [ ImageInline, ]

How do I add an extra field to ImageInline that is not a field in Image model?

Upvotes: 4

Views: 8122

Answers (4)

Create "CustomImageForm" class with "extra_field" and set it to "ImageInline" class as shown below:

# "admin.py"

from django.contrib import admin
from django import forms
from .models import Image, ObjectAdmin

# Here
class CustomImageForm(forms.ModelForm):
    extra_field = forms.CharField()
    
class ImageInline(admin.TabularInline):
    model = Image
    form = CustomImageForm # Here

@admin.register(ObjectAdmin)
class ObjectAdmin(admin.ModelAdmin):
    inlines = [ImageInline,]

Upvotes: 0

Nishita Giduturi
Nishita Giduturi

Reputation: 11

class ReactionInline(admin.StackedInline):

    model = Reaction

    def formfield_for_dbfield(self, db_field, request, **kwargs):

        if db_field.name == "reaction":

            from fb_post.constants.enums import Reactions
            select_choices = [('None', None)] + Reactions.get_list_of_tuples()

            kwargs['widget'] = forms.Select(choices=select_choices) # the other case for ModelAdmin "kwargs['widget'].choices = select_choices" this would work

        return super(ReactionInline, self).formfield_for_dbfield(db_field, request, **kwargs)

For adding dropdown menu feature for an enum field of app model(say Reaction) on admin panel, for class extending StackedInline or TabularInline class; this would work. The solution for the other case, class extending ModelAdmin is also given in comment

Upvotes: 0

raratiru
raratiru

Reputation: 9616

8 years after, the accepted answer will not work.

For example to render custom form fields in MyInlineForm in the following setup in Django-2.2.3:

class MyInlineAdmin(admin.StackedInline):
    model = MyInlineModel
    form = MyInlineForm

@admin.register(MyModelAdmin)
class MyModelAdmin(admn.ModelAdmin):
    inlines = (MyInlineAdmin,)

Inspired from @santhoshnsscoe, this can be achieved by overriding ModelFormMetaclass.__new__:

Solution 1:

from django.forms.models import ModelFormMetaclass


class MyModelFormMetaclass(ModelFormMetaclass):
    def __new__(cls, name, bases, attrs):
        for field in ['test_1', 'test_2', 'test_3']:
            attrs[field] = forms.CharField(max_length=30)
        return super().__new__(cls, name, bases, attrs)


class MyInlinelForm(forms.ModelForm, metaclass=MyModelFormMetaclass):
    class Meta:
        model = MyInlineModel
        fields = '__all__'

Reviewing the relevant code, ModelFormMetaclass inherits from DeclarativeFieldsMetaclass where attrs are passed to declared_fields of the form:

for key, value in list(attrs.items()):
    if isinstance(value, Field):
        current_fields.append((key, value))
        attrs.pop(key)
attrs['declared_fields'] = OrderedDict(current_fields)

Based on this observation, and following the relevant docs of ModelAdmin.get_formsets_with_inlines I tried to enrich the declared_fields using that method of ModelAdmin and it also worked:

Solution 2:

class MyInlineForm(forms.ModelForm):
    class Meta:
        model = MyInlineModel
        fields = '__all__'

class MyInlineAdmin(admin.StackedInline):
    model = MyInlineModel
    form = MyInlineForm

@admin.register(MyModelAdmin)
class MyModelAdmin(admn.ModelAdmin):
    inlines = (MyInlineAdmin,)

def get_formsets_with_inlines(self, request, obj=None):
    for inline in self.get_inline_instances(request, obj):
        if isinstance(inline, MyInlineAdmn):
            for field in ['test_1', 'test_2', 'test_3']:
                inline.form.declared_fields[field] = forms.CharField(max_length=30)

        yield inline.get_formset(request, obj), inline

Upvotes: 2

Timmy O'Mahony
Timmy O'Mahony

Reputation: 53971

The same way you would do it with a normal ModelAdmin. The InlineModelAdmin can accept a form attribute, it's mentioned in the docs. So create a custom form, add the extra fields you want and in your Inline:

class ImageInline(admin.TabularInline):
   model = Image
   form = MyCustomForm

Upvotes: 4

Related Questions