I want to have additional fields regarding value of one field. Therefor I build a custom admin form to add some new fields.
Related to the blogpost of jacobian 1 this is what I came up with:
class ProductAdminForm(forms.ModelForm):
class Meta:
model = Product
def __init__(self, *args, **kwargs):
super(ProductAdminForm, self).__init__(*args, **kwargs)
self.fields['foo'] = forms.IntegerField(label="foo")
class ProductAdmin(admin.ModelAdmin):
form = ProductAdminForm
admin.site.register(Product, ProductAdmin)
But the additional field 'foo' does not show up in the admin. If I add the field like this, all works fine but is not as dynamic as required, to add the fields regarding the value of another field of the model
class ProductAdminForm(forms.ModelForm):
foo = forms.IntegerField(label="foo")
class Meta:
model = Product
class ProductAdmin(admin.ModelAdmin):
form = ProductAdminForm
admin.site.register(Product, ProductAdmin)
So is there any initialize method that i have to trigger again to make the new field working? Or is there any other attempt?
You can create dynamic fields and fieldset using the form meta class. Sample code is given below. Add the loop logic as per you requirements.
The accepted answer above worked in older versions of django, and that's how I was doing it. This has now broken in later django versions (I am on 1.68 at the moment, but even that is old now).
The reason it is now broken is because any fields within fieldsets you return from ModelAdmin.get_fieldsets() are ultimately passed as the fields= parameter to modelform_factory(), which will give you an error because the fields on your list do not exist (and will not exist until your form is instantiated and its __ init __ is called).
In order to fix this, we must override ModelAdmin.get_form() and supply a list of fields that does not include any extra fields that will be added later. The default behavior of get_form is to call get_fieldsets() for this information, and we must prevent that from happening:
Stephan's answer is elegant, but when I used in in dj1.6 it required the field to be a tuple. The complete solution looked like this:
I for a long time could not solve a problem with dynamic addition of fields. The solution "little_birdie" really works. Thank you Birdie)) The only nuance is: "Self.declared_fieldsets" should be replaced with "self.fieldsets".
I used version 1.10. Perhaps something has changed.
If someone finds an even simpler and elegant solution, show here.
Thanks to all )))
While Jacob's post might work all right for regular
ModelForm
s (even though it's more than a year and a half old), the admin is a somewhat different matter.All the declarative way of defining models, forms ModelAdmins and whatnot makes heavy use of metaclasses and class introspection. Same with the admin – when you tell a
ModelAdmin
to use a specific form istead of creating a default one, it introspects the class. It gets the list of fields and other stuff from the class itself without instantiating it.Your custom class, however, does not define the extra form field at class level, instead it dynamically adds one after it has been instantiated – that's too late for the
ModelAdmin
to recognize this change.One way to go about your problem might be to subclass
ModelAdmin
and override itsget_fieldsets
method to actually instantiate theModelForm
class and get the list of fields from the instance instead of the class. You'll have to keep in mind, though, that this might be somewhat slower than the default implementation.This works for adding dynamic fields in Django 1.9.3, using just a ModelAdmin class (no ModelForm) and by overriding
get_fields
. I don't know yet how robust it is: