Między innymi możesz chcieć dowiedzieć się więcej o proxy skojarzeń . Serwer proxy asocjacji informuje SQLAlchemy, że istnieje relacja wiele-do-wielu, w której pośredniczy tabela pośrednia, która może zawierać dodatkowe dane. W Twoim przypadku każdy User
może wysyłać wiele żądań, a także otrzymywać wiele żądań i Relationship
jest tabelą pośredniczącą, która zawiera status
kolumna jako dodatkowe dane.
Oto wariant Twojego kodu, który pozostaje stosunkowo zbliżony do tego, co napisałeś:
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.
(Zauważ, jak nieco inaczej uporządkowałem linie i jak użyłem _id
sufiks dla kolumn klucza obcego z zachowaniem tej samej nazwy bez sufiksu dla odpowiedniego db.relationship
s. Sugerowałbym, abyś również przyjął ten styl.)
Teraz masz łatwy dostęp do przychodzących i wychodzących próśb o dodanie do znajomych, a także odpowiednich użytkowników bezpośrednio z Twojego User
Model. Jednak nadal jest to mniej niż idealne, ponieważ musisz napisać następujący kod, aby uzyskać wszystkie potwierdzone znajomi użytkownika:
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()
(Nie testowałem tego; być może będziesz musiał także join
z User
w obu zapytaniach w kolejności dla union
do pracy.)
Co gorsza, nazwa modelu Relationship
jak również imiona kilku członków w modelach nie wydają się dobrze oddawać tego, co faktycznie mają na myśli.
Możesz poprawić sytuację, usuwając Relationship.status
i zmiana nazwy Relationship
do FriendshipRequest
. Następnie dodaj drugiego User
-to-User
model asocjacji o nazwie Friendship
i dodaj odpowiedni drugi zestaw db.Relationship
s z backref
s i association_proxy
s do User
. Gdy ktoś wysyła prośbę o dodanie do znajomych, przesyłasz rekord do FriendshipRequest
. Jeśli prośba zostanie zaakceptowana, usuwasz rekord i zastępujesz go nowym rekordem w Friendship
. W ten sposób, zamiast używać kodu statusu, status przyjaźni jest kodowany przez tabelę, w której przechowujesz parę użytkowników. Friendship
model może wyglądać tak:
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.)
(Odpowiadający db.relationship
s i association_proxy
s w User
są pozostawione czytelnikowi jako ćwiczenie.)
Takie podejście pozwala zaoszczędzić połowę operacji filtrowania, gdy potrzebujesz potwierdzonych znajomych użytkownika. Mimo to musisz utworzyć union
dwóch zapytań, ponieważ Twoim użytkownikiem może być user1
lub user2
w każdym wystąpieniu Friendship
. Jest to z natury trudne, ponieważ mamy do czynienia z refleksyjną relacją symetryczną. Myślę, że można wymyślić jeszcze bardziej eleganckie sposoby, aby to zrobić, ale myślę, że byłoby to wystarczająco skomplikowane, aby uzasadnić nowe pytanie tutaj na Stack Overflow.