2

I have a ModelForm where one of the field (named creator) is a ForeignKey, so for {{ form.creator }} Django renders the <select> tag like this:

<select id="id_approver" name="approver">
    <option selected="selected" value="">---------</option>
    <option value="1">hobbes3</option>
    <option value="2">tareqmd</option>
    <option value="3">bob</option>
    <option value="4">sam</option>
    <option value="5">jane</option>
</select>

But I want to add a onchange event attribute so I can use AJAX later to do something else. I also want to change --------- to say something else and display the approvers' full name, not their username.

So is it possible to get a list of possible approvers and generate my own select options? Kind of like

<select id="id_approver" name="approver" onchange="some_ajax_function()">
    <option select="selected" value="0">Choose a user</option>
{% for approver in form.approver.all %} <!-- This won't work -->
    <option value="{{ approver.pk }}">{{ approver.get_full_name }}</option>
{% endfor %}
</select>

And I'm also thinking most of the list of approvers get too large (like over 50), then I'll eventually want some sort of a searchable auto-complete field for the approver. So then I'll definitely need to write my own HTML.

In case anyone needs it, my ModelForm looks like this:

class OrderCreateForm( ModelForm ) :
    class Meta :
        model = Order
        fields = (
            'creator',
            'approver',
            'work_type',
            'comment',
        )
hobbes3
  • 28,078
  • 24
  • 87
  • 116

1 Answers1

1

The ModelChoiceField documentation explains how to do this.

To change the empty label:

empty_label

    By default the <select> widget used by ModelChoiceField
    will have an empty choice at the top of the list. You can change the text
    of this label (which is "---------" by default) with the empty_label
    attribute, or you can disable the empty label entirely by setting
    empty_label to None:

    # A custom empty label
    field1 = forms.ModelChoiceField(queryset=..., empty_label="(Nothing)")

    # No empty label
    field2 = forms.ModelChoiceField(queryset=..., empty_label=None)

As for your second query, it is also explained the in docs:

The __unicode__ method of the model will be called to generate string
representations of the objects for use in the field's choices;
to provide customized representations, subclass ModelChoiceField and override
label_from_instance. This method will receive a model object, and should return
a string suitable for representing it. For example:

class MyModelChoiceField(ModelChoiceField):
    def label_from_instance(self, obj):
        return "My Object #%i" % obj.id

Finally, to pass some custom ajax, use the attrs argument for the select widget (which is what is used in the ModelForm field).

In the end, you should have something like this:

creator = MyCustomField(queryset=...,
                        empty_label="Please select",
                        widget=forms.Select(attrs={'onchange':'some_ajax_function()'})
Burhan Khalid
  • 169,990
  • 18
  • 245
  • 284
  • So for your `creator` example above, I'll have to remove `creator` from `fields` in `OrderCreateForm` and add your line of code for `creator = MyCustomField(...)`? Or do I keep `creator` as part of the list in `fields` (under `class Meta`)? – hobbes3 Apr 11 '12 at 15:13
  • 1
    You should remove it since you are rendering it with a custom field. – Burhan Khalid Apr 11 '12 at 17:34
  • I ended up hardcoding the ` – hobbes3 Apr 12 '12 at 06:50
  • You could have created a custom renderer (with a custom widget), and it would have taken care of the HTML as well. – Burhan Khalid Apr 12 '12 at 07:08
  • Well the `creator` list is dynamic depending on the `user.profile.user_type`. I understand I can change the `creator` list with `queryset` in `MyCustomField`, but unless I can pass an optional variable `user_type` to `class OrderCreateForm( ModelForm )`, then I'll have to create like 5 different (but mostly same) `OrderCreateForm`. – hobbes3 Apr 12 '12 at 14:39
  • You should open a new question, because what you ask is completely doable but the comments system isn't for long discussions. – Burhan Khalid Apr 12 '12 at 16:07
  • After some tinkering and `pdb` debugging, I found out that you actually need to do `fields['creator'] = MyCustomField(...)`. Otherwise you can't use `{{ form.creator }}` in the template. Just to let you know! :-) – hobbes3 Apr 16 '12 at 02:41