【问题标题】:Dynamic Table Creation and ORM mapping in SqlAlchemySqlAlchemy 中的动态表创建和 ORM 映射
【发布时间】:2010-11-01 16:32:41
【问题描述】:

我对使用关系数据库还很陌生,所以我更喜欢使用良好的 ORM 来简化事情。我花时间评估不同的 Python ORM,我认为 SQLAlchemy 是我需要的。但是,我已经走到了精神上的死胡同。

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

有人可以给我一些提示来帮助我克服大脑冻结吗?这甚至可能吗?

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

【问题讨论】:

  • 创建动态表是个坏主意。向表中添加键以区分玩家实例是一个好主意。你想通过这个“动态表格”业务来完成什么?
  • 基本上每个玩家都有自己的分数表来跟踪他们在一段时间内的分数。可以在任何时间点添加一个球员,所以很难在一个包含所有球员的巨大分数表中跟踪它......至少对我来说是这样。
  • 当然,提问的过程让我重新思考。我可能可以创建一个巨大的分数表,其中包含玩家的 ID 以及他们的分数信息。然后我可以对玩家 ID 进行查询以获取他们的分数。谢谢大家。
  • 是的,(得分表中玩家 ID 的外键)正是正确的方法。你说,“我对使用关系数据库还很陌生,所以我更喜欢使用好的 ORM 来简化事情”,但你真的应该花时间学习关系数据库的工作原理,即使你使用的是 ORM,否则你最终会得到糟糕的架构和查询。

标签: python database sqlalchemy


【解决方案1】:

我们被 SQLAlchemy 宠坏了。
以下内容直接取自tutorial,
并且非常容易设置和开始工作。

而且因为经常这样做,
2011 年 8 月的文档 moved to full declarative

设置您的环境(我正在使用 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 映射的情况下获得该输出,对吧?
【解决方案2】:

这是一个非常古老的问题。无论如何,如果您更喜欢 ORM,生成具有类型的表类非常容易:

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String


Base = declarative_base()

Test = type('Test', (Base,), {
    '__tablename__': 'test',
    'test_id': Column(Integer, primary_key=True, autoincrement=True),
    'fldA': Column(String),  
    ... other columns
    }
)

Base.metadata.create_all(engine)

#  passed session create with sqlalchemy
session.query(Test).all()

创建类工厂,很容易为类和数据库表分配名称。

【讨论】:

    【解决方案3】:

    如果您希望创建动态类和表,您可以使用基于我在此处找到的本教程 URL (http://sparrigan.github.io/sql/sqla/2016/01/03/dynamic-tables.html) 的以下技术,我稍微修改了他的做法。

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

    首先包含所有需要的依赖项并创建您的会话和基础。

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

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

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

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

    请注意,我们传入的是 attr_dict,这将为我们的类提供所需的 tablename 和列信息,但不同的是我们是在 定义类通过字符串命名!这意味着您可以创建一个循环,例如遍历字符串数组以开始动态创建表!

    接下来你只需调用

    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)
    

    这种技术的优点是现在您可以动态地添加到表中,甚至无需定义列名!

    这些列名可以存储在一个字符串数组中,或者任何你想要的,你可以从那里获取。

    【讨论】:

      【解决方案4】:

      也许看看SQLSoup,它位于 SQLAlchemy 之上。

      您也可以使用纯 SQL 创建表,并动态映射,如果它们还没有创建表功能,请使用这些库。

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

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

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

      class BaseTable(object): pass
      

      否则您不需要将任何碱基传递给type(...)

      【讨论】:

      • 从哪里导入 BaseTable?我在 sqlalchemy 中找不到。
      • Elixir 自 2009 年以来一直没有更新,似乎已被废弃。它不支持 python 2.7+。
      【解决方案5】:

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

      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
      

      【讨论】:

        【解决方案6】:

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

        【讨论】:

          【解决方案7】:

          也许我不太明白你想要什么,但是这个食谱在不同的 __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

          【讨论】:

            猜你喜欢
            • 2017-07-04
            • 2020-05-10
            • 1970-01-01
            • 2017-09-29
            • 2019-06-20
            • 2020-01-24
            • 2020-09-21
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多