class DynForm(forms.BaseForm): """ Given defined fields exclude,include,model, and widgets this class will attempt to build a form from a model definition like form_for_model/instance. The only required field is model. exclude,include, and widgets can be left off in which case all fields except the primary key field will be used with default widgets. exclude =[List of model field names] Can be empty or undefined which will exclude only the primary key field include =[List of model field names] Can be empty or undefined which will make it use all fields in model widgets = {'field': (widgetmodel,{widget args}),} formfields = {'field': (formfieldmodel,{formfield args}),} the widgets dict above will override any widgets specified here. If you specify an 'include' list it's pointless to specify and 'exclude' list because only the fields in include will be used. 'exclude' is really only useful if you want almost all the model fields except for one or two, which you'd put in the list and leave include empty or undefined to get all the other fields automatically. This not really a normal form, so some things may not work the same as you might expect. To emmulate form_for_model you'd basically do the bare minimum class MyForm(DynForm): model = MyModel Unbound form: form = AgentEditForm() Bound to a pk for the model defined, and will use that data to populate as initial if it exists. form = AgentEditForm(None,pk=5) # Agent.objects.get(pk=5) Bound to a pk, and processing POST form = AgentEditForm(request.POST,pk=5) optional keyword arguments pk - primary key. Will fallback to None if pk not valid widget_kwargs - Dictionary field keys, and a dict with keyword args for the fields widget used instead of those in form definition (models_def) form.save() - If no primary key (pk) was given at form creation, or the pk was invalud a new object will be created and the object will be returned. Any keyword arguments passed to save will be used to instantiate a new new instance for the model, but will be overridden by form data if present. This is to allow you to define values for required fields you excluded in the form definition. ------------------------------------------------------------------------------------------------------------------------------------------------------ Example form: class AgentReminderForm(DynForm): model= AgentReminder include_fields=['who','what','when','priority','notice','date'] exclude_fields=[] widgets= { 'what': (forms.Textarea,{'attrs':{'rows':'5','cols':'10'},}), 'notice': (forms.Select,{'choices': [('15','15 Minutes'),('60','1 Hour')]}), } formfields ={ 'when': (forms.DateTimeField,{'input_formats': ["%b-%d-%Y %I:%M:%S %p"] }) } ------------------------------------------------------------------------------------------------------------------------------------------------------ In View: def myview(request,id=None) if request.POST: form = AgentReminderForm(request.POST,pk=id) if form.is_valid(): form.save(date=datetime.now()) else: form=AgentReminderForm(pk=id) ... """ def __init__(self, *args, **kwargs): try: getattr(self,'include') except: self.include = [] try: getattr(self,'exclude') except: self.exclude = [] try: getattr(self,'widgets') except: self.widgets = {} try: getattr(self,'formfields') except: self.formfields = {} newkwargs = kwargs self.base_fields = {} # Since we are bypassing Form and using BaseForm if 'widget_kwargs' in kwargs: self.widget_kwargs = newkwargs.pop('widget_kwargs') else: self.widget_kwargs = {} self.primary_key=None if 'pk' in kwargs: self.primary_key = newkwargs.pop('pk') try: mi = self.model.objects.get(pk=self.primary_key) except: self.primary_key = None if self.primary_key: newinitial = mi.__dict__ if 'initial' in newkwargs: newinitial.update(newkwargs['initial']) newkwargs['initial'] = newinitial super(DynForm,self).__init__(*args,**newkwargs) mm = self.model._meta # grab the model info include = [] if self.include: for field in mm.fields + mm.many_to_many: if field.attname in self.include: include.append(field) else: include = mm.fields + mm.many_to_many self.exclude.append(mm.pk.attname) for f in include: if (f.attname not in self.exclude) and (f is not mm.pk.attname) : if f.attname in self.formfields: formfield = self.formfields[f.attname][0] ffargs = self.formfields[f.attname][1] else: formfield = f.formfield ffargs = {} # Handle Widgets if f.attname in self.widget_kwargs: #use what was given at form instantiation ffargs['widget'] =self.widgets[f.attname][0](**self.widget_kwargs[f.attname][1]) elif f.attname in self.widgets: # use what was given at form definition ffargs['widget'] =self.widgets[f.attname][0](**self.widgets[f.attname][1] ) self.fields[f.attname] = formfield(**ffargs) self.base_fields=self.fields # don't actually need this, but just in case someone tries to treat as a normal form def save(self, **kwargs): if not self.is_valid(): return None rel_wait = [] # start with no many to many relations to worry about q_wait = [] # start with no fields to queue until after initial save if self.primary_key is None: # treat as new pm = self.model(**kwargs) # since this is new, put any waiting fields in the rel_wait list for mf in pm.__class__._meta.many_to_many: rel_wait.append(mf.attname) else: pm = self.model.objects.get(pk=self.primary_key) for f in self.fields: if f not in rel_wait: setattr(pm,f,self.clean_data[f]) else: q_wait.append(f) pm.save() # Must save here, so relations work if len(q_wait) > 0: # something is queued, and can now be added since the insert is done and pm has a pk for f in q_wait: # now that it's inserted we can take care of the waiting relations setattr(pm,f,self.clean_data[f]) pm.save() return pm