Antara lain, Anda mungkin ingin mempelajari tentang proksi asosiasi . Proksi asosiasi memberi tahu SQLAlchemy bahwa Anda memiliki hubungan banyak ke banyak yang dimediasi oleh tabel perantara yang mungkin berisi data tambahan. Dalam kasus Anda, setiap User
dapat mengirim beberapa permintaan dan juga menerima beberapa permintaan dan Relationship
adalah tabel mediasi yang berisi status
kolom sebagai data tambahan.
Berikut adalah varian dari kode Anda yang tetap relatif dekat dengan apa yang Anda tulis:
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.
(Perhatikan bagaimana saya memesan baris sedikit berbeda dan bagaimana saya menggunakan _id
akhiran untuk kolom kunci asing saat memesan nama yang sama tanpa akhiran untuk db.relationship
yang sesuai s. Saya menyarankan Anda untuk mengadopsi gaya ini juga.)
Sekarang Anda memiliki cara yang bersih untuk mengakses permintaan pertemanan masuk dan keluar serta pengguna terkait langsung dari User
Anda model. Namun, ini masih kurang ideal karena Anda perlu menulis kode berikut untuk mendapatkan semua konfirmasi teman pengguna:
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()
(Saya tidak menguji ini; Anda mungkin juga perlu join
dengan User
di kedua kueri agar union
untuk bekerja.)
Untuk memperburuk keadaan, nama model Relationship
serta nama beberapa anggota dalam model tampaknya tidak menyampaikan dengan baik apa yang sebenarnya mereka maksud.
Anda dapat memperbaiki masalah dengan menghapus Relationship.status
dan mengganti nama Relationship
ke FriendshipRequest
. Kemudian, tambahkan User
kedua -ke-User
model asosiasi yang disebut Friendship
dan tambahkan set kedua yang sesuai dari db.Relationship
s dengan backref
s dan association_proxy
s ke User
. Saat seseorang mengirim permintaan pertemanan, Anda memasukkan catatan ke FriendshipRequest
. Jika permintaan diterima, Anda menghapus catatan dan menggantinya dengan catatan baru di Friendship
. Dengan cara ini, alih-alih menggunakan kode status, status pertemanan dikodekan oleh tabel tempat Anda menyimpan sepasang pengguna. Friendship
modelnya mungkin terlihat seperti ini:
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.)
(Sesuai db.relationship
s dan association_proxy
s di User
yang tersisa sebagai latihan untuk pembaca.)
Pendekatan ini menghemat setengah dari operasi pemfilteran saat Anda membutuhkan teman pengguna yang dikonfirmasi. Tetap saja, Anda perlu membuat union
dari dua kueri karena pengguna Anda dapat berupa user1
atau user2
di setiap contoh Friendship
. Ini pada dasarnya sulit karena kita berhadapan dengan hubungan simetris refleksif. Saya pikir masih mungkin untuk menemukan cara yang lebih elegan untuk melakukannya, tetapi saya pikir itu akan cukup rumit untuk menjamin pertanyaan baru di sini di Stack Overflow.