SQLAlchemy期望对象,却找到一个表

13 投票
3 回答
11339 浏览
提问于 2025-04-17 06:30

我现在在学习sqlalchemy。在我当前的项目中,有一部分是用Flask做的,还有一部分是通过命令行来完成的。Flask的部分运行得很好,和sqlalchemy的连接也没问题,但命令行的部分就不行了。

我遇到的错误是

ArgumentError("Class object expected, got 'Table('documentos', 
 MetaData(bind=Engine(postgresql://user:password@localhost/clasificador)), 
 Column('id', Integer(), table=<documentos>, primary_key=True, nullable=False),
 Column('nombre', String(length=248), table=<documentos>), schema=None)'.",)

我试着在谷歌上找解决办法,也看了关于sqlalchemy的文档,但找不到问题出在哪里。模块里的代码是:

from sqlalchemy.orm import sessionmaker
from db import engine,Base
#some other code
session = sessionmaker(bind=engine)
doc = modelos.documento.Documento(os.path.basename(nelto))
session.add(doc) #here fails
session.remove()

db是我用来存放sqlalchemy公共代码的模块,大部分内容来自Flask的文档,而db_session只用于Flask,我为另一个模块做了一个不同的会话。

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

sqldebug=True 

engine = create_engine( 
    'postgresql://user:passwd@localhost/clasificador',
    convert_unicode=True,
    echo=sqldebug)
db_session = scoped_session(sessionmaker(autocommit=False,
                                     autoflush=False,
                                     bind=engine))
Base = declarative_base(bind=engine)
Base.query = db_session.query_property()

最后,这是“documento”模块,虽然我怀疑问题不在这里。代码如下:

from sqlalchemy import Column, Integer, String from db import Base
class Documento(Base):
    '''Clase definiendo los documentos'''
    __tablename__ = "documentos"

    id = Column(Integer,primary_key=True) 
    nombre = Column(String(248))

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

    def __repr__(self):
        return '<Documento %r>' % self.nombre

一些注释/名称是西班牙语的,但我觉得你可以忽略这些,如果需要的话我可以翻译。

根据Lafada的代码,我创建了另一个文件,内容只有:

from sqlalchemy.orm import sessionmaker
from modelos.documento import Documento
from db import Base, engine
import os

Session = sessionmaker(bind=engine)
session = Session()
doc = Documento(os.path.basename('/tmp/test.py')) #here fails
session.add(doc) 
session.commit()

而且它运行得很好。我能发现的唯一不同之处是会话的创建方式,我在原始代码中也修改了这一点,但还是出现同样的错误。

我找到了问题的根源,问题不在我展示的代码中,而是在另一个类里,它试图与这个类建立关系,但链接的是表而不是对象。在我尝试了其他几种方法之前,我一直没能找到真正的问题。

3 个回答

0

我对sqlalchemy和它的声明式格式不是很熟悉,但我觉得有一点不太对,就是你在重写init方法的时候,没有调用它的父类(这里是Base)。

要么去掉init,要么调用Base.init(self)

29

我之前也遇到过这个错误,原因是我忘记了 ForeignKey() 需要的是数据库表和字段的名字,而 relationship() 需要的是ORM类的名字。也就是说,我有时候会写成:

movie_id = Column(Integer, ForeignKey('movie.id'))
movie = relationship('movie')  # WRONG!
# Exception: "SQLAlchemy expects to find an object…"

其实我应该这样写,假设 movie 是数据库表的名字(顺便说一下,SQL对表名的大小写是不敏感的!),而 Movie 是我在Python中定义的ORM类的名字,应该是:

movie_id = Column(Integer, ForeignKey('movie.id'))
movie = relationship('Movie')  # Works!
6

我在我的电脑上试了你的代码,使用的是mysql(我这儿没有postgress :()。我把代码放在这里,请你检查一下,因为对我来说运行得很好。

#filename: /tmp/test.py
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import scoped_session, sessio

nmaker
from sqlalchemy.ext.declarative import declarative_base
import os

sqldebug=True 

engine = create_engine('mysql://test:test@localhost/test1', 
            convert_unicode=True,
            echo=sqldebug)

#create_engine( 
#           'postgresql://user:passwd@localhost/clasificador',
#           convert_unicode=True,
#           echo=sqldebug)

db_session = scoped_session(sessionmaker(autocommit=False,
                                         autoflush=False,
                                         bind=engine))
Base = declarative_base(bind=engine)
Base.query = db_session.query_property()

class Documento(Base):
    '''Clase definiendo los documentos'''
    __tablename__ = "documentos"

    id = Column(Integer,primary_key=True)
    nombre = Column(String(248))

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

    def __repr__(self):
        return '<Documento %r>' % self.nombre

Base.metadata.create_all(engine)


from sqlalchemy.orm import sessionmaker
#some other code
Session = sessionmaker(bind=engine)
session = Session()
doc = Documento(os.path.basename('/tmp/test.py'))
session.add(doc) #here fails
session.commit()

输出日志

In [11]: ed /tmp/test.py
Editing... done. Executing edited code...
2011-11-18 08:48:41,254 INFO sqlalchemy.engine.base.Engine SELECT DATABASE()
2011-11-18 08:48:41,254 INFO sqlalchemy.engine.base.Engine ()
2011-11-18 08:48:41,259 INFO sqlalchemy.engine.base.Engine SHOW VARIABLES LIKE 'character_set%%'
2011-11-18 08:48:41,259 INFO sqlalchemy.engine.base.Engine ()
2011-11-18 08:48:41,290 INFO sqlalchemy.engine.base.Engine SHOW VARIABLES LIKE 'lower_case_table_names'
2011-11-18 08:48:41,290 INFO sqlalchemy.engine.base.Engine ()
2011-11-18 08:48:41,320 INFO sqlalchemy.engine.base.Engine SHOW COLLATION
2011-11-18 08:48:41,320 INFO sqlalchemy.engine.base.Engine ()
2011-11-18 08:48:41,339 INFO sqlalchemy.engine.base.Engine SHOW VARIABLES LIKE 'sql_mode'
2011-11-18 08:48:41,339 INFO sqlalchemy.engine.base.Engine ()
2011-11-18 08:48:41,343 INFO sqlalchemy.engine.base.Engine DESCRIBE `documentos`
2011-11-18 08:48:41,343 INFO sqlalchemy.engine.base.Engine ()
2011-11-18 08:48:41,389 INFO sqlalchemy.engine.base.Engine ROLLBACK
2011-11-18 08:48:41,391 INFO sqlalchemy.engine.base.Engine 
CREATE TABLE documentos (
    id INTEGER NOT NULL AUTO_INCREMENT, 
    nombre VARCHAR(248), 
    PRIMARY KEY (id)
)


2011-11-18 08:48:41,391 INFO sqlalchemy.engine.base.Engine ()
2011-11-18 08:48:41,683 INFO sqlalchemy.engine.base.Engine COMMIT
2011-11-18 08:48:41,698 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2011-11-18 08:48:41,700 INFO sqlalchemy.engine.base.Engine INSERT INTO documentos (nombre) VALUES (%s)
2011-11-18 08:48:41,700 INFO sqlalchemy.engine.base.Engine ('test.py',)
2011-11-18 08:48:41,701 INFO sqlalchemy.engine.base.Engine COMMIT

根据日志,这段代码会在数据库里添加一条新记录。如果你还有其他问题,我很乐意帮你解决 :)

撰写回答