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
e0f6dace
Commit
e0f6dace
authored
Oct 08, 2018
by
PJ Janse van Rensburg
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'sqla-sort-multiple' into nested-categories
parents
704d8d9b
70f3d336
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
92 additions
and
46 deletions
+92
-46
changelog.rst
doc/changelog.rst
+1
-1
app.py
examples/sqla/app.py
+38
-29
view.py
flask_admin/contrib/sqla/view.py
+18
-12
base.py
flask_admin/model/base.py
+6
-0
test_basic.py
flask_admin/tests/sqla/test_basic.py
+29
-4
No files found.
doc/changelog.rst
View file @
e0f6dace
...
...
@@ -4,7 +4,7 @@ Changelog
next release
-----
* S
ort on multiple columns with `column_default_sor
t`
* S
QLA: Sort on multiple columns with `column_default_sort` and related models in `column_sortable_lis
t`
* Upgrade Leaflet and Leaflet.draw plugins, used for geoalchemy integration
* Specify `minimum_input_length` for ajax widget
* SQLAlchemy fix that lets you use inline model forms where models have multiple primary keys
...
...
examples/sqla/app.py
View file @
e0f6dace
...
...
@@ -28,11 +28,10 @@ class User(db.Model):
id
=
db
.
Column
(
db
.
Integer
,
primary_key
=
True
)
first_name
=
db
.
Column
(
db
.
String
(
100
))
last_name
=
db
.
Column
(
db
.
String
(
100
))
username
=
db
.
Column
(
db
.
String
(
80
),
unique
=
True
)
email
=
db
.
Column
(
db
.
String
(
120
),
unique
=
True
)
def
__str__
(
self
):
return
self
.
username
return
"{}, {}"
.
format
(
self
.
last_name
,
self
.
first_name
)
# Create M2M table
...
...
@@ -46,7 +45,7 @@ class Post(db.Model):
id
=
db
.
Column
(
db
.
Integer
,
primary_key
=
True
)
title
=
db
.
Column
(
db
.
String
(
120
))
text
=
db
.
Column
(
db
.
Text
,
nullable
=
False
)
date
=
db
.
Column
(
db
.
Date
Time
)
date
=
db
.
Column
(
db
.
Date
)
user_id
=
db
.
Column
(
db
.
Integer
(),
db
.
ForeignKey
(
User
.
id
))
user
=
db
.
relationship
(
User
,
backref
=
'posts'
)
...
...
@@ -54,7 +53,7 @@ class Post(db.Model):
tags
=
db
.
relationship
(
'Tag'
,
secondary
=
post_tags_table
)
def
__str__
(
self
):
return
self
.
title
return
"{}"
.
format
(
self
.
title
)
class
Tag
(
db
.
Model
):
...
...
@@ -62,7 +61,7 @@ class Tag(db.Model):
name
=
db
.
Column
(
db
.
Unicode
(
64
))
def
__str__
(
self
):
return
self
.
name
return
"{}"
.
format
(
self
.
name
)
class
UserInfo
(
db
.
Model
):
...
...
@@ -75,7 +74,7 @@ class UserInfo(db.Model):
user
=
db
.
relationship
(
User
,
backref
=
'info'
)
def
__str__
(
self
):
return
'
%
s -
%
s'
%
(
self
.
key
,
self
.
value
)
return
"{} - {}"
.
format
(
self
.
key
,
self
.
value
)
class
Tree
(
db
.
Model
):
...
...
@@ -85,7 +84,7 @@ class Tree(db.Model):
parent
=
db
.
relationship
(
'Tree'
,
remote_side
=
[
id
],
backref
=
'children'
)
def
__str__
(
self
):
return
self
.
name
return
"{}"
.
format
(
self
.
name
)
# Flask views
...
...
@@ -96,28 +95,39 @@ def index():
# Customized User model admin
class
UserAdmin
(
sqla
.
ModelView
):
column_list
=
[
'id'
,
'last_name'
,
'first_name'
,
'email'
,
]
column_default_sort
=
[(
'last_name'
,
False
),
(
'first_name'
,
False
)]
# sort on multiple columns
inline_models
=
(
UserInfo
,)
# Customized Post model admin
class
PostAdmin
(
sqla
.
ModelView
):
# Visible columns in the list view
column_exclude_list
=
[
'text'
]
# List of columns that can be sorted. For 'user' column, use User.username as
# a column.
column_sortable_list
=
(
'title'
,
(
'user'
,
'user.username'
),
'date'
)
# Rename 'title' columns to 'Post Title' in list view
column_labels
=
dict
(
title
=
'Post Title'
)
column_searchable_list
=
(
'title'
,
User
.
username
,
'tags.name'
)
column_filters
=
(
'user'
,
'title'
,
'date'
,
'tags'
,
filters
.
FilterLike
(
Post
.
title
,
'Fixed Title'
,
options
=
((
'test1'
,
'Test 1'
),
(
'test2'
,
'Test 2'
))))
column_default_sort
=
(
'date'
,
True
)
column_sortable_list
=
[
'title'
,
'date'
,
(
'user'
,
(
'user.last_name'
,
'user.first_name'
)),
# sort on multiple columns
]
column_labels
=
dict
(
title
=
'Post Title'
)
# Rename 'title' column in list view
column_searchable_list
=
[
'title'
,
User
.
first_name
,
User
.
last_name
,
'tags.name'
,
]
column_filters
=
[
'user'
,
'title'
,
'date'
,
'tags'
,
filters
.
FilterLike
(
Post
.
title
,
'Fixed Title'
,
options
=
((
'test1'
,
'Test 1'
),
(
'test2'
,
'Test 2'
))),
]
# Pass arguments to WTForms. In this case, change label for text field to
# be 'Big Text' and add required() validator.
...
...
@@ -127,11 +137,11 @@ class PostAdmin(sqla.ModelView):
form_ajax_refs
=
{
'user'
:
{
'fields'
:
(
User
.
username
,
User
.
email
)
'fields'
:
(
User
.
first_name
,
User
.
last_name
)
},
'tags'
:
{
'fields'
:
(
Tag
.
name
,),
'minimum_input_length'
:
0
,
'minimum_input_length'
:
0
,
# show suggestions, even before any user input
'placeholder'
:
'Please select'
,
'page_size'
:
5
,
},
...
...
@@ -174,8 +184,8 @@ def build_sample_db():
'Riley'
,
'William'
,
'James'
,
'Geoffrey'
,
'Lisa'
,
'Benjamin'
,
'Stacey'
,
'Lucy'
]
last_names
=
[
'Brown'
,
'
Smith
'
,
'Patel'
,
'Jones'
,
'Williams'
,
'Johnson'
,
'Taylor'
,
'Thomas'
,
'Roberts'
,
'Khan'
,
'
Lewis'
,
'Jackson
'
,
'Clarke'
,
'James'
,
'Phillips'
,
'Wilson'
,
'Brown'
,
'
Brown
'
,
'Patel'
,
'Jones'
,
'Williams'
,
'Johnson'
,
'Taylor'
,
'Thomas'
,
'Roberts'
,
'Khan'
,
'
Clarke'
,
'Clarke
'
,
'Clarke'
,
'James'
,
'Phillips'
,
'Wilson'
,
'Ali'
,
'Mason'
,
'Mitchell'
,
'Rose'
,
'Davis'
,
'Davies'
,
'Rodriguez'
,
'Cox'
,
'Alexander'
]
...
...
@@ -183,9 +193,8 @@ def build_sample_db():
for
i
in
range
(
len
(
first_names
)):
user
=
User
()
user
.
first_name
=
first_names
[
i
]
user
.
username
=
first_names
[
i
]
.
lower
()
user
.
last_name
=
last_names
[
i
]
user
.
email
=
user
.
username
+
"@example.com"
user
.
email
=
first_names
[
i
]
.
lower
()
+
"@example.com"
user_list
.
append
(
user
)
db
.
session
.
add
(
user
)
...
...
flask_admin/contrib/sqla/view.py
View file @
e0f6dace
...
...
@@ -484,13 +484,21 @@ class ModelView(BaseModelView):
for
c
in
self
.
column_sortable_list
:
if
isinstance
(
c
,
tuple
):
column
,
path
=
tools
.
get_field_with_path
(
self
.
model
,
c
[
1
])
column_name
=
c
[
0
]
if
isinstance
(
c
[
1
],
tuple
):
column
,
path
=
[],
[]
for
item
in
c
[
1
]:
column_item
,
path_item
=
tools
.
get_field_with_path
(
self
.
model
,
item
)
column
.
append
(
column_item
)
path
.
append
(
path_item
)
column_name
=
c
[
0
]
else
:
column
,
path
=
tools
.
get_field_with_path
(
self
.
model
,
c
[
1
])
column_name
=
c
[
0
]
else
:
column
,
path
=
tools
.
get_field_with_path
(
self
.
model
,
c
)
column_name
=
text_type
(
c
)
if
path
and
hasattr
(
path
[
0
],
'property'
):
if
path
and
(
hasattr
(
path
[
0
],
'property'
)
or
isinstance
(
path
[
0
],
list
)
):
self
.
_sortable_joins
[
column_name
]
=
path
elif
path
:
raise
Exception
(
"For sorting columns in a related table, "
...
...
@@ -836,15 +844,9 @@ class ModelView(BaseModelView):
column
=
sort_field
if
alias
is
None
else
getattr
(
alias
,
sort_field
.
key
)
if
sort_desc
:
if
isinstance
(
column
,
tuple
):
query
=
query
.
order_by
(
*
map
(
desc
,
column
))
else
:
query
=
query
.
order_by
(
desc
(
column
))
query
=
query
.
order_by
(
desc
(
column
))
else
:
if
isinstance
(
column
,
tuple
):
query
=
query
.
order_by
(
*
column
)
else
:
query
=
query
.
order_by
(
column
)
query
=
query
.
order_by
(
column
)
return
query
,
joins
...
...
@@ -860,7 +862,11 @@ class ModelView(BaseModelView):
sort_field
=
self
.
_sortable_columns
[
sort_column
]
sort_joins
=
self
.
_sortable_joins
.
get
(
sort_column
)
query
,
joins
=
self
.
_order_by
(
query
,
joins
,
sort_joins
,
sort_field
,
sort_desc
)
if
isinstance
(
sort_field
,
list
):
for
field_item
,
join_item
in
zip
(
sort_field
,
sort_joins
):
query
,
joins
=
self
.
_order_by
(
query
,
joins
,
join_item
,
field_item
,
sort_desc
)
else
:
query
,
joins
=
self
.
_order_by
(
query
,
joins
,
sort_joins
,
sort_field
,
sort_desc
)
else
:
order
=
self
.
_get_default_order
()
for
sort_field
,
sort_joins
,
sort_desc
in
order
:
...
...
flask_admin/model/base.py
View file @
e0f6dace
...
...
@@ -382,6 +382,12 @@ class BaseModelView(BaseView, ActionsMixin):
class MyModelView(BaseModelView):
column_sortable_list = ('name', ('user', 'user.username'))
You can also specify multiple fields to be used while sorting::
class MyModelView(BaseModelView):
column_sortable_list = (
'name', ('user', ('user.first_name', 'user.last_name')))
When using SQLAlchemy models, model attributes can be used instead
of strings::
...
...
flask_admin/tests/sqla/test_basic.py
View file @
e0f6dace
...
...
@@ -1732,13 +1732,17 @@ def test_complex_sort():
app
,
db
,
admin
=
setup
()
M1
,
M2
=
create_models
(
db
)
m1
=
M1
(
'b
'
)
m1
=
M1
(
test1
=
'c'
,
test2
=
'x
'
)
db
.
session
.
add
(
m1
)
db
.
session
.
add
(
M2
(
'c'
,
model1
=
m1
))
m2
=
M1
(
'a
'
)
m2
=
M1
(
test1
=
'b'
,
test2
=
'x
'
)
db
.
session
.
add
(
m2
)
db
.
session
.
add
(
M2
(
'c'
,
model1
=
m2
))
db
.
session
.
add
(
M2
(
'b'
,
model1
=
m2
))
m3
=
M1
(
test1
=
'a'
,
test2
=
'y'
)
db
.
session
.
add
(
m3
)
db
.
session
.
add
(
M2
(
'a'
,
model1
=
m3
))
db
.
session
.
commit
()
...
...
@@ -1750,9 +1754,30 @@ def test_complex_sort():
client
=
app
.
test_client
()
rv
=
client
.
get
(
'/admin/model2/?sort=1'
)
rv
=
client
.
get
(
'/admin/model2/?sort=0'
)
eq_
(
rv
.
status_code
,
200
)
_
,
data
=
view
.
get_list
(
0
,
'model1.test1'
,
False
,
None
,
None
)
eq_
(
data
[
0
]
.
model1
.
test1
,
'a'
)
eq_
(
data
[
1
]
.
model1
.
test1
,
'b'
)
eq_
(
data
[
2
]
.
model1
.
test1
,
'c'
)
# test sorting on multiple columns in related model
view2
=
CustomModelView
(
M2
,
db
.
session
,
column_list
=
[
'string_field'
,
'model1'
],
column_sortable_list
=
[(
'model1'
,
(
'model1.test2'
,
'model1.test1'
))],
endpoint
=
"m1_2"
)
admin
.
add_view
(
view2
)
rv
=
client
.
get
(
'/admin/m1_2/?sort=0'
)
eq_
(
rv
.
status_code
,
200
)
_
,
data
=
view2
.
get_list
(
0
,
'model1'
,
False
,
None
,
None
)
eq_
(
data
[
0
]
.
model1
.
test1
,
'b'
)
eq_
(
data
[
1
]
.
model1
.
test1
,
'c'
)
eq_
(
data
[
2
]
.
model1
.
test1
,
'a'
)
@
raises
(
Exception
)
def
test_complex_sort_exception
():
...
...
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