2017-01-18 5 views
1

Следующая модель содержит две почти идентичные функции list_ancestors и list_descendants. Что было бы хорошим способом написать этот код только один раз?Как избежать дублирования кода с помощью аналогичных методов модели Django?

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 

EDIT: раствор, полученный из приведенных ниже ответов:

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') 
+0

@Sayse: Я не понимаю ваш вопрос. Предки - это обобщение родителей, поэтому 'list_ancestors' использует' list_parents'. Потомки - это обобщение детей, поэтому 'list_descendants' использует' list_children'. – Watchduck

+0

'list_ancestors' сначала получает родителей, а затем рекурсивно получает предков родителей. 'list_descendants' сначала получает детей, а затем рекурсивно получает потомков детей. Поскольку эти две аналогичные функции, которые я пытаюсь унифицировать, являются рекурсивными, мое «решение» 'list_withindirect' также рекурсивно. – Watchduck

+0

О, извините, я пропустил эту часть – Sayse

ответ

2

Используйте строку и вызвать 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') 
+0

Это не работает. Когда я пытаюсь использовать 'list_ancestors', я получаю некоторые' ImportError' и 'TypeError: list_withindirect() принимает 2 позиционных аргумента, но 3 дано'. Я понятия не имею, что вы пытаетесь сделать с «getattr» BTW. – Watchduck

+1

@Watchduck удалить вторую 'self' в последней строке – schwobaseggl

+0

Это сделало это. Благодаря! – Watchduck

0

Это очень похоже на проблемы в связанной и несвязанной задачи методы.

Когда вы изначально проходите self.list_parents до , все в порядке.

Но когда вы рекурсивно проходите то же самое! self.list_parents - d.list_withindirect (т. Е. Потомкам), вы случайно заполняете переменную direct родителями самого верхнего объекта-вызывающего, а не d.

Например, она может быть решена с помощью getattr, как он ответил на 2ps(UPD: ошибка в его исходном коде был найден в комментариях там).

+1

'd.list_direct' приводит к' AttributeError: 'Node' объект не имеет атрибута 'list_direct''. – Watchduck

+0

Конечно. Поэтому я бы не стал искать решение wo getaatr (я уверен, что это возможно), так как у вас уже есть ответ. Я оставляю только объяснения причин оригинальной проблемы в своем посте. –

Смежные вопросы