SQLAlchemy: значения полей отношений - PullRequest
0 голосов
/ 04 июля 2018

У меня есть три таблицы [MySQL]: Person, Role, PersonRole

class Person(Base):

    __tablename__ = 'people'

    id = Column(INTEGER(unsigned=True), primary_key=True)
    full_name = Column(String(120), nullable=False)
    email = Column(String(128))
    username = Column(String(50))
    last_modified = Column(TIMESTAMP, nullable=False,
                           server_default=text('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'))
    created = Column(TIMESTAMP, server_default=text('CURRENT_TIMESTAMP'))

    roles = relationship('Role',
                         secondary='person_role',
                         primaryjoin="and_(Person.id==PersonRole.person_id,"
                                     "Role.active==True,"
                                     "PersonRole.active==True)",
                         back_populates='people')

    def __repr__(self):
        """String representation."""
        return '''<Person(id='%s', full_name='%s')>''' % (
            str(self.id), str(self.full_name)
        )


class Role(Base):

    __tablename__ = 'role'

    id = Column(INTEGER(unsigned=True), Sequence('role_id_seq'), primary_key=True)
    role_name = Column(String(16))
    active = Column(Boolean, default=True)
    last_modified = Column(TIMESTAMP, nullable=False,
                           server_default=text('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'))
    created = Column(TIMESTAMP, server_default=text('CURRENT_TIMESTAMP'))

    users = relationship('Person',
                         secondary='person_role',
                         primaryjoin="and_(Role.id==PersonRole.role_id,"
                                     "Role.active==True,"
                                     "PersonRole.active==True)",
                         back_populates='roles')

    def __repr__(self):
        """String representation."""
        return '''<Role(role_id='%s', role_name='%s')>''' % (
            str(self.role_id), str(self.role_name)
        )


class PersonRole(Base):
    __tablename__ = 'person_role'

    ds_id = Column(INTEGER(unsigned=True),
                   ForeignKey('person.id'), primary_key=True)
    role_id = Column(INTEGER(unsigned=True),
                     ForeignKey('role.id'), primary_key=True)
    active = Column(Boolean, default=True)
    last_modified = Column(TIMESTAMP, nullable=False,
                           server_default=text('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'))
    created = Column(TIMESTAMP,
                     server_default=text('CURRENT_TIMESTAMP'))

Получение человека примерно так:

... настройка сеанса как s ...

person = s.query(Person).get(123)

и перечислить их роли:

for role in person.roles:
    print(role.id, role.role_name, role.active)

Теперь в каждой таблице есть столбец active. Это необходимо для отслеживания активного состояния [шокера], так как мы не хотим удалять данные из таблицы, просто сохраняем их состояние. Теперь о двух проблемах, которые не позволяют мне использовать SQLAlchemy в целом и заставляют меня писать и выполнять SQL вручную -

  1. Часть active в последнем цикле отображает активное состояние role.active, а не фактическое состояние person_role.active.
  2. Удаление роли приведет к удалению строки отношения вместо выполнения требуемого действия, person_role.active = 0
  3. Даже если я деактивирую отношения, добавление их снова приведет к ошибке Duplicate Key.

Есть ли разумный, действительный способ выполнить это без реструктуризации моих данных?


Изменить для уточнения:

В этом случае основными таблицами являются person и person_role. Персона - это главная таблица, в которой находятся наши пользователи. PersonRole выполняет роли, которые фактически выполняет человек, в форме от person.id до role.id. Таблица ролей - это просто таблица поиска для определения роли (для получения имен).

Полагаю, что я хочу, это способ перехвата того, как ORM фактически добавляет / удаляет данные. Добавление должно [более или менее] делать «upsert», а удаление должно, в основном, запускать запрос на обновление, например: update person_role set active = 0 where person_id = %s and role_id = %s.

Я прочитал много документов, но, как правило, теряюсь в терминологии. : S

1 Ответ

0 голосов
/ 04 июля 2018

Не уверен, что я полностью понял ваши проблемы, но позвольте мне попытаться помочь вам:

1) Это то, что меня больше всего смущает: person.roles сопоставляется с коллекцией Role сущностей. Разве это не то, что вы ожидаете?

2, 3) Вы установили связь между человеком и ролью, используя PersonRole в качестве вторичного. Деактивация означает установку PersonRole.status в неактивным , а не ее удаление. Попытка добавить его снова покажет ошибку Duplicate Key!

Я думаю, что вы хотите выполнить запрос для загрузки сущности PersonRole по ds_id и role_id, обновления ее статуса до active и сохранения изменений. Я понимаю, что иногда это может быть утомительно, поэтому, возможно, немедленное решение, которое не потребует от вас перемещения данных, будет сопоставлять Person и Role с PersonRole. Таким образом, несмотря на наличие Person.roles, у вас будет Person.personRoles, поэтому у вас есть доступ к PersonRole сущностям и вы можете установить его статус.

Конечно, это очень немедленное решение. SQLAlchemy чрезвычайно полезен, поэтому, возможно, вы можете перехватить удаление Person.roles и настроить его поведение, чтобы установить PersonRole в неактивно . Вы также можете прочитать больше о каскадном в документации.

Надеюсь, мне удалось кое-что прояснить:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...