在 Class 基于视图中为动态创建的 ModelChoiceField 设置初始值

Set initial value for dynamically created ModelChoiceField in Class Based View

我正在创建一个 Form,它有一个 ModelChoiceField。我可以按如下方式设置此字段的值:

class DeviceForm(Form):
    devices = ModelChoiceField(queryset=None, widget=RadioSelect, empty_label=None)

    def __init__(self, *args, **kwargs):
        super(DeviceForm, self).__init__(*args, **kwargs)
        user_id = kwargs['initial']['user_id']
        devices = Device.objects.filter(user_id=user_id, is_validated=True)
        self.fields['devices'].queryset = devices

这很好用,但我似乎无法设置初始值。我尝试了多种方法,例如向 get_initial() 添加初始值:

def get_initial(self):
    initial = super(DeviceView, self).get_initial()
    initial['user_id'] = self.kwargs['user_id']
    default_device = Device.objects.get(user_id=self.kwargs['user_id'], is_validated=True, is_default=True)
    initial['devices'] = default_device.id
    return initial

但这不起作用。我在 Whosebug 上遇到了多个问题,这些问题实例化了表单并传入了一个初始值,如下所示:

#Get default device
form = DeviceForm(initial={'devices': default_device.id})

但理想情况下我想避免它,因为我试图坚持使用 Django 通用基础视图(看起来像这样,并且据我所知不允许 initial kwarg我知道):

def get(self, request, user_id, **kwargs):
    # Do stuff

    form = self.get_form()
    return self.render_to_response(self.get_context_data(form=form))

get_initial() 中设置设备应该有效,因为 self.get_form_kwargs() returns:

{'prefix': None, 'initial': {'user_id': u'1', 'devices': 42}}

或者,取决于我应该选择什么:

{'prefix': None, 'initial': {'user_id': u'1', 'devices': <Device: Device object>}}

执行以下操作仍然无法正常工作(基本上与手动 get_form() 相同)。

def get(self, request, user_id, **kwargs):
    #Do stuff
    form_class = self.get_form_class()
    form = form_class(**self.get_form_kwargs())
    return self.render_to_response(self.get_context_data(form=form))

在 Form 的构造函数中设置初始值也不起作用:

class DeviceForm(Form):
    devices = ModelChoiceField(queryset=None, widget=RadioSelect, empty_label=None)

    def __init__(self, *args, **kwargs):
        super(DeviceForm, self).__init__(*args, **kwargs)
        user_id = kwargs['initial']['user_id']
        devices = Device.objects.filter(user_id=user_id, is_validated=True)
        self.fields['devices'].queryset = devices
        default_device = devices.filter(is_default=True)
        self.fields['devices'].initial = default_device  # or default_device.id

有谁知道实现这个的方法吗?

您需要使用建议的方式分配 initial,但它在基于 class 的视图中仍然可用。引用自 django doc:

class MyFormView(View):
    form_class = MyForm
    initial = {'key': 'value'}
    template_name = 'form_template.html'

    def get(self, request, *args, **kwargs):
        form = self.form_class(initial=self.initial)
        return render(request, self.template_name, {'form': form})

由于您已经覆盖了表单的 __init__ 方法,因此在那里设置初始值可能是最简单的方法。

class DeviceForm(Form):
    devices = ModelChoiceField(queryset=None, widget=RadioSelect, empty_label=None)

    def __init__(self, *args, **kwargs):
        super(DeviceForm, self).__init__(*args, **kwargs)
        user_id = kwargs['initial']['user_id']
        devices = Device.objects.filter(user_id=user_id, is_validated=True)
        self.fields['devices'].queryset = devices
        default_device = Device.objects.get(user_id=user_id, is_validated=True, is_default=True)
        self.fields['devices'].initial = default_device

最后是我在模板中犯了一个愚蠢的错误。该值实际上已在视图中正确设置,但我忘记在模板中设置 checked 值。我以为它只是使用 {{form}},但我不是。仅供想知道如何手动设置的人使用:

{% for device in form.devices.field.choices.queryset %}
    <input type="radio" name="devices" value="{{ device.pk }}" class="radio-device"
        {% ifequal form.devices.value device.pk %}checked{% endifequal %}
    />
{% endfor %}