Giới thiệu Formset
Formset là thành phần Django cho phép xác thực và xử lý nhiều biểu mẫu cùng lúc, phù hợp cho các tác vụ hàng loạt.
Triển khai Thao tác Hàng loạt
1. Định nghĩa Model
from django.db import models
class MenuItem(models.Model):
"""Quản lý menu"""
label = models.CharField('Tên menu', max_length=32)
icon_class = models.CharField('Biểu tượng', max_length=32)
class AccessPermission(models.Model):
"""Quản lý quyền truy cập"""
permission_name = models.CharField('Tên quyền', max_length=32)
path = models.CharField('URL', max_length=128)
parent = models.ForeignKey(
'self',
verbose_name='Quyền cha',
null=True,
blank=True,
on_delete=models.CASCADE
)
menu = models.ForeignKey(
MenuItem,
verbose_name='Menu',
null=True,
blank=True,
on_delete=models.CASCADE
)
alias = models.CharField('Bí danh', max_length=32, unique=True)
2. Tạo Formset
from django import forms
class BatchPermissionForm(forms.Form):
permission_name = forms.CharField(widget=forms.TextInput(attrs={"class":"form-input"}))
path = forms.CharField(widget=forms.TextInput(attrs={"class": "form-input"}))
alias = forms.CharField(widget=forms.TextInput(attrs={"class": "form-input"}))
menu_id = forms.ChoiceField(
choices=[(None, '----')],
widget=forms.Select(attrs={"class": "form-select"}),
required=False
)
parent_id = forms.ChoiceField(
choices=[(None, '----')],
widget=forms.Select(attrs={"class": "form-select"}),
required=False
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['menu_id'].choices += MenuItem.objects.values_list('id', 'label')
self.fields['parent_id'].choices += AccessPermission.objects.filter(
parent__isnull=True
).exclude(menu__isnull=True).values_list("id", 'permission_name')
3. Xử lý Logic trong View
from django.forms import formset_factory
def batch_create(request):
FormSet = formset_factory(BatchPermissionForm, extra=2)
if request.method == "POST":
formset = FormSet(request.POST)
if formset.is_valid():
valid_data = formset.cleaned_data
success = True
for idx, data in enumerate(valid_data):
if not data:
continue
try:
new_permission = AccessPermission(**data)
new_permission.validate_unique()
new_permission.save()
except Exception as error:
formset.errors[idx].update(error)
success = False
if success:
return HttpResponse("Thành công!")
return render(request, 'template.html', {'formset': formset})
return render(request, 'template.html', {'formset': FormSet()})
4. Giao diện Người dùng
<form method="post">
{% csrf_token %}
{{ formset.management_form }}
<table>
<thead>
<tr>
<th>Tên quyền</th>
<th>Đường dẫn</th>
<th>Bí danh</th>
<th>Menu</th>
<th>Quyền cha</th>
</tr>
</thead>
<tbody>
{% for form in formset %}
<tr>
{% for field in form %}
<td>{{ field }}<span class="error">{{ field.errors.0 }}</span></td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
<button type="submit">Xác nhận</button>
</form>
Lưu ý Quan trọng
- Bắt buộc thêm
{{ formset.management_form }}trong template - Sử dụng vòng lặp kép để hiển thị formset
- Xử lý lỗi duy nhất bằng
validate_unique()