SqlAlchemy中的动态表创建和ORM映射

时间:2009-06-10 02:51:11

标签: python database sqlalchemy

我是使用关系数据库的新手,所以我更喜欢使用一个好的ORM来简化事情。我花时间评估不同的Python ORM,我认为SQLAlchemy就是我需要的。但是,我已经陷入了精神上的死胡同。

我需要创建一个新表来与我在应用程序的播放器表中创建的每个玩家实例一起使用。我想我知道如何通过元数据更改表的名称然后调用create函数来创建表,但我不知道如何将它映射到新的动态类。

有人可以给我一些提示,帮助我解决大脑冻结问题吗?这甚至可能吗?

注意:如果我要求的内容更容易实现,我会对Python中的其他ORM开放。请告诉我如何: - )

7 个答案:

答案 0 :(得分:23)

我们绝对被SqlAlchemy宠坏了。
以下内容直接取自tutorial
并且非常容易设置和工作。

因为经常这样做,所以 Mike Bayer让这更容易 使用all-in-one "declarative" method

设置环境(我正在使用SQLite内存数据库进行测试):

>>> from sqlalchemy import create_engine
>>> engine = create_engine('sqlite:///:memory:', echo=True)
>>> from sqlalchemy import Table, Column, Integer, String, MetaData
>>> metadata = MetaData()

定义你的表格:

>>> players_table = Table('players', metadata,
...   Column('id', Integer, primary_key=True),
...   Column('name', String),
...   Column('score', Integer)
... )
>>> metadata.create_all(engine) # create the table

如果您已启用日志记录,您将看到SqlAlchemy为您创建的SQL。

定义你的班级:

>>> class Player(object):
...     def __init__(self, name, score):
...         self.name = name
...         self.score = score
...
...     def __repr__(self):
...        return "<Player('%s','%s')>" % (self.name, self.score)

将班级映射到你的桌子:

>>> from sqlalchemy.orm import mapper
>>> mapper(Player, players_table) 
<Mapper at 0x...; Player>

创建一名玩家:

>>> a_player = Player('monty', 0)
>>> a_player.name
'monty'
>>> a_player.score
0

就是这样,你现在有了一张你的牌桌。
此外,SqlAlchemy谷歌组很棒。
Mike Bayer很快回答问题。

答案 1 :(得分:3)

也许看看SQLSoup,这是SQLAlchemy的一层。

您也可以使用纯SQL创建表,并动态映射,如果它们已经没有create table函数,请使用这些库。

或者创建一个动态类并映射它:

tableClass = type(str(table.fullname), (BaseTable.BaseTable,), {})
mapper(tableClass, table)

其中BaseTable可以是您希望所有表类继承的任何Python类,例如:这样的Base类可以具有一些实用或常用方法,例如基本的CRUD方法:

class BaseTable(object): pass

否则你不需要将任何依据传递给type(...)

答案 2 :(得分:3)

