SQLAlchemy太复杂了吗?

35 投票
6 回答
16040 浏览
提问于 2025-04-15 11:34

这听起来可能有点争论,但我刚刚看完了SQLAlchemy的ORM教程,结果写出了以下代码:

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“更强大”,但这种强大似乎是有代价的,还是我理解错了什么?

6 个回答

1

我用过SQLObject(只了解过SQLAlchemy),可以说SQLObject的一个优点就是它让你做事情变得简单又方便。而且,它还有一个很棒的邮件讨论组(https://lists.sourceforge.net/lists/listinfo/sqlobject-discuss),你可以在这里提问,通常能很快得到回复。

10

你给的代码示例不太一样。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,所以不能具体说SQLAlchemy做得更好在哪里。但我在使用SQLAlchemy时有过很好的体验,尤其是在处理复杂的、现实中的旧数据库结构时。它默认能生成不错的SQL查询,而且有很多方法可以调整这些查询。

我发现SQLAlchemy作者的简短介绍在实际使用中也很靠谱。

88

好吧,你可能忽略了一点:你提到的教程并没有“构建”一个完整的例子,里面的不同代码片段并不是要拼接成一个完整的源文件。相反,它们是用来展示这个库可以如何使用的不同方法。你不需要自己一次又一次地尝试做同样的事情。

如果不考虑实际使用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,而不需要实际去实例化它(如果当前线程没有现成的会自动实例化一个新的会话,否则就会重用已有的会话)。

撰写回答