Reputation: 5263
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
Reputation: 1
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
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
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
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