Mahi
Mahi

Reputation: 21923

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?

Upvotes: 8

Views: 604

Answers (1)

univerio
univerio

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

Related Questions