For my first Django tip on this brand new blog, I will provide some insight into my most recent head-scratcher: How do you get a ModelForm to display something like this…

…when you have your models laid out like this:
class Category(models.Model):
name = models.CharField(max_length=50) # Auto, Beauty, etc.
class SubCategory(models.Model):
category = models.ForeignKey(Category)
name = models.CharField(max_length=50) # Parts, Fragrances, etc.
class Merchant(models.Model):
sub_categories = models.ManyToManyField(SubCategory, verbose_name='Areas your business deals in')
On Dealit, the SubCategory model is used by many other models in a ManyToMany relationship, for example, when a merchant signs up (as I’m showing in the Merchant model above). But building out a ModelForm for Merchant will only display SubCategories without the related Category in an <optgroup>.
In order to fix that, we do this in our form…
class MerchantForm(ModelForm):
def __init__(self, *args, **kwargs):
super(MerchantForm, self).__init__(*args, **kwargs)
self.fields['sub_categories'].choices = categories_as_choices()
def categories_as_choices():
categories = []
for category in Category.objects.all():
new_category = []
sub_categories = []
for sub_category in category.subcategory_set.all():
sub_categories.append([sub_category.id, sub_category.name])
new_category = [category.name, sub_categories]
categories.append(new_category)
return categories
Here, we create a lot of nested lists with categories_as_choices() and tell Django to override the default list in the form. This is how Django wants it if you want it displaying <optgroup>s. Here’s an example from the documentation:
MEDIA_CHOICES = (
('Audio', (
('vinyl', 'Vinyl'),
('cd', 'CD'),
)
),
('Video', (
('vhs', 'VHS Tape'),
('dvd', 'DVD'),
)
),
('unknown', 'Unknown'),
)
This is exactly what we end up creating with categories_as_choices(), except we create lists instead of tuples. Both are acceptable.
Hope that helps.
nice one. didn’t know about this. thx for sharing
Glad you learned something new!
Man, this is sweet. Thanks for sharing it.
You welcome! Glad you found it helpful.
oh boy was that great!
now onto some js
This was definitely the easy part then :-p
Very useful tip!
Thanks!