Hi
I have a domain model, used in Django app, which I’d like to present on a single form. I’ve created my application with custom ModelForms (not much changes, some fields excluded etc). The model’s dependencies are as follows:
Complaint
\
.--- CarInfo
.--- Customer
My view function looks like this:
def make(request):
if request.method == 'POST':
parameters = copy.copy(request.POST)
complaint = Complaint()
carInfo = CarInfo()
customer = Customer()
customer_form = CustomerForm(parameters, instance=customer)
carInfo_form = CarInfoForm(parameters, instance=carInfo)
parameters['complaint_date'] = get_current_date()
parameters['customer'] = 1 # dummy value to allow validation success
parameters['car_info'] = 1 # dummy value to allow validation success
form = ComplaintForm(parameters, instance=complaint)
if form.is_valid() and customer_form.is_valid() and carInfo_form.is_valid():
carInfo_form.save()
customer_form.save()
parameters['customer'] = customer.id
parameters['car_info'] = carInfo.id
form = ComplaintForm(parameters, instance=complaint)
form.save()
return index(request)
else:
form = ComplaintForm()
carInfo_form = CarInfoForm()
customer_form = CustomerForm()
return render_to_response('complaints/make_complaint.html', {'complaint_form' : form, 'customer_form' : customer_form, 'carInfo' : carInfo_form})
I don’t like this approach too much, moreover it doesn’t work in all environments -thou I haven’t found the reason for it not working. I’ve been looking into fixing this code a bit and found something like inline formset (http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#inline-formsets). This solution seems ok, but since my forms are custom tailored I can use it.
Perhaps someone could offer me some advice on how to properly solve such case. Cleaner solutions are much appreciated.
EDITED
There is a case for me, where this solutions just doesn’t work. Despite of setting dummy values on foreign keys, when I call is_valid() I get FALSE, with error message saying that these fields are not set. I’m observing this problem with django 1.2.5 – it occurs on server I intent to run this app, however my laptop (also django 1.2.5) doesn’t have this problem.
You can change your Complaint model’s complaint_date to something like this
complaint_date = models.DateField(default=datetime.date.today())that way you can get rid of
parameters['complaint_date'] = get_current_date()As for your multiform view you can use an unbound forms for you desired behavior.
By excluding the fk to the car and customer on the complaint form, the forms should validate. Check the .is_valid() of all 3 forms at the same time and then save the 2 forms that your complaint object is dependent on, create the complaint object with out commit to database (commit=False), add the id’s of the customer and car to that object, then save.
in your view.
edit:
models look like this:
forms.py should look like this:
Lets walk through the view I wrote out above:
form in view docsOn first pass, there is no request.method so we create 3 unbound forms.
these forms are passed to the template and rendered.
When the view is called again with request.method == “POST” evaluating true, we create the 3 bound form instances using the data from our request.POST.
Next we call the .is_valid() method on each form. In our example because we excluded the ‘customer_info’ and ‘car_info’ foreign key fields in our complaint modelform, each form is only checking to see that the char input field validates.
If validation all passes then we can start saving our forms to models and this where we need to be careful about populating our complaint’s required fk’s:
With those 2 forms we can call the .save() as usual. We will however assign the return value to car_instance and customer_instance. These will contain the instances of the CarInfo and Customer models we just created using the .save() method on the form.
Next, using the
commit=Falseargument in the .save() method, we are able to create an object from the bound form (containing the request.POST data) and not save it to the database.To make this clearer, you could also have created a new Complaint object like this:
render