Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use base class's property/attribute as a table column?

A game engine provides me with a Player class with a steamid property (coming from C++, this is just a basic example on what it would look like in Python):

# game_engine.py

class Player:

    def __init__(self, steamid):
        self.__steamid = steamid

    @property
    def steamid(self):
        return self.__steamid

I then proceed to subclass this class while adding a gold attribute:

# my_plugin.py

class MyPlayer(game_engine.Player, Base):
    gold = Column(Integer)

Now I need to store the player's gold to a database with the player's steamid as a primary key to identify the player. How do I tell SQLAlchemy to use the base class's steamid property as the primary key?

Here's something silly I tried:

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.hybrid import hybrid_property

import game_engine

Base = declarative_base()

class Player(game_engine.Player, Base):
    __tablename__ = 'player'

    _steamid = game_engine.Player.steamid

    @hybrid_property
    def steamid(self):
        return type(self)._steamid.__get__(self)

But yeah, it was a long shot...

sqlalchemy.exc.ArgumentError: Mapper Mapper|Player|player could not assemble any primary key columns for mapped table 'player'
like image 807
Markus Meskanen Avatar asked Jul 28 '16 10:07

Markus Meskanen


2 Answers

This is simpler than you might expect. The solution below is roughly equivalent to the one from r-m-n, but more straightforward because it uses modern declarative mapping. There is no need for @hybrid_property, you can just inherit steamid from the parent class.

# my_plugin.py

class MyPlayer(game_engine.Player, Base):

    def __init__(self, steamid, gold):
        super().__init__(steamid)
        self._id = self.steamid
        self.gold = gold

    _id = Column('steamid', Integer, primary_key=True)
    gold = Column(Integer)
like image 67
Julian Avatar answered Nov 07 '22 10:11

Julian


this could be done using classical mapping

from sqlalchemy import Column, Integer, Table
from sqlalchemy.orm import mapper
from sqlalchemy.ext.hybrid import hybrid_property


class MyPlayer(Player):
    def __init__(self, steamid, gold):
        super().__init__(steamid)
        self.gold = gold
        self._steamid = super().steamid


player = Table('player', Base.metadata,
    Column('_steamid', Integer, primary_key=True),
    Column('gold', Integer),
)


mapper(MyPlayer, player)
like image 20
r-m-n Avatar answered Nov 07 '22 09:11

r-m-n