【问题标题】:How to avoid code duplication with similar Django model methods?如何避免使用类似 Django 模型方法的代码重复?
【发布时间】:2017-01-18 01:52:56
【问题描述】:

以下模型包含两个几乎相同的函数list_ancestorslist_descendants。什么是只编写一次代码的好方法?

class Node(models.Model):
    name = models.CharField(max_length=120, blank=True, null=True)
    parents = models.ManyToManyField('self', blank=True, symmetrical=False)

    def list_parents(self):
        return self.parents.all()

    def list_children(self):
        return Node.objects.filter(parents=self.id)

    def list_ancestors(self):
        parents = self.list_parents()
        ancestors = set(parents)
        for p in parents:
            ancestors |= set(p.list_ancestors())  # set union
        return list(ancestors)

    def list_descendants(self):
        children = self.list_children()
        descendants = set(children)
        for c in children:
            descendants |= set(c.list_descendants())  # set union
        return list(descendants)

    def __str__(self):
        return self.name

编辑:从以下答案得出的解决方案:

def list_withindirect(self, arg):
    direct = getattr(self, arg)()
    withindirect = set(direct)
    for d in direct:
         withindirect |= set(d.list_withindirect(arg))
    return list(withindirect)

def list_ancestors(self):
     return self.list_withindirect('list_parents')

def list_descendants(self):
     return self.list_withindirect('list_children')

【问题讨论】:

  • @Sayse:我不明白你的问题。祖先是父母的概括,所以list_ancestors 使用list_parents。后代是孩子的概括,所以list_descendants 使用list_children
  • list_ancestors 先获取父母,然后递归获取父母的祖先。 list_descendants 先获取孩子,然后递归获取孩子的后代。由于我尝试统一的这两个相似函数是递归的,所以我的“解决方案”list_withindirect 也是递归的。
  • 抱歉,我错过了那部分

标签: django methods code-duplication


【解决方案1】:

使用字符串并在对象上调用 getattr 以获取可调用函数。

def list_withindirect(self, fn1):
    direct = getattr(self, fn1)()
    withindirect = set(direct)
    for d in direct:
         withindirect |= set(d.list_withindirect(fn1))

    return list(withindirect)

def list_ancestors(self):
     return self.list_withindirect('list_parents')

【讨论】:

  • 那行不通。当我尝试使用list_ancestors 时,我得到了一些ImportErrorTypeError: list_withindirect() takes 2 positional arguments but 3 were given。我不知道你想用getattr BTW 做什么。
  • @Watchduck 删除最后一行的第二个self
  • 做到了。谢谢!
  • @schwobaseggl: (faceplam)。谢谢你接听。
【解决方案2】:

这看起来像是 boundunbound 方法的问题。

当您最初将self.list_parents 传递给self.list_withindirect(list_direct) 时,一切正常。

但是当你递归地传递相同的! self.list_parentsd.list_withindirect(即后代),你不小心用最顶层调用者对象的父对象填充了你的 direct 变量,而不是 d

例如,它可以使用getattr 解决,就像它被 2ps 回答一样 (更新:他的原始代码中的错误是在 cmets 那里找到的) .

【讨论】:

  • d.list_direct 导致AttributeError: 'Node' object has no attribute 'list_direct'
  • 当然。所以,我不会试图找到 wo getaatr 解决方案(我相信这是可能的),因为你已经得到了答案。我只在帖子中留下原始问题的原因解释。
猜你喜欢
  • 2021-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-29
  • 2016-02-08
  • 1970-01-01
  • 2011-03-04
相关资源
最近更新 更多