educommon 3.12.0__py3-none-any.whl → 3.13.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.
- educommon/__init__.py +0 -1
- educommon/about/ui/actions.py +16 -30
- educommon/about/ui/ui.py +3 -12
- educommon/about/utils.py +6 -5
- educommon/async_task/__init__.py +0 -1
- educommon/async_task/actions.py +18 -13
- educommon/async_task/apps.py +4 -0
- educommon/async_task/locker.py +2 -5
- educommon/async_task/migrations/0001_initial.py +55 -9
- educommon/async_task/migrations/0002_task_type_and_status_data.py +94 -89
- educommon/async_task/migrations/0003_alter_runningtask_options.py +0 -1
- educommon/async_task/models.py +9 -6
- educommon/async_task/tasks.py +11 -7
- educommon/async_task/ui.py +16 -35
- educommon/async_tasks/__init__.py +0 -1
- educommon/async_tasks/apps.py +4 -0
- educommon/async_tasks/locks.py +11 -21
- educommon/async_tasks/migrations/0001_initial.py +68 -8
- educommon/async_tasks/migrations/0002_load_initial_data.py +0 -1
- educommon/async_tasks/models.py +9 -29
- educommon/async_tasks/tasks.py +25 -54
- educommon/audit_log/__init__.py +1 -0
- educommon/audit_log/actions.py +27 -36
- educommon/audit_log/app_meta.py +7 -4
- educommon/audit_log/apps.py +44 -29
- educommon/audit_log/constants.py +7 -4
- educommon/audit_log/error_log/actions.py +1 -3
- educommon/audit_log/helpers.py +2 -4
- educommon/audit_log/management/commands/reinstall_audit_log.py +11 -7
- educommon/audit_log/migrations/0001_initial.py +91 -16
- educommon/audit_log/migrations/0002_install_audit_log.py +13 -13
- educommon/audit_log/migrations/0003_logproxy.py +1 -3
- educommon/audit_log/migrations/0004_reinstall_audit_log.py +1 -4
- educommon/audit_log/migrations/0005_postgresql_error.py +4 -2
- educommon/audit_log/migrations/0006_auto_20200806_1707.py +3 -4
- educommon/audit_log/migrations/0007_create_selective_tables_function.py +8 -5
- educommon/audit_log/migrations/0008_table_logged.py +0 -1
- educommon/audit_log/migrations/0009_reinstall_audit_log.py +0 -1
- educommon/audit_log/models.py +36 -42
- educommon/audit_log/permissions.py +11 -9
- educommon/audit_log/proxies.py +12 -23
- educommon/audit_log/ui.py +18 -15
- educommon/audit_log/utils/__init__.py +28 -60
- educommon/audit_log/utils/operations.py +16 -2
- educommon/auth/__init__.py +0 -3
- educommon/auth/rbac/__init__.py +2 -4
- educommon/auth/rbac/actions.py +148 -145
- educommon/auth/rbac/app_meta.py +9 -6
- educommon/auth/rbac/backends/base.py +2 -8
- educommon/auth/rbac/backends/caching.py +27 -37
- educommon/auth/rbac/backends/simple.py +1 -4
- educommon/auth/rbac/checker.py +1 -3
- educommon/auth/rbac/management/commands/rbac.py +6 -11
- educommon/auth/rbac/manager.py +18 -47
- educommon/auth/rbac/migrations/0001_initial.py +73 -12
- educommon/auth/rbac/migrations/0002_model_modifier_metaclass_fix.py +7 -6
- educommon/auth/rbac/migrations/0003_permission_hidden.py +1 -5
- educommon/auth/rbac/migrations/0004_auto_20171024_1245.py +26 -19
- educommon/auth/rbac/models.py +63 -68
- educommon/auth/rbac/permissions.py +6 -7
- educommon/auth/rbac/ui.py +83 -84
- educommon/auth/rbac/utils.py +10 -11
- educommon/auth/rbac/validators.py +4 -5
- educommon/auth/simple_auth/__init__.py +1 -5
- educommon/auth/simple_auth/actions.py +79 -92
- educommon/auth/simple_auth/app_meta.py +2 -9
- educommon/auth/simple_auth/checkers.py +3 -3
- educommon/auth/simple_auth/migrations/0001_initial.py +23 -4
- educommon/auth/simple_auth/validators.py +0 -1
- educommon/contingent/actions.py +7 -7
- educommon/contingent/app_meta.py +1 -4
- educommon/contingent/base.py +10 -15
- educommon/contingent/catalogs.py +424 -540
- educommon/contingent/contingent_plugin/actions.py +4 -15
- educommon/contingent/contingent_plugin/apps.py +10 -4
- educommon/contingent/contingent_plugin/migrations/0001_initial.py +5 -6
- educommon/contingent/contingent_plugin/migrations/0002_add_contingent_model_deleted.py +6 -11
- educommon/contingent/contingent_plugin/model_views.py +2 -12
- educommon/contingent/contingent_plugin/models.py +2 -7
- educommon/contingent/contingent_plugin/observer.py +14 -13
- educommon/contingent/contingent_plugin/plugin_meta.py +1 -3
- educommon/contingent/contingent_plugin/storage.py +8 -7
- educommon/contingent/contingent_plugin/utils.py +6 -6
- educommon/django/db/fields.py +72 -86
- educommon/django/db/migration/__init__.py +3 -7
- educommon/django/db/migration/operations.py +29 -51
- educommon/django/db/mixins/__init__.py +16 -10
- educommon/django/db/mixins/date_interval.py +47 -75
- educommon/django/db/mixins/validation.py +26 -26
- educommon/django/db/model_view/__init__.py +18 -22
- educommon/django/db/models.py +9 -8
- educommon/django/db/observer.py +9 -27
- educommon/django/db/partitioning/__init__.py +66 -92
- educommon/django/db/partitioning/management/commands/apply_partitioning.py +3 -13
- educommon/django/db/partitioning/management/commands/clear_table.py +18 -14
- educommon/django/db/partitioning/management/commands/split_table.py +18 -13
- educommon/django/db/routers.py +6 -15
- educommon/django/db/signals.py +149 -2
- educommon/django/db/utils.py +14 -19
- educommon/django/db/validators/__init__.py +1 -0
- educommon/django/db/validators/simple.py +72 -100
- educommon/django/storages/atcfs/api.py +39 -53
- educommon/django/storages/atcfs/app_meta.py +1 -1
- educommon/django/storages/atcfs/management/commands/atcfs_migrate.py +42 -55
- educommon/django/storages/atcfs/models.py +0 -3
- educommon/django/storages/atcfs/monkey_patching.py +18 -12
- educommon/django/storages/atcfs/storage.py +14 -23
- educommon/extjs/fields/input_params.py +15 -45
- educommon/importer/XLSReader.py +143 -241
- educommon/importer/__init__.py +86 -4
- educommon/importer/api.py +53 -84
- educommon/importer/constants.py +4 -14
- educommon/importer/loggers.py +16 -26
- educommon/importer/proxy.py +131 -176
- educommon/importer/proxy_import.py +11 -12
- educommon/importer/report.py +4 -6
- educommon/importer/ui.py +32 -26
- educommon/importer/validators.py +4 -7
- educommon/integration_entities/helpers.py +14 -18
- educommon/ioc/__init__.py +3 -6
- educommon/logger/loggers.py +10 -14
- educommon/m3/__init__.py +20 -38
- educommon/m3/extensions/__init__.py +1 -0
- educommon/m3/extensions/listeners/__init__.py +22 -38
- educommon/m3/extensions/listeners/delete_check/listeners.py +31 -41
- educommon/m3/extensions/listeners/delete_check/mixins.py +20 -25
- educommon/m3/extensions/listeners/delete_check/signals.py +2 -2
- educommon/m3/extensions/listeners/delete_check/ui.py +15 -14
- educommon/m3/extensions/listeners/delete_check/utils.py +9 -11
- educommon/m3/extensions/ui.py +15 -33
- educommon/m3/transaction_context.py +17 -19
- educommon/objectpack/actions.py +70 -88
- educommon/objectpack/apps.py +5 -0
- educommon/objectpack/filters.py +9 -15
- educommon/objectpack/ui.py +59 -77
- educommon/report/__init__.py +9 -5
- educommon/report/actions.py +29 -32
- educommon/report/constructor/__init__.py +5 -8
- educommon/report/constructor/app_meta.py +1 -3
- educommon/report/constructor/apps.py +1 -0
- educommon/report/constructor/base.py +33 -80
- educommon/report/constructor/builders/excel/_base.py +138 -286
- educommon/report/constructor/builders/excel/_header.py +2 -9
- educommon/report/constructor/builders/excel/product.py +13 -34
- educommon/report/constructor/builders/excel/with_merged_cells.py +18 -14
- educommon/report/constructor/config.py +2 -0
- educommon/report/constructor/editor/actions.py +101 -215
- educommon/report/constructor/editor/ui.py +71 -93
- educommon/report/constructor/exceptions.py +6 -12
- educommon/report/constructor/migrations/0001_initial.py +36 -44
- educommon/report/constructor/migrations/0002_report_filters.py +86 -72
- educommon/report/constructor/migrations/0003_reportfilter_exclude.py +5 -5
- educommon/report/constructor/migrations/0004_reportfilter_fields.py +22 -18
- educommon/report/constructor/migrations/0005_reportcolumn_visible.py +5 -4
- educommon/report/constructor/migrations/0006_reportsorting.py +21 -17
- educommon/report/constructor/migrations/0007_include_available_units.py +14 -14
- educommon/report/constructor/migrations/0008_auto_20170407_1318.py +4 -5
- educommon/report/constructor/migrations/0009_auto_20180405_0642.py +1 -4
- educommon/report/constructor/migrations/0010_add_aggregate_fields.py +7 -8
- educommon/report/constructor/mixins.py +14 -15
- educommon/report/constructor/models.py +76 -124
- educommon/report/constructor/utils.py +3 -8
- educommon/report/constructor/validators.py +1 -3
- educommon/report/reporter.py +25 -43
- educommon/report/utils.py +14 -40
- educommon/rest/actions.py +7 -11
- educommon/rest/context.py +6 -16
- educommon/rest/controllers.py +10 -10
- educommon/rest/mixins.py +29 -27
- educommon/secure_media/app_meta.py +9 -9
- educommon/utils/__init__.py +3 -2
- educommon/utils/caching.py +1 -3
- educommon/utils/conversion.py +1 -3
- educommon/utils/crypto.py +1 -2
- educommon/utils/date.py +13 -26
- educommon/utils/db/__init__.py +17 -26
- educommon/utils/db/postgresql.py +1 -4
- educommon/utils/fonts/__init__.py +3 -4
- educommon/utils/licence/__init__.py +5 -16
- educommon/utils/misc.py +9 -18
- educommon/utils/object_grid.py +55 -62
- educommon/utils/phone_number/modelfields.py +1 -3
- educommon/utils/phone_number/phone_number.py +5 -8
- educommon/utils/phone_number/validators.py +8 -23
- educommon/utils/plugins.py +15 -28
- educommon/utils/registry.py +2 -1
- educommon/utils/seqtools.py +1 -3
- educommon/utils/serializer.py +9 -16
- educommon/utils/storage.py +3 -2
- educommon/utils/system.py +1 -3
- educommon/utils/system_app/management/commands/delete_objects.py +17 -34
- educommon/utils/ui.py +87 -84
- educommon/utils/xml/__init__.py +2 -7
- educommon/utils/xml/resolver.py +1 -0
- educommon/ws_log/actions.py +31 -76
- educommon/ws_log/base.py +6 -20
- educommon/ws_log/migrations/0001_initial.py +25 -8
- educommon/ws_log/migrations/0002_auto_20160628_1334.py +0 -1
- educommon/ws_log/migrations/0003_add_fields_to_smev_logs.py +20 -4
- educommon/ws_log/migrations/0004_auto_20160727_1600.py +7 -6
- educommon/ws_log/migrations/0005_auto_20161130_1615.py +14 -4
- educommon/ws_log/migrations/0006_auto_20170327_1027.py +3 -2
- educommon/ws_log/migrations/0007_auto_20180607_1040.py +8 -9
- educommon/ws_log/migrations/0008_auto_20180713_1445.py +23 -10
- educommon/ws_log/migrations/0009_auto_20201130_1553.py +7 -2
- educommon/ws_log/models.py +21 -35
- educommon/ws_log/provider.py +2 -1
- educommon/ws_log/report.py +8 -13
- educommon/ws_log/smev/applications.py +12 -27
- educommon/ws_log/smev/exceptions.py +2 -3
- educommon/ws_log/ui.py +32 -32
- educommon/ws_log/utils.py +1 -3
- educommon-3.13.2.dist-info/METADATA +57 -0
- educommon-3.13.2.dist-info/RECORD +354 -0
- {educommon-3.12.0.dist-info → educommon-3.13.2.dist-info}/WHEEL +1 -1
- educommon/utils/patches.py +0 -27
- educommon/version.conf +0 -11
- educommon-3.12.0.dist-info/METADATA +0 -47
- educommon-3.12.0.dist-info/RECORD +0 -357
- educommon-3.12.0.dist-info/dependency_links.txt +0 -1
- {educommon-3.12.0.dist-info → educommon-3.13.2.dist-info}/top_level.txt +0 -0
@@ -8,24 +8,13 @@ from educommon.contingent.contingent_plugin.models import (
|
|
8
8
|
|
9
9
|
|
10
10
|
class ContingentModelChangedPack(ObjectPack):
|
11
|
+
"""Пак для отображения изменённых объектов контингента."""
|
11
12
|
|
12
13
|
title = 'Измененные объекты контингента'
|
13
14
|
model = ContingentModelChanged
|
14
15
|
|
15
|
-
columns = [
|
16
|
-
{
|
17
|
-
'data_index': 'content_type',
|
18
|
-
'header': 'Тип'
|
19
|
-
},
|
20
|
-
{
|
21
|
-
'data_index': 'content_object',
|
22
|
-
'header': 'Объект'
|
23
|
-
}
|
24
|
-
]
|
16
|
+
columns = [{'data_index': 'content_type', 'header': 'Тип'}, {'data_index': 'content_object', 'header': 'Объект'}]
|
25
17
|
|
26
18
|
def extend_menu(self, menu):
|
27
|
-
|
28
|
-
|
29
|
-
self.title, self.list_window_action
|
30
|
-
)
|
31
|
-
)
|
19
|
+
"""Добавляет пункт в подменю 'Администрирование'."""
|
20
|
+
return menu.SubMenu('Администрирование', menu.Item(self.title, self.list_window_action))
|
@@ -14,6 +14,10 @@ _VERSION = VERSION[:2]
|
|
14
14
|
|
15
15
|
|
16
16
|
class ContingentPluginAppConfig(AppConfig):
|
17
|
+
"""Конфигурация плагина контингента.
|
18
|
+
|
19
|
+
Отвечает за регистрацию представлений связанных моделей при старте приложения.
|
20
|
+
"""
|
17
21
|
|
18
22
|
name = __package__
|
19
23
|
|
@@ -27,10 +31,12 @@ class ContingentPluginAppConfig(AppConfig):
|
|
27
31
|
)
|
28
32
|
|
29
33
|
model_views = import_module(self.name + '.model_views')
|
30
|
-
registries['related_objects'].register(
|
31
|
-
*model_views.related_model_views
|
32
|
-
)
|
34
|
+
registries['related_objects'].register(*model_views.related_model_views)
|
33
35
|
|
34
36
|
def ready(self):
|
35
|
-
|
37
|
+
"""Вызывается при готовности приложения.
|
38
|
+
|
39
|
+
Производит регистрацию представлений связанных моделей.
|
40
|
+
"""
|
41
|
+
super().ready()
|
36
42
|
self._register_related_objects_views()
|
@@ -12,7 +12,6 @@ from django.db import (
|
|
12
12
|
|
13
13
|
|
14
14
|
class Migration(migrations.Migration):
|
15
|
-
|
16
15
|
initial = True
|
17
16
|
|
18
17
|
dependencies = [
|
@@ -23,10 +22,7 @@ class Migration(migrations.Migration):
|
|
23
22
|
# первым в списке выполняемых миграций. Начиная с версии 1.10 данный
|
24
23
|
# способ не подходит ввиду ввода проверки на корректность плана миграций
|
25
24
|
if VERSION < (1, 10):
|
26
|
-
run_before = [
|
27
|
-
(app_name.split('.')[-1], '__first__')
|
28
|
-
for app_name in settings.PROJECT_APPS
|
29
|
-
]
|
25
|
+
run_before = [(app_name.split('.')[-1], '__first__') for app_name in settings.PROJECT_APPS]
|
30
26
|
|
31
27
|
operations = [
|
32
28
|
migrations.CreateModel(
|
@@ -34,7 +30,10 @@ class Migration(migrations.Migration):
|
|
34
30
|
fields=[
|
35
31
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
36
32
|
('object_id', models.PositiveIntegerField()),
|
37
|
-
(
|
33
|
+
(
|
34
|
+
'content_type',
|
35
|
+
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType'),
|
36
|
+
),
|
38
37
|
],
|
39
38
|
),
|
40
39
|
migrations.AlterUniqueTogether(
|
@@ -6,7 +6,6 @@ from django.db import (
|
|
6
6
|
|
7
7
|
|
8
8
|
class Migration(migrations.Migration):
|
9
|
-
|
10
9
|
dependencies = [
|
11
10
|
('contenttypes', '0002_remove_content_type_name'),
|
12
11
|
('contingent_plugin', '0001_initial'),
|
@@ -16,17 +15,13 @@ class Migration(migrations.Migration):
|
|
16
15
|
migrations.CreateModel(
|
17
16
|
name='ContingentModelDeleted',
|
18
17
|
fields=[
|
19
|
-
('id',
|
20
|
-
models.AutoField(auto_created=True,
|
21
|
-
primary_key=True,
|
22
|
-
serialize=False,
|
23
|
-
verbose_name='ID')),
|
18
|
+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
24
19
|
('object_id', models.PositiveIntegerField()),
|
25
|
-
('data',
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
20
|
+
('data', models.TextField(verbose_name='Данные об удалённом объекте')),
|
21
|
+
(
|
22
|
+
'content_type',
|
23
|
+
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType'),
|
24
|
+
),
|
30
25
|
],
|
31
26
|
),
|
32
27
|
migrations.AlterUniqueTogether(
|
@@ -9,21 +9,11 @@ related_model_views = (
|
|
9
9
|
HtmlTableView(
|
10
10
|
model='contingent_plugin.ContingentModelChanged',
|
11
11
|
description=Text('Измененная модель'),
|
12
|
-
columns=(
|
13
|
-
dict(
|
14
|
-
header=Text('Идентификатор объекта'),
|
15
|
-
data=AttrValue('object_id')
|
16
|
-
),
|
17
|
-
)
|
12
|
+
columns=(dict(header=Text('Идентификатор объекта'), data=AttrValue('object_id')),),
|
18
13
|
),
|
19
14
|
HtmlTableView(
|
20
15
|
model='contingent_plugin.ContingentModelDeleted',
|
21
16
|
description=Text('Удаленная модель'),
|
22
|
-
columns=(
|
23
|
-
dict(
|
24
|
-
header=Text('Идентификатор объекта'),
|
25
|
-
data=AttrValue('object_id')
|
26
|
-
),
|
27
|
-
)
|
17
|
+
columns=(dict(header=Text('Идентификатор объекта'), data=AttrValue('object_id')),),
|
28
18
|
),
|
29
19
|
)
|
@@ -13,7 +13,6 @@ from m3_django_compat.models import (
|
|
13
13
|
|
14
14
|
|
15
15
|
class ContingentModelChanged(models.Model):
|
16
|
-
|
17
16
|
"""Данные об измененных моделях."""
|
18
17
|
|
19
18
|
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
|
@@ -54,14 +53,10 @@ if VERSION >= (1, 10):
|
|
54
53
|
value = False
|
55
54
|
index = 0
|
56
55
|
for ind, migration_tuple in enumerate(plan):
|
57
|
-
if (migration_tuple[0].name == '0001_initial') and (
|
58
|
-
migration_tuple[0].app_label == 'contingent_plugin'
|
59
|
-
):
|
56
|
+
if (migration_tuple[0].name == '0001_initial') and (migration_tuple[0].app_label == 'contingent_plugin'):
|
60
57
|
value = migration_tuple
|
61
58
|
|
62
|
-
if (
|
63
|
-
migration_tuple[0].name == '0002_remove_content_type_name'
|
64
|
-
) and (
|
59
|
+
if (migration_tuple[0].name == '0002_remove_content_type_name') and (
|
65
60
|
migration_tuple[0].app_label == 'contenttypes'
|
66
61
|
):
|
67
62
|
index = ind
|
@@ -15,10 +15,7 @@ from educommon.utils.storage import (
|
|
15
15
|
)
|
16
16
|
|
17
17
|
|
18
|
-
class ContingentFieldsObserver(
|
19
|
-
OriginalObjectMixin,
|
20
|
-
ModelObserverBase
|
21
|
-
):
|
18
|
+
class ContingentFieldsObserver(OriginalObjectMixin, ModelObserverBase):
|
22
19
|
"""Фиксирует изменения в полях моделей.
|
23
20
|
|
24
21
|
Ссылки на измененные объекты сохраняются в модели
|
@@ -39,7 +36,7 @@ class ContingentFieldsObserver(
|
|
39
36
|
"""
|
40
37
|
|
41
38
|
def __init__(self, model_fields=None):
|
42
|
-
super(
|
39
|
+
super().__init__()
|
43
40
|
|
44
41
|
self.model_fields = model_fields
|
45
42
|
|
@@ -50,7 +47,7 @@ class ContingentFieldsObserver(
|
|
50
47
|
"""Проверяет наличие указаных полей model_fields в моделях."""
|
51
48
|
for (app_label, model_name), fields in model_fields.items():
|
52
49
|
model = get_model(app_label, model_name)
|
53
|
-
for field_name in
|
50
|
+
for field_name in fields(model) if callable(fields) else fields:
|
54
51
|
model._meta.get_field(field_name)
|
55
52
|
|
56
53
|
self.__checked = True
|
@@ -58,7 +55,9 @@ class ContingentFieldsObserver(
|
|
58
55
|
return True
|
59
56
|
|
60
57
|
def _is_observable(self, model):
|
58
|
+
"""Проверяет, указана ли модель в конфигурации наблюдаемых полей."""
|
61
59
|
key = model._meta.app_label, model.__name__
|
60
|
+
|
62
61
|
return key in self.model_fields
|
63
62
|
|
64
63
|
def observe(self, model):
|
@@ -73,6 +72,7 @@ class ContingentFieldsObserver(
|
|
73
72
|
"""
|
74
73
|
key = model._meta.app_label, model.__name__
|
75
74
|
fields = self.model_fields.get(key, ())
|
75
|
+
|
76
76
|
return fields(model) if callable(fields) else fields
|
77
77
|
|
78
78
|
def _has_changes(self, original, instance):
|
@@ -102,15 +102,14 @@ class ContingentFieldsObserver(
|
|
102
102
|
ContentType = get_model('contenttypes', 'ContentType')
|
103
103
|
content_type = ContentType.objects.get_for_model(instance)
|
104
104
|
|
105
|
-
ContingentModelChanged = get_model(
|
106
|
-
'contingent_plugin', 'ContingentModelChanged'
|
107
|
-
)
|
105
|
+
ContingentModelChanged = get_model('contingent_plugin', 'ContingentModelChanged')
|
108
106
|
ContingentModelChanged.objects.get_or_create(
|
109
107
|
content_type=content_type,
|
110
108
|
object_id=instance.pk,
|
111
109
|
)
|
112
110
|
|
113
111
|
def post_save(self, context, instance, sender, *args, **kwargs):
|
112
|
+
"""Обработчик сигнала post_save для наблюдаемых моделей."""
|
114
113
|
if not self.__checked:
|
115
114
|
assert self._is_model_fields_valid(self.model_fields)
|
116
115
|
|
@@ -118,9 +117,7 @@ class ContingentFieldsObserver(
|
|
118
117
|
self._fix_changed_object(instance)
|
119
118
|
|
120
119
|
|
121
|
-
class PreDeletionDataSavingObserver(
|
122
|
-
OriginalObjectMixin, ModelOnlyObserverMixin, ModelObserverBase
|
123
|
-
):
|
120
|
+
class PreDeletionDataSavingObserver(OriginalObjectMixin, ModelOnlyObserverMixin, ModelObserverBase):
|
124
121
|
"""Класс для перехвата и сохранения объекта модели перед удалением."""
|
125
122
|
|
126
123
|
def __init__(self, storage, observables=None):
|
@@ -136,10 +133,14 @@ class PreDeletionDataSavingObserver(
|
|
136
133
|
assert isinstance(storage, AbstractInstanceStorage)
|
137
134
|
self._storage = storage
|
138
135
|
|
139
|
-
super(
|
136
|
+
super().__init__()
|
140
137
|
if observables:
|
141
138
|
for model in observables:
|
142
139
|
self.observe(model)
|
143
140
|
|
144
141
|
def pre_delete(self, instance, context, **kwargs):
|
142
|
+
"""Сохраняет данные объекта перед его удалением.
|
143
|
+
|
144
|
+
Вызывается автоматически перед удалением объекта модели.
|
145
|
+
"""
|
145
146
|
self._storage.save(instance, context=context, **kwargs)
|
@@ -7,13 +7,10 @@ from educommon.utils.storage import (
|
|
7
7
|
)
|
8
8
|
|
9
9
|
|
10
|
-
ContingentModelDeleted = get_model(
|
11
|
-
'contingent_plugin', 'ContingentModelDeleted')
|
10
|
+
ContingentModelDeleted = get_model('contingent_plugin', 'ContingentModelDeleted')
|
12
11
|
|
13
12
|
|
14
|
-
class ContingentDeletedInstancesDataStorage(
|
15
|
-
AbstractInstanceDataStorage
|
16
|
-
):
|
13
|
+
class ContingentDeletedInstancesDataStorage(AbstractInstanceDataStorage):
|
17
14
|
"""Класс для сохранения данных объекта модели для Контингента.
|
18
15
|
|
19
16
|
Предполагается использование объекта этого класса вместе с классом
|
@@ -40,15 +37,18 @@ class ContingentDeletedInstancesDataStorage(
|
|
40
37
|
:rtype: ContentType
|
41
38
|
"""
|
42
39
|
ContentType = get_model('contenttypes', 'ContentType')
|
40
|
+
|
43
41
|
return ContentType.objects.get_for_model(instance)
|
44
42
|
|
45
43
|
def _is_instance_already_saved(self, instance, **kwargs):
|
44
|
+
"""Проверяет, сохранён ли уже объект как удалённый."""
|
46
45
|
return ContingentModelDeleted.objects.filter(
|
47
46
|
content_type=self._get_instance_content_type(instance),
|
48
47
|
object_id=instance.pk,
|
49
48
|
).exists()
|
50
49
|
|
51
50
|
def _save_instance_data(self, instance, **kwargs):
|
51
|
+
"""Сохраняет данные удаляемого объекта в модель ContingentModelDeleted."""
|
52
52
|
instance_data = self._get_instance_data(instance)
|
53
53
|
if instance_data is not None:
|
54
54
|
ContingentModelDeleted.objects.get_or_create(
|
@@ -67,6 +67,7 @@ class ContingentDeletedInstancesDataStorage(
|
|
67
67
|
pass
|
68
68
|
|
69
69
|
def save(self, instance, **kwargs):
|
70
|
-
|
71
|
-
|
70
|
+
"""Сохраняет данные и вызывает удаление зависимостей после сохранения."""
|
71
|
+
super().save(instance, **kwargs)
|
72
|
+
|
72
73
|
self.delete_some_objects_after_saving(instance, **kwargs)
|
@@ -73,8 +73,7 @@ def get_params_from_deleted_model(model, object_id):
|
|
73
73
|
:raise: ApplicationLogicException
|
74
74
|
"""
|
75
75
|
obj = ContingentModelDeleted.objects.filter(
|
76
|
-
content_type=ContentType.objects.get_for_model(model),
|
77
|
-
object_id=object_id
|
76
|
+
content_type=ContentType.objects.get_for_model(model), object_id=object_id
|
78
77
|
).first()
|
79
78
|
|
80
79
|
if not obj:
|
@@ -102,6 +101,7 @@ def get_param_value_from_deleted_model(model, object_id, param_name):
|
|
102
101
|
"""
|
103
102
|
params = get_params_from_deleted_model(model, object_id)
|
104
103
|
param_value = params.get(param_name)
|
104
|
+
|
105
105
|
return param_value
|
106
106
|
|
107
107
|
|
@@ -130,10 +130,10 @@ def get_new_param_tuples(params_tuples, model):
|
|
130
130
|
result_tuples = []
|
131
131
|
for params_tuple in params_tuples:
|
132
132
|
param_name = params_tuple[0]
|
133
|
-
new_function = partial(
|
134
|
-
get_param_value_from_deleted_model, model, param_name=param_name)
|
133
|
+
new_function = partial(get_param_value_from_deleted_model, model, param_name=param_name)
|
135
134
|
param_tuple = (param_name, ('object_id', new_function), None, None)
|
136
135
|
result_tuples.append(param_tuple)
|
136
|
+
|
137
137
|
return tuple(result_tuples)
|
138
138
|
|
139
139
|
|
@@ -152,11 +152,11 @@ def get_original_and_deleted_instances_info(model, query_config):
|
|
152
152
|
:rtype: Tuple[Tuple[QuerySet, tuple], Tuple[QuerySet, tuple]]
|
153
153
|
"""
|
154
154
|
model_content_type = ContentType.objects.get_for_model(model)
|
155
|
-
new_query = ContingentModelDeleted.objects.filter(
|
156
|
-
content_type=model_content_type)
|
155
|
+
new_query = ContingentModelDeleted.objects.filter(content_type=model_content_type)
|
157
156
|
|
158
157
|
_, params_tuples = query_config
|
159
158
|
new_param_tuples = get_new_param_tuples(params_tuples, model)
|
160
159
|
|
161
160
|
deleted_objects_query_config = new_query, new_param_tuples
|
161
|
+
|
162
162
|
return query_config, deleted_objects_query_config
|
educommon/django/db/fields.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
"""Кастомные поля моделей Django"""
|
2
|
+
|
2
3
|
from datetime import (
|
3
4
|
datetime,
|
4
5
|
timedelta,
|
@@ -61,188 +62,173 @@ class SingleErrorDecimalField(fields.DecimalField):
|
|
61
62
|
|
62
63
|
@cached_property
|
63
64
|
def validators(self):
|
64
|
-
"""
|
65
|
-
Переопрелеление стандартного валидатора Decimal.
|
65
|
+
"""Переопрелеление стандартного валидатора Decimal.
|
66
66
|
|
67
67
|
:return: Список валидаторов.
|
68
68
|
"""
|
69
69
|
validators = super(SingleErrorDecimalField, self).validators
|
70
|
+
|
70
71
|
validators.pop()
|
71
|
-
validators.append(
|
72
|
-
|
73
|
-
self.max_digits, self.decimal_places
|
74
|
-
)
|
75
|
-
)
|
72
|
+
validators.append(simple.SingleErrorDecimalValidator(self.max_digits, self.decimal_places))
|
73
|
+
|
76
74
|
return validators
|
77
75
|
|
78
76
|
|
79
77
|
class FIOField(fields.CharField, IMaskRegexField):
|
78
|
+
"""Поле для ввода ФИО с маской и валидацией.
|
79
|
+
|
80
|
+
Разрешены только буквы, пробелы и дефисы.
|
81
|
+
"""
|
82
|
+
|
80
83
|
_mask_re = r'^[а-яА-ЯёЁa-zA-Z\s-]*$'
|
81
84
|
|
82
|
-
default_validators = [
|
83
|
-
simple.FIOValidator()
|
84
|
-
]
|
85
|
+
default_validators = [simple.FIOValidator()]
|
85
86
|
|
86
87
|
|
87
88
|
class RangedDateField(fields.DateField):
|
88
89
|
"""Поле, реализующее валидаторы по умолчанию для границ периода"""
|
89
90
|
|
90
|
-
def __init__(self, minimum_date=datetime(
|
91
|
-
|
92
|
-
|
93
|
-
self.validators.append(simple.date_range_validator(
|
94
|
-
minimum=minimum_date, maximum=maximum_date))
|
91
|
+
def __init__(self, minimum_date=datetime(1, 1, 1).date(), maximum_date=None, **kwargs):
|
92
|
+
super().__init__(**kwargs)
|
93
|
+
self.validators.append(simple.date_range_validator(minimum=minimum_date, maximum=maximum_date))
|
95
94
|
|
96
95
|
|
97
96
|
class LastNameField(FIOField):
|
98
97
|
"""Расширение поля ФИО для фамилии"""
|
99
98
|
|
100
99
|
def __init__(self, verbose_name='Фамилия', max_length=30, **kwargs):
|
101
|
-
super(
|
102
|
-
verbose_name=verbose_name, max_length=max_length, **kwargs)
|
100
|
+
super().__init__(verbose_name=verbose_name, max_length=max_length, **kwargs)
|
103
101
|
|
104
102
|
|
105
103
|
class FirstNameField(FIOField):
|
106
104
|
"""Расширение поля ФИО для имени"""
|
107
105
|
|
108
106
|
def __init__(self, verbose_name='Имя', max_length=30, **kwargs):
|
109
|
-
super(
|
110
|
-
verbose_name=verbose_name, max_length=max_length, **kwargs)
|
107
|
+
super().__init__(verbose_name=verbose_name, max_length=max_length, **kwargs)
|
111
108
|
|
112
109
|
|
113
110
|
class MiddleNameField(FIOField):
|
114
111
|
"""Расширение поля ФИО для отчества"""
|
115
112
|
|
116
|
-
def __init__(self, verbose_name='Отчество',
|
117
|
-
|
118
|
-
super(MiddleNameField, self).__init__(
|
119
|
-
verbose_name=verbose_name, null=null, blank=blank,
|
120
|
-
max_length=max_length, **kwargs)
|
113
|
+
def __init__(self, verbose_name='Отчество', null=True, blank=True, max_length=30, **kwargs):
|
114
|
+
super().__init__(verbose_name=verbose_name, null=null, blank=blank, max_length=max_length, **kwargs)
|
121
115
|
|
122
116
|
|
123
117
|
class SNILSField(fields.CharField, IMaskRegexField):
|
118
|
+
"""Поле модели для ввода СНИЛС с маской и валидацией."""
|
124
119
|
|
125
120
|
_mask_re = r'^[-\s\d]{0,14}$'
|
126
121
|
|
127
|
-
default_validators = [
|
128
|
-
simple.SNILSValidator()
|
129
|
-
]
|
122
|
+
default_validators = [simple.SNILSValidator()]
|
130
123
|
|
131
|
-
def __init__(
|
132
|
-
self, verbose_name='СНИЛС', **kwargs
|
133
|
-
):
|
124
|
+
def __init__(self, verbose_name='СНИЛС', **kwargs):
|
134
125
|
kwargs.setdefault('max_length', 14)
|
135
|
-
|
136
|
-
|
126
|
+
|
127
|
+
super().__init__(verbose_name=verbose_name, **kwargs)
|
137
128
|
|
138
129
|
|
139
130
|
class BirthDateField(RangedDateField):
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
131
|
+
"""Поле даты рождения с ограничением по минимальной и максимальной дате."""
|
132
|
+
|
133
|
+
def __init__(
|
134
|
+
self,
|
135
|
+
minimum_date=datetime(1, 1, 1).date(),
|
136
|
+
maximum_date=date_difference_as_callable(timedelta(days=1)),
|
137
|
+
verbose_name='Дата рождения',
|
138
|
+
**kwargs,
|
139
|
+
):
|
140
|
+
super().__init__(minimum_date=minimum_date, maximum_date=maximum_date, verbose_name=verbose_name, **kwargs)
|
146
141
|
|
147
142
|
|
148
143
|
class DocumentSeriesField(fields.CharField, IMaskRegexField):
|
144
|
+
"""Поле серии документа с маской и соответствующим валидатором."""
|
145
|
+
|
149
146
|
_mask_re = r'^[a-zA-Zа-яА-ЯёЁ\d\s|\-|\.|\,|\\|\/]*$'
|
150
147
|
|
151
|
-
default_validators = [
|
152
|
-
simple.DocumentSeriesValidator()
|
153
|
-
]
|
148
|
+
default_validators = [simple.DocumentSeriesValidator()]
|
154
149
|
|
155
|
-
def __init__(
|
156
|
-
|
157
|
-
):
|
158
|
-
super(DocumentSeriesField, self).__init__(
|
159
|
-
verbose_name=verbose_name, **kwargs)
|
150
|
+
def __init__(self, verbose_name='Серия документа', **kwargs):
|
151
|
+
super().__init__(verbose_name=verbose_name, **kwargs)
|
160
152
|
|
161
153
|
|
162
154
|
class DocumentNumberField(fields.CharField, IMaskRegexField):
|
155
|
+
"""Поле номера документа с маской и соответствующим валидатором."""
|
156
|
+
|
163
157
|
_mask_re = r'^[a-zA-Zа-яА-ЯёЁ\d\s|\-|\.|\,|\\|\/]*$'
|
164
158
|
|
165
|
-
default_validators = [
|
166
|
-
simple.DocumentNumberValidator()
|
167
|
-
]
|
159
|
+
default_validators = [simple.DocumentNumberValidator()]
|
168
160
|
|
169
|
-
def __init__(
|
170
|
-
|
171
|
-
):
|
172
|
-
super(DocumentNumberField, self).__init__(
|
173
|
-
verbose_name=verbose_name, **kwargs)
|
161
|
+
def __init__(self, verbose_name='Номер документа', **kwargs):
|
162
|
+
super().__init__(verbose_name=verbose_name, **kwargs)
|
174
163
|
|
175
164
|
|
176
165
|
class PassportSeriesField(DocumentSeriesField):
|
166
|
+
"""Поле серии паспорта с числовой маской и ограничением по длине."""
|
167
|
+
|
177
168
|
_mask_re = r'^\d{0,4}$'
|
178
169
|
|
179
|
-
default_validators = [
|
180
|
-
simple.PassportSeriesValidator()
|
181
|
-
]
|
170
|
+
default_validators = [simple.PassportSeriesValidator()]
|
182
171
|
|
183
|
-
def __init__(
|
184
|
-
self, verbose_name='Серия паспорта', **kwargs
|
185
|
-
):
|
172
|
+
def __init__(self, verbose_name='Серия паспорта', **kwargs):
|
186
173
|
kwargs.setdefault('max_length', 4)
|
187
|
-
|
188
|
-
|
174
|
+
|
175
|
+
super().__init__(verbose_name=verbose_name, **kwargs)
|
189
176
|
|
190
177
|
|
191
178
|
class PassportNumberField(DocumentNumberField):
|
179
|
+
"""Поле номера паспорта с числовой маской и ограничением по длине."""
|
180
|
+
|
192
181
|
_mask_re = r'^\d{0,6}$'
|
193
182
|
|
194
|
-
default_validators = [
|
195
|
-
simple.PassportNumberValidator()
|
196
|
-
]
|
183
|
+
default_validators = [simple.PassportNumberValidator()]
|
197
184
|
|
198
|
-
def __init__(
|
199
|
-
self, verbose_name='Номер паспорта', **kwargs
|
200
|
-
):
|
185
|
+
def __init__(self, verbose_name='Номер паспорта', **kwargs):
|
201
186
|
kwargs.setdefault('max_length', 6)
|
202
|
-
|
203
|
-
|
187
|
+
|
188
|
+
super().__init__(verbose_name=verbose_name, **kwargs)
|
204
189
|
|
205
190
|
|
206
191
|
class INNField(fields.CharField, IMaskRegexField):
|
192
|
+
"""Поле ИНН с маской и встроенной валидацией."""
|
193
|
+
|
207
194
|
_mask_re = r'^\d{0,12}$'
|
208
195
|
|
209
|
-
default_validators = [
|
210
|
-
simple.inn_validator
|
211
|
-
]
|
196
|
+
default_validators = [simple.inn_validator]
|
212
197
|
|
213
198
|
def __init__(self, verbose_name='ИНН', **kwargs):
|
214
199
|
kwargs.setdefault('max_length', 12)
|
215
|
-
|
200
|
+
|
201
|
+
super().__init__(verbose_name=verbose_name, **kwargs)
|
216
202
|
|
217
203
|
|
218
204
|
class KPPField(fields.CharField, IMaskRegexField):
|
205
|
+
"""Поле КПП с маской, без дублирования ошибок превышения длины."""
|
206
|
+
|
219
207
|
_mask_re = r'^\d{0,9}$'
|
220
208
|
|
221
|
-
default_validators = [
|
222
|
-
simple.kpp_validator
|
223
|
-
]
|
209
|
+
default_validators = [simple.kpp_validator]
|
224
210
|
|
225
211
|
def __init__(self, verbose_name='КПП', **kwargs):
|
226
212
|
kwargs.setdefault('max_length', 9)
|
227
|
-
|
228
|
-
|
213
|
+
|
214
|
+
super().__init__(verbose_name=verbose_name, **kwargs)
|
215
|
+
|
229
216
|
# из за стандартного валидатора дублируются сообщения об ошибке
|
230
217
|
# привышения длинны поля
|
231
218
|
try:
|
232
|
-
self.validators.remove(
|
233
|
-
validators.MaxLengthValidator(self.max_length)
|
234
|
-
)
|
219
|
+
self.validators.remove(validators.MaxLengthValidator(self.max_length))
|
235
220
|
except ValueError:
|
236
221
|
pass
|
237
222
|
|
238
223
|
|
239
224
|
class OGRNField(fields.CharField, IMaskRegexField):
|
225
|
+
"""Поле ОГРН с маской и встроенной валидацией."""
|
226
|
+
|
240
227
|
_mask_re = r'^\d{0,15}$'
|
241
228
|
|
242
|
-
default_validators = [
|
243
|
-
simple.ogrn_validator
|
244
|
-
]
|
229
|
+
default_validators = [simple.ogrn_validator]
|
245
230
|
|
246
231
|
def __init__(self, verbose_name='ОГРН', **kwargs):
|
247
232
|
kwargs.setdefault('max_length', 15)
|
248
|
-
|
233
|
+
|
234
|
+
super().__init__(verbose_name=verbose_name, **kwargs)
|
@@ -7,8 +7,7 @@ from operator import (
|
|
7
7
|
)
|
8
8
|
|
9
9
|
|
10
|
-
def date_difference_as_callable(
|
11
|
-
diff, date_fn=datetime.date.today, operator_=sub):
|
10
|
+
def date_difference_as_callable(diff, date_fn=datetime.date.today, operator_=sub):
|
12
11
|
"""Функция, возвращающая callable-объект, не принимающий аргументов и
|
13
12
|
возвращающий дату.
|
14
13
|
Используется для валидации дат, когда нужно проверить не точную дату, а
|
@@ -24,13 +23,10 @@ def date_difference_as_callable(
|
|
24
23
|
:return:
|
25
24
|
:rtype: Callable[[], datetime.date]
|
26
25
|
"""
|
27
|
-
return partial(
|
28
|
-
_get_time_difference_from_callable,
|
29
|
-
operator_=operator_, date_fn=date_fn, diff=diff)
|
26
|
+
return partial(_get_time_difference_from_callable, operator_=operator_, date_fn=date_fn, diff=diff)
|
30
27
|
|
31
28
|
|
32
|
-
def _get_time_difference_from_callable(
|
33
|
-
operator_, date_fn, diff):
|
29
|
+
def _get_time_difference_from_callable(operator_, date_fn, diff):
|
34
30
|
"""Вспомогательная функция для сериализации валидатора при генерации
|
35
31
|
файла-миграции. Нужен для того, чтобы при генерации миграции функция
|
36
32
|
date_fn не разворачивалась в точное значение, а оставалась функцией.
|