Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 955
Markus Meskanen Avatar asked Jan 29 '17 16:01

Markus Meskanen


1 Answers

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.

like image 180
univerio Avatar answered Oct 14 '22 23:10

univerio