【问题标题】:Python and SQLAlchemy Classical Mapping using autoload: Mapper could not assemble any primary key columns for mapped table使用自动加载的 Python 和 SQLAlchemy 经典映射:映射器无法为映射表组装任何主键列
【发布时间】:2017-10-26 00:25:37
【问题描述】:

为了了解更多关于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()

我使用下表将插入“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              | 

但是当我运行脚本时出现以下错误:

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

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

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

【问题讨论】:

  • \d 人输出什么?您已发布列,但未发布这些列之后显示的索引。您能否修改您的帖子以包含这部分?

标签: python sqlalchemy


【解决方案1】:
  • 关于数据库URI格式:用户名在用户密码之前,所以在你的例子中应该是'postgresql://someuser:somepassword@127.0.0.1/hello'

  • 我们可以在create_engine中传递echo标志:这比设置属性要好,而且默认设置为False,所以在特殊情况下我们根本不需要它。

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

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

  • 1234563你的情况我们可以在没有flush方法调用的情况下工作,你可以阅读更多关于flushcommithere的信息。

对于所有这些 cmets,这样的东西应该可以工作

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()

但是使用Column对象而不是像反射一样创建peopleTable对象会更好(因为它更明确,second principle

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

此外,如果您想将 People 类与 'people' 表映射到 Object Relational Tutorial 之后,我们将有类似的东西

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

或没有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)

我们也可以添加__init__ method,所以最终我们将拥有

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()

进一步改进

PEP-8中所说:

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

所以你的台词

from sqlalchemy import *

应该更具体,因为里面有很多东西,你不需要全部。

【讨论】:

  • 最后通过将我的表映射到一个班级作品(最后一个例子)其他人对我不起作用。
  • 到底发生了什么?
猜你喜欢
  • 1970-01-01
  • 2021-09-26
  • 2021-08-15
  • 2019-10-29
  • 2021-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-19
相关资源
最近更新 更多