Reputation: 9476
I have the following forms in my Django application:
class SurveyAreaForm(ModelForm):
class Media(object):
js = formset_media_js
class Meta:
model = SurveyArea
exclude = ['survey', ]
widgets = {
'action_by': CustomDateInput(),
}
AreaFormSet = inlineformset_factory(Survey, SurveyArea, form=SurveyAreaForm)
class SurveyForm(ModelForm):
class Meta:
model = Survey
exclude = ['tech', 'operator', ]
widgets = {
'date': CustomDateInput(),
'client': FilteredJQMRadioSelectWithAdd(),
'assessorSignature': SignatureInput(attrs={'id': 'assessorSignature'}),
}
def __init__(self, *args, **kwargs):
super(SurveyForm, self).__init__(*args, **kwargs)
self.fields['client'].empty_label = None
I'm using https://pypi.python.org/pypi/django-formset-js/0.3.0 to provide the JavaScript to add additional forms.
Each Survey object can have one or more SurveyAreas associated with it, and I want to make them editable using the same form. However, I'm running into an issue with rendering the initial data. Here's my view:
class SurveyUpdateView(SurveyValidateMixin, UpdateViewWithTech):
model = Survey
form_class = SurveyForm
success_url="/forms/survey/updated/"
template_name="my_django_app/survey_update.html"
def get(self, request, *args, **kwargs):
"""
Handles GET requests and instantiates blank version of the form
and its inline formsets.
"""
self.object = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
# Get areas
areas = SurveyArea.objects.filter(survey=self.object).order_by('name').values()
# Render form
area_form = AreaFormSet(initial=areas)
return self.render_to_response(
self.get_context_data(form=form,
area_form = area_form))
def post(self, request, *args, **kwargs):
"""
Handles POST requests, instantiating a form instance and its inline
formsets with the passed POST variables and them checking them for
validity.
"""
self.object = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
area_form = AreaFormSet(self.request.POST)
if (form.is_valid() and area_form.is_valid()):
return self.form_valid(form, area_form)
else:
return self.form_invalid(form, area_form)
I also have a SurveyCreateView
, which works fine, and the validation is shared between these views using the SurveyValidateMixin
. The SurveyUpdateView
also inherits from UpdateViewWithTech
, which basically just limits the queryset by user and automatically sets a field representing the user.
The problem I'm having is in rendering the initial data. In the get()
method of SurveyUpdateView
, I'm fetching all of the areas currently associated with that survey, and using PDB I've been able to confirm that at the point where I fetch the areas relating to this survey (in the variable areas), the data appears to be correct. Here's how it looks with 8 items:
[{'description': u'A', 'photo': u'', 'action_required': u'A', 'action_by': datetime.date(2013, 11, 14), 'survey_id': 12L, u'id': 21L, 'action_taken': True, 'name': u'A'}, {'description': u'A', 'photo': u'', 'action_required': u'A', 'action_by': datetime.date(2013, 11, 14), 'survey_id': 12L, u'id': 19L, 'action_taken': True, 'name': u'A'}, {'description': u'A', 'photo': u'', 'action_required': u'A', 'action_by': datetime.date(2013, 11, 14), 'survey_id': 12L, u'id': 29L, 'action_taken': True, 'name': u'A'}, {'description': u'A', 'photo': u'', 'action_required': u'A', 'action_by': datetime.date(2013, 11, 14), 'survey_id': 12L, u'id': 30L, 'action_taken': True, 'name': u'A'}, {'description': u'B', 'photo': u'', 'action_required': u'B', 'action_by': datetime.date(2013, 11, 14), 'survey_id': 12L, u'id': 22L, 'action_taken': True, 'name': u'B'}, {'description': u'B', 'photo': u'', 'action_required': u'B', 'action_by': datetime.date(2013, 11, 14), 'survey_id': 12L, u'id': 20L, 'action_taken': True, 'name': u'B'}, {'description': u'B', 'photo': u'', 'action_required': u'B', 'action_by': datetime.date(2013, 11, 14), 'survey_id': 12L, u'id': 31L, 'action_taken': True, 'name': u'B'}, {'description': u'C', 'photo': u'', 'action_required': u'C', 'action_by': datetime.date(2013, 11, 14), 'survey_id': 12L, u'id': 23L, 'action_taken': True, 'name': u'C'}, {'description': u'X', 'photo': u'', 'action_required': u'X', 'action_by': datetime.date(2013, 11, 14), 'survey_id': 12L, u'id': 24L, 'action_taken': True, 'name': u'X'}, {'description': u'Y', 'photo': u'', 'action_required': u'Y', 'action_by': datetime.date(2013, 11, 14), 'survey_id': 12L, u'id': 25L, 'action_taken': True, 'name': u'Y'}, {'description': u'Z', 'photo': u'', 'action_required': u'Z', 'action_by': datetime.date(2013, 11, 14), 'survey_id': 12L, u'id': 26L, 'action_taken': True, 'name': u'Z'}]
However, passing that data through as the value of initial
when instantiating AreaFormSet
doesn't render the data correctly. If in the definition of AreaFormSet
, I set the value of extra
, I get that number of areas rendered (if undefined, it shows three, which I believe is the default value of extra
). The behaviour I want to see is for each existing area to be rendered in its own form.
Again using PDB, if I dump the value of area_form after it's set with area_form.as_table()
, I only get the number of forms set in extra
, so the issue does appear to be with passing through the initial data.
Am I passing through the value of initial
correctly? I understood that the value of initial
should be a list of dictionaries, and it looks correct to me, but I'm not getting the correct number of areas rendering.
Upvotes: 2
Views: 4771
Reputation: 700
@Adam Starrh is right, although he didn't give any explanation.
I see you are trying to get the related items so you can populate your formset properly. But it turns out that django will do that for you, so you only need to pass the main object itself as an instance for the formset() like so:
# Render form
# put the object instance in the formset
area_form = AreaFormSet(instance=self.object)
I also faced this problem, and i even tried using a for loop like so:
items = Model.objects.filter(order_details=self.object)
def get_inline_items():
for item in items:
return item
then i passed the filtered objects as instances like so:
area_form = AreaFormSet(instance = get_inline_items())
But Django gave and error that says something like this "Must be 'ParentModel' instance".
I hope this serves as a good explanation for posterity
Upvotes: 0
Reputation: 89
Have you tried
area_form = AreaFormSet(instance=self.object)
instead of
area_form = AreaFormSet(initial=areas)
?
Upvotes: 1