【问题标题】:How to get all ancestors of a Django object when ForeignKey is not self-referencing?当 ForeignKey 不是自引用时,如何获取 Django 对象的所有祖先?
【发布时间】:2019-11-22 22:06:20
【问题描述】:

我有一个模型 Person 和另一个模型 Relation。 在创建新关系之前,我想检查这种关系是否可行。

这篇文章和其他一些类似的文章提供了一个解决方案,但对于自引用模型,我的模型不是自引用的。 Django self-recursive foreignkey filter query for all childs

class Person(models.Model):
    identifier = IntegerField(null=True)
    title = CharField(max_length=100, null=True)

    def get_all_parent_ids(self):
        # I want to get all the ancestors of this person
        # but this method only gets immediate parents right now
        return [parent.parent.id for parent in self.parenting.all()]

    def get_all_children_ids(self):
        # I want to get all the descendants of this person
        # but this method only gets immediate children right now
        return [child.children.id for child in self.baby_sitting.all()]


class Relation(models.Model):
    name = CharField(max_length=50, null=True)
    parent = ForeignKey(Person, on_delete=models.PROTECT, related_name="parenting")
    children = ForeignKey(Person, on_delete=models.PROTECT, related_name="baby_sitting")

    class Meta:
        unique_together = ('parent', 'children')

def is_relation_possible(new_parent_id, new_children_id):
    new_parent_person = Person.objects.get(pk=new_parent_id)
    all_parents = new_parent_person.get_all_parent_ids()
    if new_children_id in all_parents:
        return False
    else:
        return True

例如:现有关系 - A到B - B到C - C 到 D - D 到 E

我希望 is_relation_possible(E, A) 返回 False,因为 E 有一个祖先 A。

目前它只检查直系父母,而不是所有父母。

【问题讨论】:

    标签: django django-models recursive-query


    【解决方案1】:

    你应该使用递归:

    def is_relation_possible(new_parent_id, new_children_id):
        new_parent_person = Person.objects.get(pk=new_parent_id)
        all_parents = new_parent_person.get_all_parent_ids()
        related = ( new_children_id in all_parents 
                    or 
                    any( is_relation_possible( ancestor_id, new_children_id) 
                         for ancestor_id in all_parents )  # (*1)
                   )
        return related
    

    (*1) 这里的诀窍是:如果自身相关或通过其祖先相关,则相关。

    注意 1:如果您使用的是图形而不是层次结构,它可能会打开一个无限循环。

    注意 2:未经测试。测试一下然后回来:)

    【讨论】:

    • 看起来它至少适用于班轮关系。还需要再测试一下。我也试图让它返回一个关系不可能的原因,比如只列出使它相关的相关关系。像这种关系是不可能的,因为你已经有了这些关系 D -> C -> B -> A
    猜你喜欢
    • 1970-01-01
    • 2015-10-08
    • 1970-01-01
    • 2016-03-15
    • 2011-07-23
    • 1970-01-01
    • 1970-01-01
    • 2017-08-06
    • 2010-11-08
    相关资源
    最近更新 更多