Skip to main content
 首页 » 编程设计

python之使用 Django ModelForm 的条件表单显示逻辑

2025年05月04日23think

我想我可以用很多很多数据库查询和很多可怕的 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 填充后续字段。

希望能帮助到你!