Reputation: 251
I am working on a catalog of classified grouped by categories.
However when I submit my form, I get the following error message:
Caught ValueError while rendering: Cannot assign "u'9'": "Classified.category" must be a "Category" instance.
I believe this is because Django expects a Category objects instead of a simple integer which corresponds to the chosen Category ID.
Here is how I wrote the system: A classified is linked to one category. The category system is hierarchical with a parent category and a list of child categories. For example I have something like this:
Electronics
|-- IPad
|-- IPods
|-- ...
So I have the following models:
class Category(BaseModel):
# [...]
name = models.CharField(u'Name', max_length=50)
slug = AutoSlugField(populate_from='name', slugify=slugify, unique=True,
unique_with='name', max_length=255, default='')
parent = models.IntegerField(u'parent', max_length=10, null=False,
default=0)
objects = CategoryManager()
# [...]
class Classified(BaseModel):
# [...]
category = models.ForeignKey(Category, related_name='classifieds')
I created the following manager:
class CategoryManager(Manager):
def categoryTree(self):
tree = self.raw("SELECT"
" P.id, P.name parent_name, P.slug parent_slug, P.id parent_id,"
" C.name child_name, C.slug child_slug, C.id child_id"
" FROM classified_category C"
" LEFT JOIN classified_category P ON P.id = C.parent"
" WHERE C.parent <> 0"
" ORDER BY P.name, C.name;")
categoryTree = []
current_parent_id = tree[0].parent_id
current_parent_name = tree[0].parent_name
option_list = []
for c in tree:
if current_parent_id != c.parent_id:
categoryTree.append((current_parent_name, option_list))
option_list = []
current_parent_id = c.parent_id
current_parent_name = c.parent_name
option_list.append((c.child_id, c.child_name))
categoryTree.append((current_parent_name, option_list))
return category
And my Django form contains the following:
class ClassifiedForm(forms.ModelForm):
# [...]
category = forms.ChoiceField(label=u'Category', required=True,
choices=Category.objects.categoryTree(), widget=forms.Select())
# [...]
If I use category = forms.ModelChoiceField(Category.objects.all()) everything works fine but I need to control how the <select>
field is displayed with a list of <optgroup>
. This is why use categoryTree()
But unfortunately using CategoryManager.categoryTree() breaks my form validation and I do not know how to fix my problem.
If I could be pointed to where I was wrong and how I can fix this, that would be awesome.
Thanks in advance for your help.
Upvotes: 2
Views: 415
Reputation: 2468
You can and should still use a ModelChoiceField. The list of choices can be modified in the init method of the form class - i.e.
class ClassifiedForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ClassifiedForm, self).__init__(*args, **kwargs)
# Set the queryset for validation purposes.
# May not be necessary if categoryTree contains all categories
self.fields['category'].queryset = Category.objects.categoryTreeObjects()
# Set the choices
self.fields['category'].choices = Category.objects.categoryTree()
Also, you should look carefully at the django-mptt package. It looks like you may be reinventing the wheel here.
Upvotes: 0
Reputation: 31981
Quick solution is to save category manually
class ClassifiedForm(forms.ModelForm):
# [...]
category = forms.ChoiceField(label=u'Category', required=True,
choices=Category.objects.categoryTree(), widget=forms.Select())
class Meta:
exclude=('category',)
def save(self):
classified = super(ClassifiedForm, self).save(commit=False)
classified.category = Category.objects.get(id=self.cleaned_data['category'])
classified.save()
return classified
Upvotes: 1