SQLAlchemy:如果你和我在一起,我就和你在一起 - 再次自引用 M2M 关系
我正在尝试在 SQLAlchemy 中创建一个类似于多对多映射的社交网络。也就是说,我有一个字符类和一个字符关系交叉表来从字符链接到字符。到目前为止,这很容易:
character_relationships = Table('character_relationships', Base.metadata,
Column('me_id', Integer, ForeignKey('characters.id', ondelete=True, onupdate=True), nullable=False, primary_key=True),
Column('other_id', Integer, ForeignKey('characters.id', ondelete=True, onupdate=True), nullable=False, primary_key=True),
UniqueConstraint('me_id', 'other_id', name='uix_1')
)
class CharacterRelationship(object):
def __init__(self, me_id, other_id):
self.me_id = me_id
self.other_id = other_id
mapper(CharacterRelationship, character_relationships)
class Character(IdMixin, TimestampMixin, Base):
__tablename__ = "characters"
name = Column(Unicode, nullable=False)
friends = relationship(lambda: Character, secondary=character_relationships,
primaryjoin=lambda: Character.id==character_relationships.c.me_id,
secondaryjoin=lambda: Character.id==character_relationships.c.other_id,
backref=backref('knows_me')
)
有两种可能的关系:单边和双边。在交叉表中,我可以得到
CharacterRelationship(me_id=1, other_id=2)
CharacterRelationship(me_id=2, other_id=1)
上面给出的 Character.friends 参数是关于单层关系的。如何为双边关系添加属性/column_property?
也就是说,如果关系“来自双方”,则 my_character.real_friends 仅包含来自 CharacterRelationship 的条目。
我知道我可以使用类似的东西
@property
def real_friends(self):
return set(my_character.friends) & set(my_character.knows_me)
,但这并不能很好地转换为 sql,并且我希望在数据库级别执行此集合交集能获得巨大的加速。但还有更多需要实现!
更好的是有一个最终对象,例如:
character.friends.other_character
character.friends.relationship_type = -1 | 0 | 1
其中
- -1 表示我单方面认识其他人,
- 0 双边,
- 1 表示其他人单方面认识我
您认为有什么理由将此逻辑放在数据库级别吗?如果是的话,你会怎么做? 如果知道,您知道如何将简单的 real_friend 双边部分放在数据库级别吗?
I'm trying to create a social-network like many-to-many mapping in SQLAlchemy. That is I have a Character class and a character_relations cross table to link from Character to Character. Until now this is easy:
character_relationships = Table('character_relationships', Base.metadata,
Column('me_id', Integer, ForeignKey('characters.id', ondelete=True, onupdate=True), nullable=False, primary_key=True),
Column('other_id', Integer, ForeignKey('characters.id', ondelete=True, onupdate=True), nullable=False, primary_key=True),
UniqueConstraint('me_id', 'other_id', name='uix_1')
)
class CharacterRelationship(object):
def __init__(self, me_id, other_id):
self.me_id = me_id
self.other_id = other_id
mapper(CharacterRelationship, character_relationships)
class Character(IdMixin, TimestampMixin, Base):
__tablename__ = "characters"
name = Column(Unicode, nullable=False)
friends = relationship(lambda: Character, secondary=character_relationships,
primaryjoin=lambda: Character.id==character_relationships.c.me_id,
secondaryjoin=lambda: Character.id==character_relationships.c.other_id,
backref=backref('knows_me')
)
There are two possible relationships: unilateral and bilateral. That is in the cross table I can have
CharacterRelationship(me_id=1, other_id=2)
CharacterRelationship(me_id=2, other_id=1)
The Character.friends argument as given above is about unilater relationships. How can one add a property/column_property for bilateral relationships?
That is my_character.real_friends contains only entries from CharacterRelationship if the relationship "stands from both sides".
I know that I could use something like
@property
def real_friends(self):
return set(my_character.friends) & set(my_character.knows_me)
but this doesn't translate well to sql, and I would expect a huge speedup doing this set intersection at the database level. But there is even more to achieve!
Even better would be to have a final object like:
character.friends.other_character
character.friends.relationship_type = -1 | 0 | 1
where
- -1 means I know other unilaterally,
- 0 bilateral,
- 1 means other knows me unilaterally
Do you see any reason to put this logic at the database level? If yes, how would you do it?
If know, do you know how to put the simple real_friend bilateral part at the database level?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
对于 real_friends,您需要在数据库级别查询。它应该看起来像这样:
您当然可以将其设置为关系。
对于 other_character (我假设这些是非共同的朋友),我会执行类似的查询,但使用外连接。
对于relationship_type,我会在缓存knows_person()时执行以下操作:
For real_friends, you would want to query this at the database level. It should look something like this:
You can of course set this as a relationship.
For other_character (I'm assuming these are non-mutual friends), I would do a similar query but with an outerjoin.
For relationship_type, I would do the following while caching knows_person():
定义了您的关系
friends
后,real_friends
很容易完成,如下所示:,但这会给
User< 带来两个多余的
JOIN
。 /code> 表,所以虽然在我看来,它是一个更好的代码,但它可能不如 Jonathan 的版本,其中使用简单的JOIN
到CharacterRelationship
表。至于您要求的甚至更好选项:这可以再次在如下查询中完成(以@Jonathan的答案作为参考):
需要注意的事项:
过滤器
被移至join
条件——这对于外连接非常重要,否则结果将是错误的。[(User, _code_), ..]
,其中_code_
为:0 - 未连接、1 - 朋友、2 - 认识我、3 - 真实朋友(双向)Having your relationship
friends
defined,real_friends
is easily done as following:, but this will make two more redundant
JOIN
s toUser
table, so although IMO, it is a better code, it might be inferior to the version of Jonathan where simpleJOIN
s to theCharacterRelationship
table are used.As for the even better option you ask for: this can again be done in a query like below (taken @Jonathan's answer as reference):
Few things to note:
filter
is moved to thejoin
condition - this is very important for outer joins, or else the result would be wrong.[(User, _code_), ..]
, where_code_
is: 0 - no connected, 1 - friend, 2 - knows me, 3 - real friend (both ways)