如果您要创建动态类和表,则可以根据我在此处(http://sparrigan.github.io/sql/sqla/2016/01/03/dynamic-tables.html)找到的本教程URL使用以下技术,我对此做了一些修改。

from sqlalchemy import create_engine
engine = create_engine('sqlite:///test.db', echo=True)
from sqlalchemy import Column, Integer,Float,DateTime, String, MetaData
metadata = MetaData()
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Session = sessionmaker(bind=engine)
session = Session() # create a Session
Base = declarative_base()

首先包括所有必需的依赖项,然后创建会话和Base。

动态创建它的关键是这里:

attr_dict = {'__tablename__': 'default','id': Column(Integer, primary_key=True, auto_increment=True)}

您可以利用python中的'type'函数从此创建一个表。

myClass = type('ClassnameHere', (Base,), attr_dict)

请注意,我们正在传递 attr_dict ,这将为我们的班级提供必需的表名和列信息,但是区别在于我们是定义班级通过字符串命名!!这意味着您可以创建一个循环,例如遍历字符串数组以开始动态创建表!

接下来您要做的就是简单地致电

Base.metadata.create_all(engine)

由于我们创建的动态类是从 Base 继承的,因此该命令将仅创建表!

您现在像这样添加到该表中:

SomeRow = myClass(id='2')
session.add(SomeRow)
session.commit()

如果您也不知道列名,则可以更进一步。只需参考本文即可了解如何做到这一点。

尽管如此,您实际上还是会做类似的事情:

firstColName = "Ill_decide_later"
secondColName = "Seriously_quit_bugging_me"

new_row_vals = myClass(**{firstColName: 14, secondColName: 33})

**操作符获取对象并将其解压缩,以便将firstColName和secondColName与赋值操作符一起添加,因此它基本上是同一件事:

new_row_vals = myClass(firstColName=14, secondColName=33)

此技术的优势在于,您现在可以动态添加到表中,而不必定义列名!

这些列名可以存储在例如字符串数组中,也可以存储在所需的任何字符串中,只需从那里获取即可。

答案 3 :(得分:2)

您可以使用声明方法在数据库中动态创建表

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey


Base = declarative_base()

class Language(Base):
    __tablename__ = 'languages'

    id = Column(Integer, primary_key=True)
    name = Column(String(20))
    extension = Column(String(20))

    def __init__(self, name, extension):
        self.name = name
        self.extension = extension

答案 4 :(得分:1)

当我尝试使用SQLAlchemy自动执行简单的CRUD任务时,我遇到了同样的问题。 这是简单的解释和一些代码:http://www.devx.com/dbzone/Article/42015

答案 5 :(得分:1)

这是一个非常老的问题。无论如何,如果您喜欢ORM,则可以很容易地生成具有以下类型的表类:

export default class ScreenLayout extends React.Component { 

    state = {
        posts: []
    }

    db = firebase.firestore()
    path = this.db.collection('usersData').doc(firebase.auth().currentUser.uid).collection("posts")


    onCollectionUpdate = (querySnapshot) => {
        const posts = [];    
            querySnapshot.forEach((doc) => { 
                const {summary, time, stringTime, user, userId} = doc.data();

                posts.push({
                    key: doc.id, doc, summary,
                    time, stringTime, user, userId
                });
            });

            this.setState({
                posts
            });

    }

    componentDidMount() {
        const {currentUser} = firebase.auth();
        this.setState({currentUser})

        this.unsubscribe = this.path.onSnapshot(this.onCollectionUpdate) 
    }

    componentWillUnmount() {
        this.unsubscribe(); 
    }

    render() { 
        return ( 
        <FlatListLayout
            data={this.state.posts}
            onPress={(data, index) => {console.log(data); console.log(this.state.posts[index].key)}}
        />
        )
    }
}

建立类工厂,可以很容易地为类和数据库表分配名称。

答案 6 :(得分:0)

也许我不太明白你想要什么,但这个食谱在不同的__tablename中创建了相同的列__

class TBase(object):
    """Base class is a 'mixin'.
    Guidelines for declarative mixins is at:

    http://www.sqlalchemy.org/docs/orm/extensions/declarative.html#mixin-classes

    """
    id = Column(Integer, primary_key=True)
    data = Column(String(50))

    def __repr__(self):
        return "%s(data=%r)" % (
            self.__class__.__name__, self.data
        )

class T1Foo(TBase, Base):
    __tablename__ = 't1'

class T2Foo(TBase, Base):
    __tablename__ = 't2'

engine = create_engine('sqlite:///foo.db', echo=True)

Base.metadata.create_all(engine)

sess = sessionmaker(engine)()

sess.add_all([T1Foo(data='t1'), T1Foo(data='t2'), T2Foo(data='t3'),
         T1Foo(data='t4')])

print sess.query(T1Foo).all()
print sess.query(T2Foo).all()
sess.commit()

info in example sqlalchemy