8

I have a model which is accessible through the Django admin area, something like the following:

# model
class Foo(models.Model):
    field_a = models.CharField(max_length=100)
    field_b = models.CharField(max_length=100)

# admin.py
class FooAdmin(admin.ModelAdmin):
    pass

Let's say that I want to show field_a and field_b if the user is adding an object, but only field_a if the user is editing an object. Is there a simple way to do this, perhaps using the fields attribute?

If if comes to it, I could hack a JavaScript solution, but it doesn't feel right to do that at all!

John McCollum
  • 5,162
  • 4
  • 34
  • 50

2 Answers2

6

You can create a custom ModelForm for the admin to drop the field in the __init__

class FooForm(forms.ModelForm):
    class Meta(object):
        model = Foo

    def __init__(self, *args, **kwargs):
        super(FooForm, self).__init__(*args, **kwargs)
        if self.instance and self.instance.pk:
            # Since the pk is set this is not a new instance
            del self.fields['field_b']

class FooAdmin(admin.ModelAdmin):
    form = FooForm

EDIT: Taking a hint from John's comment about making the field read-only, you could make this a hidden field and override the clean to ensure the value doesn't change.

class FooForm(forms.ModelForm):
    class Meta(object):
        model = Foo

    def __init__(self, *args, **kwargs):
        super(FooForm, self).__init__(*args, **kwargs)
        if self.instance and self.instance.pk:
            # Since the pk is set this is not a new instance
            self.fields['field_b'].widget = forms.HiddenInput()

    def clean_field_b(self):
        if self.instance and self.instance.pk:
            return self.instance.field_b
        else:
            return self.cleaned_data['field_b']  
Mark Lavin
  • 24,664
  • 5
  • 76
  • 70
  • Thanks for your answer! It looks like it should work, but I get a TemplateSyntaxError when editing an object - "Key 'field_b' not found in Form". Any idea how that might be fixed? – John McCollum May 19 '10 at 15:49
  • I got round this by changing the field to a CharField (it was initially a select list) and marking it as readonly - this is fine for my purposes. Something like: self.fields['field_b'] = forms.CharField(initial="initial value"); self.fields['field_b'].widget.attrs['readonly'] = True – John McCollum May 19 '10 at 16:03
  • Crap I forgot that the admin uses the ModelAdmin fieldset rather than the fields on the model to generate the template but I'm glad you got it worked out. – Mark Lavin May 19 '10 at 16:10
  • No worries. I played with swapping it for a hidden field too, but it leaves the label to the left, which is a little ugly. :) Thanks again for your help! – John McCollum May 19 '10 at 17:37
  • For reference, if one DOES want to create a custom ModelForm, it is not sufficient to specify the form in the ModelAdmin as in the first example here. You must override the get_form() method of ModelAdmin, as described here: http://stackoverflow.com/questions/4291516/dynamic-forms-in-django-admin/4292564#4292564 – rych Aug 30 '11 at 21:28
  • For reference you can find the Django docs on custom validation in the admin: https://docs.djangoproject.com/en/1.3/ref/contrib/admin/#adding-custom-validation-to-the-admin – Mark Lavin Aug 30 '11 at 22:37
0

You can also do the following

class FooAdmin(admin.ModelAdmin)
    def change_view(self, request, object_id, extra_context=None):       
        self.exclude = ('field_b', )
        return super(SubSectionAdmin, self).change_view(request, object_id, extra_context)

Taken from here Django admin: exclude field on change form only

Community
  • 1
  • 1
Abhishek J
  • 2,386
  • 2
  • 21
  • 22