sql >> Databáze >  >> RDS >> PostgreSQL

sqlalchemy symetrické mnoho k jednomu přátelství

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.




  1. myisam umístit zámek stolu na stůl, i když se zabýváte dotazem „vybrat“?

  2. MS SSQL:Jak použít případ, kdy jako parametr exec

  3. Jak vytvořit správnou třídu rozšíření mysqli s připravenými příkazy?

  4. Jak povolím php pracovat s postgresql?