Как лучше всего создать или обновить зависимые модели при создании экземпляров другой модели после того, как идентификатор известен - PullRequest
0 голосов
/ 12 января 2020

Я использую SQLAlchemy с Flask -SQLAlchemy и пытаюсь настроить пару событий после создания новых элементов. Я хочу, чтобы это работало, когда люди взаимодействуют с моделями без необходимости понимать логику c. Он должен просто работать, когда они создают новые объекты.

В настоящее время он выглядит примерно так:

class A(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    b_id = db.Column(db.Integer, db.ForeignKey('b.id'), nullable=True)
    generated_from_id = db.Column(db.String)

@event.listens_for(A, 'after_insert')
def generate_something(mapper, connection, target):
    id = target.id
    a_table = target.__table__
    generated_thing = <do some operation on id>
    connection.execute(
        a_table.update().where(a_table.columns.id==target.id).values(generated_from_id=generated_thing)
    )


class B(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    some_attribute = db.Column(db.String)

@event.listens_for(B, 'after_insert')
def create_or_update_class_a(mapper, connection, target):
    if target.some_attribute == 'whatever':
        # we need to create a new A

        # this creates a new A, but the event listener on A does not catch the insert
        connection.execute(A.__table__.insert().values(b_id=target.id))

        # this creates a new A and the event listener on A does catch the insert, but it shows warning:
        # sqlalchemy/orm/session.py:2488: SAWarning: Usage of the 'Session.add()' operation is not currently supported within the execution stage of the flush process. Results may not be consistent.
        new_a = A()
        db.session.add(new_a)

    else:
        # we need to update an existing A
        target_a = <some logic to find which A to update>

        # this does update the existing A, but it seems that sometimes the b_id is stale and I need to do something like db.session.refresh(some_a)
        connection.execute(A.__table__.update().where(A.__table__.columns.id==target_a.id).values(b_id=target.id))

Так что всякий раз, когда я создаю новые B, такие как:

new_b = B(some_attribute='whatever')
db.session.add(new_b)
db.session.commit()

Я ожидаю что мой новый B создан и создан соответствующий A. Когда я делаю что-то вроде:

new_b = B(some_attribute='something-else')
db.session.add(new_b)
db.session.commit()

Я ожидаю, что мой существующий A будет обновляться, в идеале, без каких-либо устаревших данных при запросе As для B.

Как отмечено в комментариях выше:

  1. Использование db.session.add() в событии after_insert для B показывает предупреждение SA, но в остальном кажется в основном нормальным. Использование .update() в событии after_insert для B обновляет db для единственного значения на A, но не вызывает обработчик события A для generate_something(). Есть ли лучший подход?
  2. Использование .update() в другом случае в create_or_update_class_a приводит к некоторым устаревшим данным. Это кажется не совсем идеальным, так как я не буду знать, когда мне нужно обновить sh моих объектов. Как мне узнать, когда нужно обновить sh мой объект? Есть ли лучший способ сделать это?
...