educommon 3.13.0__py3-none-any.whl → 3.14.0__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.
Files changed (221) hide show
  1. educommon/__init__.py +0 -1
  2. educommon/about/ui/actions.py +16 -30
  3. educommon/about/ui/ui.py +3 -12
  4. educommon/about/utils.py +6 -5
  5. educommon/async_task/__init__.py +0 -1
  6. educommon/async_task/actions.py +18 -13
  7. educommon/async_task/apps.py +4 -0
  8. educommon/async_task/locker.py +2 -5
  9. educommon/async_task/migrations/0001_initial.py +55 -9
  10. educommon/async_task/migrations/0002_task_type_and_status_data.py +94 -89
  11. educommon/async_task/migrations/0003_alter_runningtask_options.py +0 -1
  12. educommon/async_task/models.py +9 -6
  13. educommon/async_task/tasks.py +11 -7
  14. educommon/async_task/ui.py +16 -35
  15. educommon/async_tasks/__init__.py +0 -1
  16. educommon/async_tasks/apps.py +4 -0
  17. educommon/async_tasks/locks.py +11 -21
  18. educommon/async_tasks/migrations/0001_initial.py +68 -8
  19. educommon/async_tasks/migrations/0002_load_initial_data.py +0 -1
  20. educommon/async_tasks/models.py +9 -29
  21. educommon/async_tasks/tasks.py +25 -54
  22. educommon/audit_log/README.rst +64 -0
  23. educommon/audit_log/__init__.py +1 -0
  24. educommon/audit_log/actions.py +108 -89
  25. educommon/audit_log/app_meta.py +6 -7
  26. educommon/audit_log/apps.py +44 -29
  27. educommon/audit_log/constants.py +7 -4
  28. educommon/audit_log/error_log/actions.py +1 -3
  29. educommon/audit_log/helpers.py +2 -4
  30. educommon/audit_log/management/commands/reinstall_audit_log.py +11 -7
  31. educommon/audit_log/migrations/0001_initial.py +91 -16
  32. educommon/audit_log/migrations/0002_install_audit_log.py +13 -13
  33. educommon/audit_log/migrations/0003_logproxy.py +1 -3
  34. educommon/audit_log/migrations/0004_reinstall_audit_log.py +1 -4
  35. educommon/audit_log/migrations/0005_postgresql_error.py +4 -2
  36. educommon/audit_log/migrations/0006_auto_20200806_1707.py +3 -4
  37. educommon/audit_log/migrations/0007_create_selective_tables_function.py +8 -5
  38. educommon/audit_log/migrations/0008_table_logged.py +0 -1
  39. educommon/audit_log/migrations/0009_reinstall_audit_log.py +0 -1
  40. educommon/audit_log/models.py +36 -42
  41. educommon/audit_log/permissions.py +11 -9
  42. educommon/audit_log/proxies.py +33 -28
  43. educommon/audit_log/ui.py +19 -22
  44. educommon/audit_log/utils/__init__.py +27 -81
  45. educommon/audit_log/utils/operations.py +16 -2
  46. educommon/auth/__init__.py +0 -3
  47. educommon/auth/rbac/__init__.py +2 -4
  48. educommon/auth/rbac/actions.py +148 -145
  49. educommon/auth/rbac/app_meta.py +9 -6
  50. educommon/auth/rbac/backends/base.py +2 -8
  51. educommon/auth/rbac/backends/caching.py +27 -37
  52. educommon/auth/rbac/backends/simple.py +1 -4
  53. educommon/auth/rbac/checker.py +1 -3
  54. educommon/auth/rbac/management/commands/rbac.py +6 -11
  55. educommon/auth/rbac/manager.py +18 -47
  56. educommon/auth/rbac/migrations/0001_initial.py +73 -12
  57. educommon/auth/rbac/migrations/0002_model_modifier_metaclass_fix.py +7 -6
  58. educommon/auth/rbac/migrations/0003_permission_hidden.py +1 -5
  59. educommon/auth/rbac/migrations/0004_auto_20171024_1245.py +26 -19
  60. educommon/auth/rbac/models.py +63 -68
  61. educommon/auth/rbac/permissions.py +6 -7
  62. educommon/auth/rbac/ui.py +83 -84
  63. educommon/auth/rbac/utils.py +10 -11
  64. educommon/auth/rbac/validators.py +4 -5
  65. educommon/auth/simple_auth/__init__.py +1 -5
  66. educommon/auth/simple_auth/actions.py +79 -92
  67. educommon/auth/simple_auth/app_meta.py +2 -9
  68. educommon/auth/simple_auth/checkers.py +3 -3
  69. educommon/auth/simple_auth/migrations/0001_initial.py +23 -4
  70. educommon/auth/simple_auth/validators.py +0 -1
  71. educommon/contingent/actions.py +7 -7
  72. educommon/contingent/app_meta.py +1 -4
  73. educommon/contingent/base.py +10 -15
  74. educommon/contingent/catalogs.py +424 -540
  75. educommon/contingent/contingent_plugin/actions.py +4 -15
  76. educommon/contingent/contingent_plugin/apps.py +10 -4
  77. educommon/contingent/contingent_plugin/migrations/0001_initial.py +5 -6
  78. educommon/contingent/contingent_plugin/migrations/0002_add_contingent_model_deleted.py +6 -11
  79. educommon/contingent/contingent_plugin/model_views.py +2 -12
  80. educommon/contingent/contingent_plugin/models.py +2 -7
  81. educommon/contingent/contingent_plugin/observer.py +14 -13
  82. educommon/contingent/contingent_plugin/plugin_meta.py +1 -3
  83. educommon/contingent/contingent_plugin/storage.py +8 -7
  84. educommon/contingent/contingent_plugin/utils.py +6 -6
  85. educommon/django/db/fields.py +72 -86
  86. educommon/django/db/migration/__init__.py +3 -7
  87. educommon/django/db/migration/operations.py +29 -51
  88. educommon/django/db/mixins/__init__.py +16 -10
  89. educommon/django/db/mixins/date_interval.py +47 -75
  90. educommon/django/db/mixins/validation.py +26 -26
  91. educommon/django/db/model_view/__init__.py +18 -22
  92. educommon/django/db/models.py +9 -8
  93. educommon/django/db/observer.py +9 -27
  94. educommon/django/db/partitioning/__init__.py +66 -92
  95. educommon/django/db/partitioning/management/commands/apply_partitioning.py +3 -13
  96. educommon/django/db/partitioning/management/commands/clear_table.py +18 -14
  97. educommon/django/db/partitioning/management/commands/split_table.py +18 -13
  98. educommon/django/db/routers.py +6 -15
  99. educommon/django/db/signals.py +4 -2
  100. educommon/django/db/utils.py +14 -19
  101. educommon/django/db/validators/__init__.py +1 -0
  102. educommon/django/db/validators/simple.py +72 -100
  103. educommon/django/storages/atcfs/api.py +39 -53
  104. educommon/django/storages/atcfs/app_meta.py +1 -1
  105. educommon/django/storages/atcfs/management/commands/atcfs_migrate.py +42 -55
  106. educommon/django/storages/atcfs/models.py +0 -3
  107. educommon/django/storages/atcfs/monkey_patching.py +18 -12
  108. educommon/django/storages/atcfs/storage.py +14 -23
  109. educommon/extjs/fields/input_params.py +15 -45
  110. educommon/importer/XLSReader.py +143 -241
  111. educommon/importer/__init__.py +86 -4
  112. educommon/importer/api.py +53 -84
  113. educommon/importer/constants.py +4 -14
  114. educommon/importer/loggers.py +16 -26
  115. educommon/importer/proxy.py +131 -176
  116. educommon/importer/proxy_import.py +11 -12
  117. educommon/importer/report.py +4 -6
  118. educommon/importer/ui.py +32 -26
  119. educommon/importer/validators.py +4 -7
  120. educommon/integration_entities/helpers.py +14 -18
  121. educommon/ioc/__init__.py +3 -6
  122. educommon/logger/loggers.py +10 -14
  123. educommon/m3/__init__.py +20 -38
  124. educommon/m3/extensions/__init__.py +1 -0
  125. educommon/m3/extensions/listeners/__init__.py +22 -38
  126. educommon/m3/extensions/listeners/delete_check/listeners.py +31 -41
  127. educommon/m3/extensions/listeners/delete_check/mixins.py +20 -25
  128. educommon/m3/extensions/listeners/delete_check/signals.py +2 -2
  129. educommon/m3/extensions/listeners/delete_check/ui.py +15 -14
  130. educommon/m3/extensions/listeners/delete_check/utils.py +9 -11
  131. educommon/m3/extensions/ui.py +15 -33
  132. educommon/m3/transaction_context.py +17 -19
  133. educommon/objectpack/actions.py +70 -88
  134. educommon/objectpack/apps.py +5 -0
  135. educommon/objectpack/filters.py +9 -15
  136. educommon/objectpack/ui.py +59 -77
  137. educommon/report/__init__.py +9 -5
  138. educommon/report/actions.py +29 -32
  139. educommon/report/constructor/__init__.py +5 -8
  140. educommon/report/constructor/app_meta.py +1 -3
  141. educommon/report/constructor/apps.py +1 -0
  142. educommon/report/constructor/base.py +33 -80
  143. educommon/report/constructor/builders/excel/_base.py +138 -286
  144. educommon/report/constructor/builders/excel/_header.py +2 -9
  145. educommon/report/constructor/builders/excel/product.py +13 -34
  146. educommon/report/constructor/builders/excel/with_merged_cells.py +18 -14
  147. educommon/report/constructor/config.py +2 -0
  148. educommon/report/constructor/editor/actions.py +101 -215
  149. educommon/report/constructor/editor/ui.py +71 -93
  150. educommon/report/constructor/exceptions.py +6 -12
  151. educommon/report/constructor/migrations/0001_initial.py +36 -44
  152. educommon/report/constructor/migrations/0002_report_filters.py +86 -72
  153. educommon/report/constructor/migrations/0003_reportfilter_exclude.py +5 -5
  154. educommon/report/constructor/migrations/0004_reportfilter_fields.py +22 -18
  155. educommon/report/constructor/migrations/0005_reportcolumn_visible.py +5 -4
  156. educommon/report/constructor/migrations/0006_reportsorting.py +21 -17
  157. educommon/report/constructor/migrations/0007_include_available_units.py +14 -14
  158. educommon/report/constructor/migrations/0008_auto_20170407_1318.py +4 -5
  159. educommon/report/constructor/migrations/0009_auto_20180405_0642.py +1 -4
  160. educommon/report/constructor/migrations/0010_add_aggregate_fields.py +7 -8
  161. educommon/report/constructor/mixins.py +14 -15
  162. educommon/report/constructor/models.py +76 -124
  163. educommon/report/constructor/utils.py +3 -8
  164. educommon/report/constructor/validators.py +1 -3
  165. educommon/report/reporter.py +25 -43
  166. educommon/report/utils.py +14 -40
  167. educommon/rest/actions.py +7 -11
  168. educommon/rest/context.py +6 -16
  169. educommon/rest/controllers.py +10 -10
  170. educommon/rest/mixins.py +29 -27
  171. educommon/secure_media/app_meta.py +9 -9
  172. educommon/utils/__init__.py +3 -2
  173. educommon/utils/caching.py +1 -3
  174. educommon/utils/conversion.py +1 -3
  175. educommon/utils/crypto.py +1 -2
  176. educommon/utils/date.py +13 -26
  177. educommon/utils/db/__init__.py +17 -26
  178. educommon/utils/db/postgresql.py +1 -4
  179. educommon/utils/fonts/__init__.py +3 -4
  180. educommon/utils/licence/__init__.py +5 -16
  181. educommon/utils/misc.py +9 -18
  182. educommon/utils/object_grid.py +55 -62
  183. educommon/utils/phone_number/modelfields.py +1 -3
  184. educommon/utils/phone_number/phone_number.py +5 -8
  185. educommon/utils/phone_number/validators.py +8 -23
  186. educommon/utils/plugins.py +15 -28
  187. educommon/utils/registry.py +2 -1
  188. educommon/utils/seqtools.py +1 -3
  189. educommon/utils/serializer.py +9 -16
  190. educommon/utils/storage.py +3 -2
  191. educommon/utils/system.py +1 -3
  192. educommon/utils/system_app/management/commands/delete_objects.py +17 -34
  193. educommon/utils/ui.py +87 -84
  194. educommon/utils/xml/__init__.py +2 -7
  195. educommon/utils/xml/resolver.py +1 -0
  196. educommon/ws_log/actions.py +31 -76
  197. educommon/ws_log/base.py +6 -20
  198. educommon/ws_log/migrations/0001_initial.py +25 -8
  199. educommon/ws_log/migrations/0002_auto_20160628_1334.py +0 -1
  200. educommon/ws_log/migrations/0003_add_fields_to_smev_logs.py +20 -4
  201. educommon/ws_log/migrations/0004_auto_20160727_1600.py +7 -6
  202. educommon/ws_log/migrations/0005_auto_20161130_1615.py +14 -4
  203. educommon/ws_log/migrations/0006_auto_20170327_1027.py +3 -2
  204. educommon/ws_log/migrations/0007_auto_20180607_1040.py +8 -9
  205. educommon/ws_log/migrations/0008_auto_20180713_1445.py +23 -10
  206. educommon/ws_log/migrations/0009_auto_20201130_1553.py +7 -2
  207. educommon/ws_log/models.py +21 -35
  208. educommon/ws_log/provider.py +2 -1
  209. educommon/ws_log/report.py +8 -13
  210. educommon/ws_log/smev/applications.py +12 -27
  211. educommon/ws_log/smev/exceptions.py +2 -3
  212. educommon/ws_log/ui.py +32 -32
  213. educommon/ws_log/utils.py +1 -3
  214. {educommon-3.13.0.dist-info → educommon-3.14.0.dist-info}/METADATA +26 -14
  215. educommon-3.14.0.dist-info/RECORD +355 -0
  216. educommon/utils/patches.py +0 -27
  217. educommon/version.conf +0 -11
  218. educommon-3.13.0.dist-info/RECORD +0 -357
  219. educommon-3.13.0.dist-info/dependency_links.txt +0 -1
  220. {educommon-3.13.0.dist-info → educommon-3.14.0.dist-info}/WHEEL +0 -0
  221. {educommon-3.13.0.dist-info → educommon-3.14.0.dist-info}/top_level.txt +0 -0
