Tuesday, February 7, 2017

Override a read-only property with a read-only Column that gets the same value

Leave a Comment

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?

1 Answers

Answers 1

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.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment