Django模型加入一对多关系以在模板中显示

时间:2011-02-24 22:57:27

标签: database django django-models django-templates left-join

不确定描述问题的最佳方法是什么。我有2个表联系和属性。联系表每人有1个条目,属性表每人有0个,1个或多个条目。他们目前加入了一个“假的”外键,这个外键实际上不是外键。如果我需要添加外键,那么处理旧数据并不是什么大问题,原来没有外键。所以表格如下:

接触:

class contact(models.Model):
    contactId = models.AutoField(primary_key=True, db_column='contactId')
    firstName = models.CharField(max_length=255, null=True, db_column='firstName')
    middleName = models.CharField(max_length=255, null=True, db_column='middleName')
    lastName = models.CharField(max_length=255, null=True, db_column='lastName')

属性:

class attribute(models.Model):
    attributeId = models.AutoField(primary_key=True, db_column='attributeId')
    contactId = models.IntegerField(db_index=True, null=True, db_column='contactId')
    attributeValue = models.TextField(null=True, db_column='attributeValue')

所以我已经正确设置了Django模型来表示这些表。现在我需要完成的是一个循环遍历这些表的视图和模板,以便它以下列格式生成一个xml doc:

<contacts>
    <contact>
        <contactId></contactId>
        <firstName></firstName>
        <lastName></lastName>
        <attributes>
            <attribute>
                <attributeId></attributeId>
                <attributeValue></attributeValue>
            </attribute>
        </attributes>
    </contact>
</contacts>

因此,将列出所有联系人以及与每个联系人关联的所有属性。

我确信有一种简单的方法可以实现这一目标。在其他语言中,我会简单地编写两个循环查询来遍历联系人,然后遍历每个联系人的属性。然而,我工作的公司正在迁移到一个新的平台,并希望用django / python编写新的应用程序,我仍然在努力学习这些。

任何人都可以提供的帮助表示赞赏。

3 个答案:

答案 0 :(得分:8)

假设您已经设置了django模型以使用当前的数据库设置,如果我没有设置外键,我会执行以下操作。

contacts = Contact.objects.all()

for contact in contacts:
    contact.attributes = Attribute.objects.filter(contactId=contact.pk) 


return render_to_response("mytemplate.html", {'contacts': contacts })

或者,保存一些查询;

attributes_map = dict( 
    [(attribute.contactId, attribute) for attribute in \
        Attribute.objects.filter(contactId__in=[contact.pk for contact in contacts])]
    )

for contact in contacts:
    contact.attributes = attributes_map.get(contact.pk)

return render_to_response("mytemplate.html", {'contacts': contacts })

模板

<contacts>
{% for contact in contacts %}
    <contact>
        <contactId>{{ contact.pk }}</contactId>
        <firstName>{{ contact.firstName }}</firstName>
        <lastName>{{ contact.lastName }}</lastName>

        {% if contact.attributes %}
            <attributes>
            {% for attribute in contact.attributes %}
                <attribute>
                    <attributeId>{{ attribute.pk }}</attributeId>
                    <attributeValue>{{ attribute.attributeValue }}</attributeValue>
                </attribute>
            {% endfor %}
            </attributes>
        {% endif %}

    </contact>
{% endfor %}
</contacts>

答案 1 :(得分:1)

听起来你在遗产db地狱。

因此,您可能会发现很难使用像ForeignKey这样的标准django模型,但是您仍然应该努力将这样的代码保留在视图代码之外。

因此,在您的联系人类中定义一些方法可能会更好,例如......

def attributes(self):
    return Attribute.objects.filter(contactId=self.contactId)

然后,您可以在模板中使用它。

<attributes>
    {% for attribute in contact.attributes.all %}
        <attribute>
            ...
        </attribute>
    {% endfor %}
</attribute>

尽管如此,模板中的contact.attributes.all可能并不理想(确定模板中的关系)。但是添加到视图中以将某些内容添加到上下文中会很简单。

答案 2 :(得分:0)

Yuji的回答很像Nick Johnson的App Engine的prefetch_refprops: http://blog.notdot.net/2010/01/ReferenceProperty-prefetching-in-App-Engine

我找到了优化此代码的方法:

attributes_map = dict( 
    [(attribute.contactId, attribute) for attribute in \
        Attribute.objects.filter(contactId__in=[contact.pk for contact in contacts])]
    )

将其替换为:

attributes_map = dict(Attribute.objects
    .filter(contactId__in=set([contact.pk for contact in contacts]))
    .values_list('contactId', 'attribute'))

此替换通过以下方式节省查询时间:

  • 将键列表压缩为set()和
  • 仅检索dict()使用的两个字段。

它还利用了.values_list()返回dict()所期望的元组列表这一事实,但这仅在第一个字段(即'contactId')是唯一的时才有效。

要为多个外键扩展此解决方案,请使用上面的代码为每个键字段创建一个映射。然后,在这个循环内部:

for contact in contacts:
    contact.attributes = attributes_map.get(contact.pk)

为每个外键添加一行。例如,

attributes_map = dict(Attribute.objects
    .filter(contactId__in=set([contact.pk for contact in contacts]))
    .values_list('contactId', 'attribute'))
names_map = dict(Name.objects
    .filter(nameId__in=set([contact.nameId for contact in contacts]))
    .values_list('nameId', 'name'))

for contact in contacts:
    contact.attributes = attributes_map.get(contact.pk)
    contact.name = names_map.get(contact.nameId)

如果你曾经有一个模板为结果集的每一行生成大量额外的SQL调用(为了查找每个外键的值),这种方法可以通过预加载所有数据来节省大量资金在将其发送到模板之前。