Mar*_*nen 8 python sqlalchemy properties class-attributes python-3.x
假设我正在制作带有物品的游戏(想想Minecraft,CS:GO武器,LoL和Dota物品等).游戏中可能存在大量相同的项目,但细节差异很小,例如条件/耐久性或项目中剩余的弹药数量:
player1.give_item(Sword(name='Sword', durability=50))
player2.give_item(Sword(name='Sword', durability=80))
player2.give_item(Pistol(name='Pistol', ammo=12))
Run Code Online (Sandbox Code Playgroud)
但是因为我不想每次都给我的剑和手枪命名(由于名字总是相同的),我希望它能够非常容易地创建新的项目类,我想我会name
上课属性:
class Item:
name = 'unnamed item'
Run Code Online (Sandbox Code Playgroud)
现在我只是将其子类化:
class Sword(Item):
name = 'Sword'
def __init__(self, durability=100):
self.durability = durability
class Pistol(Item):
name = 'Pistol'
def __init__(self, ammo=10):
self.ammo = ammo
Run Code Online (Sandbox Code Playgroud)
我们有工作班:
>>> sword = Sword(30)
>>> print(sword.name, sword.durability, sep=', ')
Sword, 30
Run Code Online (Sandbox Code Playgroud)
但有没有办法以这种或那种方式使用SQLAlchemy 这些类属性(有时甚至是classproperties)?比如,我想将项目的持久性(实例属性)和名称(类属性)与其class_id
(类属性)作为主键存储:
class Item:
name = 'unnamed item'
@ClassProperty # see the classproperty link above
def class_id(cls):
return cls.__module__ + '.' + cls.__qualname__
class Sword(Item):
name = 'Sword'
def __init__(self, durability=100):
self.durability = durability
Run Code Online (Sandbox Code Playgroud)
耐用性可以通过以下方式轻松完成:
class Sword(Item):
durability = Column(Integer)
Run Code Online (Sandbox Code Playgroud)
但是name
class属性和class_id
类属性怎么样?
实际上,我有更大的继承树,每个类都有多个属性/属性以及更多的实例属性.
更新:我在帖子中不清楚这些表格.我只想为这些项目设置一个表,其中class_id
它用作主键.这就是我用元数据构建表的方法:
items = Table('items', metadata,
Column('steamid', String(21), ForeignKey('players.steamid'), primary_key=True),
Column('class_id', String(50), primary_key=True),
Column('name', String(50)),
Column('other_data', String(100)), # This is __RARELY__ used for something like durability, so I don't need separate table for everything
)
Run Code Online (Sandbox Code Playgroud)
这是我的第二个答案,基于单表继承。
\n\n该问题包含一个示例,其中Item
子类具有自己的特定实例属性。例如,Pistol
是继承层次结构中唯一具有属性的类ammo
。当在数据库中表示这一点时,您可以通过为父类创建一个表(其中包含每个公共属性的列)并将特定于子类的属性存储在每个子类的单独表中来节省空间。SQLAlchemy 开箱即用地支持此功能,并将其称为连接表继承(因为您需要连接表才能收集公共属性和子类特有的属性)。Ilja Everil\xc3\xa4 的答案和我之前的答案都假设连接表继承是可行的方法。
事实证明,Markus Meskanen 的实际代码有点不同。子类没有特定的实例属性,它们只是有一个level
共同的属性。另外,Markus 评论说他希望所有子类都存储在同一个表中。使用单个表的一个可能的优点是您可以添加和删除子类,而不会每次都对数据库模式造成重大更改。
SQLAlchemy 也提供了对此的支持,它被称为单表继承。如果子类确实具有特定属性,它甚至可以工作。它只是效率稍低一些,因为每一行都必须存储每个可能的属性,即使它属于不同子类的项目。
\n\n这是我之前的答案(最初复制自Ilja 的答案)的解决方案 1 的稍微修改版本。此版本(“解决方案 1B”)使用单表继承,因此所有项目都存储在同一个表中。
\n\nclass Item(Base):\n name = \'unnamed item\'\n\n @classproperty\n def class_id(cls):\n return \'.\'.join((cls.__module__, cls.__qualname__))\n\n __tablename__ = \'item\'\n id = Column(Integer, primary_key=True)\n type = Column(String(50))\n durability = Column(Integer, default=100)\n ammo = Column(Integer, default=10)\n\n __mapper_args__ = {\n \'polymorphic_identity\': \'item\',\n \'polymorphic_on\': type\n }\n\n\nclass Sword(Item):\n name = \'Sword\'\n\n __mapper_args__ = {\n \'polymorphic_identity\': \'sword\',\n }\n\n\nclass Pistol(Item):\n name = \'Pistol\'\n\n __mapper_args__ = {\n \'polymorphic_identity\': \'pistol\',\n }\n
Run Code Online (Sandbox Code Playgroud)\n\n当我们将其与原始解决方案 1 进行比较时,有一些事情很突出。和属性已移至基类,因此它的每个实例durability
或其子类之一现在都具有 a和 an 。和子类已丢失其s 以及所有列属性。这告诉 SQLAlchemy和没有自己的关联表;换句话说,我们要使用单表继承。栏目属性和业务还在;这些为 SQLAlchemy 提供信息来确定表中的任何给定行是否属于,或类。这就是我说专栏是消歧器的意思。ammo
Item
Item
durability
ammo
Sword
Pistol
__tablename__
Sword
Pistol
Item.type
__mapper_args__
item
Item
Sword
Pistol
type
现在,Markus 还评论说,他不想自定义子类来创建具有单表继承的数据库映射。Markus 希望从没有数据库映射的现有类层次结构开始,然后只需编辑基类即可立即创建整个单表继承数据库映射。这意味着添加__mapper_args__
和Sword
子Pistol
类(如上面的解决方案 1B 中那样)是不可能的。事实上,如果可以“自动”计算消歧器,则可以节省大量样板文件,尤其是在有许多子类的情况下。
这可以通过使用 来完成@declared_attr
。输入解决方案4:
class Item(Base):\n name = \'unnamed item\'\n\n @classproperty\n def class_id(cls):\n return \'.\'.join((cls.__module__, cls.__qualname__))\n\n __tablename__ = \'item\'\n id = Column(Integer, primary_key=True)\n type = Column(String(50))\n durability = Column(Integer, default=100)\n ammo = Column(Integer, default=10)\n\n @declared_attr\n def __mapper_args__(cls):\n if cls == Item:\n return {\n \'polymorphic_identity\': cls.__name__,\n \'polymorphic_on\': type,\n }\n else:\n return {\n \'polymorphic_identity\': cls.__name__,\n }\n\n\nclass Sword(Item):\n name = \'Sword\'\n\n\nclass Pistol(Item):\n name = \'Pistol\'\n
Run Code Online (Sandbox Code Playgroud)\n\n这产生与解决方案 1B 相同的结果,只不过消歧器的值(仍然是type
列)是根据类计算的,而不是任意选择的字符串。在这里,它只是类的名称 ( cls.__name__
)。如果您可以保证每个子类都会覆盖 ,我们可以选择完全限定名称 ( cls.class_id
) 甚至自定义name
属性 ( ) 。只要值和类之间存在一对一的映射,您将什么作为消歧器的值并不重要。cls.name
name
归档时间: |
|
查看次数: |
2635 次 |
最近记录: |