我正在为Django做一个防火墙管理应用程序,这是(简化)模型:
class Port(models.Model):
number = models.PositiveIntegerField(primary_key=True)
application = models.CharField(max_length=16, blank=True)
class Rule(models.Model):
port = models.ForeignKey(Port)
ip_source = models.IPAddressField()
ip_mask = models.IntegerField(validators=[MaxValueValidator(32)])
machine = models.ForeignKey("vmm.machine")
然而,我想要做的是向用户显示一个用于输入规则的表单,但其组织与模型完全不同:
O未打开
O Everywhere
O具体地址:
---------删除字段
---------删除字段
+
添加地址字段
...等
其中Not open
表示给定端口没有规则,Everywhere
表示给定端口只有一个规则(0.0.0.0/0),specific addresses
您可以根据需要添加任意数量的地址(我使用JQuery执行此操作),这将生成尽可能多的规则。
现在我做了一个完全“手工制作”的版本,这意味着我完全在我的模板中创建表单,使用前缀设置输入名称,并在我的视图中解析所有POST的内容(这非常痛苦,并且意味着有没有必要使用Web框架。)
我还有一个将规则聚合在一起的类,可以轻松地预填表单,其中包含“不开放,无处不在......”的信息。我将这些列表传递给模板,因此它充当我的模型和我的“手工”形式之间的接口:
class MachinePort(object):
def __init__(self, machine, port):
self.machine = machine
self.port = port
@property
def fully_open(self):
for rule in self.port.rule_set.filter(machine=self.machine):
if ipaddr.IPv4Network("%s/%s" % (rule.ip_source, rule.ip_mask)) == ipaddr.IPv4Network("0.0.0.0/0"):
return True
else :
return False
@property
def partly_open(self):
return bool(self.port.rule_set.filter(machine=self.machine)) and not self.fully_open
@property
def not_open(self):
return not self.partly_open and not self.fully_open
但这一切都相当难看!你是否有人知道是否有一种优雅的方式来做到这一点?特别是表单...我不知道如何有一个可以有一个未定义的字段数量的表单,也不知道如何将这些字段转换为Rule
个对象(因为所有的规则字段都必须是从表单中收集),既没有如何保存多个对象......我可以尝试入侵Form类,但对于这样一个特殊情况似乎太过分了。我有什么不好的功能吗?
答案 0 :(得分:1)
您可以通过继承Form
并在构造函数中添加字段来创建常用的Forms对象,如:
self.base_fields[field_name] = field_instance
对于Rule
,您可以根据自己的规则创建Field
自定义的自定义validate()
,并将其添加到您的自定义表单中。
是的,它必须是handmande(AFAIK),但它不是代码。
答案 1 :(得分:1)
好的,最后我通过使模型更接近我想要呈现给用户的模型来运行它。但与问题的主题相关:
1)嵌套的表单/表单集不是内置的Django功能,很难自己实现,实际上并不需要......相反,应该使用表单和表单集'前缀。
2)尝试使用不基于模型的表单,处理数据,然后将其重新注入模型中,代码要比修改模型稍微好一点,以获得更好的模型基于形式。 所以我做的是修改了这样的模型:
class PortConfig(Serializable):
port = models.ForeignKey(Port, editable=False)
machine = models.ForeignKey("vmm.machine", editable=False)
is_open = models.CharField(max_length=16, default="not_open", choices=is_open_choices)
class Rule(Serializable):
ip_source = models.CharField(max_length=24)
port_config = models.ForeignKey(PortConfig)
然后我只使用了PortConfig
的“模型表单集”和Rule
的“模型内联表单集”,其中PortConfig
作为外键,它完美无缺
3)我使用这个优秀的JS库http://code.google.com/p/django-dynamic-formset/来添加“添加字段”和“删除字段”链接......你几乎无所事事。