【问题标题】:sqlalchemy autoloaded orm persistencesqlalchemy 自动加载的 orm 持久性
【发布时间】:2012-07-31 21:30:39
【问题描述】:

我们正在使用 sqlalchemy 的自动加载功能进行列映射,以防止在我们的代码中进行硬编码。

class users(Base):
    __tablename__ = 'users'
    __table_args__ = {
        'autoload': True,
        'mysql_engine': 'InnoDB',
        'mysql_charset': 'utf8'
    }

有没有办法序列化或缓存自动加载的元数据/orm,这样我们就不必每次需要从其他脚本/函数引用我们的 orm 类时都经过自动加载过程?

我查看了烧杯缓存和泡菜,但没有找到明确的答案,如果可能或如何做到这一点。

理想情况下,我们仅在已提交对数据库结构的更改但从所有其他脚本/函数引用数据库映射的非自动加载/持久/缓存版本时才运行自动加载映射脚本,

有什么想法吗?

【问题讨论】:

  • 为什么不反过来:在 SA 中定义完整的模型。作为副作用,这将充当数据库模式的源代码控制。 当然,这仅在您的 SA 应用程序对您正在使用的数据库具有主要控制权时才有效
  • 在我的情况下,数据库开发是单独处理的,这意味着应用程序不会完全控制。但是,我找到了一种腌制元数据的方法,所以我只需要通过数据库连接反映一次来创建腌制,我使用腌制元数据反映的时间只是通过数据库连接的一小部分时间(见下文)。

标签: python caching orm sqlalchemy autoload


【解决方案1】:

我现在正在做的是在通过数据库连接 (MySQL) 运行反射后腌制元数据,一旦有腌制可用的腌制元数据,使用该腌制的元数据来反映模式,并将元数据绑定到 SQLite 引擎。

cachefile='orm.p'
dbfile='database'
engine_dev = create_engine(#db connect, echo=True)
engine_meta = create_engine('sqlite:///%s' % dbfile,echo=True)
Base = declarative_base()
Base.metadata.bind = engine_dev
metadata = MetaData(bind=engine_dev)

# load from pickle 
try:
    with open(cachefile, 'r') as cache:
        metadata2 = pickle.load(cache)
        metadata2.bind = engine_meta
        cache.close()
    class Users(Base):
        __table__ = Table('users', metadata2, autoload=True)

    print "ORM loaded from pickle"

# if no pickle, use reflect through database connection    
except:
    class Users(Base):
        __table__ = Table('users', metadata, autoload=True)

print "ORM through database autoload"

# create metapickle
metadata.create_all()
with open(cachefile, 'w') as cache:
    pickle.dump(metadata, cache)
    cache.close()

如果这没问题(它有效)或者有什么我可以改进的,有任何 cmets 吗?

【讨论】:

  • 您可能会简化此操作,只使用一个 MetaData 对象,也只需执行一个简单的“if os.path.exists(cachefile)”来确定您是否正在取消腌制。 "Table('users', metadata, autoload=True)" 等只需要声明一次,因为正如您已经看到的,如果表已经在 MetaData 中,它会跳过反射。
  • 我认为在with 语句中使用时不需要关闭文件,但这不相关。您的方法似乎很有趣,是否按预期工作?
【解决方案2】:

我的解决方案与@user1572502 的解决方案没有太大区别,但可能有用。我将缓存的元数据文件放在~/.sqlalchemy_cache,但它们可以在任何地方。


# assuming something like this:
Base = declarative_base(bind=engine)

metadata_pickle_filename = "mydb_metadata_cache.pickle"

# ------------------------------------------
# Load the cached metadata if it's available
# ------------------------------------------
# NOTE: delete the cached file if the database schema changes!!
cache_path = os.path.join(os.path.expanduser("~"), ".sqlalchemy_cache")
cached_metadata = None
if os.path.exists(cache_path):
    try:
        with open(os.path.join(cache_path, metadata_pickle_filename), 'rb') as cache_file:
            cached_metadata = pickle.load(file=cache_file)
    except IOError:
        # cache file not found - no problem
        pass
# ------------------------------------------

# -----------------------------
# Define database table classes
# -----------------------------
class MyTable(Base):
    if cached_metadata:
        __table__ = cached_metadata.tables['my_schema.my_table']
    else:
        __tablename__ = 'my_table'
        __table_args__ = {'autoload':True, 'schema':'my_schema'}

# ... continue for any other tables ...

# ----------------------------------------
# If no cached metadata was found, save it
# ----------------------------------------
if cached_metadata is None:
    # cache the metadata for future loading
    # - MUST DELETE IF THE DATABASE SCHEMA HAS CHANGED
    try:
        if not os.path.exists(cache_path):
            os.makedirs(cache_path)
        # make sure to open in binary mode - we're writing bytes, not str
        with open(os.path.join(cache_path, metadata_pickle_filename), 'wb') as cache_file:
            pickle.dump(Base.metadata, cache_file)
    except:
        # couldn't write the file for some reason
        pass

重要提示!!如果数据库架构发生变化,您必须删除缓存文件以强制代码自动加载并创建新缓存。如果不这样做,更改将反映在代码中。这很容易忘记。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-15
    • 1970-01-01
    • 2011-04-06
    • 2018-08-08
    • 1970-01-01
    • 2014-05-29
    • 2012-12-04
    相关资源
    最近更新 更多