U kunt onder andere meer willen weten over verenigingsproxy's . Een associatieproxy vertelt SQLAlchemy dat u een veel-op-veel-relatie hebt die wordt gemedieerd door een tussenliggende tabel die aanvullende gegevens kan bevatten. In uw geval heeft elke User
kan meerdere verzoeken verzenden en ook meerdere verzoeken ontvangen en Relationship
is de bemiddelingstabel die de status
. bevat kolom als aanvullende gegevens.
Hier is een variant van je code die relatief dicht bij wat je hebt geschreven blijft:
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.
(Merk op hoe ik de regels iets anders heb gerangschikt en hoe ik de _id
heb gebruikt achtervoegsel voor vreemde-sleutelkolommen met behoud van dezelfde naam zonder het achtervoegsel voor de corresponderende db.relationship
s. Ik stel voor dat je deze stijl ook overneemt.)
Nu heb je een schone manier om toegang te krijgen tot inkomende en uitgaande vriendschapsverzoeken, evenals de bijbehorende gebruikers, rechtstreeks vanaf je User
model. Dit is echter nog steeds niet ideaal omdat je de volgende code moet schrijven om alles bevestigd te krijgen vrienden van een gebruiker:
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()
(Ik heb dit niet getest; misschien moet je ook join
met User
in beide zoekopdrachten om de union
aan het werk.)
Om het nog erger te maken, de modelnaam Relationship
evenals de namen van verschillende leden binnen de modellen lijken niet goed over te brengen wat ze eigenlijk bedoelen.
U kunt zaken verbeteren door Relationship.status
te verwijderen en hernoemen van Relationship
naar FriendshipRequest
. Voeg vervolgens een tweede User
toe -naar-User
associatiemodel genaamd Friendship
en voeg een overeenkomstige tweede set van db.Relationship
. toe s met backref
s en association_proxy
s naar User
. Wanneer iemand een vriendschapsverzoek stuurt, dien je een record in bij FriendshipRequest
. Als het verzoek wordt geaccepteerd, verwijdert u de record en vervangt u deze door een nieuwe record in Friendship
. Op deze manier wordt, in plaats van een statuscode te gebruiken, de status van een vriendschap gecodeerd door de tabel waarin u een paar gebruikers opslaat. De Friendship
model kan er als volgt uitzien:
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.)
(Overeenkomende db.relationship
s en association_proxy
s in User
worden als oefening aan de lezer overgelaten.)
Deze aanpak bespaart u de helft van de filterbewerkingen wanneer u de bevestigde vrienden van een gebruiker nodig heeft. Toch moet je een union
maken van twee zoekopdrachten omdat uw gebruiker user1
. kan zijn of user2
in elk geval van Friendship
. Dit is inherent moeilijk omdat we te maken hebben met een reflexieve symmetrische relatie. Ik denk dat het mogelijk is om nog elegantere manieren te bedenken om het te doen, maar ik denk dat dat ingewikkeld genoeg zou zijn om een nieuwe vraag hier op Stack Overflow te rechtvaardigen.