guinunez
guinunez

Reputation: 150

Python: How to read an attribute from another atribute's object?

I have no idea how to even look for this, here is a simplified example

I have a class:

class MyField():

    def __init__(self, model=None, source=None, id=None):
        self.source = source
        self.model = model
        self.id = id

    def somemethod(self):
        ...

That is used on this other class

class MyClass(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    somefield = db.Column(db.String(255))

    @property
    def someproperty(self):

        specs = MyField(
            source=self.somefield,
            model=self.__class__.__name__,
            id=self.id
        )

        return specs.somemethod()

By that is no ideal to me, I want to have something simpler to code for the second class, so making it look like this:

class MyClass(db.Model):
    somefield = db.Column(db.String(255))

    someproperty = MyField(source='somefield')

and have on the first class all the logic that handles that data. Here I have no idea on how to read the contents of 'somefield' and id, 'somefield' may vary and have another name, thats why I use it as an argument, and 'id' is always the same for every class

Upvotes: 0

Views: 91

Answers (1)

abarnert
abarnert

Reputation: 365717

All that @property does is return a custom descriptor whose __get__ method calls your function. So, you can just do that directly.

I can't test this, because I don't have sqlalchemy installed here, but something like this:

class FieldProperty(object):
    def __init__(self, field, source):
        self.field, self.source = field, source
    def __get__(self, obj, typ=None):
        if typ is None:
            typ = obj.__class__
        specs = self.field(
            source = getattr(obj, self.source),
            model = typ.__name__,
            id = obj.id)
        return specs.somemethod()

Now:

class MyClass(db.Model):
    somefield = db.Column(db.String(255))

    someproperty = FieldProperty(MyField, source='somefield')

Or, if you prefer, you can create a base class or mixin for all your fields that adds a class method that does this for you:

class BaseField(object):
    @classmethod
    def make_property(cls, source):
        return FieldProperty(cls, source)

And now:

class MyClass(db.Model):
    somefield = db.Column(db.String(255))

    someproperty = MyField.make_property('somefield')

Note the FieldProperty(object) above. Descriptors have to be new-style classes, and they only work in new-style classes. In Python 3.x, new-style classes is all there is, but if you're using 2.x, every time you define a class with no base class, you get a old-style class. This is just one of the many reasons you don't want an old-style class; they also handle some special methods wrong, break multiple inheritance, make you look like a retro hipster, and sneak into your room and steal money out of your wallet while you sleep.

Upvotes: 1

Related Questions