Reputation:
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
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
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