3
votes

I'm trying to make fully dynamic form in django admin. Found solution in goole like this and this. But this solution does not work for me. For example this code:

class DeviceAdmin(admin.ModelAdmin):
    form = MyDeviceAdminForm
    def get_fieldsets(self, request, obj=None):
        fieldsets = super(DeviceAdmin, self).get_fieldsets(request, obj)
        fieldsets[0][1]['fields'] += ('foo',)
        return fieldsets

class MyDeviceAdminForm(forms.ModelForm):
    class Meta:
        model = Device
    def __init__(self, *args, **kwargs):
        super(MyDeviceAdminForm, self).__init__(*args, **kwargs)
        self.fields['foo'] = forms.IntegerField(label="foo")

Got this error: "Unknown field(s) (item_type) specified for Device. Check fields/fieldsets/exclude attributes of class DeviceAdmin."

And i can't find solution for this. I understand that i need to define in some other place foo field, but does not know where.

2
You need to specify which fields to use, or exclude the ones you don't want to use: docs.djangoproject.com/en/dev/topics/forms/modelforms/… - rnevius
@rnevius thank you! But i can't do that because i don't know filed names to set it explicitly in Meta of ModelForm. That is my problem - i want to generate some fields depend on initial of another field. And i can't declare them in ModelForm init method because it is too late for ModelAdmin to recognize this changes and final html page will be out of this fields. - valex

2 Answers

3
votes

Django > 1.4 introduced a change that causes this breakage. Basically get_fieldsets is called before the form factory, and then the factory complains about the extra field you introduce in get_fieldsets. Luckily get_fieldsets is called more than once, presenting opportunity to subvert the flow. My solution is to add a marker attribute to the request while it passes through the form mechanism:

def get_fieldsets(self, request, obj=None):
    fieldsets = super(DeviceAdmin, self).get_fieldsets(request, obj)
    if hasattr(request, "_gfs_marker"):
        fieldsets[0][1]['fields'] += ('foo',)
    setattr(request, "_gfs_marker", 1)
    return fieldsets
0
votes

If it is just the field name visible to the user that you want to alter (the internal field name will not be visible anyway), you could use a class factory:

def get_MyDeviceAdminForm(field_name):
    class MyDeviceAdminForm(forms.ModelForm):
        my_new_field_internal = forms.CharField(label=field_name)
        class Meta:
            model = InfoLog
    return MyDeviceAdminForm
class DeviceAdmin(admin.ModelAdmin):
    form = get_MyDeviceAdminForm("Verbose name of the new field")

This works in Django 1.6.