Passing a dynamic ChoiceField response to a ForeignKey instance

125 views
Skip to first unread message

simong

unread,
May 15, 2009, 12:58:58 PM5/15/09
to Django users
There's probably a very simple answer to this but I can't see it at
the moment:

This model:

class Product(models.Model):
user = models.ForeignKey(User, related_name="product_list",
editable=False)
productid = models.CharField(max_length=40, blank=True)
prodname = models.CharField(max_length=255, verbose_name='Product
name')
proddesc = models.TextField(blank=True, verbose_name='Description')
prodtopcat = models.ForeignKey(ProductTopCategory,
verbose_name='Product Main Category')
prodsubcat = models.ForeignKey(ProductSubCategory,
verbose_name='Product Sub Category')
unit = models.ForeignKey(Units, verbose_name='Unit')
video = models.ManyToManyField('Video', blank=True,
verbose_name='Video')
costperunit = models.DecimalField(max_digits=6, decimal_places=2,
verbose_name='Cost per unit')
costperlot = models.DecimalField(max_digits=6, decimal_places=2,
verbose_name='Cost per lot')
available = models.BooleanField(default=True)
featured = models.BooleanField(blank=True, null=True)

is used to generate a ModelForm with two modified fields that enable
the field 'prodsubcat' to be dynamically populated based on the
selection of 'prodtopcat'.

class DynamicChoiceField(forms.ChoiceField):
def clean(self, value):
return value

class ProductForm(ModelForm):
prodsubcat = DynamicChoiceField(widget=forms.Select(attrs=
{'disabled': 'true'}), choices =(('-1', 'Select Top Category'),))

I took this from an item I found on the web. I think setting the
'disabled' attribute is all I actually need.

The dynamic lookup is performed using jQuery.getJSON with this view:

def feeds_subcat(request, cat_id):
from django.core import serializers
json_subcat = serializers.serialize("json",
ProductSubCategory.objects.filter(prodcat = cat_id), fields=('id',
'shortname', 'longname'))
return HttpResponse(json_subcat, mimetype="application/javascript")

and the SELECT HTML is generated like this:

options += '<option value="' + j[i].pk + '">' + j[i].fields
['longname'] + '</option>';

This all works, but on submitting the form, I get the error 'Cannot
assign "u'17'": "Product.prodsubcat" must be a "ProductSubCategory"
instance' where "u'17'" is the option value of id_prodsubcat

What type of variable should I be passing (I assume int) and where
should I be converting it? In validation, in the pre-save signal, in
the save signal? Or should I be trying to convert it on the form
itself? Where is the ForeignKey instance save process documented?

TIA
Simon

Simon Greenwood

unread,
May 18, 2009, 7:02:03 AM5/18/09
to Django users
To bump this and to add more detail: now, when I attempt to save the
form, I am getting the error 'Cannot assign "u'266'":
"Product.prodsubcat" must be a "ProductSubCategory" instance.' where
266 is the primary key of the ProductSubCategory object selected from
a list that is looked up by the selection of a ProductTopCategory
option as shown in the Product model above. How can I return a
ProductSubCategory instance from the variable at form save time?

Simon

Simon Greenwood

unread,
May 18, 2009, 10:32:32 AM5/18/09
to Django users
To answer myself, this post: http://monogroncho.com/2008/10/django-gotchas-part-1/
is exactly what I was looking for, although the code as given seems
rather inelegant and gives me the AttributeError 'list' object has no
attribute 'widget'. My feeling is that I don't have to be returning a
list of ProductSubCategory objects, just the one that my choice
represents. Does anyone have a view on this? I can't be the only
person combining django and Ajax in this way.

Simon

Simon Greenwood

unread,
May 19, 2009, 8:25:51 AM5/19/09
to Django users
This is a sort of solution: A ChoiceField is populated at render time
by data either called from the ModelForm relationship or from a
ModelChoiceField lookup, and populates the HTML select field on the
form. It is this instance that is validated. Populating the choices
dictionary therefore deletes the data from the lookup:

class ProductForm(ModelForm):
prodsubcat = forms.ModelChoiceField(ProductSubCategory.objects,
widget=forms.Select(attrs={'disabled': 'true'}), choices =(('-1',
'Select Top Category'),))

and breaks the validation.

If the field is rendered without the choices dictionary, but with the
'disabled' attribute, it will display as a disabled select field.
Selecting an option from the prodtopcat select field calls the Ajax
lookup, which populates the prodsubcat field with filtered data in the
browser, but not in the field object. Selecting a category from the
prodsubcat field returns an option that can be validated from the
field object.

Simon
Reply all
Reply to author
Forward
0 new messages