flask-appbuilder 3.2.1__py3-none-any.whl → 5.0.2__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.1.dist-info → flask_appbuilder-5.0.2.dist-info}/METADATA +36 -76
- flask_appbuilder-5.0.2.dist-info/RECORD +240 -0
- {Flask_AppBuilder-3.2.1.dist-info → flask_appbuilder-5.0.2.dist-info}/WHEEL +1 -1
- flask_appbuilder-5.0.2.dist-info/entry_points.txt +2 -0
- Flask_AppBuilder-3.2.1.dist-info/RECORD +0 -270
- Flask_AppBuilder-3.2.1.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.1.dist-info → flask_appbuilder-5.0.2.dist-info}/LICENSE +0 -0
- {Flask_AppBuilder-3.2.1.dist-info → flask_appbuilder-5.0.2.dist-info}/top_level.txt +0 -0
flask_appbuilder/models/base.py
CHANGED
|
@@ -3,9 +3,7 @@ from functools import reduce
|
|
|
3
3
|
import logging
|
|
4
4
|
from typing import Any, Type
|
|
5
5
|
|
|
6
|
-
from
|
|
7
|
-
|
|
8
|
-
from .filters import BaseFilterConverter, Filters
|
|
6
|
+
from flask_appbuilder.models.filters import BaseFilterConverter, Filters
|
|
9
7
|
|
|
10
8
|
try:
|
|
11
9
|
import enum
|
|
@@ -17,36 +15,25 @@ except ImportError:
|
|
|
17
15
|
log = logging.getLogger(__name__)
|
|
18
16
|
|
|
19
17
|
|
|
20
|
-
class BaseInterface
|
|
18
|
+
class BaseInterface:
|
|
21
19
|
"""
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
Base class for all data model interfaces.
|
|
21
|
+
Sub class it to implement your own interface for some data engine.
|
|
24
22
|
"""
|
|
25
23
|
|
|
26
24
|
filter_converter_class = Type[BaseFilterConverter]
|
|
27
25
|
""" when sub classing override with your own custom filter converter """
|
|
28
26
|
|
|
29
|
-
""" Messages to display on CRUD Events """
|
|
30
|
-
add_row_message = lazy_gettext("Added Row")
|
|
31
|
-
edit_row_message = lazy_gettext("Changed Row")
|
|
32
|
-
delete_row_message = lazy_gettext("Deleted Row")
|
|
33
|
-
delete_integrity_error_message = lazy_gettext(
|
|
34
|
-
"Associated data exists, please delete them first"
|
|
35
|
-
)
|
|
36
|
-
add_integrity_error_message = lazy_gettext(
|
|
37
|
-
"Integrity error, probably unique constraint"
|
|
38
|
-
)
|
|
39
|
-
edit_integrity_error_message = lazy_gettext(
|
|
40
|
-
"Integrity error, probably unique constraint"
|
|
41
|
-
)
|
|
42
|
-
general_error_message = lazy_gettext("General Error")
|
|
43
|
-
|
|
44
|
-
""" Tuple with message and text with severity type ex: ("Added Row", "info") """
|
|
45
|
-
message = ()
|
|
46
|
-
|
|
47
27
|
def __init__(self, obj: Type[Any]):
|
|
48
28
|
self.obj = obj
|
|
49
29
|
|
|
30
|
+
# def __getattr__(self, name: str) -> Any:
|
|
31
|
+
# """
|
|
32
|
+
# Make mypy happy about the injected filters like self.datamodel.FilterEqual
|
|
33
|
+
# https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html#when-you-re-puzzled-or-when-things-are-complicated
|
|
34
|
+
# """
|
|
35
|
+
# return super().__getattr__(name)
|
|
36
|
+
|
|
50
37
|
def _get_attr(self, col_name):
|
|
51
38
|
if not hasattr(self.obj, col_name):
|
|
52
39
|
# it's an inner obj attr
|
|
@@ -96,13 +83,13 @@ class BaseInterface(object):
|
|
|
96
83
|
|
|
97
84
|
def _get_values(self, lst, list_columns):
|
|
98
85
|
"""
|
|
99
|
-
|
|
100
|
-
|
|
86
|
+
Get Values: formats values for list template.
|
|
87
|
+
returns [{'col_name':'col_value',....},{'col_name':'col_value',....}]
|
|
101
88
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
89
|
+
:param lst:
|
|
90
|
+
The list of item objects from query
|
|
91
|
+
:param list_columns:
|
|
92
|
+
The list of columns to include
|
|
106
93
|
"""
|
|
107
94
|
retlst = []
|
|
108
95
|
for item in lst:
|
|
@@ -114,13 +101,13 @@ class BaseInterface(object):
|
|
|
114
101
|
|
|
115
102
|
def get_values(self, lst, list_columns):
|
|
116
103
|
"""
|
|
117
|
-
|
|
118
|
-
|
|
104
|
+
Get Values: formats values for list template.
|
|
105
|
+
returns [{'col_name':'col_value',....},{'col_name':'col_value',....}]
|
|
119
106
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
107
|
+
:param lst:
|
|
108
|
+
The list of item objects from query
|
|
109
|
+
:param list_columns:
|
|
110
|
+
The list of columns to include
|
|
124
111
|
"""
|
|
125
112
|
for item in lst:
|
|
126
113
|
retdict = {}
|
|
@@ -130,7 +117,7 @@ class BaseInterface(object):
|
|
|
130
117
|
|
|
131
118
|
def get_values_json(self, lst, list_columns):
|
|
132
119
|
"""
|
|
133
|
-
|
|
120
|
+
Converts list of objects from query to JSON
|
|
134
121
|
"""
|
|
135
122
|
result = []
|
|
136
123
|
for item in self.get_values(lst, list_columns):
|
|
@@ -210,6 +197,9 @@ class BaseInterface(object):
|
|
|
210
197
|
def is_enum(self, col_name):
|
|
211
198
|
return False
|
|
212
199
|
|
|
200
|
+
def is_json(self, col_name):
|
|
201
|
+
return False
|
|
202
|
+
|
|
213
203
|
def is_relation(self, prop):
|
|
214
204
|
return False
|
|
215
205
|
|
|
@@ -238,7 +228,7 @@ class BaseInterface(object):
|
|
|
238
228
|
return False
|
|
239
229
|
|
|
240
230
|
def is_pk_composite(self):
|
|
241
|
-
|
|
231
|
+
return False
|
|
242
232
|
|
|
243
233
|
def is_fk(self, col_name):
|
|
244
234
|
return False
|
|
@@ -255,30 +245,30 @@ class BaseInterface(object):
|
|
|
255
245
|
-----------------------------------------
|
|
256
246
|
"""
|
|
257
247
|
|
|
258
|
-
def add(self, item):
|
|
248
|
+
def add(self, item: Any, commit: bool = True) -> None:
|
|
259
249
|
"""
|
|
260
|
-
|
|
250
|
+
Adds object
|
|
261
251
|
"""
|
|
262
252
|
raise NotImplementedError
|
|
263
253
|
|
|
264
|
-
def edit(self, item):
|
|
254
|
+
def edit(self, item: Any, commit: bool = True) -> None:
|
|
265
255
|
"""
|
|
266
|
-
|
|
256
|
+
Edit (change) object
|
|
267
257
|
"""
|
|
268
258
|
raise NotImplementedError
|
|
269
259
|
|
|
270
|
-
def delete(self, item):
|
|
260
|
+
def delete(self, item: Any, commit: bool = True) -> None:
|
|
271
261
|
"""
|
|
272
|
-
|
|
262
|
+
Deletes object
|
|
273
263
|
"""
|
|
274
264
|
raise NotImplementedError
|
|
275
265
|
|
|
276
|
-
def get_col_default(self, col_name):
|
|
266
|
+
def get_col_default(self, col_name: str):
|
|
277
267
|
pass
|
|
278
268
|
|
|
279
269
|
def get_keys(self, lst):
|
|
280
270
|
"""
|
|
281
|
-
|
|
271
|
+
return a list of pk values from object list
|
|
282
272
|
"""
|
|
283
273
|
pk_name = self.get_pk_name()
|
|
284
274
|
if self.is_pk_composite():
|
|
@@ -288,7 +278,7 @@ class BaseInterface(object):
|
|
|
288
278
|
|
|
289
279
|
def get_pk_name(self):
|
|
290
280
|
"""
|
|
291
|
-
|
|
281
|
+
Returns the primary key name
|
|
292
282
|
"""
|
|
293
283
|
raise NotImplementedError
|
|
294
284
|
|
|
@@ -301,8 +291,8 @@ class BaseInterface(object):
|
|
|
301
291
|
|
|
302
292
|
def get(self, pk, filter=None):
|
|
303
293
|
"""
|
|
304
|
-
|
|
305
|
-
|
|
294
|
+
return the record from key, you can optionally pass filters
|
|
295
|
+
if pk exits on the db but filters exclude it it will return none.
|
|
306
296
|
"""
|
|
307
297
|
pass
|
|
308
298
|
|
|
@@ -311,11 +301,11 @@ class BaseInterface(object):
|
|
|
311
301
|
|
|
312
302
|
def get_related_interface(self, col_name):
|
|
313
303
|
"""
|
|
314
|
-
|
|
315
|
-
|
|
304
|
+
Returns a BaseInterface for the related model
|
|
305
|
+
of column name.
|
|
316
306
|
|
|
317
|
-
|
|
318
|
-
|
|
307
|
+
:param col_name: Column name with relation
|
|
308
|
+
:return: BaseInterface
|
|
319
309
|
"""
|
|
320
310
|
raise NotImplementedError
|
|
321
311
|
|
|
@@ -327,25 +317,25 @@ class BaseInterface(object):
|
|
|
327
317
|
|
|
328
318
|
def get_columns_list(self):
|
|
329
319
|
"""
|
|
330
|
-
|
|
320
|
+
Returns a list of all the columns names
|
|
331
321
|
"""
|
|
332
322
|
return []
|
|
333
323
|
|
|
334
324
|
def get_user_columns_list(self):
|
|
335
325
|
"""
|
|
336
|
-
|
|
326
|
+
Returns a list of user viewable columns names
|
|
337
327
|
"""
|
|
338
328
|
return self.get_columns_list()
|
|
339
329
|
|
|
340
330
|
def get_search_columns_list(self):
|
|
341
331
|
"""
|
|
342
|
-
|
|
332
|
+
Returns a list of searchable columns names
|
|
343
333
|
"""
|
|
344
334
|
return []
|
|
345
335
|
|
|
346
336
|
def get_order_columns_list(self, list_columns=None):
|
|
347
337
|
"""
|
|
348
|
-
|
|
338
|
+
Returns a list of order columns names
|
|
349
339
|
"""
|
|
350
340
|
return []
|
|
351
341
|
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
def renders(col_name):
|
|
2
2
|
"""
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Use this decorator to map your custom Model properties to actual
|
|
4
|
+
Model db properties. As an example::
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
class MyModel(Model):
|
|
7
|
+
id = Column(Integer, primary_key=True)
|
|
8
|
+
name = Column(String(50), unique = True, nullable=False)
|
|
9
|
+
custom = Column(Integer(20))
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
@renders('custom')
|
|
12
|
+
def my_custom(self):
|
|
13
|
+
# will render this columns as bold on ListWidget
|
|
14
|
+
return Markup('<b>' + self.custom + '</b>')
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
class MyModelView(ModelView):
|
|
17
|
+
datamodel = SQLAInterface(MyTable)
|
|
18
|
+
list_columns = ['name', 'my_custom']
|
|
19
19
|
|
|
20
20
|
"""
|
|
21
21
|
|
|
@@ -14,10 +14,10 @@ map_args_filter = {}
|
|
|
14
14
|
""" private map for arg_name and child Filter classes """
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
class BaseFilter
|
|
17
|
+
class BaseFilter:
|
|
18
18
|
"""
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
Base class for all data filters.
|
|
20
|
+
Sub class to implement your own custom filters
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
23
|
column_name = ""
|
|
@@ -39,14 +39,14 @@ class BaseFilter(object):
|
|
|
39
39
|
|
|
40
40
|
def __init__(self, column_name, datamodel, is_related_view=False):
|
|
41
41
|
"""
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
42
|
+
Constructor.
|
|
43
|
+
|
|
44
|
+
:param column_name:
|
|
45
|
+
Model field name
|
|
46
|
+
:param datamodel:
|
|
47
|
+
The datamodel access class
|
|
48
|
+
:param is_related_view:
|
|
49
|
+
Optional internal parameter to filter related views
|
|
50
50
|
"""
|
|
51
51
|
self.column_name = column_name
|
|
52
52
|
self.datamodel = datamodel
|
|
@@ -57,7 +57,7 @@ class BaseFilter(object):
|
|
|
57
57
|
|
|
58
58
|
def apply(self, query, value):
|
|
59
59
|
"""
|
|
60
|
-
|
|
60
|
+
Override this to implement your own new filters
|
|
61
61
|
"""
|
|
62
62
|
raise NotImplementedError
|
|
63
63
|
|
|
@@ -67,21 +67,21 @@ class BaseFilter(object):
|
|
|
67
67
|
|
|
68
68
|
class FilterRelation(BaseFilter):
|
|
69
69
|
"""
|
|
70
|
-
|
|
70
|
+
Base class for all filters for relations
|
|
71
71
|
"""
|
|
72
72
|
|
|
73
73
|
def apply(self, query, value):
|
|
74
74
|
"""
|
|
75
|
-
|
|
75
|
+
Override this to implement your own new filters
|
|
76
76
|
"""
|
|
77
77
|
raise NotImplementedError
|
|
78
78
|
|
|
79
79
|
|
|
80
80
|
class BaseFilterConverter:
|
|
81
81
|
"""
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
Base Filter Converter, all classes responsible
|
|
83
|
+
for the association of columns and possible filters
|
|
84
|
+
will inherit from this and override the conversion_table property.
|
|
85
85
|
|
|
86
86
|
"""
|
|
87
87
|
|
|
@@ -114,10 +114,10 @@ class BaseFilterConverter:
|
|
|
114
114
|
for conversion in self.conversion_table:
|
|
115
115
|
if getattr(self.datamodel, conversion[0])(col_name):
|
|
116
116
|
return [item(col_name, self.datamodel) for item in conversion[1]]
|
|
117
|
-
log.warning("Filter type not supported for column: %s"
|
|
117
|
+
log.warning("Filter type not supported for column: %s", col_name)
|
|
118
118
|
|
|
119
119
|
|
|
120
|
-
class Filters
|
|
120
|
+
class Filters:
|
|
121
121
|
filters: List[BaseFilter] = []
|
|
122
122
|
""" List of instantiated BaseFilter classes """
|
|
123
123
|
values: List[Any] = []
|
|
@@ -135,11 +135,11 @@ class Filters(object):
|
|
|
135
135
|
):
|
|
136
136
|
"""
|
|
137
137
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
138
|
+
:param filter_converter: Accepts BaseFilterConverter class
|
|
139
|
+
:param search_columns: restricts possible columns,
|
|
140
|
+
accepts a list of column names
|
|
141
|
+
:param search_filters: Add custom defined filters to specific columns
|
|
142
|
+
:param datamodel: Accepts BaseInterface class
|
|
143
143
|
"""
|
|
144
144
|
self.search_columns = search_columns or []
|
|
145
145
|
self.filter_converter = filter_converter
|
|
@@ -168,9 +168,9 @@ class Filters(object):
|
|
|
168
168
|
self.filters = []
|
|
169
169
|
self.values = []
|
|
170
170
|
|
|
171
|
-
def _add_filter(self, filter_instance,
|
|
171
|
+
def _add_filter(self, filter_instance, values):
|
|
172
172
|
self.filters.append(filter_instance)
|
|
173
|
-
self.values.append(
|
|
173
|
+
self.values.append(values)
|
|
174
174
|
|
|
175
175
|
def add_filter_index(
|
|
176
176
|
self, column_name: str, filter_instance_index: int, value: Any
|
|
@@ -226,7 +226,7 @@ class Filters(object):
|
|
|
226
226
|
return True
|
|
227
227
|
return False
|
|
228
228
|
|
|
229
|
-
def add_filter(self, column_name, filter_class, value):
|
|
229
|
+
def add_filter(self, column_name: str, filter_class: BaseFilter, value: str):
|
|
230
230
|
self._add_filter(filter_class(column_name, self.datamodel), value)
|
|
231
231
|
return self
|
|
232
232
|
|
|
@@ -240,9 +240,9 @@ class Filters(object):
|
|
|
240
240
|
self._add_filter(filter_class(column_name, self.datamodel), value)
|
|
241
241
|
return self
|
|
242
242
|
|
|
243
|
-
def get_joined_filters(self, filters):
|
|
243
|
+
def get_joined_filters(self, filters) -> "Filters":
|
|
244
244
|
"""
|
|
245
|
-
|
|
245
|
+
Creates a new filters class with active filters joined
|
|
246
246
|
"""
|
|
247
247
|
ret_filters = Filters(self.filter_converter, self.datamodel)
|
|
248
248
|
ret_filters.filters = self.filters + filters.filters
|
|
@@ -251,9 +251,9 @@ class Filters(object):
|
|
|
251
251
|
|
|
252
252
|
def copy(self):
|
|
253
253
|
"""
|
|
254
|
-
|
|
254
|
+
Returns a copy of this object
|
|
255
255
|
|
|
256
|
-
|
|
256
|
+
:return: A copy of self
|
|
257
257
|
"""
|
|
258
258
|
retfilters = Filters(self.filter_converter, self.datamodel)
|
|
259
259
|
retfilters.filters = copy.copy(self.filters)
|
|
@@ -262,7 +262,7 @@ class Filters(object):
|
|
|
262
262
|
|
|
263
263
|
def get_relation_cols(self):
|
|
264
264
|
"""
|
|
265
|
-
|
|
265
|
+
Returns the filter active FilterRelation cols
|
|
266
266
|
"""
|
|
267
267
|
retlst = []
|
|
268
268
|
for flt, value in zip(self.filters, self.values):
|
|
@@ -272,16 +272,16 @@ class Filters(object):
|
|
|
272
272
|
|
|
273
273
|
def get_filters_values(self) -> List[Tuple[BaseFilter, Any]]:
|
|
274
274
|
"""
|
|
275
|
-
|
|
275
|
+
Returns a list of tuples [(FILTER, value),(...,...),....]
|
|
276
276
|
"""
|
|
277
277
|
return [(flt, value) for flt, value in zip(self.filters, self.values)]
|
|
278
278
|
|
|
279
279
|
def get_filter_value(self, column_name: str) -> Any:
|
|
280
280
|
"""
|
|
281
|
-
|
|
281
|
+
Returns the filtered value for a certain column
|
|
282
282
|
|
|
283
|
-
|
|
284
|
-
|
|
283
|
+
:param column_name: The name of the column that we want the value from
|
|
284
|
+
:return: the filter value of the column
|
|
285
285
|
"""
|
|
286
286
|
for flt, value in zip(self.filters, self.values):
|
|
287
287
|
if flt.column_name == column_name:
|
|
@@ -294,8 +294,12 @@ class Filters(object):
|
|
|
294
294
|
]
|
|
295
295
|
|
|
296
296
|
def apply_all(self, query):
|
|
297
|
-
for flt,
|
|
298
|
-
|
|
297
|
+
for flt, values in zip(self.filters, self.values):
|
|
298
|
+
if isinstance(values, list) and flt.arg_name not in {"in", "not_in"}:
|
|
299
|
+
for value in values:
|
|
300
|
+
query = flt.apply(query, value)
|
|
301
|
+
else:
|
|
302
|
+
query = flt.apply(query, values)
|
|
299
303
|
return query
|
|
300
304
|
|
|
301
305
|
def __repr__(self):
|
|
@@ -35,13 +35,13 @@ class GenericColumn(object):
|
|
|
35
35
|
|
|
36
36
|
class MetaGenericModel(type):
|
|
37
37
|
"""
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
38
|
+
Meta class for GenericModel
|
|
39
|
+
will change default properties:
|
|
40
|
+
- instantiates internal '_col_defs' dict with
|
|
41
|
+
all the defined columns.
|
|
42
|
+
- Define pk property with the name of the primary key column
|
|
43
|
+
- Define properties with a list of all column's properties
|
|
44
|
+
- Define columns with a list of all column's name
|
|
45
45
|
"""
|
|
46
46
|
|
|
47
47
|
pk = None
|
|
@@ -68,18 +68,18 @@ class MetaGenericModel(type):
|
|
|
68
68
|
|
|
69
69
|
class GenericModel(with_metaclass(MetaGenericModel, object)):
|
|
70
70
|
"""
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
Generic Model class to define generic purpose models to use
|
|
72
|
+
with the framework.
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
Use GenericSession much like SQLAlchemy's Session Class.
|
|
75
|
+
Extend GenericSession to implement specific engine features.
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
Define your models like::
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
class MyGenericModel(GenericModel):
|
|
80
|
+
id = GenericColumn(int, primary_key=True)
|
|
81
|
+
age = GenericColumn(int)
|
|
82
|
+
name = GenericColumn(str)
|
|
83
83
|
|
|
84
84
|
"""
|
|
85
85
|
|
|
@@ -111,13 +111,13 @@ class GenericModel(with_metaclass(MetaGenericModel, object)):
|
|
|
111
111
|
|
|
112
112
|
class GenericSession(object):
|
|
113
113
|
"""
|
|
114
|
-
|
|
115
|
-
|
|
114
|
+
This class is a base, you should subclass it
|
|
115
|
+
to implement your own generic data source.
|
|
116
116
|
|
|
117
|
-
|
|
117
|
+
Override at least the **all** method.
|
|
118
118
|
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
**GenericSession** will implement filter and orders
|
|
120
|
+
based on your data generation on the **all** method.
|
|
121
121
|
"""
|
|
122
122
|
|
|
123
123
|
def __init__(self):
|
|
@@ -131,20 +131,20 @@ class GenericSession(object):
|
|
|
131
131
|
|
|
132
132
|
def clear(self):
|
|
133
133
|
"""
|
|
134
|
-
|
|
134
|
+
Deletes the entire store
|
|
135
135
|
"""
|
|
136
136
|
self.store = dict()
|
|
137
137
|
|
|
138
138
|
def delete_all(self, model_cls):
|
|
139
139
|
"""
|
|
140
|
-
|
|
140
|
+
Deletes all objects of type model_cls
|
|
141
141
|
"""
|
|
142
142
|
self.store[model_cls._name] = []
|
|
143
143
|
|
|
144
144
|
def get(self, pk):
|
|
145
145
|
"""
|
|
146
|
-
|
|
147
|
-
|
|
146
|
+
Returns the object for the key
|
|
147
|
+
Override it for efficiency.
|
|
148
148
|
"""
|
|
149
149
|
for item in self.store.get(self.query_class):
|
|
150
150
|
# coverts pk value to correct type
|
|
@@ -154,7 +154,7 @@ class GenericSession(object):
|
|
|
154
154
|
|
|
155
155
|
def query(self, model_cls):
|
|
156
156
|
"""
|
|
157
|
-
|
|
157
|
+
SQLAlchemy query like method
|
|
158
158
|
"""
|
|
159
159
|
self._filters_cmd = list()
|
|
160
160
|
self.query_filters = list()
|
|
@@ -350,8 +350,8 @@ class GenericSession(object):
|
|
|
350
350
|
|
|
351
351
|
def all(self):
|
|
352
352
|
"""
|
|
353
|
-
|
|
354
|
-
|
|
353
|
+
SQLA like 'all' method, will populate all rows and apply all
|
|
354
|
+
filters and orders to it.
|
|
355
355
|
"""
|
|
356
356
|
items = list()
|
|
357
357
|
if not self._filters_cmd:
|
|
@@ -396,7 +396,7 @@ class PSModel(GenericModel):
|
|
|
396
396
|
|
|
397
397
|
class PSSession(GenericSession):
|
|
398
398
|
regexp = (
|
|
399
|
-
"(\w+) +(\w+) +(\w+) +(\w+) +(\w+:\w+|\w+) (\?|tty\w+) +(\w+:\w+:\w+) +(.+)\n"
|
|
399
|
+
r"(\w+) +(\w+) +(\w+) +(\w+) +(\w+:\w+|\w+) (\?|tty\w+) +(\w+:\w+:\w+) +(.+)\n"
|
|
400
400
|
)
|
|
401
401
|
|
|
402
402
|
def add_object(self, line):
|
|
@@ -78,8 +78,8 @@ class FilterStartsWith(BaseFilter):
|
|
|
78
78
|
|
|
79
79
|
class GenericFilterConverter(BaseFilterConverter):
|
|
80
80
|
"""
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
Class for converting columns into a supported list of filters
|
|
82
|
+
specific for SQLAlchemy.
|
|
83
83
|
|
|
84
84
|
"""
|
|
85
85
|
|
|
@@ -107,6 +107,14 @@ class GenericFilterConverter(BaseFilterConverter):
|
|
|
107
107
|
FilterStartsWith,
|
|
108
108
|
],
|
|
109
109
|
),
|
|
110
|
-
(
|
|
110
|
+
(
|
|
111
|
+
"is_integer",
|
|
112
|
+
[
|
|
113
|
+
FilterEqual,
|
|
114
|
+
FilterNotEqual,
|
|
115
|
+
FilterGreater,
|
|
116
|
+
FilterSmaller,
|
|
117
|
+
],
|
|
118
|
+
),
|
|
111
119
|
("is_date", [FilterEqual, FilterNotEqual, FilterGreater, FilterSmaller]),
|
|
112
120
|
)
|
|
@@ -9,7 +9,6 @@ def _include_filters(obj):
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class GenericInterface(BaseInterface):
|
|
12
|
-
|
|
13
12
|
filter_converter_class = filters.GenericFilterConverter
|
|
14
13
|
|
|
15
14
|
def __init__(self, obj, session=None):
|
|
@@ -25,7 +24,6 @@ class GenericInterface(BaseInterface):
|
|
|
25
24
|
page=None,
|
|
26
25
|
page_size=None,
|
|
27
26
|
):
|
|
28
|
-
|
|
29
27
|
query = self.session.query(self.obj)
|
|
30
28
|
if filters:
|
|
31
29
|
query = filters.apply_all(query)
|
|
@@ -68,7 +66,7 @@ class GenericInterface(BaseInterface):
|
|
|
68
66
|
|
|
69
67
|
def get_keys(self, lst):
|
|
70
68
|
"""
|
|
71
|
-
|
|
69
|
+
return a list of pk values from object list
|
|
72
70
|
"""
|
|
73
71
|
pk_name = self.get_pk_name()
|
|
74
72
|
return [getattr(item, pk_name) for item in lst]
|