Django 按表单中的类型将多对多选项分组

Django 按表单中的类型将多对多选项分组,django,django-models,django-forms,Django,Django Models,Django Forms,我有一个沙箱,里面有很多由一个或多个元素组成的项目 更多属性。每个属性都属于特定的属性类型(例如颜色、形状等) 我不知道如何呈现属性与其属性类型一起分组的表单 模型 class Sandbox(models.Model): item = models.ForeignKey('Item') class Item(models.Model): name = models.CharField() sandbox = models.ForeignKey(Sandbox)

我有一个沙箱,里面有很多由一个或多个元素组成的项目 更多属性。每个属性都属于特定的属性类型(例如颜色、形状等) 我不知道如何呈现属性与其属性类型一起分组的表单

模型

class Sandbox(models.Model):
    item = models.ForeignKey('Item')

class Item(models.Model):
    name = models.CharField()
    sandbox = models.ForeignKey(Sandbox)
    attributes = models.ManyToManyField('Attribute')

class Attribute(models.Model):
    name = models.CharField()
    type = models.ForeignKey('AttributeType')

class AttributeType(models.Model):
    name = models.CharField()

class ItemAttribute(models.Model):
    # intermediary model to hold references
    item = models.ForeignKey(Item)
    type = models.ForeignKey(AttributeType)
    attribute = models.ForeignKey(Attribute)
模型

class Sandbox(ModelForm):
    class Meta:
        model = Sandbox
每个属性类型只能有一个选项。例如,某些东西只能有一种颜色 或者一个形状

    AttributeType   Attribute       Users choice
    color
                    red
                    blue            [x]
                    green
                    shape

    shape
                    triangular
                    squared         [x]
                    spherical
这就是我被卡住的原因。如何在表单中将这些属性组合在一起,以及如何使用单选按钮仅为每种类型选择一个属性? 也许我最初的简单模型表示的想法在这里有缺陷? 我尝试过文档、StackOverflow和Google,但没有成功

欢迎提供任何提示和想法

我的解决方案

我建立了一个满足我需求的解决方案。 @bmihelac为我指出了一个很好的方向,这篇文章介绍了如何创建用于创建自定义表单的工厂方法。[见下文]

def make_sandbox_form(item):

    def get_attributes(item):
        item_attributes = ItemAttribute.objects.filter(item=item)
        # we want the first attribute type
        _attr_type = item_attributes[0].attribute.all()[0].attribute_type

        choices = []        # holds the final choices
        attr_fields = {}    # to hold the final list of fields
        for item_attrs in item_attributes.all():
            attributes = item_attrs.attribute.all().order_by('attribute_type')
            for attr in attributes:
                print attr.attribute_type, ' - ' , _attr_type
                if attr.attribute_type == _attr_type:
                    choices.append( ( attr.pk, attr.value ) )
                else:
                    d = {u'%s' % _attr_type : fields.ChoiceField(choices=choices, widget=RadioSelect)}
                    attr_fields = dict(attr_fields.items() + d.items() )
                    # set the _attr_type to new type and start over with next attribute type
                    _attr_type = attr.attribute_type
                    choices = []

        return attr_fields

    form_fields = {
        'item' : fields.IntegerField(widget=HiddenInput),
    }
    form_fields = dict(form_fields.items() + get_attributes(item).items())

    return type('SandboxForm', (forms.BaseForm, ), { 'base_fields' : form_fields})
调用调用此工厂方法的表单my,如下所示: 表单=制作沙盒表单()


(我希望我能超越所有人,但作为一名StackOverflow新手,我缺乏这样做的声誉。)

一个沙箱可以有很多物品,还是一个物品可以有很多沙箱?一个项目可以同时属于多个沙盒吗

我认为您希望一个沙箱包含多个项目:

class Sandbox(models.Model):
    name = models.CharField()

class Item(models.Model):
    name = models.CharField()
    sandbox= models.ForeignKey(Sandbox)
    attributes = models.ManyToManyField('Attribute')
同样的分析在这里也适用:

一个属性有多个属性类型,还是一个属性类型有多个属性

这就是你们之间的关系,我刚刚改变了模型的顺序

class AttributeType(models.Model):
    name = models.CharField()

class Attribute(models.Model):
    name = models.CharField()
    type = models.ForeignKey(AttributeType)
因此,每个项目都有一个属性,它们总是被赋予这些属性,颜色和形状

虽然您可以有一个包含如下数据的表:

pk type
1 green
2 circular
etc
我个人不会这么做,因为我认为逻辑上相同的数据应该分组在一起。形状与颜色具有不同的属性。例如,为了说明我的观点,如果你想要一种颜色的RGB呢?然后,当形状不需要时,您会有额外的列,这是令人困惑的。反之亦然,颜色没有维度

相反,我可能会这样做:

class Color(models.Mode):
     #info about colors

class Shape(models.Mode):
     #info about shapes

class Item(models.Model):
    name = models.CharField()
    sandbox= models.ForeignKey(Sandbox)
    color= models.ForeignKey(Color)
    shape= models.ForeignKey(Shape)
这也保证了每个用户只有一个选择,因为django.Forms中的ForeignKey默认为ChioceField(iirc)

至于覆盖它并使其成为单选按钮,只需在此处查看文档:


我将创建动态表单,为每个属性类型创建一个选择字段

然后,您可以轻松地用单选按钮替换小部件

这篇文章可能会有帮助:


您看过模板标签(允许您按属性分组)了吗?是的,我看过了,但是我找不到一种方法来使用表单实现它,只使用QuerySet。我的想法是,这个问题可以通过制作一个新的表单渲染方法来解决。。。问题是如何…是的,确实,你们敏锐的眼睛注意到我错过了从项目到沙箱的参考。更新了我的问题。谢谢关键是,在这种情况下,我无法为每种类型创建单独的模型,因为它们将由用户通过管理界面添加。AttributeType的数量可能会超过我最初的想法。@DroidBallon您仍然可以将它们分开,并将数据输入推送给用户。如果一种颜色不存在,就给他们一个创建新颜色/形状的选项。事实上,我的意思是,如果用户想要创建一个新的属性类型,比如“声音”,那么开发人员就必须为这个属性类型创建一个新模型来适应变化。还是我遗漏了什么?@droidballon是的,但这通常不是一件很难做到的事。在postgres和大多数其他db实现中,添加一个新列(在本例中为item)是相当容易的,而且没有痛苦。这并不是说你不能按照你最初提议的方式去做,你当然可以,但是如果你想知道关于那种类型的其他信息呢?比如,那种声音、震动、嘎嘎声等?您将得到许多对属性类型没有意义的列。如果您永远不需要进一步定义属性类型,那么使用原始方法可能不会有问题。