How to switch a Django form in admin environment depending on Model’s instant status

To switch a Django form in admin environment, depending on Model’s status or any arbitrary environmental criteria, you need to override get_form() method of ModelAdmin. The requirement behind switching form can vary from application to application. Most simple scenario can be that you need one form for adding new instances and another for editing existing instances of the same Model. Easiest way in an overridden get_form method is to check if ModelAdmin is being called for a new object, or an existing one. It is also naturally possible to check attributes in case of an existing Model, or to check any external value, condition in either cases. As a very bare bone sample please see following code.

#coding: UTF-8
from django.contrib import admin
from django import forms

from .models import Person

class PersonForm(forms.ModelForm):
    class Meta:
        model=Person
        fields=['first_name','last_name','password',]
        widgets= {
                'password': forms.PasswordInput(),
            }
    def clean(self):
        password=self.cleaned_data.get('password')
        fname=self.cleaned_data.get('first_name')
        lname=self.cleaned_data.get('last_name')
#
# ..........................................
#
        return self.cleaned_data

class PersonAdmin(admin.ModelAdmin):
#    form=PersonForm
    c_fields=(
            ('first_name','last_name',),
            )
    a_fields=(
            ('first_name','last_name',),
			('password',),
            )
    readonly_fields=('account',)
    def add_view(self, request, form_url='', extra_context=None):
        self.fields=self.a_fields
        return admin.ModelAdmin.add_view(self, request, form_url=form_url, extra_context=extra_context)
    def change_view(self, request, object_id, form_url='', extra_context=None):
        self.fields=self.c_fields
        return admin.ModelAdmin.change_view(self, request, object_id, form_url=form_url, extra_context=extra_context)
    def get_form(self, request, obj=None, **kwargs):
        if obj is None:
            return PersonForm
        else:
            return super(PersonAdmin, self).get_form(request, obj, **kwargs)

admin.site.register(Person,PersonAdmin)

There is one important issue that needs to be noted. It is common practice to use forms statically by declaring a form=PersonForm value in ModelAdmin, as the remarked line shows. Also it is common practice to leave Meta value for field list fields=[] empty in forms called this way. However when you override get_form(), you also override handler for that empty value, which is crucial for form’s operation. So you need either simply to declare your values explicitly in the form itself fields=['first_name','last_name','password',] as this or develop your own get_form() further to put those values there. First option is simpler IMHO.