@@ -1,3 +1,4 @@
1
+ # TODO - EDUSCHL-23454
1
2
  import os
2
3
  from contextlib import (
3
4
  closing,
@@ -72,11 +73,7 @@ def configure(force_update_triggers: bool = False):
72
73
  params['need_to_update_triggers'] = force_update_triggers or changed_table
73
74
  params['lock_id'] = PG_LOCK_ID
74
75
 
75
- execute_sql_file(
76
- settings.DEFAULT_DB_ALIAS,
77
- os.path.join(SQL_FILES_DIR, 'configure_audit_log.sql'),
78
- params
79
- )
76
+ execute_sql_file(settings.DEFAULT_DB_ALIAS, os.path.join(SQL_FILES_DIR, 'configure_audit_log.sql'), params)
80
77
 
81
78
 
82
79
  def get_all_table_names(db_alias: str, schema: str) -> Set[str]:
@@ -97,11 +94,7 @@ def get_all_table_names(db_alias: str, schema: str) -> Set[str]:
97
94
 
98
95
  def get_need_to_log_table_names() -> Set[str]:
99
96
  """Возвращает перечень наименований таблиц моделей, которые отмечены как отслеживаемые."""
100
- table_names = {
101
- model._meta.db_table
102
- for model in apps.get_models()
103
- if getattr(model, 'need_to_log', False)
104
- }
97
+ table_names = {model._meta.db_table for model in apps.get_models() if getattr(model, 'need_to_log', False)}
105
98
 
106
99
  return table_names
107
100
 
@@ -114,9 +107,7 @@ def update_or_create_tables(need_to_log_table_names: Iterable[str]) -> bool:
114
107
  Table = apps.get_model('audit_log', 'Table')
115
108
 
116
109
  need_to_log_table_names = set(need_to_log_table_names)
117
- existed_table_names = set(
118
- Table.objects.filter(schema='public').values_list('name', flat=True)
119
- )
110
+ existed_table_names = set(Table.objects.filter(schema='public').values_list('name', flat=True))
120
111
 
121
112
  to_create_table_names = need_to_log_table_names - existed_table_names
122
113
  to_disable_table_names = existed_table_names - need_to_log_table_names
@@ -141,10 +132,7 @@ def update_or_create_tables(need_to_log_table_names: Iterable[str]) -> bool:
141
132
 
142
133
  # Создание записей таблиц, которые теперь отслеживаемые
143
134
  Table.objects.bulk_create(
144
- objs=[
145
- Table(name=table, schema='public')
146
- for table in to_create_table_names
147
- ],
135
+ objs=[Table(name=table, schema='public') for table in to_create_table_names],
148
136
  )
149
137
 
150
138
  if to_create_table_names or enabled_count or disabled_count:
@@ -229,10 +217,8 @@ def is_initialized(database_alias):
229
217
  # Проверка наличия таблицы postgresql_errors
230
218
  with closing(connections[database_alias].cursor()) as cursor:
231
219
  cursor.execute(
232
- 'select 1 '
233
- 'from information_schema.tables '
234
- 'where table_schema = %s and table_name = %s',
235
- ('audit', 'postgresql_errors')
220
+ 'select 1 from information_schema.tables where table_schema = %s and table_name = %s',
221
+ ('audit', 'postgresql_errors'),
236
222
  )
237
223
  if cursor.fetchone() is None:
238
224
  return False
@@ -251,12 +237,12 @@ def is_initialized(database_alias):
251
237
  for function_name in function_names:
252
238
  with closing(connections[database_alias].cursor()) as cursor:
253
239
  cursor.execute(
254
- "select 1 "
255
- "from pg_proc proc "
256
- "inner join pg_namespace ns on ns.oid = proc.pronamespace "
257
- "where proc.proname = %s and ns.nspname = %s "
258
- "limit 1",
259
- [function_name, 'audit']
240
+ 'select 1 '
241
+ 'from pg_proc proc '
242
+ 'inner join pg_namespace ns on ns.oid = proc.pronamespace '
243
+ 'where proc.proname = %s and ns.nspname = %s '
244
+ 'limit 1',
245
+ [function_name, 'audit'],
260
246
  )
261
247
  if cursor.fetchone() is None:
262
248
  return False
@@ -304,7 +290,7 @@ def get_db_connection_params():
304
290
  dbname=target_db_conf['NAME'],
305
291
  port=target_db_conf['PORT'],
306
292
  user=target_db_conf['USER'],
307
- password=target_db_conf['PASSWORD']
293
+ password=target_db_conf['PASSWORD'],
308
294
  )
