PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

sqlalchemy симетрично много към едно приятелство

Наред с други неща, може да искате да научите за проксита за асоцииране . Прокси за асоцииране казва на SQLAlchemy, че имате връзка много към много, медиирана от междинна таблица, която може да съдържа допълнителни данни. Във вашия случай всеки User може да изпраща множество заявки и също така да получава множество заявки и Relationship е посредническата таблица, която съдържа status колона като допълнителни данни.

Ето вариант на вашия код, който остава относително близък до това, което сте написали:

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.

(Забележете как подредих редовете малко по-различно и как използвах _id суфикс за колони с външен ключ, като запазва същото име без суфикс за съответния db.relationship с. Предлагам ви също да приемете този стил.)

Вече имате чист начин за достъп до входящи и изходящи заявки за приятелство, както и до съответните потребители директно от вашия User модел. Това обаче все още не е идеално, защото трябва да напишете следния код, за да получите всички потвърдени приятели на потребител:

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

(Не съм тествал това; може да се наложи също да се join с User и в двете заявки за union на работа.)

За да влоши нещата, името на модела Relationship както и имената на няколко членове в рамките на моделите изглежда не предават много добре какво всъщност означават.

Можете да подобрите нещата, като премахнете Relationship.status и преименуване на Relationship до FriendshipRequest . След това добавете втори User -to-User асоциационен модел, наречен Friendship и добавете съответния втори набор от db.Relationship s с backref s и association_proxy s към User . Когато някой изпрати молба за приятелство, вие подавате запис в FriendshipRequest . Ако заявката бъде приета, премахвате записа и го заменяте с нов запис в Friendship . По този начин, вместо да се използва код за състояние, състоянието на приятелство се кодира от таблицата, в която съхранявате двойка потребители. Friendship моделът може да изглежда така:

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

(Съответстващ db.relationship s и association_proxy s в User се оставят като упражнение на читателя.)

Този подход ви спестява половината от операциите по филтриране, когато имате нужда от потвърдените приятели на даден потребител. Все пак трябва да направите union от две заявки, защото вашият потребител може да бъде или user1 или user2 във всеки случай на Friendship . Това по своята същност е трудно, защото имаме работа с рефлексивна симетрична връзка. Мисля, че е възможно да се измислят още по-елегантни начини да се направи това, но мисля, че това би било достатъчно сложно, за да оправдае нов въпрос тук в 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. Сравнителен анализ на Postgres-XL

  2. Сортирайте клеймата за време (включително бъдеще) по абсолютно разстояние от сега

  3. Как можем да накараме „statement_timeout“ да работи вътре във функция?

  4. Формата за търсене не се насочва към правилния контролер в Rails 5.1

  5. Postgresql:Невалиден регулярен израз:невалиден номер за обратна препратка