Coverage for backend/django/Economics/shared/model_base.py: 98%
36 statements
« prev ^ index » next coverage.py v7.10.7, created at 2026-06-23 21:51 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2026-06-23 21:51 +0000
1from django.core.exceptions import ValidationError
2from django.db import models
4from core.managers import AccessControlManager
7def _validate_same_flowsheet(errors, field_name, value, flowsheet_id):
8 if value is not None and value.flowsheet_id != flowsheet_id:
9 errors[field_name] = "Referenced row must belong to the same flowsheet."
12class FlowsheetScopedEconomicsModel(models.Model):
13 same_flowsheet_fields: tuple[str, ...] = ()
15 class Meta:
16 abstract = True
18 def clean(self):
19 super().clean()
20 errors = {}
21 for field_name in self.same_flowsheet_fields:
22 field_id_name = f"{field_name}_id"
23 if getattr(self, field_id_name, None) is None:
24 continue
25 _validate_same_flowsheet(errors, field_name, getattr(self, field_name), self.flowsheet_id)
26 if errors:
27 raise ValidationError(errors)
29 def save(self, *args, **kwargs):
30 self.full_clean()
31 return super().save(*args, **kwargs)
34class GlobalReferenceDataManager(AccessControlManager):
35 """Access-control-compatible manager for global read-only reference data.
37 Project validation expects models to use ``AccessControlManager`` or
38 ``SoftDeleteManager``. Stats NZ index rows are intentionally not
39 flowsheet-owned, so this manager preserves that contract without filtering
40 reads or pinning writes to a request flowsheet. These models are populated
41 by developer-run management commands, not ordinary user APIs.
42 """
44 def create(self, **kwargs):
45 return super(AccessControlManager, self).create(**kwargs)
47 def bulk_create(self, objs, **kwargs):
48 return super(AccessControlManager, self).bulk_create(objs, **kwargs)
50 def get_queryset(self):
51 return super(AccessControlManager, self).get_queryset()
54class GlobalReferenceDataModel(models.Model):
55 """Reference-data base that preserves model validation without flowsheet scoping."""
57 class Meta:
58 abstract = True
60 def save(self, *args, **kwargs):
61 self.full_clean()
62 return super().save(*args, **kwargs)