Commit 149bae27 authored by Michael Bukachi's avatar Michael Bukachi

Merge remote-tracking branch 'upstream/master' into bootstrap4

parents 4eec03b2 35d21b68
......@@ -10,10 +10,6 @@ matrix:
env: TOX_ENV=flake8
- python: 2.7
env: TOX_ENV=docs-html
- python: 3.4
env: TOX_ENV=py34-WTForms1
- python: 3.4
env: TOX_ENV=py34-WTForms2
- python: 3.5
env: TOX_ENV=py35-WTForms1
- python: 3.5
......@@ -22,6 +18,12 @@ matrix:
env: TOX_ENV=py36-WTForms1
- python: 3.6
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:
postgresql: "9.4"
......
Copyright (c) 2014, Serge S. Koval and contributors. See AUTHORS
for more details.
BSD 3-Clause License
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
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this 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.
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL SERGE KOVAL 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.
\ No newline at end of file
2. 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.
3. Neither the name of the copyright holder nor the names of its
contributors may 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 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
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.
......@@ -55,11 +55,11 @@ To run the examples in your local environment::
3. Install requirements::
pip install -r 'examples/sqla/requirements.txt'
pip install -r examples/sqla/requirements.txt
4. Run the application::
python examples/sqla/app.py
python examples/sqla/run_server.py
Documentation
-------------
......
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
......@@ -12,6 +25,7 @@ Next release
* Update Mapbox API v1 URL format
* Update jQuery and moment dependencies in templates
* Fixed a datepicker issue, where only dates up to 2015 were showing up
* Updated Pillow dependency version
1.5.3
-----
......
import os
import os.path as op
from werkzeug import secure_filename
from werkzeug.utils import secure_filename
from sqlalchemy import event
from flask import Flask, request, render_template
......
__version__ = '1.5.3'
__version__ = '1.5.6'
__author__ = 'Flask-Admin team'
__email__ = 'serge.koval+github@gmail.com'
......
......@@ -257,7 +257,7 @@ class BaseView(with_metaclass(AdminViewMeta, BaseViewClass)):
self.static_folder = 'static'
self.static_url_path = '/static/admin'
# If name is not povided, use capitalized endpoint name
# If name is not provided, use capitalized endpoint name
if self.name is None:
self.name = self._prettify_class_name(self.__class__.__name__)
......
......@@ -8,7 +8,7 @@ import shutil
from operator import itemgetter
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 flask_admin import form, helpers
......
......@@ -42,10 +42,10 @@ class QueryAjaxModelLoader(AjaxModelLoader):
if not model:
return None
return (as_unicode(model.id), as_unicode(model))
return (as_unicode(model.pk), as_unicode(model))
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):
query = self.model.objects
......
from sqlalchemy import or_, and_, cast
from sqlalchemy import or_, and_, cast, text
from sqlalchemy.types import String
from flask_admin._compat import as_unicode, string_types
......@@ -69,11 +69,13 @@ class QueryAjaxModelLoader(AjaxModelLoader):
def get_list(self, term, offset=0, limit=DEFAULT_PAGE_SIZE):
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))
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))
if self.order_by:
......
......@@ -216,4 +216,6 @@ def is_relationship(attr):
def is_association_proxy(attr):
if hasattr(attr, 'parent'):
attr = attr.parent
return hasattr(attr, 'extension_type') and attr.extension_type == ASSOCIATION_PROXY
......@@ -1111,6 +1111,20 @@ class ModelView(BaseModelView):
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
def create_model(self, form):
"""
......@@ -1120,11 +1134,7 @@ class ModelView(BaseModelView):
Form instance
"""
try:
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, [], {})
model = self.build_new_instance()
form.populate_obj(model)
self.session.add(model)
......
import os
import os.path as op
from werkzeug import secure_filename
from werkzeug.utils import secure_filename
from werkzeug.datastructures import FileStorage
from wtforms import ValidationError, fields
......@@ -465,7 +465,10 @@ class ImageUploadField(FileUploadField):
return image
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')
with open(path, 'wb') as fp:
......
......@@ -5,7 +5,7 @@ import mimetypes
import time
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,
Response, get_flashed_messages, stream_with_context)
......
......@@ -36,7 +36,7 @@ def bool_formatter(view, value):
Value to check
"""
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))
......
......@@ -138,6 +138,8 @@
{% set form = list_forms[get_pk_value(row)] %}
{% if form.csrf_token %}
{{ 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 %}
{{ form[c](pk=get_pk_value(row), display_value=get_value(row, c)) }}
{% endif %}
......
......@@ -137,6 +137,8 @@
{% set form = list_forms[get_pk_value(row)] %}
{% if form.csrf_token %}
{{ 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 %}
{{ form[c](pk=get_pk_value(row), display_value=get_value(row, c)) }}
{% endif %}
......
......@@ -4,7 +4,10 @@ from nose.tools import eq_, ok_
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 wtforms import fields
......
......@@ -6,7 +6,7 @@ wtf-peewee
mongoengine<0.11.0
pymongo==2.8
flask-mongoengine==0.8.2
pillow==2.9.0
pillow>=3.3.2
Babel<=1.3
flask-babelex
shapely==1.5.9
......
......@@ -64,7 +64,7 @@ setup(
install_requires=install_requires,
tests_require=[
'nose>=1.0',
'pillow==2.9.0',
'pillow>=3.3.2',
'mongoengine',
'pymongo',
'wtf-peewee',
......
[tox]
envlist =
py{27,34,35,36}-WTForms{1,2}
py{27,35,36,37}-WTForms{1,2}
py38-WTForms2
flake8
docs-html
skipsdist = true
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment