我想我可以用很多很多数据库查询和很多可怕的 jQuery 拼凑起来,以一种非常丑陋的方式来解决这个问题,但我希望有一种更简单的方法。
我正在为 Django 网站构建一个小型分类应用程序。网站上有各种媒体,它们被分类为属于某种类型、子类型、子子类型和子子子类型。例如,一些媒体可能被分类为音乐(流派)、布鲁斯(子流派)、孟菲斯蓝调(子子流派)、福音蓝调(第二子子流派)和第 32 街类卓蓝调(子子子流派,这个是编的)。
Genre、SubGenre、SubSubGenre 和 SubSubSubGenre 都是模型:
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
添加到站点的每条媒体都有一个 MTM 字段,将其与这些限定符中的一个或多个相关联,如下所示:
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
我们网站上的每个用户都有一个用户配置文件(使用一对一关系的基本用户模型的扩展)。在该用户配置文件中,他们可以告诉我们他们喜欢使用哪种类型的媒体。
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')
我想创建两种形式:一种用于创建新的用户配置文件,另一种用于创建新的媒体。用户应该能够定义他们喜欢的流派、子流派等。媒体上传者应该能够以相同的方式对其媒体进行分类。
我们只有几个流派,但每个流派都有十几个或更多子流派。每个子流派有 20 多个子子流派。大多数子子流派有 20 多个子子子流派。我不能一次将所有这些选项都扔到页面上——这太可怕了。
这就是我希望发生的事情。以“新用户资料表格”为例。我不想让用户一次设置他们喜欢的流派、子流派、子子流派和子子子流派,我希望只有一个多选表单,用户可以在其中设置他们的首选流派。 Then, when the preferred genre is selected (but before the form is submitted), sub-genre choices specific to that genre appear.选择子类型时,将该子类型作为 parent 的子子流域被视为选项等。
例如,假设我们在系统中定义了三种类型:音乐、书籍和播客。我们的新用户遇到了设置他们喜欢的类型的问题,并单击了音乐和书籍的复选框,但将 Podcast 留空。我希望该用户能够为 Music 和 Book 设置他们的首选子流派,并且 CheckboxSelectMultiple 字段仅填充以 Music 或 Book 作为父级的子流派(Music.subgenre_set .all() 或 Book.subgenre_set.all() 将返回适当的选择)。
再举一个例子,假设我们有一个上传播客的用户。在上传表单上,他们遇到了询问媒体属于什么类型的问题。用户选中“播客”框。现在我们想知道媒体属于哪些子类型。我们如何仅使用具有父“播客”的子类型填充 CheckboxSelectMultiple 字段?
在任一示例中,如果“Podcast”是选择的类型,则表单下一部分的适当子类型选择将由 Podcast.subgenre_set.all() 表示。如果该播客的子类型之一是“非小说类”,则选择该子类型还应显示适当的子子类型选择:Nonfiction.subsubgenre_set.all()。我不能只硬编码流派、子流派、子子流派和子子子流派名称,但是因为将来肯定会添加新的名称,这将造成扩展的噩梦。
我也觉得只是在幕后“加载”每一个可能的whatever_set,然后用javascript技巧揭开它会给数据库带来很大的压力。
如果我将表单分散到多个页面(以及对数据库的多个查询),这很容易。但是,如何在不破坏数据库的情况下以一种形式完成这一切?
感谢您阅读所有这些!
请您参考如下方法:
首先,我会重新考虑你是如何塑造你的流派的。我认为更好的解决方案是只有一个模型 Genre ,而不是为每个 SubGenre 指定新模型.这也将允许您扩展到任意深度,而无需更改模型结构。
class Genre(models.Model):
parent = models.ForeignKey(Genre, null=True, related_name='sub_genre')
description = models.CharField(max_length=200, unique=True)
slug = models.SlugField(unique=True)
def __unicode__(self):
return self.description
@property
def is_top_level(self):
return bool(self.parent)
然后我会使用
Form而不是
ModelForm像这样:
class GenreForm(forms.Form):
genre = forms.ModelChoiceField(
queryset=Genre.objects.filter(parent__is_null=True))
sub_genre = forms.ModelChoiceField(
queryset=Genre.objects.filter(
parent__is_null=False, parent__parent__is_null=True))
对于前端,我认为最简单的解决方案是从给每个 sub_genre 选项一个
class='hidden' 开始。 ,并使用一个简单的 javascript 片段在选择父项时取消隐藏后续字段上的选项。
如果您有大量的类型,您可能会发现在页面加载时间方面最有效的方法是使用 ajax 填充后续字段。
希望能帮助到你!
