首页 > 解决方案 > 在 Python 中自动管理数据库中的双向引用

问题描述

我有一个 ZODB 数据库,其中包含彼此具有双向链接的对象。这些通常是多对一和一对多的链接,即组和成员。成员具有对其所属组的引用,并且组具有对其成员的引用列表。

我正在寻找一种方法来自动确保双向引用是一致的。例如,如果组memberA从 更改groupAgroupBmemberA则应自动从成员列表中删除groupA并添加到 列表中groupB

下面的代码是一种方法。

class Group:
    def __init__(self, name):
        self.name = name
        self.members = []
        self.db_ref = None

    def add_member(self, member, cascade=True):
        self.members.append(member)
        if cascade:
            self.db_ref.member_of_group_changed(self, member)

    def remove_member(self, member, cascade=True):
        self.members.remove(member)
        if cascade:
            self.db_ref.member_of_group_changed(self, member)

    def __str__(self):
        return self.__class__.__name__ + '("' + self.name + '")'


class Member:
    def __init__(self, name):
        self.name = name
        self.group = None
        self.db_ref = None

    def set_group(self, group, cascade=True):
        old_group = self.group
        self.group = group
        if cascade:
            self.db_ref.group_of_member_changed(self, old_group)

    def __str__(self):
        return self.__class__.__name__ + '("' + self.name + '")'

    def __repr__(self):
        return self.__str__()


class Database:
    def __init__(self):
        self.groups = []
        self.members = []

    def add_item(self, item):
        item.db_ref = self
        if type(item) is Group:
            self.groups.append(item)
        elif type(item) is Member:
            self.members.append(item)

    def group_of_member_changed(self, member, old_group):
        if not old_group is None:  # group unset
            old_group.remove_member(member, cascade=False)
        elif not member.group is None:  # group set
            member.group.add_member(member, cascade=False)

    def member_of_group_changed(self, group, member):
        if member in group.members:  # member added to group
            member.set_group(group, cascade=False)
        else:  # member removed from group
            member.set_group(None, cascade=False)


db = Database()
groupA = Group("groupA")
memberA = Member("memberA")
memberB = Member("memberB")
db.add_item(groupA)
db.add_item(memberA)
db.add_item(memberB)

# add memberA to groupA
groupA.add_member(memberA)
print(groupA.members)  # [Member("memberA")]
print(memberA.group)  # Group("groupA")

# set group of memberB to groupA
memberB.set_group(groupA)
print(memberB.group)  # Group("groupA")
print(groupA.members)  # [Member("memberA"), Member("memberB")]

# remove memberB from groupA
groupA.remove_member(memberB)
print(groupA.members)  # [Member("memberA")]
print(memberB.group)  # None

# unset group of memberA
memberA.set_group(None)
print(memberA.group)  # None
print(groupA.members)  # []

但是,感觉有点笨拙。我怀疑有更好的方法,因为我想这种情况经常发生。如果不需要定义类似的方法,set_group()或者add_member()可以修改 setter 和 append 函数,那就太好了,即:

groupA.members.append(memberA) # also sets sets memberA.group to groupA
memberB.group = groupA # also adds memberB to groupA.members

Database在提供的代码示例中,我更喜欢通过类处理更新引用。比如for memberB.group = groupA,setter方法设置memberB.groupgroupA,不应该直接调用append添加memberB到的方法groupA。数据库应该这样做。

我查看了描述符。我猜这个函数 __set__()可以替代set_group(),但这不是add_member()/的选项remove_member()

如果可以在将对象添加到数据库时动态添加此行为,那将是非常酷的。但也许我变得贪婪:)。

标签: pythondatabasebidirectional

解决方案


推荐阅读