Mimo jiné se možná budete chtít dozvědět o asociačních proxy . Proxy přidružení říká SQLAlchemy, že máte vztah many-to-many zprostředkovaný mezilehlou tabulkou, která může obsahovat další data. Ve vašem případě každý User
může odesílat více požadavků a také přijímat více požadavků a Relationship
je zprostředkující tabulka, která obsahuje status
sloupec jako další data.
Zde je varianta vašeho kódu, která zůstává relativně blízko tomu, co jste napsali:
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.
(Všimněte si, že jsem řádky seřadil trochu jinak a jak jsem použil _id
přípona pro sloupce cizího klíče při vyhrazení stejného názvu bez přípony pro odpovídající db.relationship
s. Navrhoval bych, abyste tento styl přijali také.)
Nyní máte čistý způsob, jak přistupovat k příchozím a odchozím žádostem o přátelství a také k odpovídajícím uživatelům přímo od vašeho User
Modelka. To je však stále méně než ideální, protože pro potvrzení musíte napsat následující kód přátelé uživatele:
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()
(To jsem netestoval; možná se budete muset také join
s User
v obou dotazech v pořadí union
do práce.)
Aby toho nebylo málo, název modelu Relationship
stejně jako jména několika členů v rámci modelů, jak se zdá, příliš dobře nevyjadřují, co vlastně znamenají.
Záležitosti můžete zlepšit odstraněním Relationship.status
a přejmenování Relationship
na FriendshipRequest
. Poté přidejte druhého User
-to-User
asociační model s názvem Friendship
a přidejte odpovídající druhou sadu db.Relationship
s backref
s a association_proxy
s na User
. Když někdo odešle žádost o přátelství, odešlete záznam do FriendshipRequest
. Pokud je žádost přijata, odstraníte záznam a nahradíte jej novým záznamem v Friendship
. Tímto způsobem místo použití stavového kódu je stav přátelství zakódován tabulkou, ve které máte uloženou dvojici uživatelů. Friendship
model může vypadat takto:
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.)
(Odpovídající db.relationship
s a association_proxy
s v User
jsou ponechány jako cvičení na čtenáři.)
Tento přístup vám ušetří polovinu operací filtrování, když potřebujete potvrzené přátele uživatele. Přesto musíte vytvořit union
ze dvou dotazů, protože váš uživatel může být buď user1
nebo user2
v každém případě Friendship
. To je ze své podstaty obtížné, protože máme co do činění s reflexně symetrickým vztahem. Myslím, že je možné vymyslet ještě elegantnější způsoby, jak toho dosáhnout, ale myslím si, že by to bylo dostatečně komplikované, aby to zasloužilo novou otázku zde na Stack Overflow.