Django ModelForm 中对多对多字段的筛选

72 阅读2分钟

在 Django 的 ModelForm 中,当存在多对多字段时,需要对该字段进行筛选,以便在表单中仅显示特定条件下的选项。例如,在一个项目中,存在两个模型:IdealBehaviourStandardWork,其中 IdealBehaviour 模型包含 cbs 外键字段,指向 CBS 模型,而 StandardWork 模型包含 ideal_behaviour 多对多字段,指向 IdealBehaviour 模型。在使用 TodoListForm 表单来管理 StandardWork 模型时,需要筛选 ideal_behaviour 多对多字段,以便仅显示与当前正在编辑的 CBS 相关联的 IdealBehaviour 选项。

解决方案

为了解决此问题,需要在 TodoListForm 表单的 __init__ 方法中添加代码,以根据当前正在编辑的 StandardWork 实例来筛选 ideal_behaviour 多对多字段。具体代码如下:

class TodoListForm(forms.ModelForm):
    class Meta:
        model = StandardWork
        exclude = ('cbs_and_role', 'publish', 'standard_work_number')
        widgets = {
            'system_description': forms.Textarea(attrs={'rows': 3}),
        }

    def __init__(self, *args, **kwargs):
        super(TodoListForm, self).__init__(*args, **kwargs)
        if self.instance:
            self.fields['ideal_behaviour'].queryset = IdealBehaviour.objects.filter(cbs__exact=self.instance.cbs.id)

在上面的代码中,首先定义了一个 TodoListForm 类,继承自 forms.ModelForm。然后在 __init__ 方法中,首先检查是否存在当前正在编辑的 StandardWork 实例,如果存在,则根据该实例的 cbs 外键字段,来筛选 ideal_behaviour 多对多字段。

代码示例

下面是一个完整的代码示例,展示了如何使用 TodoListForm 表单来管理 StandardWork 模型:

from django import forms
from django.db import models

# 定义 IdealBehaviour 模型
class IdealBehaviour(models.Model):
    cbs = models.ForeignKey('CBS', null=True, blank=True)
    ideal_behaviour = models.CharField(max_length=500, null=True, blank=True)
    Description = models.CharField(max_length=1000, null=True, blank=True)
    created_time = models.DateTimeField(auto_now_add=True, null=True, blank=True)

# 定义 StandardWork 模型
class StandardWork(models.Model):
    cbs_and_role = models.ManyToManyField('CbsRole', null=True, blank=True)
    standard_work_number = models.BigIntegerField(null=True, blank=True)
    system_name = models.CharField(max_length=500, null=True, blank=True)
    system_description = models.TextField(null=True, blank=True)
    ideal_behaviour = models.ManyToManyField('IdealBehaviour', null=True, blank=True)
    publish = models.BooleanField(default=False)
    created_time = models.DateTimeField(auto_now_add=True, null=True, blank=True)

# 定义 TodoListForm 表单
class TodoListForm(forms.ModelForm):
    class Meta:
        model = StandardWork
        exclude = ('cbs_and_role', 'publish', 'standard_work_number')
        widgets = {
            'system_description': forms.Textarea(attrs={'rows': 3}),
        }

    def __init__(self, *args, **kwargs):
        super(TodoListForm, self).__init__(*args, **kwargs)
        if self.instance:
            self.fields['ideal_behaviour'].queryset = IdealBehaviour.objects.filter(cbs__exact=self.instance.cbs.id)

# 在视图中使用 TodoListForm 表单
def manage_view(request, pk):
    standard_work = StandardWork.objects.get(pk=pk)
    form = TodoListForm(instance=standard_work)
    if request.method == 'POST':
        form = TodoListForm(request.POST, instance=standard_work)
        if form.is_valid():
            form.save()
            return redirect('success_url')

    context = {
        'form': form,
    }
    return render(request, 'manage.html', context)

在上面的代码中,首先定义了 IdealBehaviourStandardWork 模型。然后定义了 TodoListForm 表单类,并在 __init__ 方法中添加了 ideal_behaviour 多对多字段的筛选代码。最后在 manage_view 视图中,获取了当前正在编辑的 StandardWork 实例,并将其作为参数传递给 TodoListForm 表单,以便在表单中显示与该实例相关联的 IdealBehaviour 选项。