From 3e043eaf548b3033da951768de5212220dc1e034 Mon Sep 17 00:00:00 2001 From: afonso Date: Thu, 21 Mar 2024 15:37:47 +0000 Subject: [PATCH] SCKAN-275 feat: Add anatomical entity constraint --- ...icalentityintersection_options_and_more.py | 79 +++++++++++++++++++ backend/composer/models.py | 14 +++- 2 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 backend/composer/migrations/0050_alter_anatomicalentityintersection_options_and_more.py diff --git a/backend/composer/migrations/0050_alter_anatomicalentityintersection_options_and_more.py b/backend/composer/migrations/0050_alter_anatomicalentityintersection_options_and_more.py new file mode 100644 index 00000000..465e2b14 --- /dev/null +++ b/backend/composer/migrations/0050_alter_anatomicalentityintersection_options_and_more.py @@ -0,0 +1,79 @@ +# Generated by Django 4.1.4 on 2024-03-21 16:28 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ("composer", "0049_rename_anatomicalentitynew_anatomicalentity_and_more"), + ] + + operations = [ + migrations.AlterModelOptions( + name="anatomicalentityintersection", + options={ + "verbose_name": "Region/Layer Combination", + "verbose_name_plural": "Region/Layer Combinations", + }, + ), + migrations.AlterModelOptions( + name="anatomicalentitymeta", + options={ + "ordering": ["name"], + "verbose_name": "Anatomical Entity", + "verbose_name_plural": "Anatomical Entities", + }, + ), + migrations.AlterField( + model_name="anatomicalentity", + name="region_layer", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="composer.anatomicalentityintersection", + ), + ), + migrations.AlterField( + model_name="connectivitystatement", + name="origins", + field=models.ManyToManyField( + related_name="origins_relations", to="composer.anatomicalentity" + ), + ), + migrations.AlterField( + model_name="destination", + name="anatomical_entities", + field=models.ManyToManyField( + blank=True, + related_name="destination_connection_layers", + to="composer.anatomicalentity", + ), + ), + migrations.AlterField( + model_name="synonym", + name="anatomical_entity", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="synonyms", + to="composer.anatomicalentity", + ), + ), + migrations.AddConstraint( + model_name="anatomicalentity", + constraint=models.CheckConstraint( + check=models.Q( + models.Q( + ("region_layer__isnull", True), ("simple_entity__isnull", False) + ), + models.Q( + ("region_layer__isnull", False), ("simple_entity__isnull", True) + ), + _connector="OR", + ), + name="check_anatomical_entity_exclusivity", + ), + ), + ] diff --git a/backend/composer/models.py b/backend/composer/models.py index 93879244..ac278fa5 100644 --- a/backend/composer/models.py +++ b/backend/composer/models.py @@ -1,6 +1,6 @@ from django.contrib.auth.models import User from django.db import models, transaction -from django.db.models import Q +from django.db.models import Q, CheckConstraint from django.db.models.expressions import F from django.forms.widgets import Input as InputWidget from django_fsm import FSMField, transition @@ -257,7 +257,7 @@ class Meta: class AnatomicalEntity(models.Model): simple_entity = models.OneToOneField(AnatomicalEntityMeta, on_delete=models.CASCADE, null=True, blank=True) - region_layer = models.ForeignKey(AnatomicalEntityIntersection, on_delete=models.CASCADE, null=True) + region_layer = models.ForeignKey(AnatomicalEntityIntersection, on_delete=models.CASCADE, null=True, blank=True) @property def name(self): @@ -270,6 +270,16 @@ def name(self): else: return "Unnamed Entity" + class Meta: + constraints = [ + CheckConstraint( + check=( + Q(simple_entity__isnull=False, region_layer__isnull=True) | + Q(simple_entity__isnull=True, region_layer__isnull=False) + ), + name='check_anatomical_entity_exclusivity' + ) + ] class Synonym(models.Model): anatomical_entity = models.ForeignKey(AnatomicalEntity, on_delete=models.CASCADE,