sqlalchemy @hybrid_property v @property, @hybrid_method v @classmethod

Viewed 4679

I have a Model (which I'm using as an abstract base class), that has some common methods and properties.

SQLAlchemy allows creating properties and methods with @hybrid_property and @hybrid_method, but also the standard @property, @classmethod, @staticmethod decorators give me the results I'm after.

Are there any advantages and disadvantages using the SQLA decorators over the standard python decorators?

When should I use, or shouldn't use, the SQLA decorators?

1 Answers

The hybrid provides for an expression that works at both the Python level as well as at the SQL expression level

Let's look at an example:

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)
    firstname = Column(String(50))
    lastname = Column(String(50))

    @hybrid_property
    def fullname(self):
        return self.firstname + ' ' + self.lastname

    @property
    def long_name(self):
        return self.firstname + ' ' + self.lastname


session.add(User(firstname='Brendan', lastname='Simon'))
session.commit()
# error
# print(session.query(User).filter(User.long_name == 'Brendan Simon').first().id)
# works fine because @hybrid_property
print(session.query(User).filter(User.fullname == 'Brendan Simon').first().id)

Also you can customize SQL expression using @fullname.expression.

When should I use, or shouldn't use, the SQLA decorators?

I think you will know when you need it. For example, you can use it for fast aliases:

class MetaData(Base):
    __tablename__ = 'meta_data'
    system_field = Column(String(20))  # a lot of calculations and processing in different places
    # a lot of fields ...

One day in a few parts system_field was(or will be) renamed to new_field(doesn't matter why, who and when - just fact). You can do something like this as a quick solution:

@hybrid_property
def new_field(self):
    return self.system_field

@new_field.setter
def new_field(self, value: str):
    self.system_field = value

# data from somewhere...
# data = {'new_field': 'default', other fields...}
# will works fine + in other places will work as `system_field` with old sql queries
process_meta(MetaData(**{data}))

So this is really good feature, but if you are thinking about whether you need it or not, then you definitely don't need it.

Related