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
597ca07d
Commit
597ca07d
authored
Aug 27, 2015
by
Trevor Andreas
Committed by
Trevor Andreas
Sep 01, 2015
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement CSV export for BaseModelView.
parent
9db8c9e5
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
198 additions
and
10 deletions
+198
-10
introduction.rst
doc/introduction.rst
+6
-0
base.py
flask_admin/model/base.py
+175
-10
typefmt.py
flask_admin/model/typefmt.py
+5
-0
list.html
flask_admin/templates/bootstrap2/admin/model/list.html
+6
-0
list.html
flask_admin/templates/bootstrap3/admin/model/list.html
+6
-0
No files found.
doc/introduction.rst
View file @
597ca07d
...
...
@@ -298,6 +298,12 @@ To **manage related models inline**::
These inline forms can be customised. Have a look at the API documentation for
:meth:`~flask_admin.contrib.sqla.ModelView.inline_models`.
To **enable csv export** of the model view::
can_export = True
This will add a button to the model view that exports records, truncating at :attr:`~flask_admin.model.BaseModelView.max_export_rows`.
Adding Your Own Views
=====================
...
...
flask_admin/model/base.py
100644 → 100755
View file @
597ca07d
import
warnings
import
re
import
csv
import
time
from
werkzeug
import
secure_filename
from
flask
import
(
request
,
redirect
,
flash
,
abort
,
json
,
Response
,
get_flashed_messages
)
get_flashed_messages
,
stream_with_context
)
from
jinja2
import
contextfunction
from
wtforms.fields
import
HiddenField
from
wtforms.fields.core
import
UnboundField
...
...
@@ -23,7 +27,6 @@ from .helpers import prettify_name, get_mdict_item_or_list
from
.ajax
import
AjaxModelLoader
from
.fields
import
ListEditableFieldList
# Used to generate filter query string name
filter_char_re
=
re
.
compile
(
'[^a-z0-9 ]'
)
filter_compact_re
=
re
.
compile
(
' +'
)
...
...
@@ -95,6 +98,9 @@ class BaseModelView(BaseView, ActionsMixin):
when there are too many columns to display in the list_view.
"""
can_export
=
False
"""Is model list export allowed"""
# Templates
list_template
=
'admin/model/list.html'
"""Default list view template"""
...
...
@@ -194,14 +200,25 @@ class BaseModelView(BaseView, ActionsMixin):
pass
"""
column_formatters_export
=
None
"""
Dictionary of list view column formatters to be used for export.
Defaults to column_formatters when set to None.
Functions the same way as column_formatters except
that macros are not supported.
"""
column_type_formatters
=
ObsoleteAttr
(
'column_type_formatters'
,
'list_type_formatters'
,
None
)
"""
Dictionary of value type formatters to be used in the list view.
By default, t
wo
types are formatted:
By default, t
hree
types are formatted:
1. ``None`` will be displayed as an empty string
2. ``bool`` will be displayed as a checkmark if it is ``True``
3. ``list`` will be joined using ', '
If you don't like the default behavior and don't want any type formatters
applied, just override this property with an empty dictionary::
...
...
@@ -237,6 +254,18 @@ class BaseModelView(BaseView, ActionsMixin):
pass
"""
column_type_formatters_export
=
None
"""
Dictionary of value type formatters to be used in the export.
By default, two types are formatted:
1. ``None`` will be displayed as an empty string
2. ``list`` will be joined using ', '
Functions the same way as column_type_formatters.
"""
column_labels
=
ObsoleteAttr
(
'column_labels'
,
'rename_columns'
,
None
)
"""
Dictionary where key is column name and value is string to display.
...
...
@@ -579,6 +608,12 @@ class BaseModelView(BaseView, ActionsMixin):
action_disallowed_list = ['delete']
"""
# Export settings
export_max_rows
=
None
"""
Maximum number of rows allowed for export.
"""
# Various settings
page_size
=
20
"""
...
...
@@ -732,10 +767,17 @@ class BaseModelView(BaseView, ActionsMixin):
else
:
self
.
column_choices
=
self
.
_column_choices_map
=
dict
()
# Column formatters
if
self
.
column_formatters_export
is
None
:
self
.
column_formatters_export
=
self
.
column_formatters
# Type formatters
if
self
.
column_type_formatters
is
None
:
self
.
column_type_formatters
=
dict
(
typefmt
.
BASE_FORMATTERS
)
if
self
.
column_type_formatters_export
is
None
:
self
.
column_type_formatters_export
=
dict
(
typefmt
.
EXPORT_FORMATTERS
)
if
self
.
column_descriptions
is
None
:
self
.
column_descriptions
=
dict
()
...
...
@@ -1214,7 +1256,8 @@ class BaseModelView(BaseView, ActionsMixin):
return
None
# Database-related API
def
get_list
(
self
,
page
,
sort_field
,
sort_desc
,
search
,
filters
):
def
get_list
(
self
,
page
,
sort_field
,
sort_desc
,
search
,
filters
,
page_size
=
None
):
"""
Return a paginated and sorted list of models from the data source.
...
...
@@ -1231,6 +1274,10 @@ class BaseModelView(BaseView, ActionsMixin):
:param filters:
List of filter tuples. First value in a tuple is a search
index, second value is a search value.
:param page_size:
Number of results. Defaults to ModelView's page_size. Can be
overriden to change the page_size limit. Removing the page_size
limit requires setting page_size to 0 or False.
"""
raise
NotImplementedError
(
'Please implement get_list method'
)
...
...
@@ -1493,19 +1540,23 @@ class BaseModelView(BaseView, ActionsMixin):
"""
return
rec_getattr
(
model
,
name
)
@
contextfunction
def
get_list_value
(
self
,
context
,
model
,
name
):
def
_get_list_value
(
self
,
context
,
model
,
name
,
column_formatters
,
column_type_formatters
):
"""
Returns the value to be displayed
in the list view
Returns the value to be displayed
.
:param context:
:py:class:`jinja2.runtime.Context`
:py:class:`jinja2.runtime.Context`
if available
:param model:
Model instance
:param name:
Field name
:param column_formatters:
column_formatters to be used.
:param column_type_formatters:
column_type_formatters to be used.
"""
column_fmt
=
self
.
column_formatters
.
get
(
name
)
column_fmt
=
column_formatters
.
get
(
name
)
if
column_fmt
is
not
None
:
value
=
column_fmt
(
self
,
context
,
model
,
name
)
else
:
...
...
@@ -1516,7 +1567,7 @@ class BaseModelView(BaseView, ActionsMixin):
return
choices_map
.
get
(
value
)
or
value
type_fmt
=
None
for
typeobj
,
formatter
in
self
.
column_type_formatters
.
items
():
for
typeobj
,
formatter
in
column_type_formatters
.
items
():
if
isinstance
(
value
,
typeobj
):
type_fmt
=
formatter
break
...
...
@@ -1525,6 +1576,44 @@ class BaseModelView(BaseView, ActionsMixin):
return
value
@
contextfunction
def
get_list_value
(
self
,
context
,
model
,
name
):
"""
Returns the value to be displayed in the list view
:param context:
:py:class:`jinja2.runtime.Context`
:param model:
Model instance
:param name:
Field name
"""
return
self
.
_get_list_value
(
context
,
model
,
name
,
self
.
column_formatters
,
self
.
column_type_formatters
,
)
def
get_export_value
(
self
,
model
,
name
):
"""
Returns the value to be displayed in export.
Allows export to use different (non HTML) formatters.
:param model:
Model instance
:param name:
Field name
"""
return
self
.
_get_list_value
(
None
,
model
,
name
,
self
.
column_formatters_export
,
self
.
column_type_formatters_export
,
)
# AJAX references
def
_process_ajax_references
(
self
):
"""
...
...
@@ -1823,6 +1912,82 @@ class BaseModelView(BaseView, ActionsMixin):
"""
return
self
.
handle_action
()
@
expose
(
'/export/csv/'
)
def
export_csv
(
self
):
"""
Export a CSV of records.
"""
return_url
=
get_redirect_target
()
or
self
.
get_url
(
'.index_view'
)
if
not
self
.
can_export
:
flash
(
gettext
(
'Permission denied.'
))
return
redirect
(
return_url
)
# Macros in column_formatters are not supported.
# Macros will have a function name 'inner'
# This causes non-macro functions named 'inner' not work.
for
col
,
func
in
iteritems
(
self
.
column_formatters
):
if
func
.
__name__
==
'inner'
:
raise
NotImplementedError
(
'Macros not implemented. Override with '
'column_formatters_export. Column:
%
s'
%
(
col
,)
)
# Grab parameters from URL
view_args
=
self
.
_get_list_extra_args
()
# Map column index to column name
sort_column
=
self
.
_get_column_by_idx
(
view_args
.
sort
)
if
sort_column
is
not
None
:
sort_column
=
sort_column
[
0
]
# Get count and data
count
,
data
=
self
.
get_list
(
0
,
sort_column
,
view_args
.
sort_desc
,
view_args
.
search
,
view_args
.
filters
,
page_size
=
self
.
export_max_rows
)
# https://docs.djangoproject.com/en/1.8/howto/outputting-csv/
class
Echo
(
object
):
"""
An object that implements just the write method of the file-like
interface.
"""
def
write
(
self
,
value
):
"""
Write the value by returning it, instead of storing
in a buffer.
"""
return
value
writer
=
csv
.
writer
(
Echo
())
def
generate
():
# Needed as python 2 csvwriter does not support unicode
def
fix_unicode
(
t
):
return
as_unicode
(
t
)
.
encode
(
'utf-8'
)
# Append the column titles at the beginning
titles
=
[
fix_unicode
(
c
[
1
])
for
c
in
self
.
_list_columns
]
yield
writer
.
writerow
(
titles
)
for
row
in
data
:
vals
=
[
fix_unicode
(
self
.
get_export_value
(
row
,
c
[
0
]))
for
c
in
self
.
_list_columns
]
yield
writer
.
writerow
(
vals
)
filename
=
'{}_{}.csv'
.
format
(
self
.
name
,
time
.
strftime
(
"
%
Y-
%
m-
%
d_
%
H-
%
M-
%
S"
)
)
disposition
=
'attachment;filename={}'
.
format
(
secure_filename
(
filename
))
return
Response
(
stream_with_context
(
generate
()),
headers
=
{
'Content-Disposition'
:
disposition
},
mimetype
=
'text/csv'
)
@
expose
(
'/ajax/lookup/'
)
def
ajax_lookup
(
self
):
name
=
request
.
args
.
get
(
'name'
)
...
...
flask_admin/model/typefmt.py
100644 → 100755
View file @
597ca07d
...
...
@@ -49,3 +49,8 @@ BASE_FORMATTERS = {
bool
:
bool_formatter
,
list
:
list_formatter
,
}
EXPORT_FORMATTERS
=
{
type
(
None
):
empty_formatter
,
list
:
list_formatter
,
}
flask_admin/templates/bootstrap2/admin/model/list.html
100644 → 100755
View file @
597ca07d
...
...
@@ -26,6 +26,12 @@
</li>
{% endif %}
{% if admin_view.can_export %}
<li>
<a
href=
"{{ get_url('.export_csv', **request.args) }}"
title=
"{{ _gettext('Export') }}"
>
{{ _gettext('Export') }}
</a>
</li>
{% endif %}
{% if filters %}
<li
class=
"dropdown"
>
{{ model_layout.filter_options() }}
...
...
flask_admin/templates/bootstrap3/admin/model/list.html
100644 → 100755
View file @
597ca07d
...
...
@@ -26,6 +26,12 @@
</li>
{% endif %}
{% if admin_view.can_export %}
<li>
<a
href=
"{{ get_url('.export_csv', **request.args) }}"
title=
"{{ _gettext('Export') }}"
>
{{ _gettext('Export') }}
</a>
</li>
{% endif %}
{% if filters %}
<li
class=
"dropdown"
>
{{ model_layout.filter_options() }}
...
...
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