sql >> Databasteknik >  >> RDS >> PostgreSQL

sqlalchemy symmetrisk många till en vänskap

Bland annat kanske du vill lära dig mer om associationsproxy . En associationsproxy talar om för SQLAlchemy att du har en många-till-många-relation som förmedlas av en mellantabell som kan innehålla ytterligare data. I ditt fall, varje User kan skicka flera förfrågningar och även ta emot flera förfrågningar och Relationship är förmedlingstabellen som innehåller status kolumnen som ytterligare data.

Här är en variant av din kod som ligger relativt nära 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.

(Observera hur jag ordnade raderna lite annorlunda och hur jag använde _id suffix för främmande nyckelkolumner samtidigt som samma namn reserveras utan suffixet för motsvarande db.relationship s. Jag skulle föreslå att du också anammar den här stilen.)

Nu har du ett rent sätt att komma åt inkommande och utgående vänskapsförfrågningar samt motsvarande användare direkt från din User modell. Detta är dock fortfarande mindre än idealiskt eftersom du måste skriva följande kod för att få alla bekräftade vänner till en användare:

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

(Jag testade inte detta, du kanske också måste join med User i båda frågorna för att union ska användas att arbeta.)

För att göra saken värre, modellnamnet Relationship liksom namnen på flera medlemmar i modellerna verkar inte förmedla vad de egentligen betyder.

Du kan förbättra saker genom att ta bort Relationship.status och byter namn på Relationship till FriendshipRequest . Lägg sedan till en andra User -till-User föreningsmodell som heter Friendship och lägg till en motsvarande andra uppsättning db.Relationship s med backref s och association_proxy s till User . När någon skickar en vänskapsförfrågan arkiverar du en post till FriendshipRequest . Om begäran accepteras tar du bort posten och ersätter den med en ny post i Friendship . På så sätt, istället för att använda en statuskod, kodas statusen för en vänskap av tabellen där du lagrar ett par användare. Friendship modell kan se ut så här:

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

(Motsvarande db.relationship s och association_proxy s i User lämnas som en övning till läsaren.)

Detta tillvägagångssätt sparar hälften av filtreringsoperationerna när du behöver bekräftade vänner till en användare. Ändå måste du skapa en union av två frågor eftersom din användare kan vara antingen user1 eller user2 i varje instans av Friendship . Detta är till sin natur svårt eftersom vi har att göra med ett reflexivt symmetriskt förhållande. Jag tror att det är möjligt att uppfinna ännu mer eleganta sätt att göra det på, men jag tror att det skulle vara komplicerat nog för att motivera en ny fråga här på Stack Overflow.




  1. Formatera SQLite-frågeresultat som en kommaseparerad lista

  2. Pandas Skrivtabell till MySQL:kan inte återställas

  3. Formatera utdata för frågor i SQLPlus

  4. Vilka är de bästa metoderna för att migrera en Oracle 10g-databas till Microsoft SQL 2008 R2? Applikationen använder Hibernate