3

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.

Community
  • 1
  • 1
valex
  • 5,163
  • 2
  • 33
  • 40
  • You need to specify which fields to use, or exclude the ones you don't want to use: https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#selecting-the-fields-to-use – rnevius Nov 11 '14 at 10:40
  • @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 Nov 11 '14 at 10:58

2 Answers2

3

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
hedleyroos
  • 320
  • 3
  • 9
  • I am in the same situation, and unfortunately I can not understand how you manage to solve it. Would you mind explaining a little bit further? – mufasa Aug 12 '15 at 01:55
  • @mufasa, The ModelAdmin now calls `get_fieldsets()` twice, once before the form is initialized. If you have a custom form whose `__init__()` defines custom fields, then the first call to `get_fieldsets()` will throw this error, because as far as it's concerned, any references to your custom fields are invalid. This fix removes all custom field references for the first call, but adds them to the second, after they've been defined. It's a bit of a hack, but sadly, it seems to be the only way to support custom form fields in Django admin. – Cerin Jul 05 '17 at 18:59
  • Years later and this just saved the scruff on my bum. Big ups – Ian Price May 01 '19 at 22:16
0

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.

ger.s.brett
  • 3,267
  • 2
  • 24
  • 30
  • Thank you, but here you define extra field statically and i need to define they dynamically, they names and initial values are define by the basic fields. And as i discovered django admin can not do this. So i just write my own view. Thank you a lot! – valex Nov 17 '14 at 20:31