Unter anderem möchten Sie vielleicht mehr über Assoziations-Proxys . Ein Assoziations-Proxy teilt SQLAlchemy mit, dass Sie eine Viele-zu-Viele-Beziehung haben, die durch eine Zwischentabelle vermittelt wird, die zusätzliche Daten enthalten kann. In Ihrem Fall jeder User
kann mehrere Anfragen senden und auch mehrere Anfragen empfangen und Relationship
ist die vermittelnde Tabelle, die den status
enthält Spalte als Zusatzdaten.
Hier ist eine Variante Ihres Codes, die dem, was Sie geschrieben haben, relativ nahe kommt:
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.
(Beachten Sie, wie ich die Zeilen etwas anders angeordnet habe und wie ich die _id
verwendet habe Suffix für Fremdschlüsselspalten, während derselbe Name ohne das Suffix für die entsprechende db.relationship
reserviert wird s. Ich würde vorschlagen, dass Sie auch diesen Stil übernehmen.)
Jetzt haben Sie eine saubere Möglichkeit, direkt von Ihrem User
aus auf ein- und ausgehende Freundschaftsanfragen sowie die entsprechenden Benutzer zuzugreifen Modell. Dies ist jedoch immer noch nicht ideal, da Sie den folgenden Code schreiben müssen, um alles bestätigt zu bekommen Freunde eines Benutzers:
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()
(Ich habe dies nicht getestet; Sie müssen möglicherweise auch join
mit User
in beiden Abfragen in der Reihenfolge für die union
zu arbeiten.)
Erschwerend kommt hinzu, dass der Modellname Relationship
sowie die Namen mehrerer Mitglieder in den Modellen scheinen nicht sehr gut zu vermitteln, was sie eigentlich bedeuten.
Sie können die Angelegenheit verbessern, indem Sie Relationship.status
entfernen und Umbenennen von Relationship
zu FriendshipRequest
. Fügen Sie dann einen zweiten User
hinzu -an-User
Assoziationsmodell namens Friendship
und fügen Sie einen entsprechenden zweiten Satz von db.Relationship
hinzu s mit backref
s und association_proxy
s an User
. Wenn jemand eine Freundschaftsanfrage sendet, legen Sie einen Datensatz an FriendshipRequest
ab . Wenn die Anfrage akzeptiert wird, entfernen Sie den Datensatz und ersetzen ihn durch einen neuen Datensatz in Friendship
. Anstatt einen Statuscode zu verwenden, wird der Status einer Freundschaft auf diese Weise durch die Tabelle codiert, in der Sie ein Benutzerpaar speichern. Die Friendship
Modell könnte so aussehen:
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.)
(Entsprechende db.relationship
s und association_proxy
s in User
werden dem Leser als Übung überlassen.)
Dieser Ansatz erspart Ihnen die Hälfte der Filtervorgänge, wenn Sie die bestätigten Freunde eines Benutzers benötigen. Trotzdem müssen Sie eine union
machen von zwei Abfragen, da Ihr Benutzer entweder user1
sein kann oder user2
in jeder Instanz von Friendship
. Dies ist von Natur aus schwierig, da wir es mit einer reflexiven symmetrischen Beziehung zu tun haben. Ich denke, es ist möglich, noch elegantere Möglichkeiten zu erfinden, aber ich denke, das wäre kompliziert genug, um eine neue Frage hier auf Stack Overflow zu rechtfertigen.