I think I can figure out how to do this in a very ugly way with many, many database queries and lots of horribly cobbled together jQuery, but I'm hoping there's a simpler approach.
I am building a small classification app for a Django website. There are various pieces of media on the site that are classified as belonging to a certain genre, sub-genre, sub-sub-genre, and sub-sub-sub-genre. For instance, a bit of media might be classified as Music (genre), Blues (sub-genre), Memphis Blues (sub-sub-genre), Gospel Blues (a second sub-sub-genre), and 32nd Street Banjo Blues (sub-sub-sub-genre, this one's made up).
Genre, SubGenre, SubSubGenre, and SubSubSubGenre are all models:
class Genre(models.Model):
description = models.CharField(max_length=200, unique=True)
slug = models.SlugField(unique=True)
def __unicode__(self):
return self.description
class SubGenre(models.Model):
parent = models.ForeignKey(Genre, related_name='sub_genre')
description = models.CharField(max_length=200, unique=True)
slug = models.SlugField(unique=True)
def __unicode__(self):
return self.description
class SubSubGenre(models.Model):
parent = models.ForeignKey(SubGenre, related_name='sub_sub_genre')
description = models.CharField(max_length=200, unique=True)
slug = models.SlugField(unique=True)
def __unicode__(self):
return self.description
class SubSubSubGenre(models.Model):
parent = models.ForeignKey(SubSubGenre, related_name='sub_sub_sub_genre')
description = models.CharField(max_length=200, unique=True)
slug = models.SlugField(unique=True)
def __unicode__(self):
return self.description
Every piece of media added to the site has a MTM field associating it with one or more of these qualifiers, like so:
class Media(models.Model):
genres = models.ManyToManyField(Genre)
sub_genres = models.ManyToManyField(SubGenre)
sub_sub_genres = models.ManyToManyField(SubSubGenre)
sub_sub_sub_genres = models.ManyToManyField(SubSubSubGenre)
#other fields
Every user on our site has a user profile (an extension of the base user model using a one-to-one relationship). In that user profile, they can tell us what type of media they like to consume.
class UserProfile(models.Model):
user = models.OneToOneField(User)
preferred_genres = models.ManyToManyField(Genre, related_name='genre_pref')
preferred_sub_genres = models.ManyToManyField(SubGenre, related_name = 'subgenre_pref')
preferred_sub_sub_genres = models.ManyToManyField(SubSubGenre, related_name = 'subsubgenre_pref')
preferred_sub_sub_sub_genres = models.ManyToManyField(SubSubSubGenre, related_name = 'subsubsubgenre_pref')
I want to create two forms: one to create a new user profile, and one to create a new piece of Media. The user should be able to define their preferred genres, sub genres, etc. A media uploader should be able to classify their Media in the same way.
We only have a couple of Genres, but each one has a dozen or more sub-genres. Each sub-genre has 20+ sub-sub genres. Most sub-sub genres has 20+ sub-sub-sub genres. I can't just throw all of these options onto the page at once - it's horribly overwhelming.
Here's what I'd like to have happen. Take the "new user profile form" for example. Instead of having the user set their preferred genres, sub-genres, sub-sub genres, and sub-sub-sub genres all at once, I'd like for there to just be a multiple-choice form where the user can set their preferred genre. Then, when the preferred genre is selected (but before the form is submitted), sub-genre choices specific to that genre appear. When a sub-genre is selected, the sub-sub genres who have that sub-genre as a parent then appear as options, etc.
For example, say we have three Genres defined in the system: Music, Book and Podcast. Our new user encounters the question to set their preferred genres and clicks the checkboxes for Music and Book, but leaves Podcast blank. I would like for that user to then be able to set their preferred sub-genres for Music and for Book, and for the CheckboxSelectMultiple fields to be populated only with the sub-genres that have either Music or Book as their parent (Music.subgenre_set.all() or Book.subgenre_set.all() would return the appropriate choices).
For another example, say we have a user who uploads a podcast. On the upload form they encounter the question asking what Genre(s) the Media is in. The user checks the box for "podcast." Now we want to know what sub-genres the Media is in. How do we populate the CheckboxSelectMultiple field with only the sub-genres who have parent "podcast?"
In either example, if "Podcast" is the genre selected, then the appropriate sub-genre choices for the next part of the form would be represented by Podcast.subgenre_set.all(). If one of that podcast's subgenres is "nonfiction," then selecting that subgenre should also bring up the appropriate sub-sub genre choices: Nonfiction.subsubgenre_set.all(). I can't just hardcode the genre, sub-genre, sub-sub genre, and sub-sub-sub genre names however since new ones are certain to be added in the future and that would create a scaling nightmare.
I also feel like just "loading up" every possible whatever_set behind the scenes then unveiling it with javascript trickery puts a heck of a lot of strain on the DB.
If I spread the form out over multiple pages (and multiple queries to the DB), it's easy. But how do I do this all in one single form without annihilating my database?
Thanks for reading all of this!