【问题标题】:Django making recursive calls on relationshipDjango对关系进行递归调用
【发布时间】:2015-03-13 21:59:55
【问题描述】:

我在 Django 1.6.5 上有以下模型

class Location(models.Model):
    parent = models.ForeignKey('Location', blank=True, null=True)
    name = models.CharField(max_length=50)

    class Meta:
        db_table = 'locations'
        ordering = ('parent__name', 'name')

    def __unicode__(self, ):
        if self.parent!= None:
            return self.name + " (" + self.parent.name +  ")"
        else:
            return self.name        

我在下拉列表中列出了这些位置,所以我想显示名称 +(如果可用,则为 parent.name)

知道如何在不必打数千次电话的情况下做到这一点吗?

【问题讨论】:

  • 您可能想查看类似django-mptt 的内容,它可以让您非常高效地执行这些查询。
  • 来吧,一定有其他方法可以使用第三方库。这是世界上最常见的事情。
  • 如果你坚持在 sql 中对树数据进行分层存储,就没有办法做到这一点。您应该查看物化路径模型的嵌套集en.wikipedia.org/wiki/Nested_set_model。据我所知,django-mptt 都做到了
  • 多么奇怪的反应。这几乎不是“世界上最常见的事情”:递归关系是一个专门的用例,我们有专门的库来有效地处理查询它们。不过,避免级联数据库调用的一般用例是在最初获取位置列表的查询上使用select_related。
  • 我可能会这样做,只需为此编写一个自定义连接,因为它实际上不需要分页或任何东西。所以它应该是直接的。无论如何,我会用 django-mptt 做一个 POC ......但如果有人有更简单的方法,我还在听。

标签: python django django-models


【解决方案1】:

我可能误解了你的问题,但我想我也有类似的情况。在下拉列表中,您希望用location 的名称标记对象。如果该位置有parent,您希望在位置名称后面的括号内显示该父级的名称。

您可以通过覆盖 ModelForm 的 __init__ 方法来完成此操作:

def __init__(self, *args, **kwargs):
    def new_label_from_instance(self, obj):
        try:
            #see if there is a parent or not
            p = obj.parent 
        except:   
            #no parent; use the location's default label
            rep = obj.description
            return rep
        #if we find a location's parent, p=parent label and d=location label
        d = obj.description
        rep = "%s (%s)" % (d, p)
        return rep

    super(PublisherCreateTSCForm, self).__init__(*args, **kwargs)
    funcType = type(self.fields['location'].label_from_instance)
    self.fields['location'].label_from_instance = funcType(new_label_from_instance, self.fields['location'], forms.models.ChoiceField)

你应该在做之前知道这样做的后果。看看这个老问题:

Django CheckboxSelectMultiple override 'choices' from ModelForm

此处与label_from_instance 相关的文档(链接部分的底部):https://docs.djangoproject.com/en/1.7/ref/forms/fields/#modelchoicefield

【讨论】:

    【解决方案2】:

    最后我做到了:

    在 forms.py 我创建了一个方法:

    def get_locations_with_parents():
        location_list = [(0,'Select One ...')]
        locations = Position.objects.raw('select l.id, l.name, p.name as parent from locations l left join locations p on l.parent_id = p.id order by parent, l.name')
        for l in locations:
            if l.parent:
                location = (int(l.id), l.name + " (" + l.parent + ")")
            else:
                location = (int(l.id), l.name)
    
            location_list.append(location)
    
        return tuple(location_list)
    

    然后以我使用的形式 位置 = forms.ChoiceField(choices=get_locations_with_parents(), validators=[validate_empty])

    它成功了,不再对数据库进行 2000 次查询。有一些验证器和干净的等等......但与解决方案并不真正相关。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-10-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-14
      • 2017-05-31
      • 2013-08-04
      相关资源
      最近更新 更多