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

时间:2009-09-21 07:19:44

标签: python sqlalchemy

有两个(三个,但我不算Elixir,因为它不是“官方”)用SQLAlchemy定义持久对象的方法:

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)

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

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

我无法找到两种映射方法之间的比较,以便能够确定哪一种更适合我的项目。

我倾向于使用“正常”方式(即不是声明性扩展),因为它允许我“隐藏”,并且保持业务视图的所有ORM逻辑,但我希望听到引人注目的两种方法的论据。

4 个答案:

答案 0 :(得分:24)

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

一般无法回答。

但是,请考虑这一点。

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

SQLAlchemy做了几件事,并非所有事情都与所有问题相关。

  1. SQLAlchemy使用通用Python创建特定于数据库的SQL。如果你想搞乱SQL,或者将Python类映射到现有表,那么你必须使用显式映射,因为你的重点是SQL,而不是业务对象和ORM。

  2. SQLAlchemy可以使用声明式样式(如Django)为您创建一切。如果你想要这个,那么你就是放弃显式编写表定义并明确地搞乱SQL。

  3. Elixir是一种替代方法,可以让您不必查看SQL。

  4. 根本问题是“你想看到并触摸SQL吗?”

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

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

    • 如果您认为Elixir可能与SQLAlchemy不同,或者未能以某种方式履行其承诺,那么请不要使用它。

    • 如果您认为Elixir会帮助您,请使用它。

答案 1 :(得分:15)

在我们的团队中,我们选择了声明性语法。

理由:

    如果需要,
  • metadata很容易找到:User.metadata
  • 你的User类凭借子类Base,有一个很好的ctor,可以为所有字段提供kwargs。用于测试和其他。例如:user=User(name='doe', password='42')。所以不需要写一个ctor!
  • 如果添加属性/列,则只需执行一次。 “不要重复自己”是一个很好的原则。

关于“从业务视图中保留ORM”:实际上,当User函数使用它时,以“正常”方式定义的mapper类会被SA严重修补。 。恕我直言,声明方式更诚实,因为它尖叫:“这个类在ORM场景中使用,可能不会像对待简单的非ORM对象那样对待”。

答案 2 :(得分:5)

我发现如果使用sqlalchemy-migrate来版本化数据库模式,使用映射器对象比声明性语法简单得多(从我的角度来看,这是业务应用程序的必备条件)。如果使用映射器对象,则只需将表声明复制/粘贴到迁移版本,并使用简单的api修改数据库中的表。声明性语法使得这更难,因为在将它们复制到迁移版本之后,必须从类定义中过滤掉所有辅助函数。

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

答案 3 :(得分:1)

从现在(2019年)开始,很多年后,sqlalchemy v1.3允许同时兼顾两者的混合方法

https://docs.sqlalchemy.org/en/13/orm/extensions/declarative/table_config.html#using-a-hybrid-approach-with-table

metadata = MetaData()

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

# possibly in a different file/place the orm-declaration
Base = declarative_base(metadata)

class User(Base):
  __table__ = Base.metadata.tables['users']
  def __str__(): 
    return "<User('%s')>" % (self.name)