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
b6541d53
Commit
b6541d53
authored
Aug 18, 2012
by
mrjoes
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added ability to perform actions on more than one model.
parent
aaa36abf
Changes
7
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
196 additions
and
8 deletions
+196
-8
.gitignore
.gitignore
+1
-0
view.py
flask_admin/contrib/sqlamodel/view.py
+29
-2
__init__.py
flask_admin/model/__init__.py
+1
-0
action.py
flask_admin/model/action.py
+19
-0
base.py
flask_admin/model/base.py
+70
-2
filters.js
flask_admin/static/js/filters.js
+1
-1
list.html
flask_admin/templates/admin/model/list.html
+75
-3
No files found.
.gitignore
View file @
b6541d53
...
@@ -7,5 +7,6 @@ pyenv
...
@@ -7,5 +7,6 @@ pyenv
build
build
source/_static*
source/_static*
source/_templates*
source/_templates*
dist/*
make.bat
make.bat
venv
venv
flask_admin/contrib/sqlamodel/view.py
View file @
b6541d53
...
@@ -6,9 +6,9 @@ from wtforms.ext.sqlalchemy.orm import model_form
...
@@ -6,9 +6,9 @@ from wtforms.ext.sqlalchemy.orm import model_form
from
flask
import
flash
from
flask
import
flash
from
flask.ext.admin.babel
import
gettext
from
flask.ext.admin.babel
import
gettext
,
ngettext
from
flask.ext.admin.form
import
BaseForm
from
flask.ext.admin.form
import
BaseForm
from
flask.ext.admin.model
import
BaseModelView
from
flask.ext.admin.model
import
BaseModelView
,
action
from
flask.ext.admin.contrib.sqlamodel
import
form
,
filters
,
tools
from
flask.ext.admin.contrib.sqlamodel
import
form
,
filters
,
tools
...
@@ -549,3 +549,30 @@ class ModelView(BaseModelView):
...
@@ -549,3 +549,30 @@ class ModelView(BaseModelView):
except
Exception
,
ex
:
except
Exception
,
ex
:
flash
(
gettext
(
'Failed to delete model.
%(error)
s'
,
error
=
str
(
ex
)),
'error'
)
flash
(
gettext
(
'Failed to delete model.
%(error)
s'
,
error
=
str
(
ex
)),
'error'
)
return
False
return
False
# Default model actions
def
is_action_allowed
(
self
,
name
):
# Check delete action permission
if
name
==
'delete'
and
not
self
.
can_delete
:
return
False
return
super
(
ModelView
,
self
)
.
is_action_allowed
(
name
)
@
action
(
'delete'
,
'Delete'
,
'Are you sure you want to delete selected models?'
)
def
action_delete
(
self
,
ids
):
try
:
model_pk
=
getattr
(
self
.
model
,
self
.
_primary_key
)
query
=
self
.
session
.
query
(
self
.
model
)
.
filter
(
model_pk
.
in_
(
ids
))
# TODO: Load up ORM and delete models one by one?
count
=
query
.
delete
(
synchronize_session
=
False
)
self
.
session
.
commit
()
flash
(
ngettext
(
'Model was successfully deleted.'
,
'
%(count)
s models were sucessfully deleted.'
,
count
,
count
=
count
))
except
Exception
,
ex
:
flash
(
gettext
(
'Failed to delete models.
%(error)
s'
,
error
=
str
(
ex
)),
'error'
)
flask_admin/model/__init__.py
View file @
b6541d53
from
.base
import
BaseModelView
from
.base
import
BaseModelView
from
.action
import
action
flask_admin/model/action.py
0 → 100644
View file @
b6541d53
def
action
(
name
,
text
,
confirmation
=
None
):
"""
Use this decorator to expose mass-model actions
`name`
Action name
`text`
Action text.
Will be passed through gettext() before rendering.
`confirmation`
Confirmation text. If not provided, action will be executed
unconditionally.
Will be passed through gettext() before rendering.
"""
def
wrap
(
f
):
f
.
_action
=
(
name
,
text
,
confirmation
)
return
f
return
wrap
flask_admin/model/base.py
View file @
b6541d53
...
@@ -151,7 +151,6 @@ class BaseModelView(BaseView):
...
@@ -151,7 +151,6 @@ class BaseModelView(BaseView):
)
)
"""
"""
form_columns
=
None
form_columns
=
None
"""
"""
Collection of the model field names for the form. If set to `None` will
Collection of the model field names for the form. If set to `None` will
...
@@ -183,6 +182,16 @@ class BaseModelView(BaseView):
...
@@ -183,6 +182,16 @@ class BaseModelView(BaseView):
form_overrides = dict(name=wtf.FileField)
form_overrides = dict(name=wtf.FileField)
"""
"""
# Actions
disallowed_actions
=
[]
"""
Set of disallowed action names. For example, if you want to disable
mass model deletion, do something like this:
class MyModelView(BaseModelView):
disallowed_actions = ['delete']
"""
# Various settings
# Various settings
page_size
=
20
page_size
=
20
"""
"""
...
@@ -220,6 +229,9 @@ class BaseModelView(BaseView):
...
@@ -220,6 +229,9 @@ class BaseModelView(BaseView):
self
.
model
=
model
self
.
model
=
model
# Actions
self
.
_init_actions
()
# Scaffolding
# Scaffolding
self
.
_refresh_cache
()
self
.
_refresh_cache
()
...
@@ -263,6 +275,22 @@ class BaseModelView(BaseView):
...
@@ -263,6 +275,22 @@ class BaseModelView(BaseView):
self
.
_filter_groups
=
None
self
.
_filter_groups
=
None
self
.
_filter_types
=
None
self
.
_filter_types
=
None
# Actions
def
_init_actions
(
self
):
self
.
_actions
=
[]
self
.
_action_data
=
dict
()
for
p
in
dir
(
self
):
attr
=
getattr
(
self
,
p
)
if
hasattr
(
attr
,
'_action'
):
name
,
text
,
desc
=
attr
.
_action
self
.
_actions
.
append
((
name
,
text
))
# TODO: Use namedtuple
self
.
_action_data
[
name
]
=
(
attr
,
text
,
desc
)
# Primary key
# Primary key
def
get_pk_value
(
self
,
model
):
def
get_pk_value
(
self
,
model
):
"""
"""
...
@@ -624,6 +652,16 @@ class BaseModelView(BaseView):
...
@@ -624,6 +652,16 @@ class BaseModelView(BaseView):
return
url_for
(
view
,
**
kwargs
)
return
url_for
(
view
,
**
kwargs
)
def
is_action_allowed
(
self
,
name
):
"""
Override this method to allow or disallow actions based
on some condition.
Default implementation only checks if particular action
is not in `disallowed_actions`.
"""
return
name
not
in
self
.
disallowed_actions
# Views
# Views
@
expose
(
'/'
)
@
expose
(
'/'
)
def
index_view
(
self
):
def
index_view
(
self
):
...
@@ -677,6 +715,14 @@ class BaseModelView(BaseView):
...
@@ -677,6 +715,14 @@ class BaseModelView(BaseView):
return
self
.
_get_url
(
'.index_view'
,
page
,
column
,
desc
,
return
self
.
_get_url
(
'.index_view'
,
page
,
column
,
desc
,
search
,
filters
)
search
,
filters
)
# Actions
actions
=
filter
(
lambda
x
:
self
.
is_action_allowed
(
x
[
0
]),
self
.
_actions
)
actions_confirmation
=
dict
()
for
act
in
actions
:
name
,
_
=
act
actions_confirmation
[
name
]
=
gettext
(
self
.
_action_data
[
name
][
2
])
return
self
.
render
(
self
.
list_template
,
return
self
.
render
(
self
.
list_template
,
data
=
data
,
data
=
data
,
# List
# List
...
@@ -713,7 +759,11 @@ class BaseModelView(BaseView):
...
@@ -713,7 +759,11 @@ class BaseModelView(BaseView):
filter_groups
=
self
.
_filter_groups
,
filter_groups
=
self
.
_filter_groups
,
filter_types
=
self
.
_filter_types
,
filter_types
=
self
.
_filter_types
,
filter_data
=
filters_data
,
filter_data
=
filters_data
,
active_filters
=
filters
active_filters
=
filters
,
# Actions
actions
=
actions
,
actions_confirmation
=
actions_confirmation
)
)
@
expose
(
'/new/'
,
methods
=
(
'GET'
,
'POST'
))
@
expose
(
'/new/'
,
methods
=
(
'GET'
,
'POST'
))
...
@@ -790,3 +840,21 @@ class BaseModelView(BaseView):
...
@@ -790,3 +840,21 @@ class BaseModelView(BaseView):
self
.
delete_model
(
model
)
self
.
delete_model
(
model
)
return
redirect
(
return_url
)
return
redirect
(
return_url
)
@
expose
(
'/action/'
,
methods
=
(
'POST'
,))
def
action_view
(
self
):
"""
Mass-model action view.
"""
action
=
request
.
form
.
get
(
'action'
)
ids
=
request
.
form
.
getlist
(
'rowid'
)
handler
=
self
.
_action_data
.
get
(
action
)
if
handler
and
self
.
is_action_allowed
(
action
):
response
=
handler
[
0
](
ids
)
if
response
is
not
None
:
return
response
return
redirect
(
url_for
(
'.index_view'
))
flask_admin/static/js/filters.js
View file @
b6541d53
var
AdminFilters
=
function
(
element
,
filters_element
,
adminForm
,
operations
,
options
,
types
)
{
var
AdminFilters
=
function
(
element
,
filters_element
,
adminForm
,
operations
,
options
,
types
)
{
var
$root
=
$
(
element
)
var
$root
=
$
(
element
)
;
var
$container
=
$
(
'.filters'
,
$root
);
var
$container
=
$
(
'.filters'
,
$root
);
var
lastCount
=
0
;
var
lastCount
=
0
;
...
...
flask_admin/templates/admin/model/list.html
View file @
b6541d53
...
@@ -32,6 +32,21 @@
...
@@ -32,6 +32,21 @@
</li>
</li>
{% endif %}
{% endif %}
{% if actions %}
<li
class=
"dropdown"
>
<a
class=
"dropdown-toggle"
data-toggle=
"dropdown"
href=
"#"
>
{{ _gettext('With selected')}}
<b
class=
"caret"
></b>
</a>
<ul
id=
"action_list"
class=
"dropdown-menu"
>
{% for p in actions %}
<li>
<a
href=
"#"
onclick=
"return actionExecute('{{ p[0] }}');"
>
{{ _gettext(p[1]) }}
</a>
</li>
{% endfor %}
</ul>
</li>
{% endif %}
{% if search_supported %}
{% if search_supported %}
<li>
<li>
<form
method=
"GET"
action=
"{{ return_url }}"
class=
"search-form"
>
<form
method=
"GET"
action=
"{{ return_url }}"
class=
"search-form"
>
...
@@ -90,6 +105,11 @@
...
@@ -90,6 +105,11 @@
<table
class=
"table table-striped table-bordered model-list"
>
<table
class=
"table table-striped table-bordered model-list"
>
<thead>
<thead>
<tr>
<tr>
{% if actions %}
<th
class=
"span1"
>
<input
type=
"checkbox"
name=
"rowtoggle"
id=
"rowtoggle"
/>
</th>
{% endif %}
<th
class=
"span1"
>
</th>
<th
class=
"span1"
>
</th>
{% set column = 0 %}
{% set column = 0 %}
{% for c, name in list_columns %}
{% for c, name in list_columns %}
...
@@ -117,6 +137,11 @@
...
@@ -117,6 +137,11 @@
</thead>
</thead>
{% for row in data %}
{% for row in data %}
<tr>
<tr>
{% if actions %}
<td>
<input
type=
"checkbox"
name=
"rowid"
value=
"{{ get_pk_value(row) }}"
/>
</td>
{% endif %}
<td>
<td>
{%- if admin_view.can_edit -%}
{%- if admin_view.can_edit -%}
<a
class=
"icon"
href=
"{{ url_for('.edit_view', id=get_pk_value(row), url=return_url) }}"
>
<a
class=
"icon"
href=
"{{ url_for('.edit_view', id=get_pk_value(row), url=return_url) }}"
>
...
@@ -131,13 +156,21 @@
...
@@ -131,13 +156,21 @@
</form>
</form>
{%- endif -%}
{%- endif -%}
</td>
</td>
{% block list_columns scoped %}
{% for c, name in list_columns %}
{% for c, name in list_columns %}
<td>
{{ get_value(row, c) }}
</td>
<td>
{{ get_value(row, c) }}
</td>
{% endfor %}
{% endfor %}
{% endblock %}
</tr>
</tr>
{% endfor %}
{% endfor %}
</table>
</table>
{{ lib.pager(page, num_pages, pager_url) }}
{{ lib.pager(page, num_pages, pager_url) }}
{% if actions %}
<form
id=
"action_form"
action=
"{{ url_for('.action_view') }}"
method=
"POST"
style=
"display: none"
>
<input
type=
"hidden"
id=
"action"
name=
"action"
/>
</form>
{% endif %}
{% endblock %}
{% endblock %}
{% block tail %}
{% block tail %}
...
@@ -151,6 +184,45 @@
...
@@ -151,6 +184,45 @@
{{
admin_view
.
_filter_dict
|
tojson
|
safe
}},
{{
admin_view
.
_filter_dict
|
tojson
|
safe
}},
{{
filter_data
|
tojson
|
safe
}},
{{
filter_data
|
tojson
|
safe
}},
{{
filter_types
|
tojson
|
safe
}});
{{
filter_types
|
tojson
|
safe
}});
// Actions helpers. TODO: Move to separate file
$
(
function
()
{
$
(
'#rowtoggle'
).
change
(
function
()
{
$
(
'td input[type=checkbox]'
).
attr
(
'checked'
,
this
.
checked
);
});
var
actionForm
=
$
(
'#action_form'
);
actionForm
.
submit
(
function
()
{
$
(
'td input[type=checkbox]:checked'
).
each
(
function
()
{
actionForm
.
append
(
$
(
this
).
clone
());
});
});
});
// TODO: Cleanup this mess
var
confirmation
=
{{
actions_confirmation
|
tojson
|
safe
}};
function
actionExecute
(
name
)
{
var
selected
=
$
(
'td input[type=checkbox]:checked'
).
size
();
if
(
selected
==
0
)
{
// TODO: Translation
alert
(
'{{ _gettext("Please select at least one model.") }}'
);
return
false
;
}
var
msg
=
confirmation
[
$
(
'#action_form select'
).
val
()];
if
(
!!
msg
)
if
(
!
confirm
(
msg
))
return
false
;
$
(
'#action_form #action'
).
val
(
name
);
$
(
'#action_form'
).
submit
();
return
false
;
}
</script>
</script>
{% endif %}
{% endif %}
{% endblock %}
{% endblock %}
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