使用自动加载的Python和SQLAlchemy经典映射:映射器无法为映射表组装任何主键列

Dim*_*las 2 python sqlalchemy

为了了解有关python的更多信息,我制作了以下简单脚本:

#!/usr/bin/env python
# coding=utf-8
# -*- Mode: python; c-basic-offset: 4 -*-

from sqlalchemy import *
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import mapper

db=create_engine('postgresql://somepassword:someuser@127.0.0.1/hello')
db.echo = False
metadata = MetaData(db)

people=Table('people',metadata,autoload=True)

class People(object):
    pass

people_mapper=mapper(People,people)

db_session=sessionmaker()
db_session.configure(bind=db)

lewis_hamilton=People()
lewis_hamilton.name='Lewis'
lewis_hamilton.surname='Hamilton'
lewis_hamilton.age=44

db_session.save(lewis_hamilton)
db_session.flush()
Run Code Online (Sandbox Code Playgroud)

我使用下表将"Lewis Hamilton"插入(直接来自psql):

                                 Table "public.people"
 Column  |         Type          |                      Modifiers                      
---------+-----------------------+-----------------------------------------------------
 id      | integer               | not null default nextval('people_id_seq'::regclass)
 name    | character varying(60) | 
 surname | character varying(60) | 
 age     | smallint              | 
Run Code Online (Sandbox Code Playgroud)

但是当我运行脚本时,我收到以下错误:

sqlalchemy.exc.ArgumentError:Mapper Mapper | People | people无法为映射表'people'组装任何主键列

我也看到了以下问题,但没有提供我想要的答案:

因为我使用自动加载来加载sql映射.

Aza*_*kov 5

  • 关于数据库URI格式:用户名在用户密码之前,因此它应该'postgresql://someuser:somepassword@127.0.0.1/hello'在您的示例中.

  • 我们可以传入echoflag create_engine:它比设置属性更好,而且它False默认设置为,所以在特定情况下我们根本不需要它.

  • 如果您不想TableColumn对象编写对象模式(在使用MetaData描述数据库时有更多信息) - 只需从对象字段反映您的metadata并获取'people'表.tablesmetadata

  • sessionmakerSession对象工厂,而不是Session对象,所以你应该使用双重调用(看起来很难看)或提取sessionmaker实例作为单独的对象(在我们的例子中,我们将调用它session_factory).

  • AFAIK Session对象没有方法save,但是add.

  • 如果我们想要保存我们的更改(添加lewis_hamilton记录到表),我们应该最后调用commit方法,因为flush与数据库通信但没有提交更改记录将丢失(在您的情况下,我们可以在没有flush方法调用的情况下工作,您可以阅读更多关于flushvs commit 在这里).

所有这些评论都应该有效

from sqlalchemy import *
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import mapper

db = create_engine('postgresql://someuser:somepassword@127.0.0.1/hello')
metadata = MetaData(db)

# reflecting 'people' table from database schema
metadata.reflect(only=['people'])
people = metadata.tables['people']


class People(object):
    pass


people_mapper = mapper(People, people)

session_factory = sessionmaker(bind=db)
db_session = session_factory()

lewis_hamilton = People()
lewis_hamilton.name = 'Lewis'
lewis_hamilton.surname = 'Hamilton'
lewis_hamilton.age = 44

db_session.add(lewis_hamilton)
db_session.commit()
Run Code Online (Sandbox Code Playgroud)

但是使用对象而不是反射来创建对象会更好(因为它更明确,第二个原则)people TableColumn

people = Table('people', metadata,
               Column('id', Integer, primary_key=True),
               Column('name', String(60)),
               Column('surname', String(60)),
               Column('age', SmallInteger))
Run Code Online (Sandbox Code Playgroud)

另外,如果你想在Object Relational Tutorial之后People'people'table 映射类,我们会有类似的东西

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

db = create_engine('postgresql://someuser:somepassword@127.0.0.1/hello')
metadata = MetaData(db)
people = Table('people', metadata,
               Column('id', Integer, primary_key=True),
               Column('name', String(60)),
               Column('surname', String(60)),
               Column('age', SmallInteger))

Base = declarative_base(metadata=metadata)


class People(Base):
    __table__ = people
Run Code Online (Sandbox Code Playgroud)

或没有people Table对象:

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

db = create_engine('postgresql://someuser:somepassword@127.0.0.1/hello')

metadata = MetaData(db)
Base = declarative_base(metadata=metadata)


class People(Base):
    __tablename__ = 'people'

    id = Column('id', Integer, primary_key=True)
    name = Column('name', String(60))
    surname = Column('surname', String(60))
    age = Column('age', SmallInteger)
Run Code Online (Sandbox Code Playgroud)

我们也可以添加__init__方法,最后我们会有

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

db = create_engine('postgresql://someuser:somepassword@127.0.0.1/hello')

metadata = MetaData(db)
Base = declarative_base(metadata=metadata)


class People(Base):
    __tablename__ = 'people'

    id = Column('id', Integer, primary_key=True)
    name = Column('name', String(60))
    surname = Column('surname', String(60))
    age = Column('age', SmallInteger)

    def __init__(self, name, surname, age):
        self.name = name
        self.surname = surname
        self.age = age


session_factory = sessionmaker(bind=db)
db_session = session_factory()

lewis_hamilton = People(name='Lewis',
                        surname='Hamilton',
                        age=44)

db_session.add(lewis_hamilton)
db_session.commit()
Run Code Online (Sandbox Code Playgroud)

进一步改进

PEP-8所述:

应避免使用通配符导入(来自import*),因为它们不清楚命名空间中存在哪些名称,使读者和许多自动化工具混淆.通配符导入有一个可防御的用例,即将内部接口重新发布为公共API的一部分(例如,使用可选加速器模块中的定义覆盖接口的纯Python实现,以及确切的定义将是被覆盖的事先不知道).

你的路线

from sqlalchemy import *
Run Code Online (Sandbox Code Playgroud)

应该更具体,因为里面有很多东西,你不需要它们.