user1162541
user1162541

Reputation:

Django Modelforms: how to show a select for a CharField?

I'm using ModelForms in Django to create my form. This works well. However, there is a field that is a charfield in the database, but for a particular form I want to restrict the options that users can enter to a list of specific items - ideally shown in a <select>. What would be the right way to do this? Here is my code:

form = ModelForm(request.POST or None, instance=Street.objects.get(pk=1))

In my models:

class Street(models.Model):
    name = models.CharField(max_length=255)

I have tried the following:

from django import forms
CHOICES = [('1', 'First'), ('2', 'Second')]
choice_field = forms.ChoiceField(widget=forms.Select, choices=CHOICES)
form.fields["street"].widget = choice_field

Which gives this error:

'ChoiceField' object has no attribute 'attrs'

Upvotes: 2

Views: 4113

Answers (2)

mahrous-amer
mahrous-amer

Reputation: 106

If you would like to fix this attribute error you can either pass your choices as a python Tuple or set TypedChoiceField() and give it the attribute coerce reference this: https://stackoverflow.com/a/18200849/14127147

from django import forms
CHOICES = (("1", "First"), ("2", "Second"))
choice_field = forms.ChoiceField(widget=forms.Select, choices=CHOICES)
form.fields["street"].widget = choice_field

Upvotes: 4

marcinn
marcinn

Reputation: 546

The preferred way to use ModelForm is to create own class:

class StreetForm(forms.ModelForm):
    class Meta:
        model = Street
        fields = '__all__'

This would create a form for editing Street attributes like name.

If you want to create a form with streets to select, and you should define a new form - an another ModelForm if Street is in a relation to the other model (i.e. defined by ForeignKey), or a plain Form otherwise.

Assuming the second scenario you should define:


class OtherForm(forms.Form):
    street = forms.ModelChoiceField(queryset=Street.objects.all())

To narrow streets you have two choices, depending on the filtering criteria.

For static filtering (where criteria would not change at runtime) you can narrow records by defining a filtered queryset:


class OtherForm(forms.Form):
    street = forms.ModelChoiceField(
        queryset=Street.objects.filter(name__ilike='a%')
    )

For dynamic filtering you may override a field's queryset instance in __init__ method:

class OtherForm(forms.Form):
    street = forms.ModelChoiceField(
        queryset=Street.objects.all()
    )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)  # must be called first
        self.fields["street"].queryset = Street.objects.filter(...)

The default widget for ModelChoiceField is a Select widget.

But if you need to prepare custom choices for street names, you should define a choices list (as in your example) and redefine a name field of the StreetForm:


STREET_NAME_CHOICES = [
   ('Street name 1', 'Street name 1'), 
   ('Street name 2', 'Street name 2')
]

class StreetForm(forms.ModelForm):
    name = forms.ChoiceField(
        widget=forms.Select,
        choices=STREET_NAME_CHOICES
    )
    class Meta:
        model = Street
        fields = '__all__'

Upvotes: 5

Related Questions