Reputation: 21923
A game engine provides me with a Player
class that has an uniqueid
read-only property to identify players. I would like to "convert" this into an SQLAlchemy's Column
so that I can query players with it like this:
query = session.query(Player).filter(Player.uniqueid=='STEAM_0:0:1234567')
player = query.one_or_none()
if player is None:
player = Player(uniqueid='STEAM_0:0:1234567')
Here's what my class currently looks like:
class Player(game.Player, db.Model):
_uniqueid = Column('uniqueid', String(21), unique=True, nullable=False)
def __init__(self, index, **kwargs):
game.Player.__init__(index) # initializes uniqueid
db.Model.__init__(_uniqueid=self.uniqueid, **kwargs)
Next I would like to create a read-only interface for the _uniqueid
, so the API users can no longer write to the variable (well, they can through _uniqueid
, but that's on their responsibility since accessing it should happen through the non-private uniqueid
).
I think of overriding the original uniqueid
with a new one:
@property
def uniqueid(self):
return self._uniqueid
This is read-only and "hides" the original _uniqueid
, preventing anyone from writing to it unless they intentionally access the private one (I won't even list it in documentation, I'll only expose the non-private one).
The only problem is that this completely overrides the old one, meaning that my __init__
's _uniqueid=self.uniqueid
won't work due to self.uniqueid
using the new getter, not the old one.
So summed up, what I want is to convert a read-only property into a read-only something that can be used to query with SQLAlchemy. Is this possible, and if, how?
Upvotes: 8
Views: 604
Reputation: 20518
You can use super
to access the property of game.Player
. We can test using a simple C extension type created with Cython:
# game.pyx
cdef class Player:
cdef int index;
def __init__(self, index):
self.index = index
@property
def uniqueid(self):
return "foo"
# test.py
class Player(game.Player, Base):
__tablename__ = "player"
id = Column(Integer, primary_key=True)
_uniqueid = Column('uniqueid', String(21), unique=True, nullable=False)
def __init__(self, index, **kwargs):
game.Player.__init__(self, index) # initializes uniqueid
Base.__init__(self, _uniqueid=super().uniqueid, **kwargs)
@property
def uniqueid(self):
return self._uniqueid
print(Player(1).uniqueid) # "foo"
Because of the precariousness of inheriting from C extension types, this may or may not work depending on exactly what C magic the game.Player
type uses.
Also, because the ORM bypasses __init__
when it loads instances from the database, you'll have to hook into the load
event in order to initialize the game.Player
type.
Upvotes: 1