SQLAlchemy: Запрос участников отношений «один ко многим», чьи родители являются участниками отношений «многие ко многим»?

#python #sqlite #flask #sqlalchemy

Вопрос:

Я создаю приложение для планирования событий с помощью Flask и пытаюсь написать функцию, которая возвращает все события, видимые текущему пользователю. К ним относятся:

  • Собственные события пользователя
  • Все публичные мероприятия
  • Друзья-только события, принадлежащие друзьям пользователя
  • События только по приглашению, на которые пользователь был приглашен/получил приглашение

Вот соответствующие модели и таблицы:

 friendship = db.Table('friendships', db.Model.metadata,
    db.Column('a_id', db.Integer, db.ForeignKey('users.id'), primary_key=True),
    db.Column('b_id', db.Integer, db.ForeignKey('users.id'), primary_key=True)
)

class User(db.Model, UserMixin):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    events = db.relationship('Event', backref='author', lazy='dynamic')
    
    friends = db.relationship('User',
            secondary=friendship,
            primaryjoin=id==friendship.c.a_id,
            secondaryjoin=id==friendship.c.b_id,
            lazy='dynamic')
    
    # Filter in case event privacy has been updated
    def associated_events(self, user=None):
        return self.invited_events
            .union(self.going_events)
            .union(self.not_going_events)
            .filter(~Event.privacy == 1)

# going and not_going are virtually identical
invited = db.Table('invited_users_table', db.Model.metadata,
    db.Column('event_id', db.Integer, db.ForeignKey('events.id'), primary_key=True),
    db.Column('user_id', db.Integer, db.ForeignKey('users.id'), primary_key=True)
)

class Event(db.Model):
    __tablename__ = 'events'
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    #   1 - private, 2 - invite only, 3 - friends only, 4 - public
    privacy = db.Column(db.Integer, default=1)
    
    # going_users and not_going_users are virtually identical
    invited_users = db.relationship('User',
                        secondary=invited,
                        backref=db.backref('invited_events', lazy='dynamic'),
                        lazy='dynamic')
 

Я написал функцию, которая работает, но она очень медленная и, по общему признанию, взломана с помощью нескольких часов проб и ошибок:

 def visible_events():
    # Public events
    result = Event.query.filter(Event.privacy == 4)
    
    if current_user.is_authenticated:
        result = result.union_all(
                # Own events
                current_user.events,
                # Invited/RSVP'd events
                current_user.associated_events(),
                # Friends' events
                Event.query.filter(Event.privacy == 3)
                    .join(User, Event.author)
                    .join(friendship, friendship.c.a_id == User.id)
                    .filter(friendship.c.b_id == current_user.id)
                )
    
    return result
 

Есть ли лучший способ подойти к этой функции или, возможно, к базовым классам и таблицам? Спасибо!

Комментарии:

1. Именно по этой причине я просто пишу запросы в своем коде и вызываю sql, а также обхожу ORM все вместе.

2. @eatmeimadanish, я вижу. На самом деле я никогда не писал SQL, и до этого проекта я вообще никогда не работал с базой данных. Извините, но у вас есть какие-нибудь предложения по написанию такого сложного запроса?