There are a number of ways to accomplish this. Firstly, a hybrid attribute, a property that defines full_name on an instance and on the class (for querying).
This example is pure SQLAlchemy, but the hybrid attributes should be the same in Flask-SQLAlchemy.
import sqlalchemy as sa
from sqlalchemy.ext import hybrid
class User(Base):
__tablename__ = 'users'
id = sa.Column(sa.Integer, primary_key=True)
first_name = sa.Column(sa.String)
last_name = sa.Column(sa.String)
@hybrid.hybrid_property
def full_name(self):
return self.first_name + ' ' + self.last_name
users = session.query(User).filter_by(full_name='Joan Doe').all()
Credit to Ilja for pointing out that this can be done using a single method in this case. This works because SQLAlchemy maps the + operator to the database's CONCAT function. If this weren't the case, an additional property decorated with hybrid.expression would be necessary to implement the behaviour at the class level.
SQLAlchemy inserts the necessary expressions into SQL statements as required, for example:
SELECT users.id, users.first_name, users.last_name
FROM users
WHERE users.first_name || ? || users.last_name = ?
Another way to achieve this is by defining a column_property:
from sqlalchemy import orm
class User(Base):
...
full_name = orm.column_property(first_name + " " + last_name)
This inserts an expression into all queries on the model, so
query = sa.select(User)
will generate this SQL:
SELECT users.first_name || ? || users.last_name AS anon_1,
users.id,
users.first_name,
users.last_name
FROM users
Finally, there are computed columns. These are conceptually similar to column properties, but implemented on the database side (provided the database supports the required syntax). The database may or may not physically store the computed value; it may be possible to condition this behaviour by passing the persisted boolean keyword argument to Computed.
class User(Base):
...
full_name = sa.Column(sa.String, sa.Computed(first_name + ' ' + last_name))