代码之家  ›  专栏  ›  技术社区  ›  sebpiq

django:以django-ish的方式呈现一个与模型非常不同的表单,并具有多个字段值?

  •  1
  • sebpiq  · 技术社区  · 16 年前

    我目前正在为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")
    

    但是,我想做的是向用户显示一个用于输入规则的表单,但其组织与模型非常不同:

    端口80

    o未打开

    o无处不在

    o具体地址:

    -------删除字段

    -------删除字段

    + 添加地址字段

    端口443

    …等

    在哪里? Not open 表示给定端口没有规则, Everywhere 表示给定端口只有一个规则(0.0.0.0/0),并且 specific addresses ,您可以添加任意多的地址(我用jquery完成了这个操作),这将生成任意多的规则。

    现在我做了一个完全“手工制作”的版本,这意味着我完全在模板中创建表单,用前缀设置输入名称,并解析视图中所有已发布的内容(这非常痛苦,意味着使用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 对象(因为所有规则字段都必须从表单中收集),也不知道如何保存多个对象…嗯,我可以尝试侵入表单类,但对于这样一个特殊的案例来说,似乎工作量太大了。有什么好的功能我不见了吗?

    2 回复  |  直到 16 年前
        1
  •  1
  •   Almad    16 年前

    您可以通过子类化创建常规表单对象 Form 在构造器中添加字段,如:

    self.base_fields[field_name] = field_instance
    

    至于 Rule ,可以创建自定义 Field 那会的 validate() 根据您的规则添加到您的自定义表单中。

    所以是的,它一定是handmande(afaik),但它不是那么多代码。

        2
  •  1
  •   sebpiq    16 年前

    好的,最后我让模型更接近我想要向用户展示的内容,从而运行了它。但与问题主题相关:

    1) 嵌套的表单/表单集不是一个内置的Django功能,是一个由您自己实现的难题,实际上并不需要…相反,应该使用“forms”和“formset”前缀。

    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 ,带有 端口配置 作为外键,它运行得很完美

    3) 我用了这个很棒的JS库 http://code.google.com/p/django-dynamic-formset/ 要放置“添加字段”和“删除字段”链接…你几乎什么都没做。