Commit 08305dd0 authored by Paul Brown's avatar Paul Brown

fix hybrid_property filter support, add example

parent 7585da1d
Example of how to use (and filter on) a hybrid_property with the SQLAlchemy backend.
Hybrid properties allow you to treat calculations (for example: first_name + last_name)
like any other database column.
To run this example:
1. Clone the repository::
git clone https://github.com/flask-admin/flask-admin.git
cd flask-admin
2. Create and activate a virtual environment::
virtualenv env
source env/bin/activate
3. Install requirements::
pip install -r 'examples/sqla-hybrid_property/requirements.txt'
4. Run the application::
python examples/sqla-hybrid_property/app.py
The first time you run this example, a sample sqlite database gets populated automatically. To suppress this behaviour,
comment the following lines in app.py:::
if not os.path.exists(database_path):
build_sample_db()
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.ext.hybrid import hybrid_property
import flask_admin as admin
from flask_admin.contrib import sqla
from flask_admin.contrib.sqla.filters import IntGreaterFilter
# Create application
app = Flask(__name__)
# Create dummy secrey key so we can use sessions
app.config['SECRET_KEY'] = '123456790'
# Create in-memory database
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///sample_db_2.sqlite'
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)
# Flask views
@app.route('/')
def index():
return '<a href="/admin/">Click me to get to Admin!</a>'
class Screen(db.Model):
__tablename__ = 'screen'
id = db.Column(db.Integer, primary_key=True)
width = db.Column(db.Integer, nullable=False)
height = db.Column(db.Integer, nullable=False)
@hybrid_property
def number_of_pixels(self):
return self.width * self.height
class ScreenAdmin(sqla.ModelView):
''' Flask-admin can not automatically find a hybrid_property yet. You will
need to manually define the column in list_view/filters/sorting/etc.'''
list_columns = ['id', 'width', 'height', 'number_of_pixels']
column_sortable_list = ['id', 'width', 'height', 'number_of_pixels']
# make sure the type of your filter matches your hybrid_property
column_filters = [IntGreaterFilter(Screen.number_of_pixels,
'Number of Pixels')]
# Create admin
admin = admin.Admin(app, name='Example: SQLAlchemy2', template_mode='bootstrap3')
admin.add_view(ScreenAdmin(Screen, db.session))
if __name__ == '__main__':
# Create DB
db.create_all()
# Start app
app.run(debug=True)
Flask
Flask-Admin
Flask-SQLAlchemy
...@@ -4,7 +4,7 @@ import inspect ...@@ -4,7 +4,7 @@ import inspect
from sqlalchemy.orm.attributes import InstrumentedAttribute from sqlalchemy.orm.attributes import InstrumentedAttribute
from sqlalchemy.orm import joinedload, aliased from sqlalchemy.orm import joinedload, aliased
from sqlalchemy.sql.expression import desc from sqlalchemy.sql.expression import desc, ColumnElement
from sqlalchemy import Boolean, Table, func, or_ from sqlalchemy import Boolean, Table, func, or_
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
...@@ -550,8 +550,11 @@ class ModelView(BaseModelView): ...@@ -550,8 +550,11 @@ class ModelView(BaseModelView):
if attr is None: if attr is None:
raise Exception('Failed to find field for filter: %s' % name) raise Exception('Failed to find field for filter: %s' % name)
# Figure out filters for related column # Figure out filters for related column, unless it's a hybrid_property
if hasattr(attr, 'property') and hasattr(attr.property, 'direction'): if isinstance(attr, ColumnElement):
warnings.warn(('Unable to scaffold the filter for %s, scaffolding '
'for hybrid_property is not supported yet.') % name)
elif hasattr(attr, 'property') and hasattr(attr.property, 'direction'):
filters = [] filters = []
for p in self._get_model_iterator(attr.property.mapper.class_): for p in self._get_model_iterator(attr.property.mapper.class_):
...@@ -620,7 +623,9 @@ class ModelView(BaseModelView): ...@@ -620,7 +623,9 @@ class ModelView(BaseModelView):
if isinstance(filter, sqla_filters.BaseSQLAFilter): if isinstance(filter, sqla_filters.BaseSQLAFilter):
column = filter.column column = filter.column
if self._need_join(column.table): # hybrid_property joins are not supported yet
if (isinstance(column, InstrumentedAttribute) and
self._need_join(column.table)):
self._filter_joins[column] = [column.table] self._filter_joins[column] = [column.table]
return filter return filter
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment