带有Django ModelForm的条件表单显示逻辑

时间:2014-04-21 18:07:56

标签: python django

我想我可以用非常丑陋的方式弄清楚如何以非常丑陋的方式执行此操作,许多数据库查询和许多可靠的拼凑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')

我想创建两个表单:一个用于创建新的用户配置文件,另一个用于创建新的Media。用户应该能够定义他们喜欢的流派,子流派等。媒体上传者应该能够以相同的方式对他们的媒体进行分类。

我们只有几个类型,但每个类型都有十几个或多个子类型。每个子流派都有20多个子流派。大多数子类型有20多个子子类型。我不能一下子把所有这些选项都扔到页面上 - 这太可怕了。

这就是我想要发生的事情。以“新用户配置文件表单”为例。而不是让用户同时设置他们喜欢的流派,子流派,子流派和子子流派,我希望这里只是一个多选形式,用户可以设置他们的首选类型。然后,当选择优选类型时(但在提交表单之前),出现特定于该类型的子类型选择。当选择子流派时,将子流派作为父节目的子流派则显示为选项等。

例如,假设我们在系统中定义了三种类型:音乐,书籍和播客。我们的新用户遇到了设置其首选流派的问题,并点击了音乐和图书的复选框,但将Podcast留空。我希望该用户能够为Music和Book设置他们喜欢的子类型,并且CheckboxSelectMultiple字段只能填充以Music或Book作为其父级的子类型(Music.subgenre_set) .all()或Book.subgenre_set.all()将返回适当的选项)。

再举一个例子,假设我们有一位上传播客的用户。在上传表格中,他们遇到了询问媒体所属类型的问题。用户选中“播客”框。现在我们想知道媒体所处的子流派。我们如何仅使用具有父“podcast?”的子流派来填充CheckboxSelectMultiple字段?

在任何一个示例中,如果“Podcast”是选定的类型,则表单下一部分的相应子类型选项将由Podcast.subgenre_set.all()表示。如果其中一个播客的子类是“非小说”,那么选择该子类型也应该提出适当的子子类型选择:Nonfiction.subsubgenre_set.all()。我不能仅仅对类型,子类型,子子类型和子子类型名称进行硬编码,因为将来肯定会添加新的名称,这会产生缩放噩梦。

我也觉得只是“加载”幕后的每一个可能的设置然后用javascript技巧揭开它,这给数据库带来了很多压力。

如果我将表单分散到多个页面(以及对DB的多个查询),这很容易。但是如何在不消灭我的数据库的情况下以单一形式完成所有这些操作?

感谢您阅读所有这些内容!

2 个答案:

答案 0 :(得分:4)

首先,我会重新考虑你是如何建模你的流派的。我认为一个更好的解决方案是只有一个模型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填充后续字段。

希望它有所帮助!

答案 1 :(得分:2)

首先,我认为你的模型太多了。所有模型都具有完全相同的字段,因此您最好只使用一个模型(称为分类),其名称,描述,父级和级别:因此类型将具有级别0而没有父级,SubGenre将具有级别1和0级的父级,依此类推。 (你也可以用MPTT之类的东西来做这件事,但由于订购并不重要,这可能有点过头了。)

要回答你的问题,我认为Javascript是要走的路,但不是通过预加载一切。相反,您应该使用Ajax:在选择类型时,您向服务器执行Ajax请求,返回相关的子类型。由于这是一个轻量级请求,因此用户不应注意延迟,因此您可以构建复选框或其他任何内容供用户选择。

相关问题