sqlalchemy对称多对一友谊
我正在尝试使用SQLAlchemy ORM建立友谊模型。我试图建立的关系是对称的。与Facebook类似,如果用户a要添加用户b,则用户b必须批准该友谊请求。我目前的模式如下sqlalchemy对称多对一友谊,sql,postgresql,sqlalchemy,flask-sqlalchemy,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.Colu
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'), )
类用户(db.Model):
__tablename_uu='User'
id=db.Column(db.Integer,主键=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))
#定义关系
requester=db.relationship('relationship',foreign_key='relationship.requesting_user',backref='requester')
receiver=db.relationship('relationship',foreign_key='relationship.receiving_user',backref='received')
定义报告(自我):
返回“”%(self.username)
类关系(db.Model):
__tablename_uu='Relationship'
id=db.Column(db.Integer,主键=True)
正在请求_user=db.Column(db.Integer,db.ForeignKey('user.id'))
接收_user=db.Column(db.Integer,db.ForeignKey(“user.id”))
status=db.Column(db.Integer)
__表_args_uuuu=(db.UniqueConstraint('receiving_user','requising_user',name='u receiving_user_uc'))
这个模型是可行的,但是,我认为它没有被正确地建模。是否要求我使用状态?我假设它可以建模,这样每个朋友关系都有自己的条目。目前,用户可以与其他用户发起好友请求。当其他用户批准请求时,状态更改为已接受。我已经研究了一些关联表,但不太确定它们将如何在这样的模型中发挥作用。非常感谢您对我当前的模型以及如何改进的任何建议。除此之外,您可能还想了解。关联代理告诉SQLAlchemy,您有一个多对多关系,该关系由一个可能包含额外数据的中间表中介。在您的情况下,每个
用户
可以发送多个请求,也可以接收多个请求,关系
是中介表,其中包含状态
列作为附加数据
下面是代码的一个变体,它与您编写的代码保持相对接近:
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.
(请注意,我是如何对行进行稍微不同的排序的,以及我是如何为外键列使用\u id
后缀,同时为相应的db.relationship
s保留相同的名称,而不使用后缀。我建议您也采用这种样式。)
现在,您有了一种直接从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()
(我没有对此进行测试;您可能还需要在这两个查询中与User
进行join
,以便union
工作。)
更糟糕的是,模型名称关系
以及模型中几个成员的名称似乎没有很好地表达它们的实际含义
您可以通过删除Relationship.status
并将Relationship
重命名为FriendshipRequest
来改善问题。然后,将第二个用户
-添加到名为友谊
的用户
关联模型,并向用户
添加相应的第二组db.Relationship
s和关联代理
s。当有人发送友谊请求时,您将记录归档到FriendshipRequest
。如果请求被接受,您将删除该记录并在中用新记录替换它。这样,友谊的状态由存储一对用户的表编码,而不是使用状态代码。友谊
模型可能如下所示:
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.)
(将User
中对应的db.relationship
s和association\u proxy
s留给读者作为练习。)
当您需要用户的确认好友时,这种方法可以为您节省一半的筛选操作。尽管如此,您仍然需要对两个查询进行联合
,因为在友谊
的每个实例中,您的用户可以是user1
或user2
。这本质上是困难的,因为我们处理的是自反对称关系。我认为有可能发明更优雅的方法来实现这一点,但我认为这将非常复杂,足以证明这里有一个关于堆栈溢出的新问题。在本例中,您的关系表是关联表。“朋友”关系的对称性与“友谊请求”关系的不对称性直接相反。我认为你应该把两者分开。