Form field for a foreign key in ModelForm with too many choices for ModelChoiceField?

Solution 1:

The ModelChoiceField documentation says to use something else if the number of choices is large.

The documentation suggests using a different widget (otherwise a user will have to select from a dropdown with too many items), but you don't necessarily need an entirely different field.

If you want the field of the bound form to be n instance of Sample, then ModelChoiceField is still appropriate.

To avoid the problem anticipated in the documentation, you could just change the widget for the field. You might need to decide exactly what that is. One simple choice would be to use a NumberInput widget where the user just enters an integer for the foreign key.

from django.forms.widgets import NumberInput

class AssignmentForm(ModelForm):
    sample = ModelChoiceField(queryset=Sample.objects.all(), widget=NumberInput)
    class Meta:
        model = Assignment
        fields = ['sample']

select the sample based on the contents of the sample's alt field

What you want here is a separate issue from what you quoted from the documentation. You can choose to implement this with or without changing the widget.

If you want the user to provide the alt value rather than the primary key of the Sample, you can use the to_field_name argument for ModelChoiceField (note this is only appropriate here because your alt field is unique)

class AssignmentForm(ModelForm):
    sample = ModelChoiceField(
        queryset=Sample.objects.all(),
        widget=NumberInput,
        help_text="Enter the alt of the sample",
        to_field_name='alt'
    )

    class Meta:
        model = Assignment
        fields = ["sample"]

In order for the initial value to render correctly when rendering a bound form, you can provide the initial keyword argument when instantiating the bound form:

form = AssignmentForm(instance=inst,
                      initial={'sample': inst.sample.alt})

Alternatively, you can override the __init__ method of the form to do this automatically when the form is instantiated:

class AssignmentForm(ModelForm):
    ...
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if self.instance.pk:
            self.initial.update({'sample': self.instance.sample.alt})