Django:基于Formsets生成维护检查表表单的技术咨询
Hey there! Let's break down exactly how to build this maintenance checklist using Django formsets—this is a perfect use case for them. First, let's fix a tiny syntax issue in your model that'll cause headaches later: your is_compliant field is missing parentheses. Update it to is_compliant = models.BooleanField(default=False) (adding a default makes sense so new entries start unchecked).
We need a form that displays the maintenance item's details (as read-only) and lets users toggle compliance status. We'll pass each Maintenance_Item instance to the form to populate the static fields.
from django import forms from .models import MaintenanceCheckList, Maintenance_Item class MaintenanceCheckForm(forms.ModelForm): # Read-only fields to display item details item_name = forms.CharField(label="Item Name", disabled=True) item_description = forms.CharField( label="Description", disabled=True, widget=forms.Textarea(attrs={'rows': 2, 'cols': 50}) ) class Meta: model = MaintenanceCheckList fields = ['is_compliant'] def __init__(self, *args, **kwargs): # Grab the associated Maintenance_Item from kwargs self.maintenance_item = kwargs.pop('maintenance_item', None) super().__init__(*args, **kwargs) # Populate read-only fields if we have an item if self.maintenance_item: self.fields['item_name'].initial = self.maintenance_item.name self.fields['item_description'].initial = self.maintenance_item.name_description # Make the compliance label clearer self.fields['is_compliant'].label = "Mark as compliant today?"
Use Django's formset_factory to generate a set of forms—one for every existing Maintenance_Item. We'll disable extra empty forms since we only want forms for existing items.
MaintenanceCheckFormSet = forms.formset_factory( MaintenanceCheckForm, extra=0, # No extra empty forms can_delete=False # No delete functionality needed )
The view will handle both rendering the formset and processing submissions. We'll fetch all maintenance items, bind each to a form, and save compliance records when the form is submitted.
from django.shortcuts import render, redirect from datetime import date from .forms import MaintenanceCheckFormSet from .models import Maintenance_Item, MaintenanceCheckList def maintenance_checklist_view(request): maintenance_items = Maintenance_Item.objects.all() if request.method == 'POST': formset = MaintenanceCheckFormSet(request.POST) if formset.is_valid(): # Loop through forms and save compliance status for form, item in zip(formset, maintenance_items): is_compliant = form.cleaned_data['is_compliant'] # Avoid duplicate entries for the same day checklist_entry, created = MaintenanceCheckList.objects.get_or_create( item=item, created_date__date=date.today(), defaults={'is_compliant': is_compliant} ) # Update existing entry if it already exists if not created: checklist_entry.is_compliant = is_compliant checklist_entry.save() return redirect('maintenance_checklist') # Redirect to avoid resubmissions else: # Initialize formset with empty data, then bind each form to its item formset = MaintenanceCheckFormSet(initial=[{} for _ in maintenance_items]) for form, item in zip(formset, maintenance_items): form.maintenance_item = item return render(request, 'maintenance_checklist.html', {'formset': formset})
Create a template to display the formset. Don't forget to include the management_form—Django needs this to track formset data.
<!-- templates/maintenance_checklist.html --> <form method="post"> {% csrf_token %} {{ formset.management_form }} <div class="checklist-grid"> {% for form in formset %} <div class="checklist-card"> <h3>{{ form.item_name.value }}</h3> <p>{{ form.item_description.value }}</p> <div class="compliance-toggle"> {{ form.is_compliant }} {{ form.is_compliant.label }} </div> {{ form.hidden_fields }} </div> {% endfor %} </div> <button type="submit" class="save-btn">Save Compliance Status</button> </form>
If you're worried about form order getting messed up (e.g., if items are added/removed between page loads), add a hidden field to track the item ID in each form:
Add this to MaintenanceCheckForm:
item_id = forms.IntegerField(widget=forms.HiddenInput())
Then in the form's __init__ method:
self.fields['item_id'].initial = self.maintenance_item.id
And update the view's submission logic to fetch the item by ID instead of using zip:
for form in formset: if form.is_valid(): item_id = form.cleaned_data['item_id'] item = Maintenance_Item.objects.get(id=item_id) is_compliant = form.cleaned_data['is_compliant'] # ... rest of the save logic
内容的提问来源于stack exchange,提问作者Bryton Wishart

