Thực hiện Thao tác Hàng loạt trong Django bằng Formset

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()

Thẻ: django-formsets batch-operations django-models form-validation

Đăng vào ngày 28 tháng 6 lúc 04:01