1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149 | 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
|
Comments