Django 在 UpdateView 中设置 ModelForm ModelChoiceField 初始值 class

Django set ModelForm ModelChoiceField initial value in UpdateView class

在发布自己的问题之前,我检查了很多相关问题,但我仍然无法弄清楚。使用 Django 2.2.x.

我希望 html 表单的 owner 字段最初只显示 1 个用户名,并且此用户名必须是当前 event 实例所有者。

作为 ModelChoiceField,html 表单上的字段呈现为下拉列表。此下拉列表暂时包含所有应用程序用户,因为 ModelChoiceFieldqueryset=User.objects.all()

我试图覆盖 UpdateViewget_initial() 方法,但是,这对 ModelChoiceField 没有影响。不过,我可以设置所有 forms.CharField() 初始值。

我也尝试重写 class EventForm(forms.ModelForm)__init__ 方法,但运气不佳。

forms.py

class EventForm(forms.ModelForm):

    class Meta:
        model = Event
        fields = ["customer", "owner", ...]

    # the helper block comes from crispy_forms extension.
    # It rules the form layout, allowing to specify css classes, fields order
    # and even adding custom buttons
    helper = FormHelper()
    helper.layout = Layout(
        Field('customer'),
        Field('owner'),
        ...    
        Submit('submit', 'Update')
    )

    customer = forms.CharField(disabled=True)
    owner = forms.ModelChoiceField(queryset=User.objects.all())
    ...

views.py

class EventUpdateView(LoginRequiredMixin, UpdateView):
    """ this view renders the update form that allows service desk operators to update event status,
    ownership and event notes
    """
    model = Event
    form_class = EventForm
    template_name = "metamonitor/event_update_form.html"

    def get_initial(self):
        """ returns the initial data to populate form field on this view.
        We override this method to customize the `owner` initial value to the
        default event owner.
        """
        initial = super().get_initial()
        # the event instance being updated
        event = self.get_object()
        # this expression returns a queryset containing only 1 item,
        # like <QuerySet [<User: operator2>]>
        initial['owner'] = User.objects.filter(pk=event.owner.pk)

        return initial

    # without this, after a successfull event update we would get an error
    # because django needs a valid url to redirect to.
    def get_success_url(self):
        # app_name:url_name, as defined in the corresponding path() `name` argument in urls.py
        return reverse("metamonitor:event_update", kwargs={"pk": self.object.pk})

如何正确设置初始值?

这是解决您问题的方法。它需要两个步骤:

  1. 覆盖视图的 get_form_kwargs 方法,因此它检索 'owner' 对象并将其发送到表单,存储在 kwargs 参数中

  2. 覆盖表单的init,然后将所有者对象(取自kwargs)分配给您要更改的字段的.queryset :

在您的视图中覆盖 get_form_kwargs:

def get_form_kwargs(self):
    kwargs = super().get_form_kwargs()
    event = self.object     # no need to call get_object() as you did in your post
    owner = User.objects.filter(pk=event.owner.pk)
    kwargs.update({'owner': owner})
    return kwargs

重写 forms.py 中表单 class 的 init:

def __init__(self, *args, **kwargs):
    owner = kwargs.pop('owner')   # get the owner object from kwargs
    super().__init__(*args, **kwargs)
    # assign owner to the field; note the use of .queryset on the field
    self.fields['owner'].queryset = owner 

另一种与上面不同的方法是简单地修改视图的get_form方法:

def get_form(self, form_class=None):
    form = super().get_form(form_class)
    event = self.object
    owner = User.objects.filter(pk=event.owner.pk)
    form.fields['owner'].queryset = owner

    return form

这个解决方案比第一个解决方案实施起来要快得多,这是真的;我只在最后提到它,因为根据我的经验,当期望的目标是更改表单的显示方式时,人们似乎更喜欢修改表单 class 本身。使用最适合您需要的那个。