Reputation: 393
I am writing a simple form in Django which includes a ChoiceField allowing users to choose from a list of categories. For sure I want to pass the category_id to be processed. This is my code:
models.py:
class Category(models.Model):
category = models.CharField(max_length=128)
def __unicode__(self):
return self.category
class Product(models.Model):
code = models.CharField(max_length=75)
name = models.CharField(max_length=128)
price = models.DecimalField(max_digits=7, decimal_places=2)
category = models.ForeignKey(Category)
def __unicode__(self):
return self.name
forms.py
class AddProductForm(forms.Form):
category = forms.ChoiceField(label=_('Category'))
product = forms.CharField(label=_('Product'), widget=forms.TextInput())
code = forms.CharField(label=_('Code'), widget=forms.TextInput())
price = forms.DecimalField(label=_('Price'))
Now in the views.py I fill in the choices:
def add_product_form(request):
form = AddProductForm()
form.fields['category'].choices =[(c.id, c.category) for c in Category.objects.all()]
return render_to_response('product-form.html', {'form':form})
Now everything seems to be okay except when I submit the form. It complains about the IDs of the Category. It says: Select a valid choice. 1 is not one of the available choices
This is how I am processing the form:
def add_product(request):
if request.method == 'POST':
form = AddProductForm(request.POST)
if form.is_valid():
category = request.cleaned_data['category']
product = form.cleaned_data['product']
code = form.cleaned_data['code']
price = form.cleaned_data['price']
product = Product(code=code, name=product, price=price, category_id=category)
product.save()
return HttpResponseRedirect('/ms-admin/')
else:
form = AddProductForm() # more is required here to fill the choices again
return render_to_response('product-form.html', {'form':form})
I tried the same with TypedChoiceField but got the same invalid data. I know it has to do with the conversion between string and int and the unicode stuff. Can you please explain?
Upvotes: 0
Views: 3617
Reputation: 393
Thanks Daniel. I started learning Django not long time ago and I have this tendency to add a view to display a form, which is not necessary. So yes, I got rid of add_product_form and relied solely on add_product. As for the Category choice field now it is coded like this in the forms.py:
forms.py:
from django import forms
from django.utils.translation import ugettext_lazy as _
from myapp.models import Category
class AddProductForm(forms.Form):
category = forms.ModelChoiceField(label=_('Category'), queryset=Category.objects.all())
product = forms.CharField(label=_('Product'), widget=forms.TextInput())
code = forms.CharField(label=_('Code'), widget=forms.TextInput())
price = forms.DecimalField(label=_('Price'))
views.py:
def add_product(request):
if request.method == 'POST':
form = AddProductForm(request.POST)
if form.is_valid():
category = request.POST['category']
product = form.cleaned_data['product']
code = form.cleaned_data['code']
price = form.cleaned_data['price']
product = Product(code=code, name=product, price=price, category_id=category)
product.save()
# redirect to 'ms-admin/category_id' (the category to which belongs the newly added product
return HttpResponseRedirect('/ms-admin/'+category)
else:
form = AddProductForm()
return render_to_response('product-form.html', {'form':form})
I hope this helps newcomers to django. Please feel free to mention any ways of improving this above code ...
Upvotes: 0
Reputation: 599580
For some reason, you've separated the view that renders the form - including adding the choices - and the view that processes the submission. You don't add the choices on submission, so of course the values are not going to be valid.
You should get rid of the add_product_form
view altogether. You could move the form.fields['category'].choices...
bit into the add_product
view, but a better solution is to use the form field that's specifically designed for getting choices from a model or queryset - ie ModelChoiceField
.
Just use:
category = forms.ModelChoiceField(model=Category)
in your form definition.
Upvotes: 2