Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
453 views
in Technique[技术] by (71.8m points)

python - Django formset.is_valid() returns False, creates additional forms (even with extra=0)

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.

sample layout

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_

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
等待大神答复

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...