I have 2 formsets (RefLocFormSet
and RefNoteFormSet
), and one other form (ref_form
) that I am displaying within one HTML <form>
element.
The ref_form
is split into 2 tabs, and the other 2 tabs on the page contain one formset each. If I make changes to the notes field in the first form in the RefLocFormSet
shown below (i.e. by changing "Lethbridge and surrounding area" to some other text) and then press SAVE CHANGES, formset.is_valid()
returns False.
The cleaned_data for each form in the formset (which is set up to print in views.py when .is_valid()
returns False), appears as follows:
{'location_01': <location_01: Canada>, 'location_02': <location_02: Alberta (Province)>, 'ref_loc_note': 'Lethbridge and surrounding area, test', 'id': <location_join: Canada:Alberta (Province) (LastName2011: Title here)>}
{'location_01': <location_01: Canada>, 'location_02': <location_02: Ontario (Province)>, 'ref_loc_note': 'GTA', 'id': <location_join: Canada:Ontario (Province) (LastName2011: Title here)>}
{'location_01': None, 'location_02': None, 'ref_loc_note': ''}
{'location_01': None, 'location_02': None, 'ref_loc_note': ''}
[{}, {}, {'id': ['Select a valid choice. That choice is not one of the available choices.']}, {'id': ['Select a valid choice. That choice is not one of the available choices.']}]
Although there should be only 2 forms in the formset, it appears that there are 4. Any ideas on how to stop these extra forms from being created? I thought setting extra=0 would do the trick, but it doesn't.
views.py:
def ref_detail(request, ref_id):
try:
ref = reference.objects.get(pk=ref_id)
except ref.DoesNotExist:
raise Http404("Reference does not exist")
# If this is a POST request, process the form data
if request.method == 'POST':
# Create form instances and populate them with data from the request
# Main and Study Design tabs
ref_form = ReferenceForm(request.POST, initial=model_to_dict(ref), instance=ref)
# Location tab
ref_locs = ref.location_join_set.all().order_by(F('location_01').asc(nulls_first=True))
RefLocFormSet = modelformset_factory(location_join, form = RefLocForm, extra=0)
loc_formset = RefLocFormSet(request.POST, queryset=ref_locs)
loc_helper = RefLocFormSetHelper()
# Notes and Issues tab
ref_notes = ref.reference_note_set.all()
RefNoteFormSet = modelformset_factory(reference_note, form = RefNoteForm, extra=0)
note_formset = RefNoteFormSet(request.POST, queryset=ref_notes)
note_helper = RefNoteFormSetHelper()
# Save ref form if valid
if ref_form.is_valid():
output = ref_form.save()
else:
print('REF FORM NOT VALID')
# Save notes and issues form set if valid
if note_formset.is_valid():
note_formset.save()
# Save location form set if valid
if loc_formset.is_valid():
print('LOC FORMSET IS VALID')
for f in loc_formset:
print(f.cleaned_data)
loc_formset.save()
else:
print('LOC FORMSET NOT VALID')
for f in loc_formset:
print(f.cleaned_data)
print(loc_formset.errors)
# If request is a GET (or any other method) we'll create a blank form for each tab (pre-populated with fields that are not empty)
else:
ref_form = ReferenceForm(initial=model_to_dict(ref), instance=ref)
# Location
ref_locs = ref.location_join_set.all().order_by(F('location_01').asc(nulls_first=True))
RefLocFormSet = modelformset_factory(location_join, form = RefLocForm, extra=0)
loc_formset = RefLocFormSet(queryset=ref_locs)
loc_helper = RefLocFormSetHelper()
# Notes
ref_notes = ref.reference_note_set.all()
RefNoteFormSet = modelformset_factory(reference_note, form = RefNoteForm, extra=0)
note_formset = RefNoteFormSet(queryset=ref_notes)
note_helper = RefNoteFormSetHelper()
context = {'ref': ref,
'ref_form': ref_form,
'ref_form_helper': ref_form.helper,
'ref_locs': ref_locs,
'loc_formset': loc_formset,
'loc_helper': loc_helper,
'note_formset': note_formset,
'note_helper': note_helper,
}
return render(request, 'test_cedar_app/ref_detail.html', context)
ref_detail.html:
<form action="" method="post" id="leadform" enctype="multipart/form-data" role="form">
{% csrf_token %}
<ul class="nav nav-tabs md-tabs" id="myTabMD" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="main-tab-md" data-toggle="tab" href="#main-md" role="tab" aria-controls="main-md"
aria-selected="true">Main</a>
</li>
<li class="nav-item">
<a class="nav-link" id="study-tab-md" data-toggle="tab" href="#study-md" role="tab" aria-controls="study-md"
aria-selected="false">Study Design</a>
</li>
<li class="nav-item">
<a class="nav-link" id="loc-tab-md" data-toggle="tab" href="#loc-md" role="tab" aria-controls="loc-md"
aria-selected="false">Location</a>
</li>
<li class="nav-item">
<a class="nav-link" id="notes-tab-md" data-toggle="tab" href="#notes-md" role="tab" aria-controls="notes-md"
aria-selected="false">Notes and Issues</a>
</li>
</ul>
<div class="tab-content card pt-5" id="myTabContentMD">
{% crispy ref_form ref_form_helper %}
<div class="tab-pane fade" id="loc-md" role="tabpanel" aria-labelledby="loc-tab-md">
<button type="button" class="btn btn-success btn-xs" id="addloc">
<a href="{% url 'add_ref_info' ref_id=ref.id form_type='loc' %}">Add Location</a>
<i class="fas fa-plus-square"></i>
</button>
<br>
{% crispy loc_formset loc_helper %}
<!-- {% for loc in loc_forms %}
{% crispy loc %}
{% endfor %} -->
</div>
<div class="tab-pane fade" id="notes-md" role="tabpanel" aria-labelledby="notes-tab-md">
<button type="button" class="btn btn-success btn-xs" id="addnote">
<a href="{% url 'add_ref_info' ref_id=ref.id form_type='note' %}">Add Note</a>
<i class="fas fa-plus-square"></i>
</button>
<br>
{% crispy note_formset note_helper %}
<!-- {% for f in note_forms %}
{% crispy f %}
{% endfor %} -->
</div>
</div>
</form>
And just in case, here's the other forms and models files:
forms.py:
class RefLocForm(ModelForm):
class Meta:
model = location_join
fields = ['location_01', 'location_02', 'ref_loc_note']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.form_show_labels = False
self.helper.form_show_descriptions = False
class RefLocFormSetHelper(FormHelper):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.form_method = 'post'
self.form_tag = False
self.form_show_labels = False
self.form_show_descriptions = False
self.layout = Layout(
Div(
Column(
PrependedText('location_01', 'Country'),
HTML("""<br>"""),
Column(
Column(
Row(
Column(
PrependedText('location_02', 'Region:'),
'ref_loc_note',
css_class='form-group col-md-8 mx-0'
),
css_class='form-row'
),
),
),
FormActions(
Submit('save', 'Save changes')
),
HTML("""<hr style="border-width:5px;">""")
),
css_id='loc-md',
css_class='tab-pane fade show active',
),
)
class RefNoteForm(ModelForm):
class Meta:
model = reference_note
fields = ['note', 'user', 'resolved']
labels = {
'resolved': 'Resolved?',
}
help_texts = {}
for fieldname in fields:
help_texts[fieldname] = None
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.form_show_descriptions = False
class RefNoteFormSetHelper(FormHelper):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.form_method = 'post'
self.form_tag = False
self.form_show_descriptions = False
self.layout = Layout(
Div(
Column(
Row(
Column('user', css_class='form-group col-md-8 mx-0'),
Column('resolved', css_class='form-group col-md-4 mx-0'),
css_class='form-row'
),
'note',
FormActions(
Submit('save', 'Save changes')
),
HTML("""<hr style="border-width:5px;">"""),
),
css_id='notes-md',
css_class='tab-pane fade show active',
),
)
models.py:
class reference(models.Model):
"""
A reference/article/study.
"""
ANSWER_CHOICES = [
('Y', 'Yes'),
('N', 'No'),
('U', 'Unsure'),
('NR', 'Not Reported'),
]
name_bibtex = models.CharField(max_