309
295
 
310
296
 
@@ -355,40 +341,18 @@ def make_hstore_filter(field, value):
355
341
  :param str value: значение, по которому фильтруется queryset.
356
342
  Если строка, то разбивается на отдельные слова.
357
343
  """
358
- result = reduce(
359
- and_,
360
- (Q(**{'%s__values__icontains' % field: x}) for x in value.split(' '))
361
- )
344
+ result = reduce(and_, (Q(**{f'{field}__values__icontains': x}) for x in value.split(' ')))
362
345
  return result
363
346
 
364
347
 
365
- def make_name_filter(field, value):
366
- """Создает lookup фильтра по фамилии/имени/отчеству пользователя.
348
+ class ModelRegistry:
349
+ """Реестр моделей Django по имени таблицы.
367
350
 
368
- :param str field: название поля ('firstname', 'surname', 'patronymic').
369
- :param str value: значение, по которому фильтруется queryset.
351
+ Позволяет получать класс модели по имени таблицы из базы данных.
352
+ Использует кэшируемое свойство для построения соответствия
353
+ между именами таблиц и их моделями, включая автоматически
354
+ создаваемые модели, но исключая proxy-модели.
370
355
  """
371
- ContentType = apps.get_model('contenttypes', 'ContentType')
372
- Employee = apps.get_model('employee', 'Employee')
373
- SysAdmin = apps.get_model('sysadmin', 'SysAdmin')
374
-
375
- result = None
376
- for model in (Employee, SysAdmin):
377
- type_id = ContentType.objects.get_for_model(model).id
378
- user_ids = list(
379
- model.objects.filter(
380
- **{'person__{}__icontains'.format(field): value}
381
- ).values_list('id', flat=True)
382
- )
383
- qobj = Q(user_id__in=user_ids, user_type_id=type_id)
384
- if result:
385
- result |= qobj
386
- else:
387
- result = qobj
388
- return result
389
-
390
-
391
- class ModelRegistry:
392
356
 
393
357
  @cached_property
394
358
  def table_model(self):
@@ -415,16 +379,9 @@ def get_model_choices(excluded=None):
415
379
  table_class = apps.get_model('audit_log', 'Table')
416
380
  if excluded:
417
381
  total_exclude += tuple(excluded)
418
- tables = (
419
- table
420
- for table in table_class.objects.iterator()
421
- if (table.schema, table.name) not in total_exclude
422
- )
382
+ tables = (table for table in table_class.objects.iterator() if (table.schema, table.name) not in total_exclude)
423
383
 
424
- result = sorted(
425
- ((table.id, get_table_name(table)) for table in tables),
426
- key=lambda x: x[1]
427
- )
384
+ result = sorted(((table.id, get_table_name(table)) for table in tables), key=lambda x: x[1])
428
385
 
429
386
  return tuple(result)
430
387
 
@@ -435,12 +392,7 @@ def _get_m2m_model_fields(model):
435
392
  :return Два поля типа ForeignKey или None, если таблица не
436
393
  соответствует автоматически созданной.
437
394
  """
438
- result = [
439
- field
440
- for field
441
- in model._meta.get_fields()
442
- if isinstance(field, related.ForeignKey)
443
- ]
395
+ result = [field for field in model._meta.get_fields() if isinstance(field, related.ForeignKey)]
444
396
  if len(result) == 2:
445
397
  return result
446
398
 
@@ -454,16 +406,10 @@ def get_table_name(table):
454
406
  if model._meta.auto_created:
455
407
  fields = _get_m2m_model_fields(model)
456
408
  if fields:
457
- names = [
458
- get_related(f).parent_model._meta.verbose_name
459
- for f in fields
460
- ]
409
+ names = [get_related(f).parent_model._meta.verbose_name for f in fields]
461
410
  verbose_name = 'Связь {}, {}'.format(names[0], names[1])
462
411
 
463
- return '{} - {}'.format(
464
- verbose_name,
465
- class_name
466
- )
412
+ return f'{verbose_name} - {class_name}'
467
413
  else:
468
414
  return table.name
469
415
 
@@ -479,7 +425,7 @@ def get_audit_log_context(request):
479
425
  result = {}
480
426
 
481
427
  current_user = ioc.get('get_current_user')(request)
482
- if current_user:
428
+ if current_user and current_user.is_authenticated:
483
429
  ContentType = apps.get_model('contenttypes', 'ContentType')
484
430
 
485
431
  result['user_id'] = current_user.id
@@ -32,6 +32,7 @@ class ReinstallAuditLog(Operation):
32
32
 
33
33
  @staticmethod
34
34
  def _read_sql(filename):
35
+ """Читает SQL-файл и экранирует знаки процента."""
35
36
  sql_file_path = os.path.join(SQL_FILES_DIR, filename)
36
37
  with codecs.open(sql_file_path, 'r', 'utf-8') as sql_file:
37
38
  sql = sql_file.read().replace('%', '%%')
@@ -39,17 +40,30 @@ class ReinstallAuditLog(Operation):
39
40
 
40
41
  @property
41
42
  def _install_sql(self):
43
+ """Генерирует SQL-скрипт для установки схемы audit_log.
44
+
45
+ Подставляет параметры подключения к БД и lock_id
46
+ в шаблон SQL-файла install_audit_log.sql.
47
+ """
42
48
  params = get_db_connection_params()
43
49
  params['lock_id'] = PG_LOCK_ID
44
50
  return self._read_sql('install_audit_log.sql').format(**params)
45
51
 
46
52
  def state_forwards(self, app_label, state):
53
+ """Не изменяет состояние проекта в памяти.
54
+
55
+ Метод требуется по контракту абстрактного базового класса Operation.
56
+ """
47
57
  pass
48
58
 
49
- def database_forwards(self, app_label, schema_editor, from_state,
50
- to_state):
59
+ def database_forwards(self, app_label, schema_editor, from_state, to_state):
60
+ """Применяет SQL-скрипт установки audit_log, если используется основная БД."""
51
61
  if schema_editor.connection.alias == settings.DEFAULT_DB_ALIAS:
52
62
  schema_editor.execute(self._install_sql)
53
63
 
54
64
  def database_backwards(self, app_label, schema_editor, from_state, to_state):
65
+ """Откат миграции не реализован (no-op).
66
+
67
+ Метод присутствует, чтобы соответствовать контракту Django.
68
+ """
55
69
  return None
@@ -1,4 +1 @@
1
1
  """Средства аутентификации и авторизации."""
2
- from __future__ import (
3
- absolute_import,
4
- )
@@ -3,12 +3,10 @@
3
3
  .. note::
4
4
  RBAC - Role Based Access Control.
5
5
  """
6
+
6
7
  from django import (
7
8
  VERSION as _django_version,
8
9
  )
9
10
 
10
11
 
11
- assert _django_version >= (1, 8), (
12
- "{} app doesn't support Django {}.{}."
13
- .format(__name__, *_django_version[:2])
14
- )
12
+ assert _django_version >= (1, 8), "{} app doesn't support Django {}.{}.".format(__name__, *_django_version[:2])