Python SQLAlchemy: Reflecting the database breaks default/onupdate methods?

北城以北 提交于 2019-12-02 04:36:22

问题


I have two separate SQLAlchemy interfaces to a Postgres database. The first interface, in the context of a Flask App, contains this model:

app = create_app() # sets the SQLAlchemy Database URI, etc.
db = SQLAlchemy(app)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    updated_at = db.Column(db.DateTime, onupdate=datetime.datetime.utcnow)
    name = db.Column(db.String, nullable=False)

The second interface is not through Flask -- rather, it's a script that listens for a particular event, in which case it is meant to perform some computations and update a row in the database. To accomplish this, I have SQLAlchemy reflect the existent database:

from sqlalchemy import create_engine, MetaData, Table
from sqlalchemy.orm import mapper, sessionmaker
from sqlalchemy.ext.automap import automap_base
from os import environ

dbPath = "postgresql://" + ...
engine = create_engine(dbPath)

Base = automap_base()
Base.prepare(engine, reflect=True)

metadata = MetaData(engine)

class User(object):
    pass

users = Table('user', metadata, autoload=True, autoload_with=engine)
mapper(User, users)

Session = sessionmaker(bind=engine)
session = Session()

The issue I'm now running into is this: when I'm using the first interface to create a new entry or update one, things work as expected, and the created_at and updated_at fields are updated appropriately.

However, when I'm using the second interface -- importing the code and using session.query(User) to get an entry and to update it, the updated_at field doesn't change. Moreover, when I'm using this interface to create a new User, while it creates the new row as expected, it populates neither the created_at nor updated_at fields.

My questions:

  1. Why is this happening? Why does the reflection seemingly break the default/onupdate methods?
  2. How can I fix this?

回答1:


default and onupdate are handled entirely client side in Python and so cannot be reflected from the DB. See "Limitations of Reflection". In case of default you could use server_default:

class User(db.Model):
    ...
    created_at = db.Column(db.DateTime,
                           server_default=text("now() at time zone 'UTC'"))

and for onupdate you'd have to write a DB trigger and use server_onupdate=FetchedValue().

On the other hand you could avoid all that and just separate your models from your application code to a module, used by both your Flask application and your script. This would of course be a bit more involved as you'd have to use vanilla SQLAlchemy declarative instead of the customized db.Model base of Flask-SQLAlchemy. Or, you could use custom commands with Flask to implement your scripts, which would allow using the Flask-SQLAlchemy extensions.



来源:https://stackoverflow.com/questions/43359116/python-sqlalchemy-reflecting-the-database-breaks-default-onupdate-methods

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!