简体   繁体   中英

Set initial value for dynamically created ModelChoiceField in Class Based View

I'm creating a Form which has a ModelChoiceField . I'm able to set the values for this field as follows:

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

This works perfectly, but I can't seem to be able to set an initial value. I've tried multiple things such as adding an initial value to 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

But that doesn't work. I've come across multiple questions on StackOverflow which instantiate the form and pass in an initial value like so:

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

But ideally I would like to avoid it, since I'm trying to stick to the Django generic base views (which look like this, and don't allow for an initial kwarg as far as I'm aware):

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

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

Setting devices in get_initial() should work, since the self.get_form_kwargs() returns:

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

or, depending on what I'm supposed to choose:

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

Doing the following still doesn't get it to work (basically the the same thing as get_form() manually).

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

Setting the initial value in the constructor of the Form also doesn't work:

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

Does anyone know of a way to accomplish this?

You need to use the suggested way to assign initial , but it's still available in class based views. Quoting from 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})

Since you have already overridden your form's __init__ method, it might be easiest to set the initial value there.

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

In the end it was a dumb mistake on my part in the template. The value actually got set correctly in the view, but I forgot to set the checked value in the template. I thought it was just using {{form}} , but I wasn't. Just for people who are wondering how you can set it manually:

{% 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 %}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM