【问题标题】:SQLAlchemy is convoluted? [closed]SQLAlchemy 很复杂? [关闭]
【发布时间】:2010-10-26 00:13:24
【问题描述】:

这似乎颇有争议,但我刚刚浏览了 SQLAlchemy 的 ORM tutorial 并最终得到以下代码:

from sqlalchemy import create_engine
from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

engine = create_engine('sqlite:///:memory:', echo=True)

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

metadata.create_all(engine)

Base = declarative_base()
class User(Base):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True)
    name = Column(String)
    fullname = Column(String)
    password = Column(String)

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

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

users_table = User.__table__
metadata = Base.metadata

Session = sessionmaker(bind=engine)
Session = sessionmaker()
Session.configure(bind=engine)  # once engine is available
session = Session()

# actually using the ORM isn't too bad..
ed_user = User('ed', 'Ed Jones', 'edspassword')
session.add(ed_user)

our_user = session.query(User).filter_by(name='ed').first() 
print our_user

session.add_all([
    User('wendy', 'Wendy Williams', 'foobar'),
    User('mary', 'Mary Contrary', 'xxg527'),
    User('fred', 'Fred Flinstone', 'blah')])

ed_user.password = 'f8s7ccs'

print session.dirty
print session.new
session.commit()

for instance in session.query(User).order_by(User.id): 
    print instance.name, instance.fullname

for name, fullname in session.query(User.name, User.fullname): 
    print name, fullname

这对于一个有效的 Hello World 表来说似乎非常复杂,尤其是与大致相似的 SQLObject 代码相比:

from sqlobject import SQLObject, StringCol, sqlhub, connectionForURI

sqlhub.processConnection = connectionForURI('sqlite:/:memory:')

class Person(SQLObject):
    fname = StringCol()
    mi = StringCol(length=1, default=None)
    lname = StringCol()

Person.createTable()

p = Person(fname="John", lname="Doe")
p.mi = 'Q'
p2 = Person.get(1)
print p2
print p2 is p

我知道 SQLAlchemy “更强大”,但这种能力似乎是有代价的,还是我错过了什么?

【问题讨论】:

  • 权力是有代价的?你在说什么?
  • SQLAlchemy 的强大是以易用性为代价的?
  • 试试下面提到的 Elixir,你的 Hello World 将与 SQLObject 非常相似,你仍然可以访问 SQLAlchemy 层。我使用了 SQLObject 并对其局限性感到沮丧,到目前为止,我对 Elixir 非常满意(它缺乏一些文档和支持,但不幸的是用户群似乎有限)。
  • 人们不会简单地将问号附加到语句中。

标签: python orm sqlalchemy


【解决方案1】:

好吧,您缺少一件事:您提到的教程并没有“构建”一个完整的示例,不同的 sn-ps 代码并不意味着要连接到一个源文件中。相反,它们描述了可以使用该库的不同方式。无需自己一遍又一遍地尝试做同样的事情。

从您的示例中省略实际使用-the-orm 部分,代码可能如下所示:

from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, scoped_session

engine = create_engine('sqlite:///:memory:', echo=True)
Base = declarative_base(bind=engine)
Session = scoped_session(sessionmaker(engine))

class User(Base):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True)
    name = Column(String)
    fullname = Column(String)
    password = Column(String)

Base.metadata.create_all()

“声明性”扩展负责定义表并将其映射到您的类,因此您无需自己声明users_table。 User 类还允许使用关键字参数进行实例化,例如User(name="foo"),(但不是位置参数)。 我还添加了 scoped_session 的使用,这意味着您可以直接使用Session 而无需实际实例化它(如果当前线程中还没有一个会话,它将实例化一个新会话,否则重用现有的一个)

【讨论】:

  • 这看起来比我最终得到的代码更明智。谢谢!
  • 我对使用scoped_session 做任何事情都很犹豫;除非您知道为什么需要线程本地存储,否则您应该显式实例化会话并根据需要传递它。
  • @TokenMacGuy 我完全不同意。 scoped_session 承担了使用会话时传递的所有猜测和不必要的参数。您只需在开始时定义 scoped_session 类,然后在需要访问会话的任何地方实例化它,其余的由系统完成。我发现它对于我编写的所有网络应用程序都非常方便。
  • @CoreDumpError:对于网络应用程序,scoped_session 可能很合适;只要您的应用程序每个请​​求需要恰好一个 会话,并且您使用每个线程模型一个请求。如果这些不正确,您可能需要在请求上下文之外对数据库进行操作;如果您需要跨多个数据库协调事务,如果您以异步方式工作以提高性能,那么这种假设开始失效。
  • 是否需要在模块范围内定义engine、Base、Session?对于导入此模块的其他模块来说,这有点恶心。他们不需要知道用户的内部 ORM 成员。我也不太明白为什么需要在每个模型模块中重新实例化引擎和基础。
【解决方案2】:

您提供的代码示例并非千篇一律。 SQLAlchemy 版本可以缩减一点:

from sqlalchemy import create_engine
from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

engine = create_engine('sqlite:///:memory:', echo=True)
Base = declarative_base()

class User(Base):
    __tablename__ = 'users'

    id = Column('id', Integer, primary_key=True)
    name = Column('name', String)
    fullname = Column('fullname', String)
    password = Column('password', String)

    def __repr__(self):
       return "" % (self.name, self.fullname, self.password)

Base.metadata.create_all(engine)

Session = sessionmaker(bind=engine)
session = Session()

# actually using the ORM isn't too bad..
ed_user = User(name='ed', fullname='Ed Jones', password='edspassword')
session.add(ed_user)

our_user = session.query(User).filter_by(name='ed').first()

session.add_all([
    User(name='wendy', fullname='Wendy Williams', password='foobar'),
    User(name='mary', fullname='Mary Contrary', password='xxg527'),
    User(name='fred', fullname='Fred Flinstone', password='blah')])

ed_user.password = 'f8s7ccs'

session.flush()

for instance in session.query(User).order_by(User.id):
    print instance.name, instance.fullname

for name, fullname in session.query(User.name, User.fullname):
    print name, fullname

您可能还会发现 Elixir 更像 SQLObject(但由于我没有使用过,这只是一个猜测)。

根本没有使用过 SQLObject,我无法评论 SA 究竟在哪些方面做得更好。但我在 SA 方面拥有丰富的经验,尤其是在处理复杂的、现实世界的遗留模式时。默认情况下,它很好地提出了良好的 SQL 查询,并且有很多方法可以调整它们。

我发现 SQLAlchemy 作者的 elevator pitch 在实践中表现得很好。

【讨论】:

    【解决方案3】:

    使用过 SQLObject(并且只阅读过 SQLAlchemy),我可以说 SQLObject 的优势之一是您可以轻松简单地完成工作。此外,电子邮件组 (https://lists.sourceforge.net/lists/listinfo/sqlobject-discuss) 提供了出色的支持,可以很快得到答复。

    【讨论】:

      【解决方案4】:

      试试Quick ORM,更简单:

      from quick_orm.core import Database
      from sqlalchemy import Column, String
      
      class User(object):
          __metaclass__ = Database.DefaultMeta
          name = Column(String(30))
      
      if __name__ == '__main__':
          database = Database('sqlite://')
          database.create_tables()
      
          user = User(name = 'Hello World')
          database.session.add_then_commit(user)
      
          user = database.session.query(User).get(1)
          print 'My name is', user.name
      

      Quick ORM 是建立在 SQLAlchemy 之上的,所以我们可以说 SQLAlchemy 可以像 SQLObject 一样简单。

      【讨论】:

      • 很抱歉 quick_orm 不再维护了。
      【解决方案5】:

      好吧,SQLAlchemy 分为不同的部分,主要核心部分只是处理数据库,将您的 python 构建查询转换为底层数据库的适当 SQL 语言。然后是对会话、orm 和新的声明性语法的支持。

      看起来像 SQLObject(我不能肯定地说,已经很多年没有使用它了,即使那样,也只有一次)跳过了大部分并立即执行 ORM 部分。对于简单的数据,这通常会使事情变得更容易(在大多数情况下您可以逃脱),但 SQLAlchemy 允许更复杂的数据库布局,如果您真的需要它,可以使用数据库。

      【讨论】:

      • SQLObject 似乎支持更高级的东西,例如一对多/多对一/多对多关系(涵盖了我见过的几乎所有数据库布局)和事务。
      【解决方案6】:

      你说“令人费解”....其他人可能会说“灵活”。有时你需要它,有时你不需要。你有选择权不是很棒吗?

      【讨论】:

      • 当然,但我说“复杂”是因为它似乎迫使你使用它的许多功能来做基本的事情。灵活性很好,但如果它涉及五行导入只是开始!
      猜你喜欢
      • 2023-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-02
      • 2017-02-09
      • 2017-08-02
      相关资源
      最近更新 更多