flask-appbuilder 3.2.1rc1__py3-none-any.whl → 5.0.2rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flask_appbuilder/__init__.py +2 -3
- flask_appbuilder/_compat.py +0 -1
- flask_appbuilder/actions.py +14 -14
- flask_appbuilder/api/__init__.py +741 -527
- flask_appbuilder/api/convert.py +104 -98
- flask_appbuilder/api/manager.py +14 -8
- flask_appbuilder/api/schemas.py +12 -1
- flask_appbuilder/babel/manager.py +12 -16
- flask_appbuilder/base.py +353 -280
- flask_appbuilder/basemanager.py +1 -1
- flask_appbuilder/baseviews.py +241 -164
- flask_appbuilder/charts/jsontools.py +10 -10
- flask_appbuilder/charts/views.py +56 -60
- flask_appbuilder/cli.py +115 -70
- flask_appbuilder/const.py +52 -52
- flask_appbuilder/exceptions.py +67 -5
- flask_appbuilder/fields.py +32 -23
- flask_appbuilder/fieldwidgets.py +34 -27
- flask_appbuilder/filemanager.py +33 -45
- flask_appbuilder/filters.py +11 -13
- flask_appbuilder/forms.py +31 -35
- flask_appbuilder/hooks.py +90 -0
- flask_appbuilder/menu.py +35 -10
- flask_appbuilder/models/base.py +47 -57
- flask_appbuilder/models/decorators.py +13 -13
- flask_appbuilder/models/filters.py +42 -38
- flask_appbuilder/models/generic/__init__.py +29 -29
- flask_appbuilder/models/generic/filters.py +11 -3
- flask_appbuilder/models/generic/interface.py +1 -3
- flask_appbuilder/models/group.py +37 -39
- flask_appbuilder/models/mixins.py +22 -18
- flask_appbuilder/models/sqla/__init__.py +19 -72
- flask_appbuilder/models/sqla/base.py +24 -0
- flask_appbuilder/models/sqla/base_legacy.py +132 -0
- flask_appbuilder/models/sqla/filters.py +132 -19
- flask_appbuilder/models/sqla/interface.py +390 -276
- flask_appbuilder/security/api.py +31 -35
- flask_appbuilder/security/decorators.py +181 -83
- flask_appbuilder/security/forms.py +20 -31
- flask_appbuilder/security/manager.py +715 -489
- flask_appbuilder/security/registerviews.py +29 -112
- flask_appbuilder/security/schemas.py +43 -0
- flask_appbuilder/security/sqla/apis/__init__.py +8 -0
- flask_appbuilder/security/sqla/apis/group/__init__.py +1 -0
- flask_appbuilder/security/sqla/apis/group/api.py +227 -0
- flask_appbuilder/security/sqla/apis/group/schema.py +73 -0
- flask_appbuilder/security/sqla/apis/permission/__init__.py +1 -0
- flask_appbuilder/security/sqla/apis/permission/api.py +19 -0
- flask_appbuilder/security/sqla/apis/permission_view_menu/__init__.py +1 -0
- flask_appbuilder/security/sqla/apis/permission_view_menu/api.py +16 -0
- flask_appbuilder/security/sqla/apis/role/__init__.py +1 -0
- flask_appbuilder/security/sqla/apis/role/api.py +306 -0
- flask_appbuilder/security/sqla/apis/role/schema.py +27 -0
- flask_appbuilder/security/sqla/apis/user/__init__.py +1 -0
- flask_appbuilder/security/sqla/apis/user/api.py +292 -0
- flask_appbuilder/security/sqla/apis/user/schema.py +97 -0
- flask_appbuilder/security/sqla/apis/user/validator.py +27 -0
- flask_appbuilder/security/sqla/apis/view_menu/__init__.py +1 -0
- flask_appbuilder/security/sqla/apis/view_menu/api.py +18 -0
- flask_appbuilder/security/sqla/manager.py +421 -203
- flask_appbuilder/security/sqla/models.py +192 -57
- flask_appbuilder/security/utils.py +9 -0
- flask_appbuilder/security/views.py +232 -229
- flask_appbuilder/static/.DS_Store +0 -0
- flask_appbuilder/static/appbuilder/css/ab.css +20 -12
- flask_appbuilder/static/appbuilder/css/bootstrap-datepicker/bootstrap-datepicker3.min.css +7 -0
- flask_appbuilder/static/appbuilder/css/bootstrap.min.css.map +1 -0
- flask_appbuilder/static/appbuilder/css/flags/flags16.css +249 -245
- flask_appbuilder/static/appbuilder/css/fontawesome/all.min.css +6 -0
- flask_appbuilder/static/appbuilder/css/fontawesome/brands.min.css +6 -0
- flask_appbuilder/static/appbuilder/css/fontawesome/fontawesome.min.css +6 -0
- flask_appbuilder/static/appbuilder/css/fontawesome/regular.min.css +6 -0
- flask_appbuilder/static/appbuilder/css/fontawesome/solid.min.css +6 -0
- flask_appbuilder/static/appbuilder/css/fontawesome/svg-with-js.min.css +6 -0
- flask_appbuilder/static/appbuilder/css/fontawesome/v4-font-face.min.css +6 -0
- flask_appbuilder/static/appbuilder/css/fontawesome/v4-shims.min.css +6 -0
- flask_appbuilder/static/appbuilder/css/fontawesome/v5-font-face.min.css +6 -0
- flask_appbuilder/static/appbuilder/css/images/flags16.png +0 -0
- flask_appbuilder/static/appbuilder/css/select2/select2-bootstrap.min.css +7 -0
- flask_appbuilder/static/appbuilder/css/select2/select2.min.css +1 -0
- flask_appbuilder/static/appbuilder/css/swagger/swagger-ui.css +3 -0
- flask_appbuilder/static/appbuilder/css/webfonts/fa-brands-400.ttf +0 -0
- flask_appbuilder/static/appbuilder/css/webfonts/fa-brands-400.woff2 +0 -0
- flask_appbuilder/static/appbuilder/css/webfonts/fa-regular-400.ttf +0 -0
- flask_appbuilder/static/appbuilder/css/webfonts/fa-regular-400.woff2 +0 -0
- flask_appbuilder/static/appbuilder/css/webfonts/fa-solid-900.ttf +0 -0
- flask_appbuilder/static/appbuilder/css/webfonts/fa-solid-900.woff2 +0 -0
- flask_appbuilder/static/appbuilder/css/webfonts/fa-v4compatibility.ttf +0 -0
- flask_appbuilder/static/appbuilder/css/webfonts/fa-v4compatibility.woff2 +0 -0
- flask_appbuilder/static/appbuilder/js/ab.js +33 -23
- flask_appbuilder/static/appbuilder/js/ab_filters.js +91 -84
- flask_appbuilder/static/appbuilder/js/bootstrap-datepicker/bootstrap-datepicker.min.js +8 -0
- flask_appbuilder/static/appbuilder/js/jquery-latest.js +2 -2
- flask_appbuilder/static/appbuilder/js/select2/select2.min.js +2 -0
- flask_appbuilder/static/appbuilder/js/swagger-ui-bundle.js +3 -0
- flask_appbuilder/templates/appbuilder/baselib.html +9 -3
- flask_appbuilder/templates/appbuilder/general/lib.html +60 -34
- flask_appbuilder/templates/appbuilder/general/model/edit.html +1 -1
- flask_appbuilder/templates/appbuilder/general/model/edit_cascade.html +1 -1
- flask_appbuilder/templates/appbuilder/general/model/search.html +3 -2
- flask_appbuilder/templates/appbuilder/general/model/show.html +1 -1
- flask_appbuilder/templates/appbuilder/general/model/show_cascade.html +1 -1
- flask_appbuilder/templates/appbuilder/general/security/login_db.html +7 -7
- flask_appbuilder/templates/appbuilder/general/security/login_ldap.html +5 -5
- flask_appbuilder/templates/appbuilder/general/security/login_oauth.html +24 -49
- flask_appbuilder/templates/appbuilder/general/widgets/base_list.html +2 -1
- flask_appbuilder/templates/appbuilder/general/widgets/chart.html +4 -2
- flask_appbuilder/templates/appbuilder/general/widgets/direct_chart.html +4 -3
- flask_appbuilder/templates/appbuilder/general/widgets/multiple_chart.html +3 -2
- flask_appbuilder/templates/appbuilder/general/widgets/search.html +11 -10
- flask_appbuilder/templates/appbuilder/init.html +37 -43
- flask_appbuilder/templates/appbuilder/navbar_menu.html +1 -1
- flask_appbuilder/templates/appbuilder/navbar_right.html +2 -2
- flask_appbuilder/templates/appbuilder/swagger/swagger.html +22 -19
- flask_appbuilder/translations/de/LC_MESSAGES/messages.mo +0 -0
- flask_appbuilder/translations/de/LC_MESSAGES/messages.po +305 -161
- flask_appbuilder/translations/fa/LC_MESSAGES/messages.mo +0 -0
- flask_appbuilder/translations/fa/LC_MESSAGES/messages.po +802 -0
- flask_appbuilder/translations/fr/LC_MESSAGES/messages.po +461 -319
- flask_appbuilder/translations/pt_BR/LC_MESSAGES/messages.po +650 -650
- flask_appbuilder/translations/ru/LC_MESSAGES/messages.po +1 -1
- flask_appbuilder/translations/sl/LC_MESSAGES/messages.mo +0 -0
- flask_appbuilder/translations/sl/LC_MESSAGES/messages.po +690 -0
- flask_appbuilder/translations/tr/LC_MESSAGES/messages.mo +0 -0
- flask_appbuilder/translations/tr/LC_MESSAGES/messages.po +1015 -0
- flask_appbuilder/upload.py +20 -22
- flask_appbuilder/urltools.py +39 -19
- flask_appbuilder/utils/base.py +76 -0
- flask_appbuilder/utils/legacy.py +33 -0
- flask_appbuilder/utils/limit.py +20 -0
- flask_appbuilder/validators.py +73 -14
- flask_appbuilder/views.py +75 -424
- flask_appbuilder/widgets.py +50 -51
- {Flask_AppBuilder-3.2.1rc1.dist-info → flask_appbuilder-5.0.2rc1.dist-info}/METADATA +36 -76
- flask_appbuilder-5.0.2rc1.dist-info/RECORD +240 -0
- {Flask_AppBuilder-3.2.1rc1.dist-info → flask_appbuilder-5.0.2rc1.dist-info}/WHEEL +1 -1
- flask_appbuilder-5.0.2rc1.dist-info/entry_points.txt +2 -0
- Flask_AppBuilder-3.2.1rc1.dist-info/RECORD +0 -270
- Flask_AppBuilder-3.2.1rc1.dist-info/entry_points.txt +0 -6
- flask_appbuilder/console.py +0 -426
- flask_appbuilder/models/mongoengine/__init__.py +0 -0
- flask_appbuilder/models/mongoengine/fields.py +0 -65
- flask_appbuilder/models/mongoengine/filters.py +0 -145
- flask_appbuilder/models/mongoengine/interface.py +0 -328
- flask_appbuilder/security/mongoengine/__init__.py +0 -0
- flask_appbuilder/security/mongoengine/manager.py +0 -402
- flask_appbuilder/security/mongoengine/models.py +0 -120
- flask_appbuilder/static/appbuilder/css/font-awesome.min.css +0 -4
- flask_appbuilder/static/appbuilder/datepicker/bootstrap-datepicker.css +0 -9
- flask_appbuilder/static/appbuilder/datepicker/bootstrap-datepicker.js +0 -28
- flask_appbuilder/static/appbuilder/fonts/FontAwesome.otf +0 -0
- flask_appbuilder/static/appbuilder/fonts/fontawesome-webfont.eot +0 -0
- flask_appbuilder/static/appbuilder/fonts/fontawesome-webfont.svg +0 -2671
- flask_appbuilder/static/appbuilder/fonts/fontawesome-webfont.ttf +0 -0
- flask_appbuilder/static/appbuilder/fonts/fontawesome-webfont.woff +0 -0
- flask_appbuilder/static/appbuilder/fonts/fontawesome-webfont.woff2 +0 -0
- flask_appbuilder/static/appbuilder/img/aol.png +0 -0
- flask_appbuilder/static/appbuilder/img/flags/flags16.png +0 -0
- flask_appbuilder/static/appbuilder/img/flickr.png +0 -0
- flask_appbuilder/static/appbuilder/img/google.png +0 -0
- flask_appbuilder/static/appbuilder/img/myopenid.png +0 -0
- flask_appbuilder/static/appbuilder/img/yahoo.png +0 -0
- flask_appbuilder/static/appbuilder/js/_google_charts.js +0 -39
- flask_appbuilder/static/appbuilder/js/html5shiv.js +0 -8
- flask_appbuilder/static/appbuilder/js/respond.min.js +0 -6
- flask_appbuilder/static/appbuilder/select2/select2-spinner.gif +0 -0
- flask_appbuilder/static/appbuilder/select2/select2.css +0 -1205
- flask_appbuilder/static/appbuilder/select2/select2.js +0 -23
- flask_appbuilder/static/appbuilder/select2/select2.png +0 -0
- flask_appbuilder/static/appbuilder/select2/select2x2.png +0 -0
- flask_appbuilder/templates/appbuilder/general/security/login_oid.html +0 -129
- flask_appbuilder/templates/appbuilder/general/security/resetpassword.html +0 -29
- flask_appbuilder/tests/__init__.py +0 -0
- flask_appbuilder/tests/__pycache__/__init__.cpython-36.pyc +0 -0
- flask_appbuilder/tests/__pycache__/__init__.cpython-37.pyc +0 -0
- flask_appbuilder/tests/__pycache__/_test_auth_ldap.cpython-37.pyc +0 -0
- flask_appbuilder/tests/__pycache__/_test_auth_oauth.cpython-37.pyc +0 -0
- flask_appbuilder/tests/__pycache__/_test_ldapsearch.cpython-36.pyc +0 -0
- flask_appbuilder/tests/__pycache__/_test_oauth_registration_role.cpython-36.pyc +0 -0
- flask_appbuilder/tests/__pycache__/base.cpython-36.pyc +0 -0
- flask_appbuilder/tests/__pycache__/base.cpython-37.pyc +0 -0
- flask_appbuilder/tests/__pycache__/config_api.cpython-36.pyc +0 -0
- flask_appbuilder/tests/__pycache__/config_api.cpython-37.pyc +0 -0
- flask_appbuilder/tests/__pycache__/const.cpython-36.pyc +0 -0
- flask_appbuilder/tests/__pycache__/const.cpython-37.pyc +0 -0
- flask_appbuilder/tests/__pycache__/test_0_fixture.cpython-36.pyc +0 -0
- flask_appbuilder/tests/__pycache__/test_0_fixture.cpython-37.pyc +0 -0
- flask_appbuilder/tests/__pycache__/test_api.cpython-36.pyc +0 -0
- flask_appbuilder/tests/__pycache__/test_api.cpython-37.pyc +0 -0
- flask_appbuilder/tests/__pycache__/test_fab_cli.cpython-36.pyc +0 -0
- flask_appbuilder/tests/__pycache__/test_fab_cli.cpython-37.pyc +0 -0
- flask_appbuilder/tests/__pycache__/test_menu.cpython-36.pyc +0 -0
- flask_appbuilder/tests/__pycache__/test_menu.cpython-37.pyc +0 -0
- flask_appbuilder/tests/__pycache__/test_mongoengine.cpython-36.pyc +0 -0
- flask_appbuilder/tests/__pycache__/test_mvc.cpython-36.pyc +0 -0
- flask_appbuilder/tests/__pycache__/test_mvc.cpython-37.pyc +0 -0
- flask_appbuilder/tests/__pycache__/test_sqlalchemy.cpython-36.pyc +0 -0
- flask_appbuilder/tests/__pycache__/test_sqlalchemy.cpython-37.pyc +0 -0
- flask_appbuilder/tests/_test_auth_ldap.py +0 -1045
- flask_appbuilder/tests/_test_auth_oauth.py +0 -419
- flask_appbuilder/tests/_test_ldapsearch.py +0 -135
- flask_appbuilder/tests/_test_oauth_registration_role.py +0 -59
- flask_appbuilder/tests/app.db +0 -0
- flask_appbuilder/tests/base.py +0 -90
- flask_appbuilder/tests/config_api.py +0 -21
- flask_appbuilder/tests/const.py +0 -9
- flask_appbuilder/tests/mongoengine/__init__.py +0 -0
- flask_appbuilder/tests/mongoengine/__pycache__/__init__.cpython-36.pyc +0 -0
- flask_appbuilder/tests/mongoengine/__pycache__/__init__.cpython-37.pyc +0 -0
- flask_appbuilder/tests/mongoengine/__pycache__/models.cpython-36.pyc +0 -0
- flask_appbuilder/tests/mongoengine/models.py +0 -41
- flask_appbuilder/tests/sqla/__init__.py +0 -0
- flask_appbuilder/tests/sqla/__pycache__/__init__.cpython-36.pyc +0 -0
- flask_appbuilder/tests/sqla/__pycache__/__init__.cpython-37.pyc +0 -0
- flask_appbuilder/tests/sqla/__pycache__/models.cpython-36.pyc +0 -0
- flask_appbuilder/tests/sqla/__pycache__/models.cpython-37.pyc +0 -0
- flask_appbuilder/tests/sqla/models.py +0 -340
- flask_appbuilder/tests/test_0_fixture.py +0 -39
- flask_appbuilder/tests/test_api.py +0 -2790
- flask_appbuilder/tests/test_fab_cli.py +0 -72
- flask_appbuilder/tests/test_menu.py +0 -122
- flask_appbuilder/tests/test_mongoengine.py +0 -572
- flask_appbuilder/tests/test_mvc.py +0 -1710
- flask_appbuilder/tests/test_sqlalchemy.py +0 -24
- flask_appbuilder/translations/__pycache__/__init__.cpython-36.pyc +0 -0
- flask_appbuilder/translations/es/LC_MESSAGES/messages.po~ +0 -582
- {Flask_AppBuilder-3.2.1rc1.dist-info → flask_appbuilder-5.0.2rc1.dist-info}/LICENSE +0 -0
- {Flask_AppBuilder-3.2.1rc1.dist-info → flask_appbuilder-5.0.2rc1.dist-info}/top_level.txt +0 -0
flask_appbuilder/views.py
CHANGED
|
@@ -1,34 +1,41 @@
|
|
|
1
|
-
import json
|
|
2
1
|
import logging
|
|
3
2
|
import os.path as op
|
|
4
|
-
from typing import Set
|
|
5
3
|
|
|
6
4
|
from flask import (
|
|
7
|
-
|
|
5
|
+
current_app,
|
|
8
6
|
flash,
|
|
9
|
-
jsonify,
|
|
10
|
-
make_response,
|
|
11
7
|
redirect,
|
|
12
8
|
request,
|
|
13
9
|
send_file,
|
|
14
10
|
session,
|
|
15
|
-
url_for,
|
|
16
11
|
)
|
|
12
|
+
from flask_appbuilder._compat import as_unicode
|
|
13
|
+
from flask_appbuilder.baseviews import (
|
|
14
|
+
BaseCRUDView,
|
|
15
|
+
BaseFormView,
|
|
16
|
+
BaseView,
|
|
17
|
+
expose,
|
|
18
|
+
)
|
|
19
|
+
from flask_appbuilder.const import FLAMSG_ERR_SEC_ACCESS_DENIED, PERMISSION_PREFIX
|
|
20
|
+
from flask_appbuilder.exceptions import FABException
|
|
21
|
+
from flask_appbuilder.filemanager import uuid_originalname
|
|
22
|
+
from flask_appbuilder.security.decorators import (
|
|
23
|
+
has_access,
|
|
24
|
+
)
|
|
25
|
+
from flask_appbuilder.urltools import (
|
|
26
|
+
get_order_args,
|
|
27
|
+
get_page_args,
|
|
28
|
+
get_page_size_args,
|
|
29
|
+
)
|
|
30
|
+
from flask_appbuilder.widgets import GroupFormListWidget, ListMasterWidget
|
|
17
31
|
|
|
18
|
-
from ._compat import as_unicode, string_types
|
|
19
|
-
from .baseviews import BaseCRUDView, BaseFormView, BaseView, expose, expose_api
|
|
20
|
-
from .const import FLAMSG_ERR_SEC_ACCESS_DENIED, PERMISSION_PREFIX
|
|
21
|
-
from .filemanager import uuid_originalname
|
|
22
|
-
from .security.decorators import has_access, has_access_api, permission_name
|
|
23
|
-
from .urltools import get_filter_args, get_order_args, get_page_args, get_page_size_args
|
|
24
|
-
from .widgets import GroupFormListWidget, ListMasterWidget
|
|
25
32
|
|
|
26
33
|
log = logging.getLogger(__name__)
|
|
27
34
|
|
|
28
35
|
|
|
29
36
|
class IndexView(BaseView):
|
|
30
37
|
"""
|
|
31
|
-
|
|
38
|
+
A simple view that implements the index for the site
|
|
32
39
|
"""
|
|
33
40
|
|
|
34
41
|
route_base = ""
|
|
@@ -43,8 +50,8 @@ class IndexView(BaseView):
|
|
|
43
50
|
|
|
44
51
|
class UtilView(BaseView):
|
|
45
52
|
"""
|
|
46
|
-
|
|
47
|
-
|
|
53
|
+
A simple view that implements special util routes.
|
|
54
|
+
At the moment it only supports the back special endpoint.
|
|
48
55
|
"""
|
|
49
56
|
|
|
50
57
|
route_base = ""
|
|
@@ -57,15 +64,15 @@ class UtilView(BaseView):
|
|
|
57
64
|
|
|
58
65
|
class SimpleFormView(BaseFormView):
|
|
59
66
|
"""
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
67
|
+
View for presenting your own forms
|
|
68
|
+
Inherit from this view to provide some base processing
|
|
69
|
+
for your customized form views.
|
|
63
70
|
|
|
64
|
-
|
|
65
|
-
|
|
71
|
+
Notice that this class inherits from BaseView so all properties
|
|
72
|
+
from the parent class can be overridden also.
|
|
66
73
|
|
|
67
|
-
|
|
68
|
-
|
|
74
|
+
Implement form_get and form_post to implement your
|
|
75
|
+
form pre-processing and post-processing
|
|
69
76
|
"""
|
|
70
77
|
|
|
71
78
|
@expose("/form", methods=["GET"])
|
|
@@ -107,15 +114,15 @@ class SimpleFormView(BaseFormView):
|
|
|
107
114
|
|
|
108
115
|
class PublicFormView(BaseFormView):
|
|
109
116
|
"""
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
117
|
+
View for presenting your own forms
|
|
118
|
+
Inherit from this view to provide some base
|
|
119
|
+
processing for your customized form views.
|
|
113
120
|
|
|
114
|
-
|
|
115
|
-
|
|
121
|
+
Notice that this class inherits from BaseView
|
|
122
|
+
so all properties from the parent class can be overridden also.
|
|
116
123
|
|
|
117
|
-
|
|
118
|
-
|
|
124
|
+
Implement form_get and form_post to implement
|
|
125
|
+
your form pre-processing and post-processing
|
|
119
126
|
"""
|
|
120
127
|
|
|
121
128
|
@expose("/form", methods=["GET"])
|
|
@@ -151,373 +158,15 @@ class PublicFormView(BaseFormView):
|
|
|
151
158
|
)
|
|
152
159
|
|
|
153
160
|
|
|
154
|
-
class
|
|
161
|
+
class ModelView(BaseCRUDView):
|
|
155
162
|
"""
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
""" Flag to disable this class exposed methods, note that this class
|
|
161
|
-
will eventually get deprecated """
|
|
162
|
-
|
|
163
|
-
def __init__(self, **kwargs):
|
|
164
|
-
if self.disable_api_route_methods:
|
|
165
|
-
api_route_methods: Set = {
|
|
166
|
-
"api",
|
|
167
|
-
"api_read",
|
|
168
|
-
"api_get",
|
|
169
|
-
"api_create",
|
|
170
|
-
"api_update",
|
|
171
|
-
"api_delete",
|
|
172
|
-
"api_column_add",
|
|
173
|
-
"api_column_edit",
|
|
174
|
-
"api_readvalues",
|
|
175
|
-
}
|
|
176
|
-
self.exclude_route_methods = self.exclude_route_methods | api_route_methods
|
|
177
|
-
super().__init__(**kwargs)
|
|
178
|
-
|
|
179
|
-
def _search_form_json(self):
|
|
180
|
-
pass
|
|
181
|
-
|
|
182
|
-
def _get_api_urls(self, api_urls=None):
|
|
183
|
-
"""
|
|
184
|
-
Completes a dict with the CRUD urls of the API.
|
|
163
|
+
This is the CRUD generic view.
|
|
164
|
+
If you want to automatically implement create, edit,
|
|
165
|
+
delete, show, and list from your database tables,
|
|
166
|
+
inherit your views from this class.
|
|
185
167
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
"""
|
|
189
|
-
view_name = self.__class__.__name__
|
|
190
|
-
api_urls = api_urls or {}
|
|
191
|
-
api_urls["read"] = url_for(view_name + ".api_read")
|
|
192
|
-
api_urls["delete"] = url_for(view_name + ".api_delete", pk="")
|
|
193
|
-
api_urls["create"] = url_for(view_name + ".api_create")
|
|
194
|
-
api_urls["update"] = url_for(view_name + ".api_update", pk="")
|
|
195
|
-
return api_urls
|
|
196
|
-
|
|
197
|
-
def _get_modelview_urls(self, modelview_urls=None):
|
|
198
|
-
view_name = self.__class__.__name__
|
|
199
|
-
modelview_urls = modelview_urls or {}
|
|
200
|
-
modelview_urls["show"] = url_for(view_name + ".show", pk="")
|
|
201
|
-
modelview_urls["add"] = url_for(view_name + ".add")
|
|
202
|
-
modelview_urls["edit"] = url_for(view_name + ".edit", pk="")
|
|
203
|
-
return modelview_urls
|
|
204
|
-
|
|
205
|
-
@expose("/api", methods=["GET"])
|
|
206
|
-
@has_access_api
|
|
207
|
-
@permission_name("list")
|
|
208
|
-
def api(self):
|
|
209
|
-
log.warning("This API is deprecated and will be removed on 1.15.X")
|
|
210
|
-
view_name = self.__class__.__name__
|
|
211
|
-
api_urls = self._get_api_urls()
|
|
212
|
-
modelview_urls = self._get_modelview_urls()
|
|
213
|
-
#
|
|
214
|
-
# Collects the CRUD permissions
|
|
215
|
-
can_show = self.appbuilder.sm.has_access("can_show", view_name)
|
|
216
|
-
can_edit = self.appbuilder.sm.has_access("can_edit", view_name)
|
|
217
|
-
can_add = self.appbuilder.sm.has_access("can_add", view_name)
|
|
218
|
-
can_delete = self.appbuilder.sm.has_access("can_delete", view_name)
|
|
219
|
-
#
|
|
220
|
-
# Prepares the form with the search fields make it JSON serializable
|
|
221
|
-
form_fields = {}
|
|
222
|
-
search_filters = {}
|
|
223
|
-
dict_filters = self._filters.get_search_filters()
|
|
224
|
-
form = self.search_form.refresh()
|
|
225
|
-
for col in self.search_columns:
|
|
226
|
-
form_fields[col] = form[col]()
|
|
227
|
-
search_filters[col] = [as_unicode(flt.name) for flt in dict_filters[col]]
|
|
228
|
-
|
|
229
|
-
ret_json = jsonify(
|
|
230
|
-
can_show=can_show,
|
|
231
|
-
can_add=can_add,
|
|
232
|
-
can_edit=can_edit,
|
|
233
|
-
can_delete=can_delete,
|
|
234
|
-
label_columns=self._label_columns_json(),
|
|
235
|
-
list_columns=self.list_columns,
|
|
236
|
-
order_columns=self.order_columns,
|
|
237
|
-
page_size=self.page_size,
|
|
238
|
-
modelview_name=view_name,
|
|
239
|
-
api_urls=api_urls,
|
|
240
|
-
search_filters=search_filters,
|
|
241
|
-
search_fields=form_fields,
|
|
242
|
-
modelview_urls=modelview_urls,
|
|
243
|
-
)
|
|
244
|
-
response = make_response(ret_json, 200)
|
|
245
|
-
response.headers["Content-Type"] = "application/json"
|
|
246
|
-
return response
|
|
247
|
-
|
|
248
|
-
@expose_api(name="read", url="/api/read", methods=["GET"])
|
|
249
|
-
@has_access_api
|
|
250
|
-
@permission_name("list")
|
|
251
|
-
def api_read(self):
|
|
252
|
-
"""
|
|
253
|
-
"""
|
|
254
|
-
log.warning("This API is deprecated and will be removed on 2.3.X")
|
|
255
|
-
# Get arguments for ordering
|
|
256
|
-
if get_order_args().get(self.__class__.__name__):
|
|
257
|
-
order_column, order_direction = get_order_args().get(
|
|
258
|
-
self.__class__.__name__
|
|
259
|
-
)
|
|
260
|
-
else:
|
|
261
|
-
order_column, order_direction = "", ""
|
|
262
|
-
page = get_page_args().get(self.__class__.__name__)
|
|
263
|
-
page_size = get_page_size_args().get(self.__class__.__name__)
|
|
264
|
-
get_filter_args(self._filters)
|
|
265
|
-
joined_filters = self._filters.get_joined_filters(self._base_filters)
|
|
266
|
-
count, lst = self.datamodel.query(
|
|
267
|
-
joined_filters,
|
|
268
|
-
order_column,
|
|
269
|
-
order_direction,
|
|
270
|
-
page=page,
|
|
271
|
-
page_size=page_size,
|
|
272
|
-
)
|
|
273
|
-
result = self.datamodel.get_values_json(lst, self.list_columns)
|
|
274
|
-
pks = self.datamodel.get_keys(lst)
|
|
275
|
-
ret_json = jsonify(
|
|
276
|
-
label_columns=self._label_columns_json(),
|
|
277
|
-
list_columns=self.list_columns,
|
|
278
|
-
order_columns=self.order_columns,
|
|
279
|
-
page=page,
|
|
280
|
-
page_size=page_size,
|
|
281
|
-
count=count,
|
|
282
|
-
modelview_name=self.__class__.__name__,
|
|
283
|
-
pks=pks,
|
|
284
|
-
result=result,
|
|
285
|
-
)
|
|
286
|
-
response = make_response(ret_json, 200)
|
|
287
|
-
response.headers["Content-Type"] = "application/json"
|
|
288
|
-
return response
|
|
289
|
-
|
|
290
|
-
def show_item_dict(self, item):
|
|
291
|
-
"""Returns a json-able dict for show"""
|
|
292
|
-
d = {}
|
|
293
|
-
for col in self.show_columns:
|
|
294
|
-
v = getattr(item, col)
|
|
295
|
-
if not isinstance(v, (int, float, string_types)):
|
|
296
|
-
v = str(v)
|
|
297
|
-
d[col] = v
|
|
298
|
-
return d
|
|
299
|
-
|
|
300
|
-
@expose_api(name="get", url="/api/get/<pk>", methods=["GET"])
|
|
301
|
-
@has_access_api
|
|
302
|
-
@permission_name("show")
|
|
303
|
-
def api_get(self, pk):
|
|
304
|
-
"""
|
|
305
|
-
"""
|
|
306
|
-
log.warning("This API is deprecated and will be removed on 2.3.X")
|
|
307
|
-
# Get arguments for ordering
|
|
308
|
-
item = self.datamodel.get(pk, self._base_filters)
|
|
309
|
-
if not item:
|
|
310
|
-
abort(404)
|
|
311
|
-
ret_json = jsonify(
|
|
312
|
-
pk=pk,
|
|
313
|
-
label_columns=self._label_columns_json(),
|
|
314
|
-
include_columns=self.show_columns,
|
|
315
|
-
modelview_name=self.__class__.__name__,
|
|
316
|
-
result=self.show_item_dict(item),
|
|
317
|
-
)
|
|
318
|
-
response = make_response(ret_json, 200)
|
|
319
|
-
response.headers["Content-Type"] = "application/json"
|
|
320
|
-
return response
|
|
321
|
-
|
|
322
|
-
@expose_api(name="create", url="/api/create", methods=["POST"])
|
|
323
|
-
@has_access_api
|
|
324
|
-
@permission_name("add")
|
|
325
|
-
def api_create(self):
|
|
326
|
-
log.warning("This API is deprecated and will be removed on 2.3.X")
|
|
327
|
-
get_filter_args(self._filters)
|
|
328
|
-
exclude_cols = self._filters.get_relation_cols()
|
|
329
|
-
form = self.add_form.refresh()
|
|
330
|
-
self._fill_form_exclude_cols(exclude_cols, form)
|
|
331
|
-
if form.validate():
|
|
332
|
-
item = self.datamodel.obj()
|
|
333
|
-
form.populate_obj(item)
|
|
334
|
-
self.pre_add(item)
|
|
335
|
-
if self.datamodel.add(item):
|
|
336
|
-
self.post_add(item)
|
|
337
|
-
http_return_code = 200
|
|
338
|
-
else:
|
|
339
|
-
http_return_code = 500
|
|
340
|
-
payload = {
|
|
341
|
-
"message": self.datamodel.message[0],
|
|
342
|
-
"item": self.show_item_dict(item),
|
|
343
|
-
"severity": self.datamodel.message[1],
|
|
344
|
-
}
|
|
345
|
-
else:
|
|
346
|
-
payload = {"message": "Validation error", "error_details": form.errors}
|
|
347
|
-
http_return_code = 500
|
|
348
|
-
return make_response(jsonify(payload), http_return_code)
|
|
349
|
-
|
|
350
|
-
@expose_api(name="update", url="/api/update/<pk>", methods=["PUT"])
|
|
351
|
-
@has_access_api
|
|
352
|
-
@permission_name("edit")
|
|
353
|
-
def api_update(self, pk):
|
|
354
|
-
log.warning("This API is deprecated and will be removed on 2.3.X")
|
|
355
|
-
get_filter_args(self._filters)
|
|
356
|
-
exclude_cols = self._filters.get_relation_cols()
|
|
357
|
-
|
|
358
|
-
item = self.datamodel.get(pk, self._base_filters)
|
|
359
|
-
if not item:
|
|
360
|
-
abort(404)
|
|
361
|
-
# convert pk to correct type, if pk is non string type.
|
|
362
|
-
pk = self.datamodel.get_pk_value(item)
|
|
363
|
-
|
|
364
|
-
form = self.edit_form.refresh(request.form)
|
|
365
|
-
# fill the form with the suppressed cols, generated from exclude_cols
|
|
366
|
-
self._fill_form_exclude_cols(exclude_cols, form)
|
|
367
|
-
# trick to pass unique validation
|
|
368
|
-
form._id = pk
|
|
369
|
-
http_return_code = 500
|
|
370
|
-
if form.validate():
|
|
371
|
-
|
|
372
|
-
# Deleting form fields not specified as keys in POST data
|
|
373
|
-
# this allows for other Model columns to be left untouched when
|
|
374
|
-
# unspecified.
|
|
375
|
-
form_fields = set([t for t in form._fields.keys()])
|
|
376
|
-
for field in form_fields - set(request.form.keys()):
|
|
377
|
-
delattr(form, field)
|
|
378
|
-
|
|
379
|
-
form.populate_obj(item)
|
|
380
|
-
self.pre_update(item)
|
|
381
|
-
if self.datamodel.edit(item):
|
|
382
|
-
self.post_update(item)
|
|
383
|
-
http_return_code = 200
|
|
384
|
-
payload = {
|
|
385
|
-
"message": self.datamodel.message[0],
|
|
386
|
-
"severity": self.datamodel.message[1],
|
|
387
|
-
"item": self.show_item_dict(item),
|
|
388
|
-
}
|
|
389
|
-
else:
|
|
390
|
-
payload = {
|
|
391
|
-
"message": "Validation error",
|
|
392
|
-
"error_details": form.errors,
|
|
393
|
-
"severity": "warning",
|
|
394
|
-
}
|
|
395
|
-
return make_response(jsonify(payload), http_return_code)
|
|
396
|
-
|
|
397
|
-
@expose_api(name="delete", url="/api/delete/<pk>", methods=["DELETE"])
|
|
398
|
-
@has_access_api
|
|
399
|
-
@permission_name("delete")
|
|
400
|
-
def api_delete(self, pk):
|
|
401
|
-
log.warning("This API is deprecated and will be removed on 2.3.X")
|
|
402
|
-
item = self.datamodel.get(pk, self._base_filters)
|
|
403
|
-
if not item:
|
|
404
|
-
abort(404)
|
|
405
|
-
self.pre_delete(item)
|
|
406
|
-
if self.datamodel.delete(item):
|
|
407
|
-
self.post_delete(item)
|
|
408
|
-
http_return_code = 200
|
|
409
|
-
else:
|
|
410
|
-
http_return_code = 500
|
|
411
|
-
response = make_response(
|
|
412
|
-
jsonify(
|
|
413
|
-
{
|
|
414
|
-
"message": self.datamodel.message[0],
|
|
415
|
-
"severity": self.datamodel.message[1],
|
|
416
|
-
}
|
|
417
|
-
),
|
|
418
|
-
http_return_code,
|
|
419
|
-
)
|
|
420
|
-
response.headers["Content-Type"] = "application/json"
|
|
421
|
-
return response
|
|
422
|
-
|
|
423
|
-
def _get_related_column_data(self, col_name, filters):
|
|
424
|
-
rel_datamodel = self.datamodel.get_related_interface(col_name)
|
|
425
|
-
_filters = rel_datamodel.get_filters(rel_datamodel.get_search_columns_list())
|
|
426
|
-
get_filter_args(_filters)
|
|
427
|
-
if filters:
|
|
428
|
-
filters = _filters.add_filter_list(filters)
|
|
429
|
-
else:
|
|
430
|
-
filters = _filters
|
|
431
|
-
result = rel_datamodel.query(filters)[1]
|
|
432
|
-
ret_list = list()
|
|
433
|
-
for item in result:
|
|
434
|
-
pk = rel_datamodel.get_pk_value(item)
|
|
435
|
-
ret_list.append({"id": int(pk), "text": str(item)})
|
|
436
|
-
ret_json = json.dumps(ret_list)
|
|
437
|
-
return ret_json
|
|
438
|
-
|
|
439
|
-
@expose_api(name="column_add", url="/api/column/add/<col_name>", methods=["GET"])
|
|
440
|
-
@has_access_api
|
|
441
|
-
@permission_name("add")
|
|
442
|
-
def api_column_add(self, col_name):
|
|
443
|
-
"""
|
|
444
|
-
Returns list of (pk, object) nice to use on select2.
|
|
445
|
-
Use only for related columns.
|
|
446
|
-
Always filters with add_form_query_rel_fields, and accepts extra filters
|
|
447
|
-
on endpoint arguments.
|
|
448
|
-
:param col_name: The related column name
|
|
449
|
-
:return: JSON response
|
|
450
|
-
"""
|
|
451
|
-
log.warning("This API is deprecated and will be removed on 2.3.X")
|
|
452
|
-
filter_rel_fields = None
|
|
453
|
-
if self.add_form_query_rel_fields:
|
|
454
|
-
filter_rel_fields = self.add_form_query_rel_fields.get(col_name)
|
|
455
|
-
ret_json = self._get_related_column_data(col_name, filter_rel_fields)
|
|
456
|
-
response = make_response(ret_json, 200)
|
|
457
|
-
response.headers["Content-Type"] = "application/json"
|
|
458
|
-
return response
|
|
459
|
-
|
|
460
|
-
@expose_api(name="column_edit", url="/api/column/edit/<col_name>", methods=["GET"])
|
|
461
|
-
@has_access_api
|
|
462
|
-
@permission_name("edit")
|
|
463
|
-
def api_column_edit(self, col_name):
|
|
464
|
-
"""
|
|
465
|
-
Returns list of (pk, object) nice to use on select2.
|
|
466
|
-
Use only for related columns.
|
|
467
|
-
Always filters with edit_form_query_rel_fields, and accepts extra filters
|
|
468
|
-
on endpoint arguments.
|
|
469
|
-
:param col_name: The related column name
|
|
470
|
-
:return: JSON response
|
|
471
|
-
"""
|
|
472
|
-
log.warning("This API is deprecated and will be removed on 2.3.X")
|
|
473
|
-
filter_rel_fields = None
|
|
474
|
-
if self.edit_form_query_rel_fields:
|
|
475
|
-
filter_rel_fields = self.edit_form_query_rel_fields
|
|
476
|
-
ret_json = self._get_related_column_data(col_name, filter_rel_fields)
|
|
477
|
-
response = make_response(ret_json, 200)
|
|
478
|
-
response.headers["Content-Type"] = "application/json"
|
|
479
|
-
return response
|
|
480
|
-
|
|
481
|
-
@expose_api(name="readvalues", url="/api/readvalues", methods=["GET"])
|
|
482
|
-
@has_access_api
|
|
483
|
-
@permission_name("list")
|
|
484
|
-
def api_readvalues(self):
|
|
485
|
-
"""
|
|
486
|
-
"""
|
|
487
|
-
log.warning("This API is deprecated and will be removed on 2.3.X")
|
|
488
|
-
# Get arguments for ordering
|
|
489
|
-
if get_order_args().get(self.__class__.__name__):
|
|
490
|
-
order_column, order_direction = get_order_args().get(
|
|
491
|
-
self.__class__.__name__
|
|
492
|
-
)
|
|
493
|
-
else:
|
|
494
|
-
order_column, order_direction = "", ""
|
|
495
|
-
get_filter_args(self._filters)
|
|
496
|
-
joined_filters = self._filters.get_joined_filters(self._base_filters)
|
|
497
|
-
count, result = self.datamodel.query(
|
|
498
|
-
joined_filters, order_column, order_direction
|
|
499
|
-
)
|
|
500
|
-
|
|
501
|
-
ret_list = list()
|
|
502
|
-
for item in result:
|
|
503
|
-
pk = self.datamodel.get_pk_value(item)
|
|
504
|
-
ret_list.append({"id": int(pk), "text": str(item)})
|
|
505
|
-
|
|
506
|
-
ret_json = json.dumps(ret_list)
|
|
507
|
-
response = make_response(ret_json, 200)
|
|
508
|
-
response.headers["Content-Type"] = "application/json"
|
|
509
|
-
return response
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
class ModelView(RestCRUDView):
|
|
513
|
-
"""
|
|
514
|
-
This is the CRUD generic view.
|
|
515
|
-
If you want to automatically implement create, edit,
|
|
516
|
-
delete, show, and list from your database tables,
|
|
517
|
-
inherit your views from this class.
|
|
518
|
-
|
|
519
|
-
Notice that this class inherits from BaseCRUDView and BaseModelView
|
|
520
|
-
so all properties from the parent class can be overridden.
|
|
168
|
+
Notice that this class inherits from BaseCRUDView and BaseModelView
|
|
169
|
+
so all properties from the parent class can be overridden.
|
|
521
170
|
"""
|
|
522
171
|
|
|
523
172
|
def __init__(self, **kwargs):
|
|
@@ -547,8 +196,12 @@ class ModelView(RestCRUDView):
|
|
|
547
196
|
@expose("/list/")
|
|
548
197
|
@has_access
|
|
549
198
|
def list(self):
|
|
550
|
-
|
|
551
|
-
|
|
199
|
+
self.update_redirect()
|
|
200
|
+
try:
|
|
201
|
+
widgets = self._list()
|
|
202
|
+
except FABException as exc:
|
|
203
|
+
flash(f"An error occurred: {exc}", "warning")
|
|
204
|
+
return redirect(self.get_redirect())
|
|
552
205
|
return self.render_template(
|
|
553
206
|
self.list_template, title=self.list_title, widgets=widgets
|
|
554
207
|
)
|
|
@@ -633,14 +286,14 @@ class ModelView(RestCRUDView):
|
|
|
633
286
|
@has_access
|
|
634
287
|
def download(self, filename):
|
|
635
288
|
return send_file(
|
|
636
|
-
op.join(
|
|
637
|
-
|
|
289
|
+
op.join(current_app.config["UPLOAD_FOLDER"], filename),
|
|
290
|
+
download_name=uuid_originalname(filename),
|
|
638
291
|
as_attachment=True,
|
|
639
292
|
)
|
|
640
293
|
|
|
641
294
|
def get_action_permission_name(self, name: str) -> str:
|
|
642
295
|
"""
|
|
643
|
-
|
|
296
|
+
Get the permission name of an action name
|
|
644
297
|
"""
|
|
645
298
|
_permission_name = self.method_permission_name.get(
|
|
646
299
|
self.actions.get(name).func.__name__
|
|
@@ -653,7 +306,7 @@ class ModelView(RestCRUDView):
|
|
|
653
306
|
@expose("/action/<string:name>/<pk>", methods=["GET", "POST"])
|
|
654
307
|
def action(self, name, pk):
|
|
655
308
|
"""
|
|
656
|
-
|
|
309
|
+
Action method to handle actions from a show view
|
|
657
310
|
"""
|
|
658
311
|
# Maintains compatibility but refuses to proceed if CSRF is enabled
|
|
659
312
|
if not self.is_get_mutation_allowed():
|
|
@@ -674,7 +327,7 @@ class ModelView(RestCRUDView):
|
|
|
674
327
|
@expose("/action_post", methods=["POST"])
|
|
675
328
|
def action_post(self):
|
|
676
329
|
"""
|
|
677
|
-
|
|
330
|
+
Action method to handle multiple records selected from a list view
|
|
678
331
|
"""
|
|
679
332
|
name = request.form["action"]
|
|
680
333
|
pks = request.form.getlist("rowid")
|
|
@@ -693,18 +346,18 @@ class ModelView(RestCRUDView):
|
|
|
693
346
|
|
|
694
347
|
class MasterDetailView(BaseCRUDView):
|
|
695
348
|
"""
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
349
|
+
Implements behaviour for controlling two CRUD views
|
|
350
|
+
linked by PK and FK, in a master/detail type with
|
|
351
|
+
two lists.
|
|
699
352
|
|
|
700
|
-
|
|
353
|
+
Master view will behave like a left menu::
|
|
701
354
|
|
|
702
|
-
|
|
703
|
-
|
|
355
|
+
class DetailView(ModelView):
|
|
356
|
+
datamodel = SQLAInterface(DetailTable, db.session)
|
|
704
357
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
358
|
+
class MasterView(MasterDetailView):
|
|
359
|
+
datamodel = SQLAInterface(MasterTable, db.session)
|
|
360
|
+
related_views = [DetailView]
|
|
708
361
|
|
|
709
362
|
"""
|
|
710
363
|
|
|
@@ -744,13 +397,13 @@ class MasterDetailView(BaseCRUDView):
|
|
|
744
397
|
|
|
745
398
|
class MultipleView(BaseView):
|
|
746
399
|
"""
|
|
747
|
-
|
|
748
|
-
|
|
400
|
+
Use this view to render multiple views on the same page,
|
|
401
|
+
exposed on the list endpoint.
|
|
749
402
|
|
|
750
|
-
|
|
403
|
+
example (after defining GroupModelView and ContactModelView)::
|
|
751
404
|
|
|
752
|
-
|
|
753
|
-
|
|
405
|
+
class MultipleViewsExp(MultipleView):
|
|
406
|
+
views = [GroupModelView, ContactModelView]
|
|
754
407
|
|
|
755
408
|
"""
|
|
756
409
|
|
|
@@ -803,7 +456,7 @@ class MultipleView(BaseView):
|
|
|
803
456
|
|
|
804
457
|
class CompactCRUDMixin(BaseCRUDView):
|
|
805
458
|
"""
|
|
806
|
-
|
|
459
|
+
Mix with ModelView to implement a list with add and edit on the same page.
|
|
807
460
|
"""
|
|
808
461
|
|
|
809
462
|
@classmethod
|
|
@@ -816,8 +469,7 @@ class CompactCRUDMixin(BaseCRUDView):
|
|
|
816
469
|
|
|
817
470
|
@classmethod
|
|
818
471
|
def get_key(cls, k, default=None):
|
|
819
|
-
"""Matching get method for ``set_key``
|
|
820
|
-
"""
|
|
472
|
+
"""Matching get method for ``set_key``"""
|
|
821
473
|
k = cls.__name__ + "__" + k
|
|
822
474
|
if k in session:
|
|
823
475
|
return session[k]
|
|
@@ -826,14 +478,13 @@ class CompactCRUDMixin(BaseCRUDView):
|
|
|
826
478
|
|
|
827
479
|
@classmethod
|
|
828
480
|
def del_key(cls, k):
|
|
829
|
-
"""Matching get method for ``set_key``
|
|
830
|
-
"""
|
|
481
|
+
"""Matching get method for ``set_key``"""
|
|
831
482
|
k = cls.__name__ + "__" + k
|
|
832
483
|
session.pop(k)
|
|
833
484
|
|
|
834
|
-
def _get_list_widget(self, **
|
|
835
|
-
"""
|
|
836
|
-
widgets = super(CompactCRUDMixin, self)._get_list_widget(**
|
|
485
|
+
def _get_list_widget(self, **kwargs):
|
|
486
|
+
"""get joined base filter and current active filter for query"""
|
|
487
|
+
widgets = super(CompactCRUDMixin, self)._get_list_widget(**kwargs)
|
|
837
488
|
session_form_widget = self.get_key("session_form_widget", None)
|
|
838
489
|
|
|
839
490
|
form_widget = None
|