Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
flask-admin
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
JIRA
JIRA
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Python-Dev
flask-admin
Commits
dd66d0ee
Commit
dd66d0ee
authored
Jul 27, 2015
by
Serge S. Koval
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #962 from pawl/fix-hybrid-properties
Fix SQLAlchemy hybrid_property support, add example and test
parents
7585da1d
38c7894b
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
151 additions
and
4 deletions
+151
-4
README.rst
examples/sqla-hybrid_property/README.rst
+30
-0
__init__.py
examples/sqla-hybrid_property/__init__.py
+1
-0
app.py
examples/sqla-hybrid_property/app.py
+59
-0
requirements.txt
examples/sqla-hybrid_property/requirements.txt
+3
-0
view.py
flask_admin/contrib/sqla/view.py
+9
-4
test_basic.py
flask_admin/tests/sqla/test_basic.py
+49
-0
No files found.
examples/sqla-hybrid_property/README.rst
0 → 100644
View file @
dd66d0ee
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()
examples/sqla-hybrid_property/__init__.py
0 → 100644
View file @
dd66d0ee
examples/sqla-hybrid_property/app.py
0 → 100644
View file @
dd66d0ee
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
)
examples/sqla-hybrid_property/requirements.txt
0 → 100644
View file @
dd66d0ee
Flask
Flask-Admin
Flask-SQLAlchemy
flask_admin/contrib/sqla/view.py
View file @
dd66d0ee
...
...
@@ -4,7 +4,7 @@ import inspect
from
sqlalchemy.orm.attributes
import
InstrumentedAttribute
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.exc
import
IntegrityError
...
...
@@ -550,8 +550,11 @@ class ModelView(BaseModelView):
if
attr
is
None
:
raise
Exception
(
'Failed to find field for filter:
%
s'
%
name
)
# Figure out filters for related column
if
hasattr
(
attr
,
'property'
)
and
hasattr
(
attr
.
property
,
'direction'
):
# Figure out filters for related column, unless it's a hybrid_property
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
=
[]
for
p
in
self
.
_get_model_iterator
(
attr
.
property
.
mapper
.
class_
):
...
...
@@ -620,7 +623,9 @@ class ModelView(BaseModelView):
if
isinstance
(
filter
,
sqla_filters
.
BaseSQLAFilter
):
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
]
return
filter
...
...
flask_admin/tests/sqla/test_basic.py
View file @
dd66d0ee
...
...
@@ -8,6 +8,8 @@ from flask_admin._compat import iteritems
from
flask_admin.contrib.sqla
import
ModelView
,
filters
from
flask_babelex
import
Babel
from
sqlalchemy.ext.hybrid
import
hybrid_property
from
.
import
setup
from
datetime
import
datetime
,
time
,
date
...
...
@@ -1217,6 +1219,53 @@ def test_column_filters():
ok_
(
'test1_val_2'
not
in
data
)
def
test_hybrid_property
():
app
,
db
,
admin
=
setup
()
class
Model1
(
db
.
Model
):
id
=
db
.
Column
(
db
.
Integer
,
primary_key
=
True
)
name
=
db
.
Column
(
db
.
String
)
width
=
db
.
Column
(
db
.
Integer
)
height
=
db
.
Column
(
db
.
Integer
)
@
hybrid_property
def
number_of_pixels
(
self
):
return
self
.
width
*
self
.
height
db
.
create_all
()
db
.
session
.
add
(
Model1
(
id
=
1
,
name
=
"test_row_1"
,
width
=
25
,
height
=
25
))
db
.
session
.
add
(
Model1
(
id
=
2
,
name
=
"test_row_2"
,
width
=
10
,
height
=
10
))
db
.
session
.
commit
()
client
=
app
.
test_client
()
view
=
CustomModelView
(
Model1
,
db
.
session
,
column_default_sort
=
'number_of_pixels'
,
column_filters
=
[
filters
.
IntGreaterFilter
(
Model1
.
number_of_pixels
,
'Number of Pixels'
)]
)
admin
.
add_view
(
view
)
# filters - hybrid_property integer - greater
rv
=
client
.
get
(
'/admin/model1/?flt0_0=600'
)
eq_
(
rv
.
status_code
,
200
)
data
=
rv
.
data
.
decode
(
'utf-8'
)
ok_
(
'test_row_1'
in
data
)
ok_
(
'test_row_2'
not
in
data
)
# sorting
rv
=
client
.
get
(
'/admin/model1/?sort=0'
)
eq_
(
rv
.
status_code
,
200
)
_
,
data
=
view
.
get_list
(
0
,
None
,
None
,
None
,
None
)
eq_
(
len
(
data
),
2
)
eq_
(
data
[
0
]
.
name
,
'test_row_2'
)
eq_
(
data
[
1
]
.
name
,
'test_row_1'
)
def
test_url_args
():
app
,
db
,
admin
=
setup
()
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment