clear-skies 1.19.22__py3-none-any.whl → 2.0.23__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.
- clear_skies-2.0.23.dist-info/METADATA +76 -0
- clear_skies-2.0.23.dist-info/RECORD +265 -0
- {clear_skies-1.19.22.dist-info → clear_skies-2.0.23.dist-info}/WHEEL +1 -1
- clearskies/__init__.py +37 -21
- clearskies/action.py +7 -0
- clearskies/authentication/__init__.py +9 -38
- clearskies/authentication/authentication.py +44 -0
- clearskies/authentication/authorization.py +14 -8
- clearskies/authentication/authorization_pass_through.py +22 -0
- clearskies/authentication/jwks.py +135 -58
- clearskies/authentication/public.py +3 -26
- clearskies/authentication/secret_bearer.py +515 -44
- clearskies/autodoc/formats/oai3_json/__init__.py +2 -2
- clearskies/autodoc/formats/oai3_json/oai3_json.py +11 -9
- clearskies/autodoc/formats/oai3_json/parameter.py +6 -3
- clearskies/autodoc/formats/oai3_json/request.py +7 -5
- clearskies/autodoc/formats/oai3_json/response.py +7 -4
- clearskies/autodoc/formats/oai3_json/schema/object.py +10 -1
- clearskies/autodoc/request/__init__.py +2 -0
- clearskies/autodoc/request/header.py +4 -6
- clearskies/autodoc/request/json_body.py +4 -6
- clearskies/autodoc/request/parameter.py +8 -0
- clearskies/autodoc/request/request.py +16 -4
- clearskies/autodoc/request/url_parameter.py +4 -6
- clearskies/autodoc/request/url_path.py +4 -6
- clearskies/autodoc/schema/__init__.py +4 -2
- clearskies/autodoc/schema/array.py +5 -6
- clearskies/autodoc/schema/boolean.py +4 -10
- clearskies/autodoc/schema/date.py +0 -3
- clearskies/autodoc/schema/datetime.py +1 -4
- clearskies/autodoc/schema/double.py +0 -3
- clearskies/autodoc/schema/enum.py +4 -2
- clearskies/autodoc/schema/integer.py +4 -9
- clearskies/autodoc/schema/long.py +0 -3
- clearskies/autodoc/schema/number.py +4 -9
- clearskies/autodoc/schema/object.py +5 -7
- clearskies/autodoc/schema/password.py +0 -3
- clearskies/autodoc/schema/schema.py +11 -0
- clearskies/autodoc/schema/string.py +4 -10
- clearskies/backends/__init__.py +56 -17
- clearskies/backends/api_backend.py +1128 -166
- clearskies/backends/backend.py +54 -85
- clearskies/backends/cursor_backend.py +246 -191
- clearskies/backends/memory_backend.py +514 -208
- clearskies/backends/secrets_backend.py +68 -31
- clearskies/column.py +1221 -0
- clearskies/columns/__init__.py +71 -0
- clearskies/columns/audit.py +306 -0
- clearskies/columns/belongs_to_id.py +478 -0
- clearskies/columns/belongs_to_model.py +129 -0
- clearskies/columns/belongs_to_self.py +109 -0
- clearskies/columns/boolean.py +110 -0
- clearskies/columns/category_tree.py +273 -0
- clearskies/columns/category_tree_ancestors.py +51 -0
- clearskies/columns/category_tree_children.py +126 -0
- clearskies/columns/category_tree_descendants.py +48 -0
- clearskies/columns/created.py +92 -0
- clearskies/columns/created_by_authorization_data.py +114 -0
- clearskies/columns/created_by_header.py +103 -0
- clearskies/columns/created_by_ip.py +90 -0
- clearskies/columns/created_by_routing_data.py +102 -0
- clearskies/columns/created_by_user_agent.py +89 -0
- clearskies/columns/date.py +232 -0
- clearskies/columns/datetime.py +284 -0
- clearskies/columns/email.py +78 -0
- clearskies/columns/float.py +149 -0
- clearskies/columns/has_many.py +529 -0
- clearskies/columns/has_many_self.py +62 -0
- clearskies/columns/has_one.py +21 -0
- clearskies/columns/integer.py +158 -0
- clearskies/columns/json.py +126 -0
- clearskies/columns/many_to_many_ids.py +335 -0
- clearskies/columns/many_to_many_ids_with_data.py +274 -0
- clearskies/columns/many_to_many_models.py +156 -0
- clearskies/columns/many_to_many_pivots.py +132 -0
- clearskies/columns/phone.py +162 -0
- clearskies/columns/select.py +95 -0
- clearskies/columns/string.py +102 -0
- clearskies/columns/timestamp.py +164 -0
- clearskies/columns/updated.py +107 -0
- clearskies/columns/uuid.py +83 -0
- clearskies/configs/README.md +105 -0
- clearskies/configs/__init__.py +170 -0
- clearskies/configs/actions.py +43 -0
- clearskies/configs/any.py +15 -0
- clearskies/configs/any_dict.py +24 -0
- clearskies/configs/any_dict_or_callable.py +25 -0
- clearskies/configs/authentication.py +23 -0
- clearskies/configs/authorization.py +23 -0
- clearskies/configs/boolean.py +18 -0
- clearskies/configs/boolean_or_callable.py +20 -0
- clearskies/configs/callable_config.py +20 -0
- clearskies/configs/columns.py +34 -0
- clearskies/configs/conditions.py +30 -0
- clearskies/configs/config.py +26 -0
- clearskies/configs/datetime.py +20 -0
- clearskies/configs/datetime_or_callable.py +21 -0
- clearskies/configs/email.py +10 -0
- clearskies/configs/email_list.py +17 -0
- clearskies/configs/email_list_or_callable.py +17 -0
- clearskies/configs/email_or_email_list_or_callable.py +59 -0
- clearskies/configs/endpoint.py +23 -0
- clearskies/configs/endpoint_list.py +29 -0
- clearskies/configs/float.py +18 -0
- clearskies/configs/float_or_callable.py +20 -0
- clearskies/configs/headers.py +28 -0
- clearskies/configs/integer.py +18 -0
- clearskies/configs/integer_or_callable.py +20 -0
- clearskies/configs/joins.py +30 -0
- clearskies/configs/list_any_dict.py +32 -0
- clearskies/configs/list_any_dict_or_callable.py +33 -0
- clearskies/configs/model_class.py +35 -0
- clearskies/configs/model_column.py +67 -0
- clearskies/configs/model_columns.py +58 -0
- clearskies/configs/model_destination_name.py +26 -0
- clearskies/configs/model_to_id_column.py +45 -0
- clearskies/configs/readable_model_column.py +11 -0
- clearskies/configs/readable_model_columns.py +11 -0
- clearskies/configs/schema.py +23 -0
- clearskies/configs/searchable_model_columns.py +11 -0
- clearskies/configs/security_headers.py +39 -0
- clearskies/configs/select.py +28 -0
- clearskies/configs/select_list.py +49 -0
- clearskies/configs/string.py +31 -0
- clearskies/configs/string_dict.py +34 -0
- clearskies/configs/string_list.py +47 -0
- clearskies/configs/string_list_or_callable.py +48 -0
- clearskies/configs/string_or_callable.py +18 -0
- clearskies/configs/timedelta.py +20 -0
- clearskies/configs/timezone.py +20 -0
- clearskies/configs/url.py +25 -0
- clearskies/configs/validators.py +45 -0
- clearskies/configs/writeable_model_column.py +11 -0
- clearskies/configs/writeable_model_columns.py +11 -0
- clearskies/configurable.py +78 -0
- clearskies/contexts/__init__.py +8 -8
- clearskies/contexts/cli.py +129 -43
- clearskies/contexts/context.py +93 -56
- clearskies/contexts/wsgi.py +79 -33
- clearskies/contexts/wsgi_ref.py +87 -0
- clearskies/cursors/__init__.py +7 -0
- clearskies/cursors/cursor.py +166 -0
- clearskies/cursors/from_environment/__init__.py +5 -0
- clearskies/cursors/from_environment/mysql.py +51 -0
- clearskies/cursors/from_environment/postgresql.py +49 -0
- clearskies/cursors/from_environment/sqlite.py +35 -0
- clearskies/cursors/mysql.py +61 -0
- clearskies/cursors/postgresql.py +61 -0
- clearskies/cursors/sqlite.py +62 -0
- clearskies/decorators.py +33 -0
- clearskies/decorators.pyi +10 -0
- clearskies/di/__init__.py +11 -7
- clearskies/di/additional_config.py +117 -3
- clearskies/di/additional_config_auto_import.py +12 -0
- clearskies/di/di.py +717 -126
- clearskies/di/inject/__init__.py +23 -0
- clearskies/di/inject/akeyless_sdk.py +16 -0
- clearskies/di/inject/by_class.py +24 -0
- clearskies/di/inject/by_name.py +22 -0
- clearskies/di/inject/di.py +16 -0
- clearskies/di/inject/environment.py +15 -0
- clearskies/di/inject/input_output.py +19 -0
- clearskies/di/inject/now.py +16 -0
- clearskies/di/inject/requests.py +16 -0
- clearskies/di/inject/secrets.py +15 -0
- clearskies/di/inject/utcnow.py +16 -0
- clearskies/di/inject/uuid.py +16 -0
- clearskies/di/injectable.py +32 -0
- clearskies/di/injectable_properties.py +131 -0
- clearskies/end.py +219 -0
- clearskies/endpoint.py +1303 -0
- clearskies/endpoint_group.py +333 -0
- clearskies/endpoints/__init__.py +25 -0
- clearskies/endpoints/advanced_search.py +519 -0
- clearskies/endpoints/callable.py +382 -0
- clearskies/endpoints/create.py +201 -0
- clearskies/endpoints/delete.py +133 -0
- clearskies/endpoints/get.py +267 -0
- clearskies/endpoints/health_check.py +181 -0
- clearskies/endpoints/list.py +567 -0
- clearskies/endpoints/restful_api.py +417 -0
- clearskies/endpoints/schema.py +185 -0
- clearskies/endpoints/simple_search.py +279 -0
- clearskies/endpoints/update.py +188 -0
- clearskies/environment.py +7 -3
- clearskies/exceptions/__init__.py +19 -0
- clearskies/{handlers/exceptions/input_error.py → exceptions/input_errors.py} +1 -1
- clearskies/exceptions/missing_dependency.py +2 -0
- clearskies/exceptions/moved_permanently.py +3 -0
- clearskies/exceptions/moved_temporarily.py +3 -0
- clearskies/functional/__init__.py +2 -2
- clearskies/functional/json.py +47 -0
- clearskies/functional/routing.py +92 -0
- clearskies/functional/string.py +19 -11
- clearskies/functional/validations.py +61 -9
- clearskies/input_outputs/__init__.py +9 -7
- clearskies/input_outputs/cli.py +135 -152
- clearskies/input_outputs/exceptions/__init__.py +6 -1
- clearskies/input_outputs/headers.py +54 -0
- clearskies/input_outputs/input_output.py +77 -123
- clearskies/input_outputs/programmatic.py +62 -0
- clearskies/input_outputs/wsgi.py +36 -48
- clearskies/model.py +1894 -199
- clearskies/query/__init__.py +12 -0
- clearskies/query/condition.py +228 -0
- clearskies/query/join.py +136 -0
- clearskies/query/query.py +193 -0
- clearskies/query/sort.py +27 -0
- clearskies/schema.py +82 -0
- clearskies/secrets/__init__.py +4 -31
- clearskies/secrets/additional_configs/mysql_connection_dynamic_producer.py +15 -4
- clearskies/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssh_cert_bastion.py +11 -5
- clearskies/secrets/akeyless.py +421 -155
- clearskies/secrets/exceptions/__init__.py +7 -1
- clearskies/secrets/exceptions/not_found_error.py +2 -0
- clearskies/secrets/exceptions/permissions_error.py +2 -0
- clearskies/secrets/secrets.py +12 -11
- clearskies/security_header.py +17 -0
- clearskies/security_headers/__init__.py +8 -8
- clearskies/security_headers/cache_control.py +47 -109
- clearskies/security_headers/cors.py +38 -92
- clearskies/security_headers/csp.py +76 -150
- clearskies/security_headers/hsts.py +14 -15
- clearskies/typing.py +11 -0
- clearskies/validator.py +36 -0
- clearskies/validators/__init__.py +33 -0
- clearskies/validators/after_column.py +61 -0
- clearskies/validators/before_column.py +15 -0
- clearskies/validators/in_the_future.py +29 -0
- clearskies/validators/in_the_future_at_least.py +13 -0
- clearskies/validators/in_the_future_at_most.py +12 -0
- clearskies/validators/in_the_past.py +29 -0
- clearskies/validators/in_the_past_at_least.py +12 -0
- clearskies/validators/in_the_past_at_most.py +12 -0
- clearskies/validators/maximum_length.py +25 -0
- clearskies/validators/maximum_value.py +28 -0
- clearskies/validators/minimum_length.py +25 -0
- clearskies/validators/minimum_value.py +28 -0
- clearskies/{input_requirements → validators}/required.py +18 -9
- clearskies/validators/timedelta.py +58 -0
- clearskies/validators/unique.py +28 -0
- clear_skies-1.19.22.dist-info/METADATA +0 -46
- clear_skies-1.19.22.dist-info/RECORD +0 -206
- clearskies/application.py +0 -29
- clearskies/authentication/auth0_jwks.py +0 -118
- clearskies/authentication/auth_exception.py +0 -2
- clearskies/authentication/jwks_jwcrypto.py +0 -39
- clearskies/backends/example_backend.py +0 -43
- clearskies/backends/file_backend.py +0 -48
- clearskies/backends/json_backend.py +0 -7
- clearskies/backends/restful_api_advanced_search_backend.py +0 -138
- clearskies/binding_config.py +0 -16
- clearskies/column_types/__init__.py +0 -184
- clearskies/column_types/audit.py +0 -235
- clearskies/column_types/belongs_to.py +0 -250
- clearskies/column_types/boolean.py +0 -60
- clearskies/column_types/category_tree.py +0 -226
- clearskies/column_types/column.py +0 -373
- clearskies/column_types/created.py +0 -26
- clearskies/column_types/created_by_authorization_data.py +0 -26
- clearskies/column_types/created_by_header.py +0 -24
- clearskies/column_types/created_by_ip.py +0 -17
- clearskies/column_types/created_by_routing_data.py +0 -25
- clearskies/column_types/created_by_user_agent.py +0 -17
- clearskies/column_types/created_micro.py +0 -26
- clearskies/column_types/datetime.py +0 -108
- clearskies/column_types/datetime_micro.py +0 -12
- clearskies/column_types/email.py +0 -18
- clearskies/column_types/float.py +0 -43
- clearskies/column_types/has_many.py +0 -139
- clearskies/column_types/integer.py +0 -41
- clearskies/column_types/json.py +0 -25
- clearskies/column_types/many_to_many.py +0 -278
- clearskies/column_types/many_to_many_with_data.py +0 -162
- clearskies/column_types/select.py +0 -11
- clearskies/column_types/string.py +0 -24
- clearskies/column_types/updated.py +0 -24
- clearskies/column_types/updated_micro.py +0 -24
- clearskies/column_types/uuid.py +0 -25
- clearskies/columns.py +0 -123
- clearskies/condition_parser.py +0 -172
- clearskies/contexts/build_context.py +0 -54
- clearskies/contexts/convert_to_application.py +0 -190
- clearskies/contexts/extract_handler.py +0 -37
- clearskies/contexts/test.py +0 -94
- clearskies/decorators/__init__.py +0 -39
- clearskies/decorators/auth0_jwks.py +0 -22
- clearskies/decorators/authorization.py +0 -10
- clearskies/decorators/binding_classes.py +0 -9
- clearskies/decorators/binding_modules.py +0 -9
- clearskies/decorators/bindings.py +0 -9
- clearskies/decorators/create.py +0 -10
- clearskies/decorators/delete.py +0 -10
- clearskies/decorators/docs.py +0 -14
- clearskies/decorators/get.py +0 -10
- clearskies/decorators/jwks.py +0 -26
- clearskies/decorators/merge.py +0 -124
- clearskies/decorators/patch.py +0 -10
- clearskies/decorators/post.py +0 -10
- clearskies/decorators/public.py +0 -11
- clearskies/decorators/response_headers.py +0 -10
- clearskies/decorators/return_raw_response.py +0 -9
- clearskies/decorators/schema.py +0 -10
- clearskies/decorators/secret_bearer.py +0 -24
- clearskies/decorators/security_headers.py +0 -10
- clearskies/di/standard_dependencies.py +0 -140
- clearskies/di/test_module/__init__.py +0 -6
- clearskies/di/test_module/another_module/__init__.py +0 -2
- clearskies/di/test_module/module_class.py +0 -5
- clearskies/handlers/__init__.py +0 -41
- clearskies/handlers/advanced_search.py +0 -271
- clearskies/handlers/base.py +0 -473
- clearskies/handlers/callable.py +0 -189
- clearskies/handlers/create.py +0 -35
- clearskies/handlers/crud_by_method.py +0 -18
- clearskies/handlers/database_connector.py +0 -32
- clearskies/handlers/delete.py +0 -61
- clearskies/handlers/exceptions/__init__.py +0 -5
- clearskies/handlers/exceptions/not_found.py +0 -3
- clearskies/handlers/get.py +0 -156
- clearskies/handlers/health_check.py +0 -59
- clearskies/handlers/input_processing.py +0 -79
- clearskies/handlers/list.py +0 -530
- clearskies/handlers/mygrations.py +0 -82
- clearskies/handlers/request_method_routing.py +0 -47
- clearskies/handlers/restful_api.py +0 -218
- clearskies/handlers/routing.py +0 -62
- clearskies/handlers/schema_helper.py +0 -128
- clearskies/handlers/simple_routing.py +0 -204
- clearskies/handlers/simple_routing_route.py +0 -192
- clearskies/handlers/simple_search.py +0 -136
- clearskies/handlers/update.py +0 -96
- clearskies/handlers/write.py +0 -193
- clearskies/input_requirements/__init__.py +0 -68
- clearskies/input_requirements/after.py +0 -36
- clearskies/input_requirements/before.py +0 -36
- clearskies/input_requirements/in_the_future_at_least.py +0 -19
- clearskies/input_requirements/in_the_future_at_most.py +0 -19
- clearskies/input_requirements/in_the_past_at_least.py +0 -19
- clearskies/input_requirements/in_the_past_at_most.py +0 -19
- clearskies/input_requirements/maximum_length.py +0 -19
- clearskies/input_requirements/minimum_length.py +0 -22
- clearskies/input_requirements/requirement.py +0 -25
- clearskies/input_requirements/time_delta.py +0 -38
- clearskies/input_requirements/unique.py +0 -18
- clearskies/mocks/__init__.py +0 -7
- clearskies/mocks/input_output.py +0 -124
- clearskies/mocks/models.py +0 -142
- clearskies/models.py +0 -345
- clearskies/security_headers/base.py +0 -12
- clearskies/tests/simple_api/models/__init__.py +0 -2
- clearskies/tests/simple_api/models/status.py +0 -23
- clearskies/tests/simple_api/models/user.py +0 -21
- clearskies/tests/simple_api/users_api.py +0 -64
- {clear_skies-1.19.22.dist-info → clear_skies-2.0.23.dist-info/licenses}/LICENSE +0 -0
- /clearskies/{contexts/bash.py → autodoc/py.typed} +0 -0
- /clearskies/{handlers/exceptions → exceptions}/authentication.py +0 -0
- /clearskies/{handlers/exceptions → exceptions}/authorization.py +0 -0
- /clearskies/{handlers/exceptions → exceptions}/client_error.py +0 -0
- /clearskies/{secrets/exceptions → exceptions}/not_found.py +0 -0
- /clearskies/{tests/__init__.py → input_outputs/py.typed} +0 -0
- /clearskies/{tests/simple_api/__init__.py → py.typed} +0 -0
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
from .audit import Audit
|
|
2
|
-
from .belongs_to import BelongsTo
|
|
3
|
-
from .boolean import Boolean
|
|
4
|
-
from .category_tree import CategoryTree
|
|
5
|
-
from .column import Column
|
|
6
|
-
from .created import Created
|
|
7
|
-
from .created_by_authorization_data import CreatedByAuthorizationData
|
|
8
|
-
from .created_by_header import CreatedByHeader
|
|
9
|
-
from .created_by_ip import CreatedByIp
|
|
10
|
-
from .created_by_routing_data import CreatedByRoutingData
|
|
11
|
-
from .created_by_user_agent import CreatedByUserAgent
|
|
12
|
-
from .created_micro import CreatedMicro
|
|
13
|
-
from .datetime import DateTime
|
|
14
|
-
from .datetime_micro import DateTimeMicro
|
|
15
|
-
from .email import Email
|
|
16
|
-
from .float import Float
|
|
17
|
-
from .has_many import HasMany
|
|
18
|
-
from .integer import Integer
|
|
19
|
-
from .json import JSON
|
|
20
|
-
from .many_to_many import ManyToMany
|
|
21
|
-
from .many_to_many_with_data import ManyToManyWithData
|
|
22
|
-
from .select import Select
|
|
23
|
-
from .string import String
|
|
24
|
-
from .updated import Updated
|
|
25
|
-
from .updated_micro import UpdatedMicro
|
|
26
|
-
from .uuid import UUID
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def build_column_config(name, column_class, **kwargs):
|
|
30
|
-
return (name, {**{"class": column_class}, **kwargs})
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def audit(name, **kwargs):
|
|
34
|
-
return build_column_config(name, Audit, **kwargs)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def belongs_to(name, **kwargs):
|
|
38
|
-
return build_column_config(name, BelongsTo, **kwargs)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def boolean(name, **kwargs):
|
|
42
|
-
return build_column_config(name, Boolean, **kwargs)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def category_tree(name, **kwargs):
|
|
46
|
-
return build_column_config(name, CategoryTree, **kwargs)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def created(name, **kwargs):
|
|
50
|
-
return build_column_config(name, Created, **kwargs)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def created_by_authorization_data(name, **kwargs):
|
|
54
|
-
return build_column_config(name, CreatedByAuthorizationData, **kwargs)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def created_by_header(name, **kwargs):
|
|
58
|
-
return build_column_config(name, CreatedByHeader, **kwargs)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def created_by_ip(name, **kwargs):
|
|
62
|
-
return build_column_config(name, CreatedByIp, **kwargs)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def created_by_routing_data(name, **kwargs):
|
|
66
|
-
return build_column_config(name, CreatedByRoutingData, **kwargs)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def created_by_user_agent(name, **kwargs):
|
|
70
|
-
return build_column_config(name, CreatedByUserAgent, **kwargs)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
def created_micro(name, **kwargs):
|
|
74
|
-
return build_column_config(name, CreatedMicro, **kwargs)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
def datetime(name, **kwargs):
|
|
78
|
-
return build_column_config(name, DateTime, **kwargs)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
def datetime_micro(name, **kwargs):
|
|
82
|
-
return build_column_config(name, DateTimeMicro, **kwargs)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def email(name, **kwargs):
|
|
86
|
-
return build_column_config(name, Email, **kwargs)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def float(name, **kwargs):
|
|
90
|
-
return build_column_config(name, Float, **kwargs)
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
def has_many(name, **kwargs):
|
|
94
|
-
return build_column_config(name, HasMany, **kwargs)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
def integer(name, **kwargs):
|
|
98
|
-
return build_column_config(name, Integer, **kwargs)
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
def json(name, **kwargs):
|
|
102
|
-
return build_column_config(name, JSON, **kwargs)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
def many_to_many(name, **kwargs):
|
|
106
|
-
return build_column_config(name, ManyToMany, **kwargs)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
def many_to_many_with_data(name, **kwargs):
|
|
110
|
-
return build_column_config(name, ManyToManyWithData, **kwargs)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
def select(name, **kwargs):
|
|
114
|
-
return build_column_config(name, Select, **kwargs)
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
def string(name, **kwargs):
|
|
118
|
-
return build_column_config(name, String, **kwargs)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
def updated(name, **kwargs):
|
|
122
|
-
return build_column_config(name, Updated, **kwargs)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
def updated_micro(name, **kwargs):
|
|
126
|
-
return build_column_config(name, UpdatedMicro, **kwargs)
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
def uuid(name, **kwargs):
|
|
130
|
-
return build_column_config(name, UUID, **kwargs)
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
__all__ = [
|
|
134
|
-
"build_column_config",
|
|
135
|
-
"audit",
|
|
136
|
-
"Audit",
|
|
137
|
-
"belongs_to",
|
|
138
|
-
"BelongsTo",
|
|
139
|
-
"boolean",
|
|
140
|
-
"Boolean",
|
|
141
|
-
"category_tree",
|
|
142
|
-
"CategoryTree",
|
|
143
|
-
"Column",
|
|
144
|
-
"created",
|
|
145
|
-
"created_micro",
|
|
146
|
-
"Created",
|
|
147
|
-
"CreatdMicro",
|
|
148
|
-
"created_by_authorization_data",
|
|
149
|
-
"CreatedByAuthorizationData",
|
|
150
|
-
"created_by_ip",
|
|
151
|
-
"CreatedByIp",
|
|
152
|
-
"created_by_user_agent",
|
|
153
|
-
"CreatedByUserAgent",
|
|
154
|
-
"CreatedMicro",
|
|
155
|
-
"created_micro",
|
|
156
|
-
"datetime",
|
|
157
|
-
"datetime_micro",
|
|
158
|
-
"DateTime",
|
|
159
|
-
"DateTimeMicro",
|
|
160
|
-
"email",
|
|
161
|
-
"Email",
|
|
162
|
-
"float",
|
|
163
|
-
"Float",
|
|
164
|
-
"has_many",
|
|
165
|
-
"HasMany",
|
|
166
|
-
"integer",
|
|
167
|
-
"Integer",
|
|
168
|
-
"json",
|
|
169
|
-
"JSON",
|
|
170
|
-
"many_to_many",
|
|
171
|
-
"ManyToMany",
|
|
172
|
-
"many_to_many_with_data",
|
|
173
|
-
"ManyToManyWithData",
|
|
174
|
-
"select",
|
|
175
|
-
"Select",
|
|
176
|
-
"string",
|
|
177
|
-
"String",
|
|
178
|
-
"updated",
|
|
179
|
-
"updated_micro",
|
|
180
|
-
"Updated",
|
|
181
|
-
"UpdatedMicro",
|
|
182
|
-
"uuid",
|
|
183
|
-
"UUID",
|
|
184
|
-
]
|
clearskies/column_types/audit.py
DELETED
|
@@ -1,235 +0,0 @@
|
|
|
1
|
-
from . import string, has_many
|
|
2
|
-
from clearskies.functional.string import title_case_to_snake_case
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class Audit(has_many.HasMany):
|
|
6
|
-
"""
|
|
7
|
-
Enables auditing for a model.
|
|
8
|
-
|
|
9
|
-
Specif the audit class to use and attach this to your model. Everytime the model is created/updated/deleted,
|
|
10
|
-
the audit class will record the action and the changes. Your audit model must have the following columns:
|
|
11
|
-
|
|
12
|
-
| Name | type |
|
|
13
|
-
|-------------|----------|
|
|
14
|
-
| class | str |
|
|
15
|
-
| resource_id | str |
|
|
16
|
-
| action | str |
|
|
17
|
-
| data | json |
|
|
18
|
-
| created_at | created |
|
|
19
|
-
|
|
20
|
-
The names are not currently adjustable.
|
|
21
|
-
|
|
22
|
-
1. Class is a string that records the name of the class that the action happened for. This allows you to use
|
|
23
|
-
the same audit class for multiple, different, resources.
|
|
24
|
-
2. resource_id is the id of the record which the audit entry is for.
|
|
25
|
-
3. Action is the actual action taken (create/update/delete)
|
|
26
|
-
4. Data is a serialized record of what columns in the record were changed (both their previous and new values)
|
|
27
|
-
5. The time the audit record was created
|
|
28
|
-
|
|
29
|
-
With `exclude_columns` you can specify some names of columns to ignore. If an update happens and only columns
|
|
30
|
-
in `exclude_columns` are being set, then a history entry will not be created. Also, these columns will
|
|
31
|
-
not be included in the audit record.
|
|
32
|
-
|
|
33
|
-
With `mask_columns` you can specify the names of columns which should be noted as updated in the audit record,
|
|
34
|
-
but the actual values (before and after) should not be recorded.
|
|
35
|
-
"""
|
|
36
|
-
|
|
37
|
-
_parent_columns = None
|
|
38
|
-
|
|
39
|
-
required_configs = [
|
|
40
|
-
"audit_models_class",
|
|
41
|
-
]
|
|
42
|
-
|
|
43
|
-
my_configs = [
|
|
44
|
-
"child_models_class",
|
|
45
|
-
"exclude_columns",
|
|
46
|
-
"mask_columns",
|
|
47
|
-
"foreign_column_name",
|
|
48
|
-
"is_readable",
|
|
49
|
-
"readable_child_columns",
|
|
50
|
-
"parent_class_name",
|
|
51
|
-
"parent_id_column_name",
|
|
52
|
-
]
|
|
53
|
-
|
|
54
|
-
def __init__(self, di):
|
|
55
|
-
super().__init__(di)
|
|
56
|
-
|
|
57
|
-
def configure(self, name, configuration, model_class):
|
|
58
|
-
if "audit_models_class" not in configuration:
|
|
59
|
-
raise KeyError(
|
|
60
|
-
"Missing required configuration value 'audit_models_class' for column '{name}' in model class "
|
|
61
|
-
+ f"'{model_class.__name__}'"
|
|
62
|
-
)
|
|
63
|
-
self.validate_models_class(configuration["audit_models_class"])
|
|
64
|
-
has_many_configuration = {
|
|
65
|
-
**configuration,
|
|
66
|
-
"child_models_class": configuration.get("audit_models_class"),
|
|
67
|
-
"foreign_column_name": "resource_id",
|
|
68
|
-
"is_readable": True,
|
|
69
|
-
"readable_child_columns": ["resource_id", "action", "data", "created_at"],
|
|
70
|
-
"parent_class_name": model_class.__name__,
|
|
71
|
-
"exclude_columns": configuration.get("exclude_columns", []),
|
|
72
|
-
"mask_columns": configuration.get("mask_columns", []),
|
|
73
|
-
}
|
|
74
|
-
super().configure(name, has_many_configuration, model_class)
|
|
75
|
-
|
|
76
|
-
def _check_configuration(self, configuration):
|
|
77
|
-
super()._check_configuration(configuration)
|
|
78
|
-
error_prefix = f"Configuration error for '{self.name}' in '{self.model_class.__name__}':"
|
|
79
|
-
audit_columns = self.di.build(configuration["audit_models_class"], cache=True).raw_columns_configuration()
|
|
80
|
-
parent_columns = self.di.build(self.model_class, cache=True).raw_columns_configuration()
|
|
81
|
-
required_audit_columns = {
|
|
82
|
-
"class": string.String,
|
|
83
|
-
"resource_id": True,
|
|
84
|
-
"action": string.String,
|
|
85
|
-
"data": True,
|
|
86
|
-
"created_at": True,
|
|
87
|
-
}
|
|
88
|
-
for column_name, column_type in required_audit_columns.items():
|
|
89
|
-
if column_name not in audit_columns:
|
|
90
|
-
raise ValueError(f"{error_prefix} audit models class does not have the required column '{column_name}'")
|
|
91
|
-
if column_type == True:
|
|
92
|
-
continue
|
|
93
|
-
if audit_columns[column_name]["class"] != column_type:
|
|
94
|
-
raise ValueError(
|
|
95
|
-
f"{error_prefix} the '{column_name}' column in the audit models class should have a type of "
|
|
96
|
-
+ column_type.__name__
|
|
97
|
-
+ " but it has something else"
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
for config_name in ["exclude_columns", "mask_columns"]:
|
|
101
|
-
if config_name not in configuration:
|
|
102
|
-
continue
|
|
103
|
-
|
|
104
|
-
config_columns = configuration[config_name]
|
|
105
|
-
if not hasattr(config_columns, "__iter__"):
|
|
106
|
-
raise ValueError(f"{error_prefix} '{config_name}' should be an iterable with the list of column names.")
|
|
107
|
-
if isinstance(config_columns, str):
|
|
108
|
-
raise ValueError(
|
|
109
|
-
f"{error_prefix} '{config_name}' should be an iterable " + "with a list of column names."
|
|
110
|
-
)
|
|
111
|
-
for column_name in config_columns:
|
|
112
|
-
if column_name not in parent_columns:
|
|
113
|
-
raise ValueError(
|
|
114
|
-
f"{error_prefix} '{config_name}' references column named '{column_name}' but this"
|
|
115
|
-
+ " column does not exist in the original model class."
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
def provide(self, data, column_name):
|
|
119
|
-
return super().provide(data, column_name).where("class=" + self.config("parent_class_name"))
|
|
120
|
-
|
|
121
|
-
def save_finished(self, model):
|
|
122
|
-
super().save_finished(model)
|
|
123
|
-
old_data = model._previous_data
|
|
124
|
-
new_data = model._data
|
|
125
|
-
exclude_columns = self.config("exclude_columns")
|
|
126
|
-
mask_columns = self.config("mask_columns")
|
|
127
|
-
parent_columns = self.parent_columns
|
|
128
|
-
|
|
129
|
-
if not old_data:
|
|
130
|
-
create_data = {}
|
|
131
|
-
for key in new_data.keys():
|
|
132
|
-
if key in exclude_columns:
|
|
133
|
-
continue
|
|
134
|
-
create_data = {
|
|
135
|
-
**create_data,
|
|
136
|
-
**parent_columns[key].to_json(model),
|
|
137
|
-
}
|
|
138
|
-
if key in mask_columns and key in create_data:
|
|
139
|
-
create_data[key] = "****"
|
|
140
|
-
self.record(model, "create", data=create_data)
|
|
141
|
-
return
|
|
142
|
-
|
|
143
|
-
# note that this is fairly simple logic to get started. It's not going to detect changes that happen
|
|
144
|
-
# in other "tables". For instance, disconnecting a record by deleting an entry in a many-to-many relationship
|
|
145
|
-
# won't be picked up by this.
|
|
146
|
-
old_model = model.empty_model()
|
|
147
|
-
old_model.data = old_data
|
|
148
|
-
from_data = {}
|
|
149
|
-
to_data = {}
|
|
150
|
-
for column, new_value in new_data.items():
|
|
151
|
-
if column in exclude_columns or column not in old_data:
|
|
152
|
-
continue
|
|
153
|
-
if old_data[column] == new_value:
|
|
154
|
-
continue
|
|
155
|
-
from_data = {
|
|
156
|
-
**from_data,
|
|
157
|
-
**parent_columns[column].to_json(old_model),
|
|
158
|
-
}
|
|
159
|
-
to_data = {
|
|
160
|
-
**to_data,
|
|
161
|
-
**parent_columns[column].to_json(model),
|
|
162
|
-
}
|
|
163
|
-
if column in mask_columns and column in to_data:
|
|
164
|
-
to_data[column] = "****"
|
|
165
|
-
from_data[column] = "****"
|
|
166
|
-
if not from_data and not to_data:
|
|
167
|
-
return
|
|
168
|
-
|
|
169
|
-
self.record(
|
|
170
|
-
model,
|
|
171
|
-
"update",
|
|
172
|
-
data={
|
|
173
|
-
"from": from_data,
|
|
174
|
-
"to": to_data,
|
|
175
|
-
},
|
|
176
|
-
)
|
|
177
|
-
|
|
178
|
-
def post_delete(self, model):
|
|
179
|
-
super().post_delete(model)
|
|
180
|
-
exclude_columns = self.config("exclude_columns")
|
|
181
|
-
parent_columns = self.parent_columns
|
|
182
|
-
mask_columns = self.config("mask_columns")
|
|
183
|
-
|
|
184
|
-
final_data = {}
|
|
185
|
-
for key in model._data.keys():
|
|
186
|
-
if key in exclude_columns:
|
|
187
|
-
continue
|
|
188
|
-
final_data = {
|
|
189
|
-
**final_data,
|
|
190
|
-
**parent_columns[key].to_json(model),
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
for key in mask_columns:
|
|
194
|
-
if key not in final_data:
|
|
195
|
-
continue
|
|
196
|
-
final_data[key] = "****"
|
|
197
|
-
|
|
198
|
-
self.child_models.create(
|
|
199
|
-
{
|
|
200
|
-
"class": self.config("parent_class_name"),
|
|
201
|
-
"resource_id": model.get(self.config("parent_id_column_name")),
|
|
202
|
-
"action": "delete",
|
|
203
|
-
"data": final_data,
|
|
204
|
-
}
|
|
205
|
-
)
|
|
206
|
-
|
|
207
|
-
@property
|
|
208
|
-
def parent_columns(self):
|
|
209
|
-
if self._parent_columns == None:
|
|
210
|
-
self._parent_columns = self.di.build(self.model_class, cache=True).columns()
|
|
211
|
-
return self._parent_columns
|
|
212
|
-
|
|
213
|
-
def record(self, model, action, data=None, record_data=None):
|
|
214
|
-
audit_data = {
|
|
215
|
-
"class": self.config("parent_class_name"),
|
|
216
|
-
"resource_id": model.get(self.config("parent_id_column_name")),
|
|
217
|
-
"action": action,
|
|
218
|
-
}
|
|
219
|
-
if data is not None:
|
|
220
|
-
audit_data["data"] = data
|
|
221
|
-
if record_data is not None:
|
|
222
|
-
audit_data = {
|
|
223
|
-
**audit_data,
|
|
224
|
-
**record_data,
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
self.child_models.create(audit_data)
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
def build_column_config(name, column_class, **kwargs):
|
|
231
|
-
return (name, {**{"class": column_class}, **kwargs})
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
def audit(name, **kwargs):
|
|
235
|
-
return build_column_config(name, Audit, **kwargs)
|
|
@@ -1,250 +0,0 @@
|
|
|
1
|
-
import re
|
|
2
|
-
from .string import String
|
|
3
|
-
from ..autodoc.schema import Array as AutoDocArray
|
|
4
|
-
from ..autodoc.schema import Object as AutoDocObject
|
|
5
|
-
from ..autodoc.schema import String as AutoDocString
|
|
6
|
-
from collections import OrderedDict
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class BelongsTo(String):
|
|
10
|
-
"""
|
|
11
|
-
Controls a belongs to relationship.
|
|
12
|
-
|
|
13
|
-
This column should be named something like 'parent_id', e.g. user_id, column_id, etc... It expects the actual
|
|
14
|
-
database column to be an integer. It also provides an additional property on the model which returns the
|
|
15
|
-
related model, instead of the id, with a name given by dropping `_id` from the column name. In other words,
|
|
16
|
-
if you have a column called user_id and a particular model has a user_id of 5, then:
|
|
17
|
-
|
|
18
|
-
```
|
|
19
|
-
print(model.user_id)
|
|
20
|
-
# prints 5
|
|
21
|
-
print(model.user.id)
|
|
22
|
-
# prints 5
|
|
23
|
-
print(model.user.name)
|
|
24
|
-
# prints the name of the user with an id of 5.
|
|
25
|
-
```
|
|
26
|
-
"""
|
|
27
|
-
|
|
28
|
-
wants_n_plus_one = True
|
|
29
|
-
required_configs = [
|
|
30
|
-
"parent_models_class",
|
|
31
|
-
]
|
|
32
|
-
|
|
33
|
-
my_configs = [
|
|
34
|
-
"model_column_name",
|
|
35
|
-
"readable_parent_columns",
|
|
36
|
-
"join_type",
|
|
37
|
-
]
|
|
38
|
-
|
|
39
|
-
def __init__(self, di):
|
|
40
|
-
super().__init__(di)
|
|
41
|
-
|
|
42
|
-
def _check_configuration(self, configuration):
|
|
43
|
-
super()._check_configuration(configuration)
|
|
44
|
-
self.validate_models_class(configuration["parent_models_class"])
|
|
45
|
-
|
|
46
|
-
if not configuration.get("model_column_name") and self.name[-3:] != "_id":
|
|
47
|
-
raise ValueError(
|
|
48
|
-
f"Invalid name for column '{self.name}' in '{self.model_class.__name__}' - "
|
|
49
|
-
+ "BelongsTo column names must end in '_id', or you must set 'model_column_name' to specify the name of the column "
|
|
50
|
-
+ "that the parent model can be fetched from."
|
|
51
|
-
)
|
|
52
|
-
if configuration.get("model_column_name") and type(configuration.get("model_column_name")) != str:
|
|
53
|
-
raise ValueError(
|
|
54
|
-
f"Configuration error for '{self.name}' in '{self.model_class.__name__}': 'model_column_name' must be a string."
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
join_type = configuration.get("join_type")
|
|
58
|
-
if join_type and join_type.upper() not in ["LEFT", "INNER"]:
|
|
59
|
-
raise ValueError(
|
|
60
|
-
f"Configuration error for '{self.name}' in '{self.model_class.__name__}': join_type must be INNER or LEFT"
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
if configuration.get("readable_parent_columns"):
|
|
64
|
-
parent_columns = self.di.build(configuration["parent_models_class"], cache=True).raw_columns_configuration()
|
|
65
|
-
error_prefix = f"Configuration error for '{self.name}' in '{self.model_class.__name__}':"
|
|
66
|
-
readable_parent_columns = configuration["readable_parent_columns"]
|
|
67
|
-
if not hasattr(readable_parent_columns, "__iter__"):
|
|
68
|
-
raise ValueError(
|
|
69
|
-
f"{error_prefix} 'readable_parent_columns' should be an iterable "
|
|
70
|
-
+ "with the list of child columns to output."
|
|
71
|
-
)
|
|
72
|
-
if isinstance(readable_parent_columns, str):
|
|
73
|
-
raise ValueError(
|
|
74
|
-
f"{error_prefix} 'readable_parent_columns' should be an iterable "
|
|
75
|
-
+ "with the list of child columns to output."
|
|
76
|
-
)
|
|
77
|
-
for column_name in readable_parent_columns:
|
|
78
|
-
if column_name not in parent_columns:
|
|
79
|
-
raise ValueError(
|
|
80
|
-
f"{error_prefix} 'readable_parent_columns' references column named '{column_name}' but this"
|
|
81
|
-
+ "column does not exist in the model class."
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
def _finalize_configuration(self, configuration):
|
|
85
|
-
return {
|
|
86
|
-
**super()._finalize_configuration(configuration),
|
|
87
|
-
**{
|
|
88
|
-
"model_column_name": configuration.get("model_column_name")
|
|
89
|
-
if configuration.get("model_column_name")
|
|
90
|
-
else self.name[:-3],
|
|
91
|
-
"join_type": configuration.get("join_type", "INNER").upper(),
|
|
92
|
-
},
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
def input_error_for_value(self, value, operator=None):
|
|
96
|
-
integer_check = super().input_error_for_value(value)
|
|
97
|
-
if integer_check:
|
|
98
|
-
return integer_check
|
|
99
|
-
parent_models = self.parent_models
|
|
100
|
-
id_column_name = parent_models.get_id_column_name()
|
|
101
|
-
matching_parents = parent_models.where(f"{id_column_name}={value}")
|
|
102
|
-
input_output = self.di.build("input_output", cache=True)
|
|
103
|
-
matching_parents = matching_parents.where_for_request(
|
|
104
|
-
matching_parents,
|
|
105
|
-
input_output.routing_data(),
|
|
106
|
-
input_output.get_authorization_data(),
|
|
107
|
-
input_output,
|
|
108
|
-
)
|
|
109
|
-
if not len(matching_parents):
|
|
110
|
-
return f"Invalid selection for {self.name}: record does not exist"
|
|
111
|
-
return ""
|
|
112
|
-
|
|
113
|
-
def can_provide(self, column_name):
|
|
114
|
-
return column_name == self.config("model_column_name")
|
|
115
|
-
|
|
116
|
-
def provide(self, data, column_name):
|
|
117
|
-
# did we have data parent data loaded up with a query?
|
|
118
|
-
alias = self.join_table_alias()
|
|
119
|
-
parent_id_column_name = self.parent_models.get_id_column_name()
|
|
120
|
-
if f"{alias}_{parent_id_column_name}" in data:
|
|
121
|
-
parent_data = {parent_id_column_name: data[f"{alias}_{parent_id_column_name}"]}
|
|
122
|
-
for column_name in self.parent_columns.keys():
|
|
123
|
-
select_alias = f"{alias}_{column_name}"
|
|
124
|
-
parent_data[column_name] = data[select_alias] if select_alias in data else None
|
|
125
|
-
return self.parent_models.model(parent_data)
|
|
126
|
-
|
|
127
|
-
# if not, just look it up from the id
|
|
128
|
-
parent_id = data.get(self.name)
|
|
129
|
-
if parent_id:
|
|
130
|
-
parent_id_column_name = self.parent_models.get_id_column_name()
|
|
131
|
-
return self.parent_models.where(f"{parent_id_column_name}={parent_id}").first()
|
|
132
|
-
return self.parent_models.empty_model()
|
|
133
|
-
|
|
134
|
-
def join_table_alias(self):
|
|
135
|
-
return self.parent_models.table_name() + "_" + self.name
|
|
136
|
-
|
|
137
|
-
def configure_n_plus_one(self, models, columns=None):
|
|
138
|
-
if columns is None:
|
|
139
|
-
columns = self.config("readable_parent_columns", silent=True)
|
|
140
|
-
if not columns:
|
|
141
|
-
return models
|
|
142
|
-
|
|
143
|
-
models = self.add_join(models)
|
|
144
|
-
alias = self.join_table_alias()
|
|
145
|
-
parent_id_column_name = self.parent_models.get_id_column_name()
|
|
146
|
-
select_parts = [f"{alias}.{column_name} AS {alias}_{column_name}" for column_name in columns]
|
|
147
|
-
select_parts.append(f"{alias}.{parent_id_column_name} AS {alias}_{parent_id_column_name}")
|
|
148
|
-
return models.select(", ".join(select_parts))
|
|
149
|
-
|
|
150
|
-
@property
|
|
151
|
-
def parent_models(self):
|
|
152
|
-
return self.di.build(self.config("parent_models_class"), cache=True)
|
|
153
|
-
|
|
154
|
-
@property
|
|
155
|
-
def parent_columns(self):
|
|
156
|
-
return self.parent_models.model_columns
|
|
157
|
-
|
|
158
|
-
def to_json(self, model):
|
|
159
|
-
# if we don't have readable parent columns specified, then just return the id
|
|
160
|
-
if not self.config("readable_parent_columns", silent=True):
|
|
161
|
-
return super().to_json(model)
|
|
162
|
-
|
|
163
|
-
# otherwise return an object with the readable parent columns
|
|
164
|
-
columns = self.parent_columns
|
|
165
|
-
parent = model.__getattr__(self.config("model_column_name"))
|
|
166
|
-
json = OrderedDict()
|
|
167
|
-
if parent.id_column_name not in self.config("readable_parent_columns"):
|
|
168
|
-
json[parent.id_column_name] = list(columns[parent.id_column_name].to_json(parent).values())[0]
|
|
169
|
-
for column_name in self.config("readable_parent_columns"):
|
|
170
|
-
json = {**json, **columns[column_name].to_json(parent)}
|
|
171
|
-
id_less_name = self.config("model_column_name")
|
|
172
|
-
return {
|
|
173
|
-
**super().to_json(model),
|
|
174
|
-
id_less_name: json,
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
def documentation(self, name=None, example=None, value=None):
|
|
178
|
-
columns = self.parent_columns
|
|
179
|
-
parent_id_column_name = self.parent_models.get_id_column_name()
|
|
180
|
-
parent_properties = [columns[parent_id_column_name].documentation()]
|
|
181
|
-
|
|
182
|
-
parent_columns = self.config("readable_parent_columns", silent=True)
|
|
183
|
-
parent_id_doc = AutoDocString(name if name is not None else self.name)
|
|
184
|
-
if not parent_columns:
|
|
185
|
-
return parent_id_doc
|
|
186
|
-
|
|
187
|
-
for column_name in self.config("readable_parent_columns"):
|
|
188
|
-
if column_name == parent_id_column_name:
|
|
189
|
-
continue
|
|
190
|
-
parent_properties.append(columns[column_name].documentation())
|
|
191
|
-
|
|
192
|
-
return [
|
|
193
|
-
parent_id_doc,
|
|
194
|
-
AutoDocObject(
|
|
195
|
-
self.config("model_column_name"),
|
|
196
|
-
parent_properties,
|
|
197
|
-
),
|
|
198
|
-
]
|
|
199
|
-
|
|
200
|
-
def is_allowed_operator(self, operator, relationship_reference=None):
|
|
201
|
-
"""
|
|
202
|
-
This is called when processing user data to decide if the end-user is specifying an allowed operator
|
|
203
|
-
"""
|
|
204
|
-
if not relationship_reference:
|
|
205
|
-
return "="
|
|
206
|
-
parent_columns = self.parent_columns
|
|
207
|
-
if relationship_reference not in self.parent_columns:
|
|
208
|
-
raise ValueError(
|
|
209
|
-
"I was asked to search on a related column that doens't exist. This shouldn't have happened :("
|
|
210
|
-
)
|
|
211
|
-
return self.parent_columns[relationship_reference].is_allowed_operator(operator)
|
|
212
|
-
|
|
213
|
-
def check_search_value(self, value, operator=None, relationship_reference=None):
|
|
214
|
-
if not relationship_reference:
|
|
215
|
-
return self.input_error_for_value(value, operator=operator)
|
|
216
|
-
parent_columns = self.parent_columns
|
|
217
|
-
if relationship_reference not in self.parent_columns:
|
|
218
|
-
raise ValueError(
|
|
219
|
-
"I was asked to search on a related column that doens't exist. This shouldn't have happened :("
|
|
220
|
-
)
|
|
221
|
-
return self.parent_columns[relationship_reference].check_search_value(value, operator=operator)
|
|
222
|
-
|
|
223
|
-
def add_join(self, models):
|
|
224
|
-
parent_table = self.parent_models.table_name()
|
|
225
|
-
alias = self.join_table_alias()
|
|
226
|
-
|
|
227
|
-
if models.is_joined(parent_table, alias=alias):
|
|
228
|
-
return models
|
|
229
|
-
|
|
230
|
-
join_type = "LEFT " if self.config("join_type") == "LEFT" else ""
|
|
231
|
-
own_table_name = models.table_name()
|
|
232
|
-
parent_id_column_name = self.parent_models.get_id_column_name()
|
|
233
|
-
return models.join(
|
|
234
|
-
f"{join_type}JOIN {parent_table} as {alias} on {alias}.{parent_id_column_name}={own_table_name}.{self.name}"
|
|
235
|
-
)
|
|
236
|
-
|
|
237
|
-
def add_search(self, models, value, operator=None, relationship_reference=None):
|
|
238
|
-
if not relationship_reference:
|
|
239
|
-
return super().add_search(models, value, operator=operator)
|
|
240
|
-
|
|
241
|
-
parent_columns = self.parent_columns
|
|
242
|
-
if relationship_reference not in self.parent_columns:
|
|
243
|
-
raise ValueError(
|
|
244
|
-
"I was asked to search on a related column that doens't exist. This shouldn't have happened :("
|
|
245
|
-
)
|
|
246
|
-
|
|
247
|
-
models = self.add_join(models)
|
|
248
|
-
related_column = self.parent_columns[relationship_reference]
|
|
249
|
-
alias = self.join_table_alias()
|
|
250
|
-
return models.where(related_column.build_condition(value, operator=operator, column_prefix=f"{alias}."))
|