模块导入时执行代码是否可以?

时间:2012-02-28 17:01:52

标签: python sqlalchemy relational-database declarative python-import

我正在设计一个小型GUI应用程序来包装sqlite DB(简单的CRUD操作)。我创建了三个sqlalchemy模型(m_personm_card.pym_loan.py,全部位于/models文件夹中),并且之前在每个模型的顶部都有以下代码:< / p>

from sqlalchemy import Table, Column, create_engine
from sqlalchemy import Integer, ForeignKey, String, Unicode
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import backref, relation

engine = create_engine("sqlite:///devdata.db", echo=True)
declarative_base = declarative_base(engine)
metadata = declarative_base.metadata

这感觉有点不对(干)因此建议我将所有这些东西移到模块级别(进入models/__init__.py)。

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy import Table, Column, Boolean, Unicode
from settings import setup

engine = create_engine('sqlite:///' + setup.get_db_path(), echo=False)
declarative_base = declarative_base(engine)
metadata = declarative_base.metadata

session = sessionmaker()
session = session()

..并像这样导入declarative_base

from sqlalchemy import Table, Column, Unicode
from models import declarative_base


class Person(declarative_base):
    """
    Person model

    """
    __tablename__ = "people"

    id = Column(Unicode(50), primary_key=True)
    fname = Column(Unicode(50))
    sname = Column(Unicode(50))

但是我有很多反馈意见执行代码,因为这样的模块导入很糟糕? 我正在寻找一个明确的答案正确的方法,因为它似乎试图删除代码重复我已经介绍了一些其他不良做法。任何反馈都非常有用。

以下是来自get_db_path()的{​​{1}}方法的完整性,因为它在上面的settings/setup.py代码中调用。

models/__init__.py

4 个答案:

答案 0 :(得分:5)

一些流行的框架(Twisted就是一个例子)在导入时执行大量的初始化逻辑。能够动态构建模块内容的好处确实付出了代价,其中之一就是IDE无法始终决定模块中的“内部”。

在您的特定情况下,您可能需要重构,以便在导入时但稍后不提供特定引擎。您可以在导入时创建元数据和declarative_base类。然后在开始时间内,在定义所有类之后,调用create_engine并将结果绑定到sqlalchemy.orm.sessionmaker。但如果您的需求很简单,您甚至可能不需要那么远。

一般来说,我会说这不是Java或C.除了定义函数,类和常量之外,没有理由担心在模块级别做事情。无论如何,应用程序一个接一个地开始创建您的类。如果它简化了你的实现,那么在我看来,可以对类(在同一个模块中)进行一些修补,或创建一个或两个全局查找表。

我绝对会避免的是模块中的任何代码导致导入的顺序对用户很重要(除了简单地提供逻辑的常规方式),或者修改模块外部代码的行为。然后你的模块变成黑魔法,这在Perl世界中被接受(ala use strict;),但我发现它不是“pythonic”。

例如,如果您的模块在导入时修改了sys.stdout的属性,我认为应该将行为转移到用户可以调用或不调用的函数中。

答案 1 :(得分:3)

原则上,导入模块时执行Python代码没有任何问题,实际上每个Python模块都是这样工作的。毕竟,定义模块成员正在执行代码。

但是,在您的特定用例中,我强烈建议您不要在代码库中创建单例数据库会话对象。您将失去执行许多操作的能力,例如,针对内存中的SQLite或其他类型的数据库引擎对您的模型进行单元测试。

查看documentation for declarative_base并注意示例如何使用declarative_base提供的类来创建模型,该类尚未绑定到数据库引擎。理想情况下,您希望这样做,然后使用某种连接函数或类来管理创建会话,然后将其绑定到base。

答案 2 :(得分:2)

我也遇到了这个问题,并创建了一个带有数据库管理器类的database.py文件,之后我创建了一个全局对象。这样,类可以从我的settings.py文件中读取设置来配置数据库,并且需要使用基础对象(或会话/引擎)的第一个类将初始化全局对象,之后每个人都重新使用它。使用SQLAlchemy ORM在每个类的顶部“myproject.database import DM”,我感觉更舒服,其中DM是我的全局数据库对象,然后是DM.getBase()来获取基础对象。

这是我的database.py类:

Session = scoped_session(sessionmaker(autoflush=True))

class DatabaseManager():
    """
    The top level database manager used by all the SQLAlchemy classes to fetch their session / declarative base.
    """
    engine = None
    base = None

    def ready(self):
        """Determines if the SQLAlchemy engine and base have been set, and if not, initializes them."""
        host='<database connection details>'
        if self.engine and self.base:
            return True
        else:
            try:
                self.engine = create_engine(host, pool_recycle=3600)
                self.base = declarative_base(bind=self.engine)
                return True
            except:
                return False

    def getSession(self):
        """Returns the active SQLAlchemy session."""
        if self.ready():
            session = Session()
            session.configure(bind=self.engine)
            return session
        else:
            return None

    def getBase(self):
        """Returns the active SQLAlchemy base."""
        if self.ready():
            return self.base
        else:
            return None

    def getEngine(self):
        """Returns the active SQLAlchemy engine."""
        if self.ready():
            return self.engine
        else:
            return None

DM = DatabaseManager()

答案 3 :(得分:2)

导入时执行代码没有任何问题。

指南正在执行,以便您的模块可以使用,但不是太多,以至于导入它的速度不必要地慢,而不是说你不必要地限制它可以用它做什么。

通常这意味着定义类,函数和全局名称(实际上是模块级别 - 在那里用词不当),以及导入需要操作的类,函数等任何内容。

这通常不涉及与数据库,网站或其他外部动态资源建立连接,而是在模块用户准备好时提供建立这些连接的功能。