簡體   English   中英

使用自動加載的Python和SQLAlchemy經典映射:映射器無法為映射表組裝任何主鍵列

[英]Python and SQLAlchemy Classical Mapping using autoload: Mapper could not assemble any primary key columns for mapped table

為了了解有關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映射。

  • 關於數據庫URI格式:用戶名在用戶密碼之前,因此在您的示例中應該是'postgresql://someuser:somepassword@127.0.0.1/hello'

  • 我們可以在create_engine傳遞echo標志:它比設置屬性更好,而且默認設置為False ,所以在特定情況下我們根本不需要它。

  • 如果您不想使用Column對象編寫Table對象模式(在使用MetaData描述數據庫時獲得更多信息) - 只需反映metadata並從metadata對象的tables字段中獲取'people'表。

  • sessionmakerSession對象的工廠 ,而不是Session對象,所以你應該使用double調用(看起來很難看)或者將sessionmaker實例作為單獨的對象提取(在我們的例子中我們將其稱為session_factory )。

  • AFAIK Session對象沒有方法save ,但add

  • 如果我們想要保存我們的更改(將lewis_hamilton記錄添加到表中),我們最后應該調用commit方法,因為flush與數據庫通信但是沒有提交更改記錄將丟失(在你的情況下我們可以在沒有flush方法調用的情況下工作,你可以閱讀更多關於flush VS 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()

但是使用Column對象創建people Table對象而不是反射就更好(因為它更明確, 第二個原則

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

此外,如果您想在對象關系教程之后使用'people'表映射類People ,我們將會有類似的東西

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__方法 ,最后我們會有

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所述:

應避免使用通配符導入(來自import *),因為它們不清楚命名空間中存在哪些名稱,使讀者和許多自動化工具混淆。 通配符導入有一個可防御的用例,即將內部接口重新發布為公共API的一部分(例如,使用可選加速器模塊中的定義覆蓋接口的純Python實現,以及確切的定義將是被覆蓋的事先不知道)。

你的路線

from sqlalchemy import *

應該更具體,因為里面有很多東西,你不需要它們。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM