From 5e3c8bdde1b7ce6cbcb65da7a77942c211576e2e Mon Sep 17 00:00:00 2001 From: Ken Kinder Date: Mon, 25 Mar 2019 15:23:34 -0600 Subject: [PATCH 01/69] Fix (and add example for) using a Python function as a default value. --- examples/peewee/app.py | 6 ++++-- flask_admin/contrib/peewee/view.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/peewee/app.py b/examples/peewee/app.py index 3615b97aea..de01135757 100644 --- a/examples/peewee/app.py +++ b/examples/peewee/app.py @@ -1,11 +1,11 @@ -from flask import Flask +import uuid import peewee +from flask import Flask import flask_admin as admin from flask_admin.contrib.peewee import ModelView - app = Flask(__name__) app.config['SECRET_KEY'] = '123456790' @@ -36,6 +36,7 @@ def __unicode__(self): class Post(BaseModel): + id = peewee.UUIDField(primary_key=True, default=uuid.uuid4) title = peewee.CharField(max_length=120) text = peewee.TextField(null=False) date = peewee.DateTimeField() @@ -80,6 +81,7 @@ def index(): if __name__ == '__main__': import logging + logging.basicConfig() logging.getLogger().setLevel(logging.DEBUG) diff --git a/flask_admin/contrib/peewee/view.py b/flask_admin/contrib/peewee/view.py index dc9247f660..ee77873a4a 100644 --- a/flask_admin/contrib/peewee/view.py +++ b/flask_admin/contrib/peewee/view.py @@ -453,7 +453,7 @@ def create_model(self, form): model = self.model() form.populate_obj(model) self._on_model_change(form, model, True) - model.save() + model.save(force_insert=True) # For peewee have to save inline forms after model was saved save_inline(form, model) From 8a7bc2fca728d77eeee59c36a0e903970c13d857 Mon Sep 17 00:00:00 2001 From: Chris Mayo Date: Wed, 25 Mar 2020 19:39:33 +0000 Subject: [PATCH 02/69] Enable testing of MongoEngine backend on Python 3 WTForms2 with Python 3.7 and newer needs MongoEngine 0.16 [1]. [1] https://github.com/MongoEngine/mongoengine/commit/15451ff42b7cc --- flask_admin/tests/mongoengine/__init__.py | 5 --- flask_admin/tests/mongoengine/test_basic.py | 45 ++++++++++++--------- requirements-dev.txt | 4 +- 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/flask_admin/tests/mongoengine/__init__.py b/flask_admin/tests/mongoengine/__init__.py index 86e94cb03f..8ae4e34bbc 100644 --- a/flask_admin/tests/mongoengine/__init__.py +++ b/flask_admin/tests/mongoengine/__init__.py @@ -1,11 +1,6 @@ from nose.plugins.skip import SkipTest from wtforms import __version__ as wtforms_version -# Skip test on PY3 -from flask_admin._compat import PY2 -if not PY2: - raise SkipTest('MongoEngine is not Python 3 compatible') - if int(wtforms_version[0]) < 2: raise SkipTest('MongoEngine does not support WTForms 1.') diff --git a/flask_admin/tests/mongoengine/test_basic.py b/flask_admin/tests/mongoengine/test_basic.py index a2e7163a25..d6af153c4d 100644 --- a/flask_admin/tests/mongoengine/test_basic.py +++ b/flask_admin/tests/mongoengine/test_basic.py @@ -15,7 +15,7 @@ class CustomModelView(ModelView): def __init__(self, model, name=None, category=None, endpoint=None, url=None, **kwargs): - for k, v in kwargs.iteritems(): + for k, v in kwargs.items(): setattr(self, k, v) super(CustomModelView, self).__init__(model, @@ -49,20 +49,27 @@ class Model2(db.Document): def fill_db(Model1, Model2): - Model1('test1_val_1', 'test2_val_1').save() - Model1('test1_val_2', 'test2_val_2').save() - Model1('test1_val_3', 'test2_val_3').save() - Model1('test1_val_4', 'test2_val_4').save() - Model1(None, 'empty_obj').save() - - Model2('string_field_val_1', None, None, True).save() - Model2('string_field_val_2', None, None, False).save() - Model2('string_field_val_3', 5000, 25.9).save() - Model2('string_field_val_4', 9000, 75.5).save() - Model2('string_field_val_5', 6169453081680413441).save() - - Model1('datetime_obj1', datetime_field=datetime(2014, 4, 3, 1, 9, 0)).save() - Model1('datetime_obj2', datetime_field=datetime(2013, 3, 2, 0, 8, 0)).save() + Model1(test1='test1_val_1', test2='test2_val_1').save() + Model1(test1='test1_val_2', test2='test2_val_2').save() + Model1(test1='test1_val_3', test2='test2_val_3').save() + Model1(test1='test1_val_4', test2='test2_val_4').save() + Model1(test1=None, test2='empty_obj').save() + + Model2(string_field='string_field_val_1', int_field=None, + float_field=None, bool_field=True).save() + Model2(string_field='string_field_val_2', int_field=None, + float_field=None, bool_field=False).save() + Model2(string_field='string_field_val_3', int_field=5000, + float_field=25.9).save() + Model2(string_field='string_field_val_4', int_field=9000, + float_field=75.5).save() + Model2(string_field='string_field_val_5', + int_field=6169453081680413441).save() + + Model1(test1='datetime_obj1', + datetime_field=datetime(2014, 4, 3, 1, 9, 0)).save() + Model1(test1='datetime_obj2', + datetime_field=datetime(2013, 3, 2, 0, 8, 0)).save() def test_model(): @@ -116,7 +123,7 @@ def test_model(): rv = client.get('/admin/model1/') eq_(rv.status_code, 200) - ok_('test1large' in rv.data) + ok_(b'test1large' in rv.data) url = '/admin/model1/edit/?id=%s' % model.id rv = client.get(url) @@ -977,7 +984,7 @@ class Model1(db.Document): rv = client.post('/admin/model1/new/', data={'test1': 'test1large', 'subdoc-0-name': '', 'subdoc-0-value': 'test'}) eq_(rv.status_code, 200) - ok_('This field is required' in rv.data) + ok_(b'This field is required' in rv.data) def test_list_subdocument_validation(): @@ -1002,7 +1009,7 @@ class Model1(db.Document): rv = client.post('/admin/model1/new/', data={'test1': 'test1large', 'subdoc-0-name': '', 'subdoc-0-value': 'test'}) eq_(rv.status_code, 200) - ok_('This field is required' in rv.data) + ok_(b'This field is required' in rv.data) def test_ajax_fk(): @@ -1056,7 +1063,7 @@ def test_ajax_fk(): client = app.test_client() req = client.get(u'/admin/view/ajax/lookup/?name=model1&query=foo') - eq_(req.data, u'[["%s", "foo"]]' % model2.id) + eq_(req.data.decode('utf-8'), u'[["%s", "foo"]]' % model2.id) # Check submitting client.post('/admin/view/new/', data={u'model1': as_unicode(model.id)}) diff --git a/requirements-dev.txt b/requirements-dev.txt index 9999cf0778..ca5987948f 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -3,8 +3,8 @@ Flask>=0.7 Flask-SQLAlchemy>=0.15 peewee wtf-peewee -mongoengine<0.11.0 -pymongo==2.8 +mongoengine +pymongo flask-mongoengine==0.8.2 pillow>=3.3.2 Babel<=1.3 From 3c122e26a9003e8febdc1cdcf52f883d84d3b424 Mon Sep 17 00:00:00 2001 From: Chris Mayo Date: Wed, 25 Mar 2020 19:39:33 +0000 Subject: [PATCH 03/69] Make pymongo backend testing PyMongo 3 compatible Fix deprecation warning that only requires PyMongo 3.0.0 in test_model. --- flask_admin/tests/pymongo/__init__.py | 6 +++--- flask_admin/tests/pymongo/test_basic.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/flask_admin/tests/pymongo/__init__.py b/flask_admin/tests/pymongo/__init__.py index 5d1d21df3c..8a9d27b036 100644 --- a/flask_admin/tests/pymongo/__init__.py +++ b/flask_admin/tests/pymongo/__init__.py @@ -1,4 +1,4 @@ -import pymongo +from pymongo import MongoClient from flask import Flask from flask_admin import Admin @@ -9,8 +9,8 @@ def setup(): app.config['SECRET_KEY'] = '1' app.config['CSRF_ENABLED'] = False - conn = pymongo.Connection() - db = conn.tests + client = MongoClient() + db = client.tests admin = Admin(app) diff --git a/flask_admin/tests/pymongo/test_basic.py b/flask_admin/tests/pymongo/test_basic.py index c191895bb3..8316000277 100644 --- a/flask_admin/tests/pymongo/test_basic.py +++ b/flask_admin/tests/pymongo/test_basic.py @@ -26,7 +26,7 @@ def test_model(): admin.add_view(view) # Drop existing data (if any) - db.test.remove() + db.test.delete_many({}) eq_(view.name, 'Test') eq_(view.endpoint, 'testview') From 697271ce6b2fde769bd1d22fa6775cb47fdc4739 Mon Sep 17 00:00:00 2001 From: Chris Mayo Date: Wed, 25 Mar 2020 19:39:33 +0000 Subject: [PATCH 04/69] Don't attempt to collect pymongo test classes --- flask_admin/tests/pymongo/test_basic.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flask_admin/tests/pymongo/test_basic.py b/flask_admin/tests/pymongo/test_basic.py index 8316000277..a58cde3ecd 100644 --- a/flask_admin/tests/pymongo/test_basic.py +++ b/flask_admin/tests/pymongo/test_basic.py @@ -8,11 +8,13 @@ class TestForm(form.Form): + __test__ = False test1 = fields.StringField('Test1') test2 = fields.StringField('Test2') class TestView(ModelView): + __test__ = False column_list = ('test1', 'test2', 'test3', 'test4') column_sortable_list = ('test1', 'test2') From 51057e15acd88c4a783101bc186802c90a4c3daf Mon Sep 17 00:00:00 2001 From: "Dmitry / skype: dvska-at-skype" Date: Fri, 8 May 2020 22:02:44 +0300 Subject: [PATCH 05/69] typo fix --- doc/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/introduction.rst b/doc/introduction.rst index e4292b2869..e55092e5ee 100644 --- a/doc/introduction.rst +++ b/doc/introduction.rst @@ -206,7 +206,7 @@ details view** by setting:: can_view_details = True **Removing columns** from the list view is easy, just pass a list of column names for -the *column_excludes_list* parameter:: +the *column_exclude_list* parameter:: column_exclude_list = ['password', ] From 44cfcacaa70c96c14736476c02f766d295b3827e Mon Sep 17 00:00:00 2001 From: Daniel Butler Date: Fri, 3 Jul 2020 14:52:46 -0400 Subject: [PATCH 06/69] fix secret typo --- examples/sqla-custom-inline-forms/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/sqla-custom-inline-forms/app.py b/examples/sqla-custom-inline-forms/app.py index b0e9334c55..659d80d74b 100644 --- a/examples/sqla-custom-inline-forms/app.py +++ b/examples/sqla-custom-inline-forms/app.py @@ -19,7 +19,7 @@ # Create application app = Flask(__name__) -# Create dummy secrey key so we can use sessions +# Create dummy secret key so we can use sessions app.config['SECRET_KEY'] = '123456790' # Create in-memory database From 798e798f4a70ad9d69888cafa9935d2c3df36098 Mon Sep 17 00:00:00 2001 From: Daniel Butler Date: Wed, 8 Jul 2020 14:11:25 -0400 Subject: [PATCH 07/69] adding email validator running flask admin raised the following issue `Exception: Install 'email_validator' for email validation support.` --- examples/sqla/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/sqla/requirements.txt b/examples/sqla/requirements.txt index b965f9bd84..f010af00b6 100644 --- a/examples/sqla/requirements.txt +++ b/examples/sqla/requirements.txt @@ -7,6 +7,7 @@ enum34; python_version < '3.0' sqlalchemy_utils arrow colour +email_validator # note: for local development, replace 'Flask-Admin' above with a reference to # your local copy of the repo e.g. '-e .' if you're installing this from the From 04c3bf75e5518f4c0a3a214ebca2ab170059afab Mon Sep 17 00:00:00 2001 From: provinzkraut <25355197+provinzkraut@users.noreply.github.com> Date: Fri, 14 Aug 2020 15:26:40 +0200 Subject: [PATCH 08/69] Fix sqla.AdminModelConverter.convert_email empty string behavior --- flask_admin/contrib/sqla/form.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/flask_admin/contrib/sqla/form.py b/flask_admin/contrib/sqla/form.py index aa74d4dfc7..93a4e71fc9 100644 --- a/flask_admin/contrib/sqla/form.py +++ b/flask_admin/contrib/sqla/form.py @@ -345,7 +345,11 @@ def convert_arrow_time(self, field_args, **extra): return form.DateTimeField(**field_args) @converts('sqlalchemy_utils.types.email.EmailType') - def convert_email(self, field_args, **extra): + def convert_email(self, field_args, column=None, **extra): + if column.nullable: + filters = field_args.get('filters', []) + filters.append(lambda x: x or None) + field_args['filters'] = filters field_args['validators'].append(validators.Email()) return fields.StringField(**field_args) From e4097eaf6af04c1f88a0ce53d56be9a138d93b31 Mon Sep 17 00:00:00 2001 From: Michael Bukachi Date: Fri, 28 Aug 2020 00:24:24 +0300 Subject: [PATCH 09/69] fix: Correct styling issue in datepicker --- examples/bootstrap4/app.py | 2 + .../daterangepicker-bs4.css | 335 ++++++++++++++++++ .../templates/bootstrap4/admin/lib.html | 2 +- 3 files changed, 338 insertions(+), 1 deletion(-) create mode 100644 flask_admin/static/vendor/bootstrap-daterangepicker/daterangepicker-bs4.css diff --git a/examples/bootstrap4/app.py b/examples/bootstrap4/app.py index 972aa75d4d..bd766c17d7 100644 --- a/examples/bootstrap4/app.py +++ b/examples/bootstrap4/app.py @@ -1,3 +1,4 @@ +import datetime import os import os.path as op from flask import Flask @@ -27,6 +28,7 @@ class User(db.Model): name = db.Column(db.Unicode(64)) email = db.Column(db.Unicode(64)) active = db.Column(db.Boolean, default=True) + created_at = db.Column(db.DateTime, default=datetime.datetime.now) def __unicode__(self): return self.name diff --git a/flask_admin/static/vendor/bootstrap-daterangepicker/daterangepicker-bs4.css b/flask_admin/static/vendor/bootstrap-daterangepicker/daterangepicker-bs4.css new file mode 100644 index 0000000000..a9194f9548 --- /dev/null +++ b/flask_admin/static/vendor/bootstrap-daterangepicker/daterangepicker-bs4.css @@ -0,0 +1,335 @@ +/*! + * Stylesheet for the Date Range Picker, for use with Bootstrap 3.x + * + * Copyright 2013-2015 Dan Grossman ( http://www.dangrossman.info ) + * Licensed under the MIT license. See http://www.opensource.org/licenses/mit-license.php + * + * Built for http://www.improvely.com + */ + + .daterangepicker.dropdown-menu { + max-width: none; + z-index: 3000; +} + +.daterangepicker.opensleft .ranges, .daterangepicker.opensleft .calendar { + float: left; + margin: 4px; +} + +.daterangepicker.opensright .ranges, .daterangepicker.opensright .calendar, +.daterangepicker.openscenter .ranges, .daterangepicker.openscenter .calendar { + float: right; + margin: 4px; +} + +.daterangepicker.single .ranges, .daterangepicker.single .calendar { + float: none; +} + +.daterangepicker .ranges { + width: 160px; + text-align: left; +} + +.daterangepicker .ranges .range_inputs>div { + float: left; +} + +.daterangepicker .ranges .range_inputs>div:nth-child(2) { + padding-left: 11px; +} + +.daterangepicker .calendar { + display: none; + max-width: 270px; +} + +.daterangepicker.show-calendar .calendar { + display: block; +} + +.daterangepicker .calendar.single .calendar-date { + border: none; +} + +.daterangepicker .calendar th, .daterangepicker .calendar td { + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; + white-space: nowrap; + text-align: center; + min-width: 32px; +} + +.daterangepicker .daterangepicker_start_input label, +.daterangepicker .daterangepicker_end_input label { + color: #333; + display: block; + font-size: 0.6875rem; + font-weight: normal; + height: 20px; + line-height: 20px; + margin-bottom: 2px; + text-shadow: #fff 1px 1px 0px; + text-transform: uppercase; + width: 74px; +} + +.daterangepicker .ranges input { + font-size: 0.6875rem; +} + +.daterangepicker .ranges .input-mini { + border: 1px solid #ccc; + border-radius: 4px; + color: #555; + display: block; + font-size: 11px; + height: 30px; + line-height: 30px; + vertical-align: middle; + margin: 0 0 10px 0; + padding: 0 6px; + width: 74px; +} + +.daterangepicker .ranges ul { + list-style: none; + margin: 0; + padding: 0; +} + +.daterangepicker .ranges li { + font-size: 0.8125rem; + background: #f5f5f5; + border: 1px solid #f5f5f5; + color: #08c; + padding: 3px 12px; + margin-bottom: 8px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + cursor: pointer; +} + +.daterangepicker .ranges li.active, .daterangepicker .ranges li:hover { + background: #08c; + border: 1px solid #08c; + color: #fff; +} + +.daterangepicker .calendar-date { + border: 1px solid #ddd; + padding: 4px; + border-radius: 4px; + background: #fff; +} + +.daterangepicker .calendar-time { + text-align: center; + margin: 8px auto 0 auto; + line-height: 30px; +} + +.daterangepicker { + position: absolute; + background: #fff; + top: 100px; + left: 20px; + padding: 4px; + margin-top: 1px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.daterangepicker.opensleft:before { + position: absolute; + top: -7px; + right: 9px; + display: inline-block; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-left: 7px solid transparent; + border-bottom-color: rgba(0, 0, 0, 0.2); + content: ''; +} + +.daterangepicker.opensleft:after { + position: absolute; + top: -6px; + right: 10px; + display: inline-block; + border-right: 6px solid transparent; + border-bottom: 6px solid #fff; + border-left: 6px solid transparent; + content: ''; +} + +.daterangepicker.openscenter:before { + position: absolute; + top: -7px; + left: 0; + right: 0; + width: 0; + margin-left: auto; + margin-right: auto; + display: inline-block; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-left: 7px solid transparent; + border-bottom-color: rgba(0, 0, 0, 0.2); + content: ''; +} + +.daterangepicker.openscenter:after { + position: absolute; + top: -6px; + left: 0; + right: 0; + width: 0; + margin-left: auto; + margin-right: auto; + display: inline-block; + border-right: 6px solid transparent; + border-bottom: 6px solid #fff; + border-left: 6px solid transparent; + content: ''; +} + +.daterangepicker.opensright:before { + position: absolute; + top: -7px; + left: 9px; + display: inline-block; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-left: 7px solid transparent; + border-bottom-color: rgba(0, 0, 0, 0.2); + content: ''; +} + +.daterangepicker.opensright:after { + position: absolute; + top: -6px; + left: 10px; + display: inline-block; + border-right: 6px solid transparent; + border-bottom: 6px solid #fff; + border-left: 6px solid transparent; + content: ''; +} + +.daterangepicker.dropup{ + margin-top: -5px; +} +.daterangepicker.dropup:before{ + top: initial; + bottom:-7px; + border-bottom: initial; + border-top: 7px solid #ccc; +} +.daterangepicker.dropup:after{ + top: initial; + bottom:-6px; + border-bottom: initial; + border-top: 6px solid #fff; +} + +.daterangepicker table { + width: 100%; + margin: 0; +} + +.daterangepicker td, .daterangepicker th { + text-align: center; + width: 20px; + height: 20px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + cursor: pointer; + white-space: nowrap; +} + +.daterangepicker td.off { + color: #999; +} + +.daterangepicker td.disabled, .daterangepicker option.disabled { + color: #999; +} + +.daterangepicker td.available:hover, .daterangepicker td.available.today, .daterangepicker th.available:hover { + background: #eee; +} + +.daterangepicker td.in-range { + background: #ebf4f8; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} + +.daterangepicker td.start-date { + -webkit-border-radius: 4px 0 0 4px; + -moz-border-radius: 4px 0 0 4px; + border-radius: 4px 0 0 4px; +} + +.daterangepicker td.end-date { + -webkit-border-radius: 0 4px 4px 0; + -moz-border-radius: 0 4px 4px 0; + border-radius: 0 4px 4px 0; +} + +.daterangepicker td.start-date.end-date { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} + +.daterangepicker td.today.active, .daterangepicker td.active, .daterangepicker td.active:hover { + background-color: #357ebd; + border-color: #3071a9; + color: #fff; +} + +.daterangepicker td.week, .daterangepicker th.week { + font-size: 80%; + color: #ccc; +} + +.daterangepicker select.monthselect, .daterangepicker select.yearselect { + font-size: 0.75rem; + padding: 1px; + height: auto; + margin: 0; + cursor: default; +} + +.daterangepicker select.monthselect { + margin-right: 2%; + width: 56%; +} + +.daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.secondselect, .daterangepicker select.ampmselect { + margin-bottom: 0; +} + +.daterangepicker_start_input { + float: left; +} + +.daterangepicker_end_input { + float: left; + padding-left: 11px +} + +.daterangepicker th.month { + width: auto; +} + +/*.daterangepicker.dropdown-menu.single.opensright.show-calendar:after {*/ +/* padding: 40px !important;*/ +/* margin: 40px !important;*/ +/*}*/ \ No newline at end of file diff --git a/flask_admin/templates/bootstrap4/admin/lib.html b/flask_admin/templates/bootstrap4/admin/lib.html index 59443773c9..1fde651cfe 100644 --- a/flask_admin/templates/bootstrap4/admin/lib.html +++ b/flask_admin/templates/bootstrap4/admin/lib.html @@ -242,7 +242,7 @@

