PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

sqlalchemia symetryczna przyjaźń wiele do jednego

Między innymi możesz chcieć dowiedzieć się więcej o proxy skojarzeń . Serwer proxy asocjacji informuje SQLAlchemy, że istnieje relacja wiele-do-wielu, w której pośredniczy tabela pośrednia, która może zawierać dodatkowe dane. W Twoim przypadku każdy User może wysyłać wiele żądań, a także otrzymywać wiele żądań i Relationship jest tabelą pośredniczącą, która zawiera status kolumna jako dodatkowe dane.

Oto wariant Twojego kodu, który pozostaje stosunkowo zbliżony do tego, co napisałeś:

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.

(Zauważ, jak nieco inaczej uporządkowałem linie i jak użyłem _id sufiks dla kolumn klucza obcego z zachowaniem tej samej nazwy bez sufiksu dla odpowiedniego db.relationship s. Sugerowałbym, abyś również przyjął ten styl.)

Teraz masz łatwy dostęp do przychodzących i wychodzących próśb o dodanie do znajomych, a także odpowiednich użytkowników bezpośrednio z Twojego User Model. Jednak nadal jest to mniej niż idealne, ponieważ musisz napisać następujący kod, aby uzyskać wszystkie potwierdzone znajomi użytkownika:

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

(Nie testowałem tego; być może będziesz musiał także join z User w obu zapytaniach w kolejności dla union do pracy.)

Co gorsza, nazwa modelu Relationship jak również imiona kilku członków w modelach nie wydają się dobrze oddawać tego, co faktycznie mają na myśli.

Możesz poprawić sytuację, usuwając Relationship.status i zmiana nazwy Relationship do FriendshipRequest . Następnie dodaj drugiego User -to-User model asocjacji o nazwie Friendship i dodaj odpowiedni drugi zestaw db.Relationship s z backref s i association_proxy s do User . Gdy ktoś wysyła prośbę o dodanie do znajomych, przesyłasz rekord do FriendshipRequest . Jeśli prośba zostanie zaakceptowana, usuwasz rekord i zastępujesz go nowym rekordem w Friendship . W ten sposób, zamiast używać kodu statusu, status przyjaźni jest kodowany przez tabelę, w której przechowujesz parę użytkowników. Friendship model może wyglądać tak:

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

(Odpowiadający db.relationship s i association_proxy s w User są pozostawione czytelnikowi jako ćwiczenie.)

Takie podejście pozwala zaoszczędzić połowę operacji filtrowania, gdy potrzebujesz potwierdzonych znajomych użytkownika. Mimo to musisz utworzyć union dwóch zapytań, ponieważ Twoim użytkownikiem może być user1 lub user2 w każdym wystąpieniu Friendship . Jest to z natury trudne, ponieważ mamy do czynienia z refleksyjną relacją symetryczną. Myślę, że można wymyślić jeszcze bardziej eleganckie sposoby, aby to zrobić, ale myślę, że byłoby to wystarczająco skomplikowane, aby uzasadnić nowe pytanie tutaj na Stack Overflow.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Sequelize ma wiele, należy do lub oba?

  2. Prawidłowy indeks pełnotekstowy Rails/PostgreSQL/pg_search

  3. Postgresql jak wybrać wartości w kolumnie z jednej tabeli, które są dostępne tylko w innej tabeli?

  4. Django python-rq -- Błąd bazy danych Błąd SSL:odszyfrowanie nie powiodło się lub zły rekord mac

  5. Nie można połączyć się ze zdalną bazą danych Heroku Postgres za pomocą Play Framework 2.2.2