Django 3.2 外键字段循环渲染、表单问题文本展示及数据保存异常技术问询
Let's tackle your Django form issues step by step, with practical code adjustments and explanations:
1. Rendering Dynamic Fields for Each Question
To loop through active Question instances and render a checkbox for each, you'll need to use a formset—this lets you generate multiple form fields tied to your Checklist model, one per question.
First, Update Your Models
Your Question model is missing the is_active field referenced in Checklist's limit_choices_to, so add that:
# models.py class Question(models.Model): id = models.BigAutoField(primary_key=True) title = models.CharField(max_length=50, unique=True) question = models.CharField(max_length=255, unique=True) is_active = models.BooleanField(default=True) # Add this line
Adjust Your Forms
Modify ChecklistForm to hide the question dropdown and use the question text as the checkbox label. Then create a formset for multiple checklist entries:
# forms.py from django import forms from django.forms import modelformset_factory from declaration.models import Checklist, Question, Declaration class DeclarationForm(forms.ModelForm): class Meta: model = Declaration exclude = ['user'] # We'll set the user programmatically in the view class ChecklistForm(forms.ModelForm): is_yes = forms.BooleanField(required=False) class Meta: model = Checklist fields = ['question', 'is_yes'] widgets = { 'question': forms.HiddenInput(), # Hide the question dropdown } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Set checkbox label to the question text if initial data exists if 'question' in self.initial: question = Question.objects.get(id=self.initial['question']) self.fields['is_yes'].label = question.question # Create a formset for Checklist (one form per active question) ChecklistFormSet = modelformset_factory(Checklist, form=ChecklistForm, extra=0)
2. Fixing Checklist Saving Issues
Your original view only handled a single Checklist form, which can't process multiple question entries. Here's how to update the view to handle the formset:
# views.py from django.shortcuts import render, redirect from .forms import DeclarationForm, ChecklistFormSet from .models import Question def submit(request): active_questions = Question.objects.filter(is_active=True) if request.method == 'POST': declaration_form = DeclarationForm(request.POST) checklist_formset = ChecklistFormSet(request.POST, queryset=Checklist.objects.none()) if declaration_form.is_valid() and checklist_formset.is_valid(): # Save declaration with current user declaration = declaration_form.save(commit=False) declaration.user = request.user declaration.save() # Save each checklist entry linked to the new declaration for form in checklist_formset: checklist = form.save(commit=False) checklist.declaration = declaration checklist.save() return redirect('student-dashboard') else: # Initialize formset with one empty form per active question checklist_formset = ChecklistFormSet(queryset=Checklist.objects.none()) # Set initial question data and labels for each form for form, question in zip(checklist_formset, active_questions): form.initial['question'] = question.id form.fields['is_yes'].label = question.question declaration_form = DeclarationForm() return render(request, 'declaration/submit.html', { 'declaration_form': declaration_form, 'checklist_formset': checklist_formset, })
3. Displaying Question Text Instead of Dropdowns
The template will loop through the formset and render each checkbox with the question text as its label. Here's a sample template:
<!-- submit.html --> <form method="post"> {% csrf_token %} <!-- Declaration form (omit if you don't have other fields besides user) --> {{ declaration_form.as_p }} <!-- Formset management data (required for Django to handle the formset) --> {{ checklist_formset.management_form }} <!-- Render each question's checkbox --> {% for form in checklist_formset %} {{ form.as_p }} {% endfor %} <button type="submit">Submit Declaration</button> </form>
Key Explanations:
- Formsets: The
ChecklistFormSetcreates one form per activeQuestion, letting you handle multiple checklist entries in one submission. - Hidden Question Field: We hide the
questionfield (usingHiddenInput) so users can't modify it, but still pass the question ID to the backend. - Saving Checklist Entries: We loop through each valid form in the formset, link it to the new
Declaration, and save each entry individually.
内容的提问来源于stack exchange,提问作者tenzai

