sql >> Database teknologi >  >> RDS >> PostgreSQL

sqlalchemy symmetrisk mange til ét venskab

Du vil måske blandt andet lære om associationsproxies . En tilknytningsproxy fortæller SQLAlchemy, at du har en mange-til-mange-relation medieret af en mellemtabel, som kan indeholde yderligere data. I dit tilfælde hver User kan sende flere anmodninger og også modtage flere anmodninger og Relationship er formidlingstabellen, som indeholder status kolonne som yderligere data.

Her er en variant af din kode, som forbliver relativt tæt på det, du skrev:

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.

(Bemærk, hvordan jeg bestilte linjerne lidt anderledes, og hvordan jeg brugte _id suffiks for fremmednøglekolonner, mens det samme navn reserveres uden suffikset for den tilsvarende db.relationship s. Jeg vil foreslå, at du også tager denne stil.)

Nu har du en ren måde at få adgang til indgående og udgående venskabsanmodninger samt de tilsvarende brugere direkte fra din User model. Dette er dog stadig mindre end ideelt, fordi du skal skrive følgende kode for at få alt bekræftet venner af en bruger:

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()

(Jeg testede ikke dette; du skal muligvis også join med User i begge forespørgsler i rækkefølge for union at arbejde.)

For at gøre tingene værre, modelnavnet Relationship samt navnene på adskillige medlemmer i modellerne lader ikke til at formidle særlig godt, hvad de rent faktisk betyder.

Du kan forbedre tingene ved at fjerne Relationship.status og omdøb Relationship til FriendshipRequest . Tilføj derefter en anden User -til-User foreningsmodel kaldet Friendship og tilføje et tilsvarende andet sæt db.Relationship s med backref s og association_proxy s til User . Når nogen sender en venskabsanmodning, indsender du en registrering til FriendshipRequest . Hvis anmodningen accepteres, fjerner du posten og erstatter den med en ny post i Friendship . På denne måde, i stedet for at bruge en statuskode, bliver status for et venskab kodet af den tabel, hvor du gemmer et par brugere. Friendship modellen kan se sådan ud:

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.)

(Tilsvarende db.relationship s og association_proxy s i User overlades som en øvelse til læseren.)

Denne tilgang sparer dig for halvdelen af ​​filtreringsoperationerne, når du har brug for en brugers bekræftede venner. Alligevel skal du lave en union af to forespørgsler, fordi din bruger kan være enten user1 eller user2 i hver forekomst af Friendship . Dette er i sagens natur svært, fordi vi har at gøre med et refleksivt symmetrisk forhold. Jeg tror, ​​det er muligt at opfinde endnu mere elegante måder at gøre det på, men jeg tror, ​​det ville være kompliceret nok til at berettige et nyt spørgsmål her på Stack Overflow.




  1. Brug TOP eller Rank, når du finder de første par eller de fleste observationer

  2. chmod og-rwx server.key i windows

  3. MySQL - Ranger bruger blandt listen over topscores

  4. Sådan forbinder du lokalt hostet MySQL-database med docker-containeren