Reputation: 485
I am implementing SQLAlchemy ORM in a small blog app I am creating (as a learning exercise to Alchemy). I have stumbled upon something I am not sure of - I think I know an approach, but it may be too long winded to be the "best" one. One table / object has a "title" column. I want to be able to create a slug type string from this. I looked at hybrid properties and it seemed to do the trick.
class Post(Base):
__tablename__ = 'posts'
id = Column(Integer, primary_key=True)
title = Column(String)
@hybrid_property
def slug(self):
return self.title.replace(" ", "-").lower()
def __repr__(self):
return "<Post(id='%s', title='%s', slug='%s')>" % (
self.id, self.title, self.slug)
post = Post(title="Hello World")
session.add(post)
session.commit()
This works fine for a retrieval of the value:
>>> p = session.query(Post).filter(Post.title=='Hello World')
>>> p
>>> <Post(id='1', title='Hello World', slug='hello-world')>
However when I try to use a filter on this property:
>>> p = session.query(Post).filter(Post.slug=='hello-world')
I get this error:
>>> File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/attributes.py", line 270, in __ge
tattr__
key)
AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with
Post.title has an attribute 'replace'
Does this mean I should create a custom Comparator? Seems a lot of work, for what would be a one line in most sql. Basically, is my whole approach flawed?
Upvotes: 6
Views: 3463
Reputation: 241
from sqlalchemy import func
...
class Post(Base):
...
@hybrid_property
def slug(self):
return self.title.replace(" ", "-").lower()
@slug.expression
def slug(cls):
return func.lower(func.replace(cls.title, " ", "-"))
...
SQLAlchemy doesn't understand the Python code in the hybrid_property-decorated function, so it cannot convert it to a native SQL query. That's why you need to provide it in such a way that SQLAlchemy can understand it, like defined in the expression property, which SQLAlchemy can turn into an SQL query.
Upvotes: 6