Rob*_*her 2 sql postgresql sqlalchemy flask-sqlalchemy
我正在尝试使用 SQLAlchemy ORM 建立友谊模型。我试图建模的关系是对称的。与 Facebook 类似,如果用户 a 要添加用户 b,则用户 b 必须批准该好友请求。我当前的模型如下。
class User(db.Model):
__tablename__ = 'User'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(35), unique=False)
username = db.Column(db.String(25), index=True, unique=True)
password = db.Column(db.String(35), unique=False)
email = db.Column(db.String(35), unique=True)
phone_number = db.Column(db.String(22))
# define relationships
requester = db.relationship('Relationship', foreign_keys='Relationship.requesting_user', backref='requester')
receiver = db.relationship('Relationship', foreign_keys='Relationship.receiving_user', backref='received')
def __repr__(self):
return '<User %r>' % (self.username)
class Relationship(db.Model):
__tablename__ = 'Relationship'
id = db.Column(db.Integer, primary_key=True)
requesting_user = db.Column(db.Integer, db.ForeignKey('User.id'))
receiving_user = db.Column(db.Integer, db.ForeignKey("User.id"))
status = db.Column(db.Integer)
__table_args__ = (db.UniqueConstraint('receiving_user', 'requesting_user', name='_receiving_user_uc'), )
Run Code Online (Sandbox Code Playgroud)
该模型有效,但是,我认为它的建模不正确。我是否还需要使用状态?我假设它可以被建模,以便每个朋友关系都有自己的条目。目前,用户可以向其他用户发起好友请求。当其他用户批准请求时,状态将更改为已接受。我对关联表进行了一些研究,但不太确定它们将如何融入这样的模型中。任何有关我当前模型以及如何改进它的建议将不胜感激。
除此之外,您可能还想了解关联代理。关联代理告诉 SQLAlchemy 您有一个由可能包含其他数据的中间表调解的多对多关系。在您的情况下,每个都User可以发送多个请求,也可以接收多个请求,并且Relationship是包含该列作为附加数据的中介表status。
这是您的代码的一个变体,它与您编写的内容相对接近:
from sqlalchemy.ext.associationproxy import association_proxy
class User(db.Model):
__tablename__ = 'User'
# The above is not necessary. If omitted, __tablename__ will be
# automatically inferred to be 'user', which is fine.
# (It is necessary if you have a __table_args__, though.)
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(35), unique=False)
# and so forth
requested_rels = db.relationship(
'Relationship',
foreign_keys='Relationship.requesting_user_id',
backref='requesting_user'
)
received_rels = db.relationship(
'Relationship',
foreign_keys='Relationship.receiving_user_id',
backref='receiving_user'
)
aspiring_friends = association_proxy('received_rels', 'requesting_user')
desired_friends = association_proxy('requested_rels', 'receiving_user')
def __repr__(self):
# and so forth
class Relationship(db.Model):
# __tablename__ removed, becomes 'relationship'
# __table_args__ removed, see below
requesting_user_id = db.Column(db.Integer, db.ForeignKey('User.id'), primary_key=True)
receiving_user_id = db.Column(db.Integer, db.ForeignKey('User.id'), primary_key=True)
# Marking both columns above as primary_key creates a compound primary
# key, which at the same time saves you the effort of defining the
# UNIQUE constraint in __table_args__
status = db.Column(db.Integer)
# Implicit one-to-many relations: requesting_user, receiving_user.
# Normally it would be more convenient to define those relations on
# this side, but since you have two outgoing relationships with the
# same table (User), you chose wisely to define them there.
Run Code Online (Sandbox Code Playgroud)
(请注意我对行的排序方式略有不同,以及如何使用_id外键列的后缀,同时保留相同的名称,而没有相应的后缀db.relationship。我建议您也采用这种样式。)
现在,您可以通过一种干净的方式直接从模型访问传入和传出的友谊请求以及相应的用户User。但是,这仍然不太理想,因为您需要编写以下代码才能获取用户的所有已确认好友:
def get_friends(user):
requested_friends = (
db.session.query(Relationship.receiving_user)
.filter(Relationship.requesting_user == user)
.filter(Relationship.status == CONFIRMED)
)
received_friends = (
db.session.query(Relationship.requesting_user)
.filter(Relationship.receiving_user == user)
.filter(Relationship.status == CONFIRMED)
)
return requested_friends.union(received_friends).all()
Run Code Online (Sandbox Code Playgroud)
(我没有对此进行测试;您可能还需要在两个查询中使用joinwithUser才能使其union正常工作。)
更糟糕的是,模型名称Relationship以及模型中几个成员的名称似乎并不能很好地传达它们的实际含义。
您可以通过删除Relationship.status并重命名Relationship为来改进问题FriendshipRequest。然后,添加名为 的第二个User-to-User关联模型Friendship,并添加相应的第二组db.Relationships(其中backrefs 和association_proxys 为 )User。当有人发送好友请求时,您会将记录归档到FriendshipRequest。如果请求被接受,您将删除该记录并将其替换为 中的新记录Friendship。这样,友谊的状态就由存储一对用户的表进行编码,而不是使用状态代码。该Friendship模型可能如下所示:
class Friendship(db.Model):
user1_id = db.Column(db.Integer, db.ForeignKey('User.id'), primary_key=True)
user2_id = db.Column(db.Integer, db.ForeignKey('User.id'), primary_key=True)
# Implicit one-to-many relations: user1, user2
# (defined as backrefs in User.)
Run Code Online (Sandbox Code Playgroud)
(相应的db.relationships 和association_proxysUser留给读者作为练习。)
当您需要用户确认的好友时,这种方法可以为您节省一半的过滤操作。尽管如此,您仍然需要进行union两个查询之一,因为您的用户可以是user1或user2在 的每个实例中Friendship。这本质上是困难的,因为我们正在处理自反对称关系。我认为可以发明更优雅的方法来做到这一点,但我认为这足够复杂,需要在 Stack Overflow 上提出一个新问题。
| 归档时间: |
|
| 查看次数: |
1140 次 |
| 最近记录: |