如何将模型绑定到另外两个已经相关的模型,并显示信息?

时间:2012-06-18 14:06:40

标签: django django-models django-views

道歉,如果标题没有达到标题,我不知道如何说出我的问题。请随意编辑一个更好的。这是我想要做的:

我正在尝试使用几个不同的模型创建关系数据库。这个想法是:

  • 合同是唯一的
  • 任何合约都可以拥有(并分享)任意数量的项目
  • 需要为特定合同的每个特定项目存储唯一信息

到目前为止,我已按预期工作的前两个要点。第三点似乎有效,至少在管理面板中是这样。我可以将一条信息分配给特定合同的特定项目。

首先,我是否采取正确的方式?

其次,作为视图的一部分,如何一次列出上述所有信息?像这样:

-ContractName
    -ProjectName
    -ProjectName
        -SpecificInfo/Comments
    -ProjectName

以下是我目前的型号代码:

from django.db import models

class Project(models.Model):
    identifier = models.CharField(max_length=30)
    description = models.CharField(max_length=100)

    def __unicode__(self):
        return self.identifier

class Contract(models.Model):
    number = models.CharField(max_length=30)
    name = models.CharField(max_length=75)
    projects = models.ManyToManyField(Project)

    def __unicode__(self):
        return self.number

class Info(models.Model):
    contract = models.ForeignKey(Contract)
    project = models.ForeignKey(Project)
    title = models.CharField(max_length=75)
    info_text = models.TextField()

    def __unicode__(self):
        return self.title

以下是视图,目前仅显示合同和任何相关项目:

<ul>
{% for contract in contracts %}
    <b><li>{{ contract.number }} - {{ contract.name }}</li></b>
        {% for project in contract.projects.all %}
            <ul>
                <li>{{ project.identifier }} - {{ project.description }}</li>
            </ul>
        {% endfor %}
{% endfor %}
</ul>

1 个答案:

答案 0 :(得分:2)

子弹的子弹:

  • 合同是唯一的

    独一无二的?你可以使它对于名称的唯一或唯一,或者甚至是特定客户的唯一(虽然这没有多大意义,因为你可能希望客户有多个合同。重复业务FTW!)。要使特定字段唯一,您只需添加unique=True即可。

  • 任何合约都可以拥有(并分享)任意数量的项目

    合同有许多项目,项目有很多合同,所以这是一个明显的M2M,你已经涵盖了。

  • 需要为特定合同的每个特定项目存储唯一信息

    所以这就是你侧身的地方。如果需要将唯一信息存储在关系上,即它特别适用于合同和项目的组合,那么您需要一个“通过”模型。你基本上用Info模型创建了它,但你需要告诉Django实际使用这个模型而不是它的默认隐式模型,例如:

    projects = models.ManyToManyField(Project, through='Info')
    

(我建议将Info重命名为更具描述性的内容,例如ContractProject,以表明它是这两个模型的连接表。)

有了这个,您的模板看起来大致相似(基于您当前的代码,而不是我建议的名称更改):

{% for contract in contracts %}
    {{ contract.name }}
    {% for info in contract.info_set.all %}
        {{ info.project.name }}
        {{ info.title }}
        {{ info.info_text }}
    {% endfor %}
{% enfor %}

因此,这里的想法是,不是直接从合同中获取项目,而是需要获取连接表实例(以便您可以访问该信息),然后通过它拉动项目。请记住,虽然这是相对昂贵的,但是每个合约都有一个查询来获取Info个实例,然后为每个查询获得另一个项目查询。因此,如果您有每个合同的3个合同和3个项目,那么您已经在谈论1 + 3 * 3个查询或10个总计。这个数字当然会随着你拥有的合同/项目的数量呈指数级增长。

在Django 1.4中,您可以使用新的prefetch_related来获取一个查询中所有合同的所有信息实例,从而大大减少查询,但您仍然可以查询每个项目。

contracts = Contract.objects.prefetch_related('info')

使用前面的示例,您的查询计数将为1 + 1 + 3或总计5,因此您可以将其减半。值得庆幸的是,Django甚至允许您通过prefetch_related支持连接语法来进一步使用。

contracts = Contract.objects.prefetch_related('info_project')

这将获取所有合同,然后获取这些合同的所有信息实例,最后获取这些信息实例的所有项目。再次,使用前面的示例,将查询计数降至1 + 1 + 1,总计3。

如果您没有运行Django 1.4,您可以从名为django-batch-select的软件包中获得相同的功能。