Django:当 Form 的模型不是定义 ManyToMany 字段但 "the other class" 的模型时,ModelMultipleChoiceField 不起作用

Django: ModelMultipleChoiceField not working when Form's model is not the one where a ManyToMany field is defined but "the other class"

我正在尝试在 Django 中创建活动人员管理工具。数据模型的一部分是工作人员 (class Staff) 通过 StaffShift 通过 ManyToManyField 链接到计划班次 (ScheduledEventShift)。

每个员工都可以通过 ModelMultipleChoiceField 查看他/她和 select 他们可用的班次。这工作得很好,关系被正确写入 StaffShift table,具体取决于班次是否被勾选。

(抱歉,该工具是德语的,但从上下文中应该清楚我的意思)

我现在想做的是与之“相反”,即作为管理员,我想查看一个轮班,看看哪些工作人员申请了该轮班,并取消那些“太多”的(即需要 2 个,应用 3 个)。

当在那个方向使用 ModelMultipleChoiceField 时,我能够准确地选择我应该在 QuerySet 中选择的那些工作人员(这一切都很好)但是他们总是没有被选中(他们不应该是这样的正是那些在 StaffShift 中通过 table 的特定班次)并且如果我更改刻度,它对 StaffShift table 没有任何影响。

这里是我代码的相关部分:

models.py:

class Staff(models.Model):
    <...>
    shifts = models.ManyToManyField(ScheduledEventShift, through='StaffShift', related_name='applied_staff')

forms.py:

class ScheduledEventShiftEditForm(forms.ModelForm):

    class Meta:
        model = ScheduledEventShift
        fields = ['shift_count_required', 'applied_staff', 'event_shift_info']

    shift_count_required = models.IntegerField()
    applied_staff = forms.ModelMultipleChoiceField(
        queryset=None,
        widget=forms.CheckboxSelectMultiple)

views.py:

class ScheduledEventShiftUpdateView(ObjectUpdateView):
    model = ScheduledEventShift
    form_class = ScheduledEventShiftEditForm
    <...>

    def get_form(self, form_class=None):
        form = super().get_form(form_class=self.form_class)
        form.fields['applied_staff'].queryset = ScheduledEventShift.objects.filter(
            id=self.kwargs.get('pk')).first().applied_staff.all()

通过 table 这样的 ManyToMany 关系不是完全对称的吗(即连接的模型可以从两侧以相同的方式使用)?当我的表单模型不是定义了 ManyToManyField 和直通 table 的 class 而是“另一个 class” 时,我是否必须以不同的方式执行此操作?非常感谢


编辑:我能够从头开始创建一个最大限度减少的示例,我遇到了完全相同的问题(我想确保代码的其余部分没有任何副作用)。对于这个例子,我将 post 完整的 .py 文件。 StaffUpdateView/Form 按预期工作——它将更改保存到 StaffShift。 ShiftUpdateView/Form 显示所有员工但没有刻度,也不保存任何更改。

models.py

from django.db import models

class Shift(models.Model):
    shift_name = models.CharField(max_length=32)

class Staff(models.Model):
    staff_name = models.CharField(max_length=32)
    shifts = models.ManyToManyField(Shift, through='StaffShift', related_name='staff')

class StaffShift(models.Model):
    staff = models.ForeignKey(Staff, on_delete=models.CASCADE)
    shift = models.ForeignKey(Shift, on_delete=models.CASCADE)

views.py:

from django.views.generic import UpdateView

from .forms import *

class StaffUpdateView(UpdateView):
    form_class = StaffUpdateForm
    template_name = 'playground/base_form.html'
    model = Staff

    def get_success_url(self):
        return f'/staff/{self.get_object().id}/'

class ShiftUpdateView(UpdateView):
    form_class = ShiftUpdateForm
    template_name = 'playground/base_form.html'
    model = Shift
    def get_success_url(self):
        return f'/shift/{self.get_object().id}/'

forms.py:

from django import forms

from .models import *

class StaffUpdateForm(forms.ModelForm):

    class Meta:
        model = Staff
        fields = ['staff_name', 'shifts']

    staff_name = forms.CharField(max_length=32)
    shifts = forms.ModelMultipleChoiceField(
        queryset=Shift.objects.all(),
        widget=forms.CheckboxSelectMultiple)

class ShiftUpdateForm(forms.ModelForm):

    class Meta:
        model = Shift
        fields = ['shift_name', 'staff']

    shift_name = forms.CharField(max_length=32)
    staff = forms.ModelMultipleChoiceField(
        queryset=Staff.objects.all(),
        widget=forms.CheckboxSelectMultiple)

因为staff不是Shift模型上的一个字段,我们必须手动设置字段的初始值并手动保存更改

class ShiftUpdateForm(ModelForm):

    class Meta:
        model = Shift
        fields = ['shift_name', 'staff']

    shift_name = forms.CharField(max_length=32)
    staff = forms.ModelMultipleChoiceField(
        queryset=Staff.objects.all(),
        widget=forms.CheckboxSelectMultiple
    )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['staff'].initial = self.instance.staff.all()

    def save(self, *args, **kwargs):
        instance = super().save(*args, **kwargs)
        instance.staff.set(self.cleaned_data['staff'])
        return instance