Django中的动态表单需求

4 投票
2 回答
1719 浏览
提问于 2025-04-15 19:04

我即将在工作中开始一个大型的Django项目,主要的功能是处理表单。这个应用的用户将会使用很多表单。而且有一个要求是,应该可以在应用的管理界面中编辑这些表单。

也就是说,不需要添加或删除表单字段,但应该可以编辑字段的属性。例如,可以更改哪些字段是必填的等等。

有没有人有好的解决方案来实现这个?我在想,我可能需要对我想要动态的字段进行子类化,然后在里面做一些处理。我猜所有模型的字段都需要设置“blank=True/null=True”,并且以某种方式在一个单独的模型中添加一些元信息,来声明哪些字段是必填的。然后在显示和验证表单时使用这些信息。

在我开始做这个之前,我真的希望能得到一些关于如何设计这样一个解决方案的建议,有人有什么想法吗?


经过更多的研究,我了解到可以使用工厂函数来返回带有特定属性的表单。所以看起来这个问题并不难解决。

我会创建一个函数,返回一个设置了正确属性/字段的表单。但我还没有找到一个好的解决方案来管理管理界面中的这一部分。我在想,我可以创建一个db.Model来存储关于其他模型字段的信息,在这里我可以设置哪些是必填的等等。

然后在返回表单的函数中,遍历那个模型,返回一个带有正确属性的表单。但如何以好的方式创建那个模型(应该反映另一个模型的字段)呢?

2 个回答

2

这不是你问题的确切答案,但我做了类似的事情,觉得可能对你有帮助。我创建了一个完全可定制的表单,这样最终用户就可以根据自己的需要来调整表单。

我最后得到了两个模型,一个叫 BuiltForm,另一个叫 BuiltFormField

class BuiltForm(models.Model): 
    name = models.CharField(max_length=32) 
    def form(self, data=None, initial=None):
        form = BuiltFormGenericForm(data, initial=initial)
        form.addBuiltFormFields(BuiltFormField.objects.filter(builtform=self, disabled=0))
        return form            

class BuiltFormField(models.Model):
    builtform = models.ForeignKey(BuiltForm)
    type = models.CharField(max_length=32, choices=ALL_FIELD_TYPES)
    label = models.CharField(max_length=32)
    fieldname = models.CharField(max_length=32)
    helptext = models.CharField(max_length=256, blank=True)
    required = models.BooleanField(default=False)
    sort_order = models.IntegerField(default=0)
    disabled = models.BooleanField(default=False)
    options = models.TextField(blank=True)
    def field(self):
        field = ALL_FIELD_MAPS.get(self.type)
        ## Build out the field, supplying choices, `required`, etc.

这里有几个不太常见的地方。 ALL_FIELD_TYPES 是一个字段类型的映射表,表示表单中允许使用的字段类型。这个映射表和 dict() 一起使用,可以帮助我们判断这个字段应该用哪个类(比如 CharFieldEmailFieldChoiceField 等等)。options 也是一个经过 pickle 处理的选项列表,后面可以在 ChoiceField 中使用。这样我就能创建一个任意的选项列表,而不需要单独去数据库查询。

另一个重要的部分是一个自定义的 Form 类,它可以用 BuiltForm 中的字段来填充。我的看起来是这样的:

class BuiltFormGenericForm(forms.Form):
    built_form_fields = {}
    builtform = None
    def addBuiltFormFields(self, fields):
        for field in fields:
            self.fields[field.label] = field.field()
            self.built_form_fields[field.pk] = field
    def is_valid(self):
        # Do validation here.  My code for this is pretty big because of custom fields
        # and calculations that I have to squeeze in.

这些 BuiltFormField 对象并不是通过管理界面创建的,而是通过一个自定义的界面,里面有很多 JavaScript 让它更易于使用。不过,你当然可以在管理界面中展示 BuiltFormField 模型的一部分,以便进行更新。

希望这些能帮助你设计出适合你表单的模型。

2

你应该使用特定的模型来处理这个问题。对于每一个可能需要自定义的表单,都必须在数据库中创建一个新的条目。我想,它应该是这样的:

class FormSettings(Model):
  form = CharField(..)

class FormAttrib(Model):
  form_settings = ForeignKey(FormSettings)
  field = CharField(..)
  attrib_name=CharField(..)
  attrib_value=CharField(..)

在 FormSettings.form 中,你应该存储一些表单的地址,比如说 .,当表单被构建时(在 init 中),它应该在数据库中查找这个条目,并使用为它描述的属性。

如果你为你的表单使用自己的元类,并在创建类时让它在数据库中注册(创建合适的 FormSettings 条目),那么就可以轻松地创建数据库条目。这一过程只会在启动时执行一次,应该不会太麻烦。

希望这能帮到你一点。如果你还有其他问题,我很乐意帮助你 :-) 我喜欢这些不标准的 Django 应用 :-) 这才是乐趣的开始 :-)

编辑:

好吧,假设你有一个叫做 food 的应用和一个名为 FavouriteFoodForm 的表单,其中有一个 food_name 字段。你在数据库的 formsettings 表中存储为 food.FavouriteFoodForm,并添加一个属性设置:field='food_name',attrib_name='required',attribute_value='True'(虽然不是完美,但也还不错)。

现在在 FavouriteFoodForm 中,你需要查找数据库中的设置,所以你这样做:

settings = FormSettings.objects.get(form=app_name + self.__class__.name)

然后你遍历这些设置

for setting in settings.formattrib_set():

在这里你简单地调用 exec 并设置合适的属性:

exec("getattr(self, settings.field)[attrib_name] = %s" % attrib_value)

这应该将属性设置为 required=True。

撰写回答