Django中的动态表单需求
我即将在工作中开始一个大型的Django项目,主要的功能是处理表单。这个应用的用户将会使用很多表单。而且有一个要求是,应该可以在应用的管理界面中编辑这些表单。
也就是说,不需要添加或删除表单字段,但应该可以编辑字段的属性。例如,可以更改哪些字段是必填的等等。
有没有人有好的解决方案来实现这个?我在想,我可能需要对我想要动态的字段进行子类化,然后在里面做一些处理。我猜所有模型的字段都需要设置“blank=True/null=True”,并且以某种方式在一个单独的模型中添加一些元信息,来声明哪些字段是必填的。然后在显示和验证表单时使用这些信息。
在我开始做这个之前,我真的希望能得到一些关于如何设计这样一个解决方案的建议,有人有什么想法吗?
经过更多的研究,我了解到可以使用工厂函数来返回带有特定属性的表单。所以看起来这个问题并不难解决。
我会创建一个函数,返回一个设置了正确属性/字段的表单。但我还没有找到一个好的解决方案来管理管理界面中的这一部分。我在想,我可以创建一个db.Model来存储关于其他模型字段的信息,在这里我可以设置哪些是必填的等等。
然后在返回表单的函数中,遍历那个模型,返回一个带有正确属性的表单。但如何以好的方式创建那个模型(应该反映另一个模型的字段)呢?
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()
一起使用,可以帮助我们判断这个字段应该用哪个类(比如 CharField
、EmailField
、ChoiceField
等等)。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
模型的一部分,以便进行更新。
希望这些能帮助你设计出适合你表单的模型。
你应该使用特定的模型来处理这个问题。对于每一个可能需要自定义的表单,都必须在数据库中创建一个新的条目。我想,它应该是这样的:
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。