特に、アソシエーションプロキシ> 。アソシエーションプロキシは、追加のデータを含む可能性のある中間テーブルによって仲介される多対多の関係があることを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.
(行の順序が少し異なる方法と、_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()
(私はこれをテストしませんでした。join
も必要になる場合があります User
を使用 union
の順序で両方のクエリで 動作します。)
さらに悪いことに、モデル名Relationship
また、モデル内のいくつかのメンバーの名前は、実際の意味をうまく伝えていないようです。
Relationship.status
を削除すると、問題を改善できます。 Relationship
の名前を変更します FriendshipRequest
へ 。次に、2番目のUser
を追加します -to-User
Friendship
と呼ばれる関連付けモデル 対応する2番目のdb.Relationship
のセットを追加します s with backref
sおよびassociation_proxy
sから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.)
(対応するdb.relationship
sおよびassociation_proxy
s User
読者への演習として残されています。)
このアプローチにより、ユーザーの確認済みの友達が必要な場合に、フィルタリング操作の半分を節約できます。それでも、union
を作成する必要があります ユーザーはuser1
のいずれかである可能性があるため、2つのクエリのうち またはuser2
Friendship
の各インスタンスで 。私たちは反射対称関係を扱っているので、これは本質的に困難です。それを行うためのさらにエレガントな方法を発明することは可能だと思いますが、それはここStackOverflowで新しい質問を正当化するのに十分複雑になると思います。