{{ text }}

{% macro form_css() %} - + {% if config.MAPBOX_MAP_ID %} From 9c31b8890e19b40e724fd2e476d7c62a6954d8b1 Mon Sep 17 00:00:00 2001 From: Titanium Hocker Date: Sun, 30 Aug 2020 21:46:12 +0300 Subject: [PATCH 10/69] Fixed bs4 modal details view markup --- .../bootstrap4/admin/model/modals/details.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/flask_admin/templates/bootstrap4/admin/model/modals/details.html b/flask_admin/templates/bootstrap4/admin/model/modals/details.html index 3a684346c2..793b4d00e4 100755 --- a/flask_admin/templates/bootstrap4/admin/model/modals/details.html +++ b/flask_admin/templates/bootstrap4/admin/model/modals/details.html @@ -3,17 +3,17 @@ {% block body %} ' + '' + ''; From 7287ed90dfc589bf8d47b89aaa4fb43c7318fcd6 Mon Sep 17 00:00:00 2001 From: Titanium Hocker Date: Mon, 31 Aug 2020 01:19:05 +0300 Subject: [PATCH 12/69] Fix bs4 edit modal --- flask_admin/templates/bootstrap4/admin/model/modals/edit.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask_admin/templates/bootstrap4/admin/model/modals/edit.html b/flask_admin/templates/bootstrap4/admin/model/modals/edit.html index 7ce2c7d052..dd9e777a49 100644 --- a/flask_admin/templates/bootstrap4/admin/model/modals/edit.html +++ b/flask_admin/templates/bootstrap4/admin/model/modals/edit.html @@ -27,5 +27,5 @@