我应该创建映射器对象还是在SQLAlchemy中使用声明式语法?

53 投票
4 回答
11409 浏览
提问于 2025-04-15 14:28

有两种(其实是三种,但我不算Elixir,因为它不是“官方”的)方式来用SQLAlchemy定义一个持久化对象:

显式语法的映射器对象

from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey
from sqlalchemy.orm import mapper

metadata = MetaData()

users_table = Table('users', metadata,
    Column('id', Integer, primary_key=True),
    Column('name', String),
)

class User(object):
    def __init__(self, name):
        self.name = name

    def __repr__(self):
       return "<User('%s')>" % (self.name)

mapper(User, users_table) # &lt;Mapper at 0x...; User&gt;

声明式语法

from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

class User(Base):
     __tablename__ = 'users'
     id = Column(Integer, primary_key=True)
     name = Column(String)

     def __init__(self, name):
         self.name = name

     def __repr__(self):
         return "<User('%s')>" % (self.name)

我发现使用映射器对象时,我可以把ORM(对象关系映射)的定义和业务逻辑完全分开;而使用声明式语法时,每当我修改业务逻辑类时,我也可以直接在那儿修改数据库类(理想情况下,这个数据库类应该很少被修改)。

我不太确定的是,哪种方法对商业应用来说更容易维护?

我找不到这两种映射方法的比较,无法决定哪种更适合我的项目。

我倾向于使用“正常”的方式(也就是不使用声明式扩展),因为这样可以让我“隐藏”所有ORM逻辑,把它们与业务视图隔离开,但我也想听听对这两种方法的有力论据。

4 个回答

6

我发现使用映射器对象比声明式语法要简单得多,特别是当你用 sqlalchemy-migrate 来管理数据库的版本时(在我看来,这对于商业应用来说是必不可少的)。如果你使用映射器对象,你可以简单地复制粘贴你的表定义到迁移版本中,并用简单的接口来修改数据库里的表。而声明式语法就比较麻烦,因为你在复制到迁移版本后,需要把所有的辅助函数从类定义中筛选掉。

另外,我觉得用映射器对象的语法来表示表之间复杂的关系会更清晰,但这可能是个人的看法。

16

在我们的团队里,我们选择了声明式语法。

原因:

  • metadata 很容易获取,如果需要的话:User.metadata
  • 你的 User 类,因为继承了 Base,有一个很不错的构造函数,可以接收所有字段的关键字参数。这对测试和其他用途都很有帮助。例如:user=User(name='doe', password='42')。所以不需要自己写构造函数!
  • 如果你添加一个属性或列,只需要做一次。遵循“不要重复自己”的原则是个不错的选择。

关于“让ORM远离业务视图”:实际上,你的 User 类,如果用“正常”的方式定义,当 mapper 函数处理它时,会被SA严重修改。在我看来,声明式的方式更诚实,因为它明确表示:“这个类是用在ORM场景中的,不能像对待普通非ORM对象那样对待它”。

26

“我不太确定,哪种方法对商业应用来说更容易维护?”

这个问题不能一概而论。

不过,可以考虑以下几点。

Django的ORM(对象关系映射)是非常明确的,大家都喜欢这种方式。

而SQLAlchemy则做了很多事情,但并不是所有的功能都适合所有问题。

  1. SQLAlchemy可以从通用的Python代码生成特定数据库的SQL。如果你想要修改SQL,或者把Python类映射到已有的表中,那么你就得使用明确的映射,因为你的重点是在SQL上,而不是业务对象和ORM。

  2. SQLAlchemy也可以使用声明式风格(像Django那样)来为你创建所有内容。如果你选择这种方式,就意味着你放弃了明确写表定义和直接操作SQL的机会。

  3. Elixir是一个替代方案,可以让你不必直接接触SQL。

根本的问题是:“你想看到并操作SQL吗?”

如果你觉得直接操作SQL会让事情更“容易维护”,那么你就得使用明确的映射。

如果你觉得隐藏SQL会让事情更“容易维护”,那么你就得使用声明式风格。

  • 如果你觉得Elixir可能会和SQLAlchemy不一致,或者在某些方面无法兑现它的承诺,那就不要使用它。

  • 如果你觉得Elixir会对你有帮助,那就用它。

撰写回答