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
149bae27
Commit
149bae27
authored
Aug 27, 2020
by
Michael Bukachi
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'upstream/master' into bootstrap4
parents
4eec03b2
35d21b68
Changes
21
Hide whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
93 additions
and
49 deletions
+93
-49
.travis.yml
.travis.yml
+6
-4
LICENSE
LICENSE
+23
-20
README.rst
README.rst
+2
-2
changelog.rst
doc/changelog.rst
+15
-1
app.py
examples/sqla-custom-inline-forms/app.py
+1
-1
__init__.py
flask_admin/__init__.py
+1
-1
base.py
flask_admin/base.py
+1
-1
__init__.py
flask_admin/contrib/fileadmin/__init__.py
+1
-1
ajax.py
flask_admin/contrib/mongoengine/ajax.py
+2
-2
ajax.py
flask_admin/contrib/sqla/ajax.py
+5
-3
tools.py
flask_admin/contrib/sqla/tools.py
+2
-0
view.py
flask_admin/contrib/sqla/view.py
+15
-5
upload.py
flask_admin/form/upload.py
+5
-2
base.py
flask_admin/model/base.py
+1
-1
typefmt.py
flask_admin/model/typefmt.py
+1
-1
list.html
flask_admin/templates/bootstrap2/admin/model/list.html
+2
-0
list.html
flask_admin/templates/bootstrap3/admin/model/list.html
+2
-0
test_model.py
flask_admin/tests/test_model.py
+4
-1
requirements-dev.txt
requirements-dev.txt
+1
-1
setup.py
setup.py
+1
-1
tox.ini
tox.ini
+2
-1
No files found.
.travis.yml
View file @
149bae27
...
@@ -10,10 +10,6 @@ matrix:
...
@@ -10,10 +10,6 @@ matrix:
env
:
TOX_ENV=flake8
env
:
TOX_ENV=flake8
-
python
:
2.7
-
python
:
2.7
env
:
TOX_ENV=docs-html
env
:
TOX_ENV=docs-html
-
python
:
3.4
env
:
TOX_ENV=py34-WTForms1
-
python
:
3.4
env
:
TOX_ENV=py34-WTForms2
-
python
:
3.5
-
python
:
3.5
env
:
TOX_ENV=py35-WTForms1
env
:
TOX_ENV=py35-WTForms1
-
python
:
3.5
-
python
:
3.5
...
@@ -22,6 +18,12 @@ matrix:
...
@@ -22,6 +18,12 @@ matrix:
env
:
TOX_ENV=py36-WTForms1
env
:
TOX_ENV=py36-WTForms1
-
python
:
3.6
-
python
:
3.6
env
:
TOX_ENV=py36-WTForms2
env
:
TOX_ENV=py36-WTForms2
-
python
:
3.7
env
:
TOX_ENV=py37-WTForms1
-
python
:
3.7
env
:
TOX_ENV=py37-WTForms2
-
python
:
3.8
env
:
TOX_ENV=py38-WTForms2
addons
:
addons
:
postgresql
:
"
9.4"
postgresql
:
"
9.4"
...
...
LICENSE
View file @
149bae27
Copyright (c) 2014, Serge S. Koval and contributors. See AUTHORS
BSD 3-Clause License
for more details.
Some rights reserved.
Copyright (c) 2014, Serge S. Koval and contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
1. Redistributions of source code must retain the above copyright notice, this
notice, this list of conditions and the following disclaimer.
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Names of the contributors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
2. Redistributions in binary form must reproduce the above copyright notice,
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
this list of conditions and the following disclaimer in the documentation
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
and/or other materials provided with the distribution.
DISCLAIMED. IN NO EVENT SHALL SERGE KOVAL BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
3. Neither the name of the copyright holder nor the names of its
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
contributors may be used to endorse or promote products derived from
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
this software without specific prior written permission.
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
\ No newline at end of file
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
README.rst
View file @
149bae27
...
@@ -55,11 +55,11 @@ To run the examples in your local environment::
...
@@ -55,11 +55,11 @@ To run the examples in your local environment::
3. Install requirements::
3. Install requirements::
pip install -r
'examples/sqla/requirements.txt'
pip install -r
examples/sqla/requirements.txt
4. Run the application::
4. Run the application::
python examples/sqla/
app
.py
python examples/sqla/
run_server
.py
Documentation
Documentation
-------------
-------------
...
...
doc/changelog.rst
View file @
149bae27
Changelog
Changelog
=========
=========
Next release
1.5.6
-----
* SQLAlchemy 1.3.6 compatibility fix
* Python 3.8 support
1.5.5
-----
* Werkzeug 1.0 compatibility fix
* Use fa-circle-o icon for unchecked booleans
* A few SQLAlchemy-related bug fixes
1.5.4
-----
-----
* Fix display of inline x-editable boolean fields on list view
* Fix display of inline x-editable boolean fields on list view
...
@@ -12,6 +25,7 @@ Next release
...
@@ -12,6 +25,7 @@ Next release
* Update Mapbox API v1 URL format
* Update Mapbox API v1 URL format
* Update jQuery and moment dependencies in templates
* Update jQuery and moment dependencies in templates
* Fixed a datepicker issue, where only dates up to 2015 were showing up
* Fixed a datepicker issue, where only dates up to 2015 were showing up
* Updated Pillow dependency version
1.5.3
1.5.3
-----
-----
...
...
examples/sqla-custom-inline-forms/app.py
View file @
149bae27
import
os
import
os
import
os.path
as
op
import
os.path
as
op
from
werkzeug
import
secure_filename
from
werkzeug
.utils
import
secure_filename
from
sqlalchemy
import
event
from
sqlalchemy
import
event
from
flask
import
Flask
,
request
,
render_template
from
flask
import
Flask
,
request
,
render_template
...
...
flask_admin/__init__.py
View file @
149bae27
__version__
=
'1.5.
3
'
__version__
=
'1.5.
6
'
__author__
=
'Flask-Admin team'
__author__
=
'Flask-Admin team'
__email__
=
'serge.koval+github@gmail.com'
__email__
=
'serge.koval+github@gmail.com'
...
...
flask_admin/base.py
View file @
149bae27
...
@@ -257,7 +257,7 @@ class BaseView(with_metaclass(AdminViewMeta, BaseViewClass)):
...
@@ -257,7 +257,7 @@ class BaseView(with_metaclass(AdminViewMeta, BaseViewClass)):
self
.
static_folder
=
'static'
self
.
static_folder
=
'static'
self
.
static_url_path
=
'/static/admin'
self
.
static_url_path
=
'/static/admin'
# If name is not povided, use capitalized endpoint name
# If name is not p
r
ovided, use capitalized endpoint name
if
self
.
name
is
None
:
if
self
.
name
is
None
:
self
.
name
=
self
.
_prettify_class_name
(
self
.
__class__
.
__name__
)
self
.
name
=
self
.
_prettify_class_name
(
self
.
__class__
.
__name__
)
...
...
flask_admin/contrib/fileadmin/__init__.py
View file @
149bae27
...
@@ -8,7 +8,7 @@ import shutil
...
@@ -8,7 +8,7 @@ import shutil
from
operator
import
itemgetter
from
operator
import
itemgetter
from
flask
import
flash
,
redirect
,
abort
,
request
,
send_file
from
flask
import
flash
,
redirect
,
abort
,
request
,
send_file
from
werkzeug
import
secure_filename
from
werkzeug
.utils
import
secure_filename
from
wtforms
import
fields
,
validators
from
wtforms
import
fields
,
validators
from
flask_admin
import
form
,
helpers
from
flask_admin
import
form
,
helpers
...
...
flask_admin/contrib/mongoengine/ajax.py
View file @
149bae27
...
@@ -42,10 +42,10 @@ class QueryAjaxModelLoader(AjaxModelLoader):
...
@@ -42,10 +42,10 @@ class QueryAjaxModelLoader(AjaxModelLoader):
if
not
model
:
if
not
model
:
return
None
return
None
return
(
as_unicode
(
model
.
id
),
as_unicode
(
model
))
return
(
as_unicode
(
model
.
pk
),
as_unicode
(
model
))
def
get_one
(
self
,
pk
):
def
get_one
(
self
,
pk
):
return
self
.
model
.
objects
.
filter
(
id
=
pk
)
.
first
()
return
self
.
model
.
objects
.
filter
(
pk
=
pk
)
.
first
()
def
get_list
(
self
,
term
,
offset
=
0
,
limit
=
DEFAULT_PAGE_SIZE
):
def
get_list
(
self
,
term
,
offset
=
0
,
limit
=
DEFAULT_PAGE_SIZE
):
query
=
self
.
model
.
objects
query
=
self
.
model
.
objects
...
...
flask_admin/contrib/sqla/ajax.py
View file @
149bae27
from
sqlalchemy
import
or_
,
and_
,
cast
from
sqlalchemy
import
or_
,
and_
,
cast
,
text
from
sqlalchemy.types
import
String
from
sqlalchemy.types
import
String
from
flask_admin._compat
import
as_unicode
,
string_types
from
flask_admin._compat
import
as_unicode
,
string_types
...
@@ -69,11 +69,13 @@ class QueryAjaxModelLoader(AjaxModelLoader):
...
@@ -69,11 +69,13 @@ class QueryAjaxModelLoader(AjaxModelLoader):
def
get_list
(
self
,
term
,
offset
=
0
,
limit
=
DEFAULT_PAGE_SIZE
):
def
get_list
(
self
,
term
,
offset
=
0
,
limit
=
DEFAULT_PAGE_SIZE
):
query
=
self
.
get_query
()
query
=
self
.
get_query
()
filters
=
(
cast
(
field
,
String
)
.
ilike
(
u'
%%%
s
%%
'
%
term
)
for
field
in
self
.
_cached_fields
)
# no type casting to string if a ColumnAssociationProxyInstance is given
filters
=
(
field
.
ilike
(
u'
%%%
s
%%
'
%
term
)
if
is_association_proxy
(
field
)
else
cast
(
field
,
String
)
.
ilike
(
u'
%%%
s
%%
'
%
term
)
for
field
in
self
.
_cached_fields
)
query
=
query
.
filter
(
or_
(
*
filters
))
query
=
query
.
filter
(
or_
(
*
filters
))
if
self
.
filters
:
if
self
.
filters
:
filters
=
[
"
%
s.
%
s"
%
(
self
.
model
.
__tablename__
.
lower
(),
value
)
for
value
in
self
.
filters
]
filters
=
[
text
(
"
%
s.
%
s"
%
(
self
.
model
.
__tablename__
.
lower
(),
value
)
)
for
value
in
self
.
filters
]
query
=
query
.
filter
(
and_
(
*
filters
))
query
=
query
.
filter
(
and_
(
*
filters
))
if
self
.
order_by
:
if
self
.
order_by
:
...
...
flask_admin/contrib/sqla/tools.py
View file @
149bae27
...
@@ -216,4 +216,6 @@ def is_relationship(attr):
...
@@ -216,4 +216,6 @@ def is_relationship(attr):
def
is_association_proxy
(
attr
):
def
is_association_proxy
(
attr
):
if
hasattr
(
attr
,
'parent'
):
attr
=
attr
.
parent
return
hasattr
(
attr
,
'extension_type'
)
and
attr
.
extension_type
==
ASSOCIATION_PROXY
return
hasattr
(
attr
,
'extension_type'
)
and
attr
.
extension_type
==
ASSOCIATION_PROXY
flask_admin/contrib/sqla/view.py
View file @
149bae27
...
@@ -1111,6 +1111,20 @@ class ModelView(BaseModelView):
...
@@ -1111,6 +1111,20 @@ class ModelView(BaseModelView):
return
super
(
ModelView
,
self
)
.
handle_view_exception
(
exc
)
return
super
(
ModelView
,
self
)
.
handle_view_exception
(
exc
)
def
build_new_instance
(
self
):
"""
Build new instance of a model. Useful to override the Flask-Admin behavior
when the model has a custom __init__ method.
"""
model
=
self
.
_manager
.
new_instance
()
# TODO: We need a better way to create model instances and stay compatible with
# SQLAlchemy __init__() behavior
state
=
instance_state
(
model
)
self
.
_manager
.
dispatch
.
init
(
state
,
[],
{})
return
model
# Model handlers
# Model handlers
def
create_model
(
self
,
form
):
def
create_model
(
self
,
form
):
"""
"""
...
@@ -1120,11 +1134,7 @@ class ModelView(BaseModelView):
...
@@ -1120,11 +1134,7 @@ class ModelView(BaseModelView):
Form instance
Form instance
"""
"""
try
:
try
:
model
=
self
.
_manager
.
new_instance
()
model
=
self
.
build_new_instance
()
# TODO: We need a better way to create model instances and stay compatible with
# SQLAlchemy __init__() behavior
state
=
instance_state
(
model
)
self
.
_manager
.
dispatch
.
init
(
state
,
[],
{})
form
.
populate_obj
(
model
)
form
.
populate_obj
(
model
)
self
.
session
.
add
(
model
)
self
.
session
.
add
(
model
)
...
...
flask_admin/form/upload.py
View file @
149bae27
import
os
import
os
import
os.path
as
op
import
os.path
as
op
from
werkzeug
import
secure_filename
from
werkzeug
.utils
import
secure_filename
from
werkzeug.datastructures
import
FileStorage
from
werkzeug.datastructures
import
FileStorage
from
wtforms
import
ValidationError
,
fields
from
wtforms
import
ValidationError
,
fields
...
@@ -465,7 +465,10 @@ class ImageUploadField(FileUploadField):
...
@@ -465,7 +465,10 @@ class ImageUploadField(FileUploadField):
return
image
return
image
def
_save_image
(
self
,
image
,
path
,
format
=
'JPEG'
):
def
_save_image
(
self
,
image
,
path
,
format
=
'JPEG'
):
if
image
.
mode
not
in
(
'RGB'
,
'RGBA'
):
# New Pillow versions require RGB format for JPEGs
if
format
==
'JPEG'
and
image
.
mode
!=
'RGB'
:
image
=
image
.
convert
(
'RGB'
)
elif
image
.
mode
not
in
(
'RGB'
,
'RGBA'
):
image
=
image
.
convert
(
'RGBA'
)
image
=
image
.
convert
(
'RGBA'
)
with
open
(
path
,
'wb'
)
as
fp
:
with
open
(
path
,
'wb'
)
as
fp
:
...
...
flask_admin/model/base.py
View file @
149bae27
...
@@ -5,7 +5,7 @@ import mimetypes
...
@@ -5,7 +5,7 @@ import mimetypes
import
time
import
time
from
math
import
ceil
from
math
import
ceil
from
werkzeug
import
secure_filename
from
werkzeug
.utils
import
secure_filename
from
flask
import
(
current_app
,
request
,
redirect
,
flash
,
abort
,
json
,
from
flask
import
(
current_app
,
request
,
redirect
,
flash
,
abort
,
json
,
Response
,
get_flashed_messages
,
stream_with_context
)
Response
,
get_flashed_messages
,
stream_with_context
)
...
...
flask_admin/model/typefmt.py
View file @
149bae27
...
@@ -36,7 +36,7 @@ def bool_formatter(view, value):
...
@@ -36,7 +36,7 @@ def bool_formatter(view, value):
Value to check
Value to check
"""
"""
glyph
=
'ok-circle'
if
value
else
'minus-sign'
glyph
=
'ok-circle'
if
value
else
'minus-sign'
fa
=
'check-circle'
if
value
else
'
minus-circle
'
fa
=
'check-circle'
if
value
else
'
circle-o
'
return
Markup
(
'<span class="fa fa-
%
s glyphicon glyphicon-
%
s icon-
%
s"></span>'
%
(
fa
,
glyph
,
glyph
))
return
Markup
(
'<span class="fa fa-
%
s glyphicon glyphicon-
%
s icon-
%
s"></span>'
%
(
fa
,
glyph
,
glyph
))
...
...
flask_admin/templates/bootstrap2/admin/model/list.html
View file @
149bae27
...
@@ -138,6 +138,8 @@
...
@@ -138,6 +138,8 @@
{% set form = list_forms[get_pk_value(row)] %}
{% set form = list_forms[get_pk_value(row)] %}
{% if form.csrf_token %}
{% if form.csrf_token %}
{{ form[c](pk=get_pk_value(row), display_value=get_value(row, c), csrf=form.csrf_token._value()) }}
{{ form[c](pk=get_pk_value(row), display_value=get_value(row, c), csrf=form.csrf_token._value()) }}
{% elif csrf_token %}
{{ form[c](pk=get_pk_value(row), display_value=get_value(row, c), csrf=csrf_token()) }}
{% else %}
{% else %}
{{ form[c](pk=get_pk_value(row), display_value=get_value(row, c)) }}
{{ form[c](pk=get_pk_value(row), display_value=get_value(row, c)) }}
{% endif %}
{% endif %}
...
...
flask_admin/templates/bootstrap3/admin/model/list.html
View file @
149bae27
...
@@ -137,6 +137,8 @@
...
@@ -137,6 +137,8 @@
{% set form = list_forms[get_pk_value(row)] %}
{% set form = list_forms[get_pk_value(row)] %}
{% if form.csrf_token %}
{% if form.csrf_token %}
{{ form[c](pk=get_pk_value(row), display_value=get_value(row, c), csrf=form.csrf_token._value()) }}
{{ form[c](pk=get_pk_value(row), display_value=get_value(row, c), csrf=form.csrf_token._value()) }}
{% elif csrf_token %}
{{ form[c](pk=get_pk_value(row), display_value=get_value(row, c), csrf=csrf_token()) }}
{% else %}
{% else %}
{{ form[c](pk=get_pk_value(row), display_value=get_value(row, c)) }}
{{ form[c](pk=get_pk_value(row), display_value=get_value(row, c)) }}
{% endif %}
{% endif %}
...
...
flask_admin/tests/test_model.py
View file @
149bae27
...
@@ -4,7 +4,10 @@ from nose.tools import eq_, ok_
...
@@ -4,7 +4,10 @@ from nose.tools import eq_, ok_
from
flask
import
Flask
from
flask
import
Flask
from
werkzeug.wsgi
import
DispatcherMiddleware
try
:
from
werkzeug.middleware.dispatcher
import
DispatcherMiddleware
except
ImportError
:
from
werkzeug.wsgi
import
DispatcherMiddleware
from
werkzeug.test
import
Client
from
werkzeug.test
import
Client
from
wtforms
import
fields
from
wtforms
import
fields
...
...
requirements-dev.txt
View file @
149bae27
...
@@ -6,7 +6,7 @@ wtf-peewee
...
@@ -6,7 +6,7 @@ wtf-peewee
mongoengine<0.11.0
mongoengine<0.11.0
pymongo==2.8
pymongo==2.8
flask-mongoengine==0.8.2
flask-mongoengine==0.8.2
pillow
==2.9.0
pillow
>=3.3.2
Babel<=1.3
Babel<=1.3
flask-babelex
flask-babelex
shapely==1.5.9
shapely==1.5.9
...
...
setup.py
View file @
149bae27
...
@@ -64,7 +64,7 @@ setup(
...
@@ -64,7 +64,7 @@ setup(
install_requires
=
install_requires
,
install_requires
=
install_requires
,
tests_require
=
[
tests_require
=
[
'nose>=1.0'
,
'nose>=1.0'
,
'pillow
==2.9.0
'
,
'pillow
>=3.3.2
'
,
'mongoengine'
,
'mongoengine'
,
'pymongo'
,
'pymongo'
,
'wtf-peewee'
,
'wtf-peewee'
,
...
...
tox.ini
View file @
149bae27
[tox]
[tox]
envlist
=
envlist
=
py{27,34,35,36}-WTForms{1,2}
py{27,35,36,37}-WTForms{1,2}
py38-WTForms2
flake8
flake8
docs-html
docs-html
skipsdist
=
true
skipsdist
=
true
...
...
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