我應該在SQLAlchemy中創建映射器對象還是使用聲明性語法?

2024-04-25 04:15:26 发布

您现在位置:Python中文网/ 问答频道 /正文

使用SQLAlchemy定义持久化对象有两种(三种,但我不计算Elixir,因为它不是“正式的”)方法:

Explicit syntax for mapper objects

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;

Declarative syntax

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)

我可以看到,在使用mapper对象时,我将ORM定义与业务逻辑完全分离,而在使用声明性语法时,每当我修改业务逻辑类时,我都可以在那里编辑数据库类(理想情况下应该很少编辑)。

我不完全确定的是,对于业务应用程序,哪种方法更易于维护?

我还没有找到两种映射方法之间的比较,无法决定哪种方法更适合我的项目。

我倾向于使用“normal”方式(即,不是声明性扩展),因为它允许我“隐藏”,并将所有ORM逻辑排除在业务视图之外,但我希望听到这两种方法的有力论据。


Tags: 方法namefromimportselfstringsqlalchemydef
3条回答

我发现,如果使用sqlalchemy-migrate来版本化数据库架构(从我的角度来看,这是业务应用程序的必备功能),那么使用映射器对象比声明语法简单得多。如果您使用的是mapper对象,那么只需将表声明复制/粘贴到迁移版本,并使用简单的api修改数据库中的表。声明性语法使这一点更加困难,因为在将类定义复制到迁移版本后,必须从类定义中过滤掉所有的helper函数。

另外,在我看来,表之间的复杂关系用mapper对象语法表达得更清楚,但这可能是主观的。

“我不完全确定的是,对于业务应用程序,哪种方法更易于维护?”

一般都不能回答。

不过,考虑一下。

Django ORM是严格的声明性的——人们喜欢这样。

SQLAlchemy做了一些事情,并不是所有的事情都与所有的问题相关。

  1. SQLAlchemy从通用Python创建特定于数据库的SQL。如果要处理SQL或将Python类映射到现有表,则必须使用显式映射,因为您的重点是SQL,而不是业务对象和ORM。

  2. SQLAlchemy可以使用声明式风格(比如Django)为您创建所有内容。如果您希望这样做,那么您将放弃显式地编写表定义和显式地处理SQL。

  3. 长生不老药是另一种方法,可以省去你看SQL的麻烦。

最基本的问题是“您想看到和接触SQL吗?”

如果您认为触摸SQL使事情更“可维护”,那么您必须使用显式映射。

如果您认为隐藏SQL使事情更“可维护”,那么您必须使用声明式风格。

  • 如果你认为长生不老药可能与炼金术不同,或者在某种程度上无法实现它的承诺,那么就不要使用它。

  • 如果你认为长生不老药对你有帮助,那就用它吧。

在我们的团队中,我们决定使用声明式语法。

理由:

  • metadata如果需要的话,很容易找到:User.metadata
  • 您的User类通过子类化Base,有一个很好的ctor,它对所有字段都使用kwargs。对测试和其他方面有用。E、 g.:user=User(name='doe', password='42')。所以不需要写一个ctor!
  • 如果添加属性/列,则只需添加一次即可。”“不要重复你自己”是一个很好的原则。

关于“不让ORM出现在业务视图中”:实际上,以“正常”方式定义的User类在mapper函数按其方式运行时,会受到SA的严重攻击。IMHO,声明式的方式更诚实,因为它尖叫着:“这个类用于ORM场景中,可能不会像对待简单的非ORM对象那样对待它。”。

相关问题 更多 >