From cf7eabaf23b952833ca324de4dfafbf9452bb486 Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Wed, 23 Sep 2026 15:41:48 +0200 Subject: [PATCH 1/9] add a second set of Attribute2, AtomsGroup2, Atom2, Param2 a side of old one to be able to migrate data forward and backward without the primary keys hell for foreignkeys and many2many --- ...on2_attribute2_atomsgroup2_atom2_param2.py | 86 +++++++++ .../migrations/0004_content_migration.py | 174 ++++++++++++++++++ django_gem_taxonomy/models.py | 76 ++++++++ 3 files changed, 336 insertions(+) create mode 100644 django_gem_taxonomy/migrations/0003_version2_attribute2_atomsgroup2_atom2_param2.py create mode 100644 django_gem_taxonomy/migrations/0004_content_migration.py diff --git a/django_gem_taxonomy/migrations/0003_version2_attribute2_atomsgroup2_atom2_param2.py b/django_gem_taxonomy/migrations/0003_version2_attribute2_atomsgroup2_atom2_param2.py new file mode 100644 index 0000000..a3a2189 --- /dev/null +++ b/django_gem_taxonomy/migrations/0003_version2_attribute2_atomsgroup2_atom2_param2.py @@ -0,0 +1,86 @@ +# Generated by Django 4.2.30 on 2026-09-22 22:37 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('django_gem_taxonomy', '0002_atom_deny_alter_param_id'), + ] + + operations = [ + migrations.CreateModel( + name='Version2', + fields=[ + ('vers', models.CharField(max_length=16, primary_key=True, serialize=False)), + ('desc', models.TextField()), + ('is_default', models.BooleanField(default=False)), + ], + ), + migrations.CreateModel( + name='Attribute2', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=256)), + ('prog', models.IntegerField()), + ('title', models.TextField()), + ('vers', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='django_gem_taxonomy.version2')), + ], + options={ + 'unique_together': {('vers', 'name'), ('vers', 'prog')}, + }, + ), + migrations.CreateModel( + name='AtomsGroup2', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=256)), + ('prog', models.IntegerField()), + ('title', models.TextField()), + ('mutex', models.BooleanField(default=True)), + ('attr', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='django_gem_taxonomy.attribute2')), + ('vers', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='django_gem_taxonomy.version2')), + ], + options={ + 'unique_together': {('vers', 'attr', 'name'), ('vers', 'attr', 'prog')}, + }, + ), + migrations.CreateModel( + name='Atom2', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=32)), + ('prog', models.IntegerField()), + ('title', models.TextField()), + ('desc', models.TextField()), + ('type', models.TextField()), + ('args', models.JSONField(blank=True, null=True)), + ('params', models.JSONField(blank=True, null=True)), + ('attr', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='django_gem_taxonomy.attribute2')), + ('deny', models.ManyToManyField(related_name='revdeny', to='django_gem_taxonomy.atom2')), + ('deps', models.ManyToManyField(related_name='revdeps', to='django_gem_taxonomy.atom2')), + ('group', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='django_gem_taxonomy.atomsgroup2')), + ('vers', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='django_gem_taxonomy.version2')), + ], + options={ + 'unique_together': {('vers', 'attr', 'group', 'prog'), ('vers', 'name')}, + }, + ), + migrations.CreateModel( + name='Param2', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=32)), + ('prog', models.IntegerField()), + ('title', models.TextField()), + ('desc', models.TextField()), + ('atom', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='django_gem_taxonomy.atom2')), + ('vers', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='django_gem_taxonomy.version2')), + ], + options={ + 'unique_together': {('vers', 'atom', 'name'), ('vers', 'atom', 'prog')}, + }, + ), + ] diff --git a/django_gem_taxonomy/migrations/0004_content_migration.py b/django_gem_taxonomy/migrations/0004_content_migration.py new file mode 100644 index 0000000..57908d8 --- /dev/null +++ b/django_gem_taxonomy/migrations/0004_content_migration.py @@ -0,0 +1,174 @@ +# Generated by Django 4.2.30 on 2026-09-22 22:39 +from django.db import migrations + +def dgtaxonomy2dgtaxonomy_two(apps, schema_editor): + db_alias = schema_editor.connection.alias + + Attribute= apps.get_model("django_gem_taxonomy", "Attribute") + AtomsGroup= apps.get_model("django_gem_taxonomy", "AtomsGroup") + Atom= apps.get_model("django_gem_taxonomy", "Atom") + Param= apps.get_model("django_gem_taxonomy", "Param") + Version2= apps.get_model("django_gem_taxonomy", "Version2") + Attribute2= apps.get_model("django_gem_taxonomy", "Attribute2") + AtomsGroup2= apps.get_model("django_gem_taxonomy", "AtomsGroup2") + Atom2= apps.get_model("django_gem_taxonomy", "Atom2") + Param2= apps.get_model("django_gem_taxonomy", "Param2") + + # migrate to the new schema that includes version as part of the + # unique constraints, the old version has '4.0' as implied + try: + vers = Version2.objects.using(db_alias).get(vers='4.0') + except Version2.DoesNotExist: + vers = Version2.objects.using(db_alias).create( + vers='4.0', + desc='Version 4.0 year 2025', + is_default=True + ) + + for attr_in in Attribute.objects.using(db_alias).all(): + attr = Attribute2.objects.using(db_alias).create( + vers=vers, + name=attr_in.name, + prog=attr_in.prog, + title=attr_in.title, + ) + + for atg_in in AtomsGroup.objects.using(db_alias).all(): + AtomsGroup2.objects.using(db_alias).create( + vers=vers, + name=atg_in.name, + prog=atg_in.prog, + title=atg_in.title, + attr=Attribute2.objects.using(db_alias).get(vers=vers, name=atg_in.attr.name) + ) + + for at_in in Atom.objects.using(db_alias).all(): + if at_in.attr is not None: + attr = Attribute2.objects.using(db_alias).get(vers=vers, name=at_in.attr.name) + else: + attr = None + + if at_in.group is not None: + group = AtomsGroup2.objects.using(db_alias).get(vers=vers, attr=attr, + name=at_in.group.name) + else: + group = None + + atom = Atom2.objects.using(db_alias).create( + vers=vers, + name=at_in.name, + prog=at_in.prog, + title=at_in.title, + desc=at_in.desc, + args=at_in.args, + params=at_in.params, + type=at_in.type, + attr=attr, + group=group, + ) + + for at_in in Atom.objects.using(db_alias).all(): + atom = Atom2.objects.using(db_alias).get(vers=vers, name=at_in.name) + for dep in at_in.deps.all(): + atom.deps.add(Atom2.objects.using(db_alias).get(vers=vers, name=dep.name)) + for den in at_in.deny.all(): + atom.deny.add(Atom2.objects.using(db_alias).get(vers=vers, name=den.name)) + + for par_in in Param.objects.using(db_alias).all(): + Param2.objects.create( + vers=vers, + atom=Atom2.objects.get(vers=vers, name=par_in.atom.name), + name=par_in.name, + title=par_in.title, + desc=par_in.desc, + prog=par_in.prog, + ) + +def dgtaxonomy_two2dgtaxonomy(apps, schema_editor): + db_alias = schema_editor.connection.alias + + Attribute= apps.get_model("django_gem_taxonomy", "Attribute") + AtomsGroup= apps.get_model("django_gem_taxonomy", "AtomsGroup") + Atom= apps.get_model("django_gem_taxonomy", "Atom") + Param= apps.get_model("django_gem_taxonomy", "Param") + Version2= apps.get_model("django_gem_taxonomy", "Version2") + Attribute2= apps.get_model("django_gem_taxonomy", "Attribute2") + AtomsGroup2= apps.get_model("django_gem_taxonomy", "AtomsGroup2") + Atom2= apps.get_model("django_gem_taxonomy", "Atom2") + Param2= apps.get_model("django_gem_taxonomy", "Param2") + + vers = Version2.objects.using(db_alias).get(vers='4.0') + + Param.objects.using(db_alias).delete() + Atom.objects.using(db_alias).delete() + AtomsGroup.objects.using(db_alias).delete() + Attribute.objects.using(db_alias).delete() + + for attr_in in Attribute2.objects.using(db_alias).filter(vers=vers): + attr = Attribute.objects.using(db_alias).create( + name=attr_in.name, + prog=attr_in.prog, + title=attr_in.title, + ) + + for atg_in in AtomsGroup2.objects.using(db_alias).filter(vers=vers): + AtomsGroup.objects.using(db_alias).create( + name=atg_in.name, + prog=atg_in.prog, + title=atg_in.title, + attr=Attribute.objects.using(db_alias).get(name=atg_in.attr.name) + ) + for at_in in Atom2.objects.using(db_alias).filter(vers=vers): + if at_in.attr is not None: + attr = Attribute.objects.using(db_alias).get(name=at_in.attr.name) + else: + attr = None + + if at_in.group is not None: + group = AtomsGroup.objects.using(db_alias).get(attr=attr, + name=at_in.group.name) + else: + group = None + + atom = Atom.objects.using(db_alias).create( + name=at_in.name, + prog=at_in.prog, + title=at_in.title, + desc=at_in.desc, + args=at_in.args, + params=at_in.params, + type=at_in.type, + attr=attr, + group=group, + ) + + for at_in in Atom2.objects.using(db_alias).all(): + atom = Atom.objects.using(db_alias).get(name=at_in.name) + for dep in at_in.deps.all(): + atom.deps.add(Atom.objects.using(db_alias).get(name=dep.name)) + for den in at_in.deny.all(): + atom.deny.add(Atom.objects.using(db_alias).get(name=den.name)) + + for par_in in Param2.objects.using(db_alias).all(): + Param.objects.create( + atom=Atom.objects.get(name=par_in.atom.name), + name=par_in.name, + title=par_in.title, + desc=par_in.desc, + prog=par_in.prog, + ) + + Param2.objects.using(db_alias).delete() + Atom2.objects.using(db_alias).delete() + AtomsGroup2.objects.using(db_alias).delete() + Attribute2.objects.using(db_alias).delete() + +class Migration(migrations.Migration): + + dependencies = [ + ('django_gem_taxonomy', '0003_version2_attribute2_atomsgroup2_atom2_param2'), + ] + + operations = [ + migrations.RunPython(dgtaxonomy2dgtaxonomy_two, dgtaxonomy_two2dgtaxonomy) + ] diff --git a/django_gem_taxonomy/models.py b/django_gem_taxonomy/models.py index cae2d35..96b415a 100644 --- a/django_gem_taxonomy/models.py +++ b/django_gem_taxonomy/models.py @@ -77,3 +77,79 @@ class Meta: title = models.TextField() desc = models.TextField() prog = models.IntegerField() + + +class Version2(models.Model): + vers = models.CharField(max_length=16, primary_key=True) + desc = models.TextField() + is_default = models.BooleanField(default=False) + +class Attribute2(models.Model): + vers = models.ForeignKey(Version2, on_delete=models.CASCADE) + name = models.CharField(max_length=256) + prog = models.IntegerField() + title = models.TextField() + + class Meta: + unique_together = [['vers', 'name'], + ['vers', 'prog']] + +class AtomsGroup2(models.Model): + vers = models.ForeignKey(Version2, on_delete=models.CASCADE) + attr = models.ForeignKey(Attribute2, on_delete=models.CASCADE) + name = models.CharField(max_length=256) + prog = models.IntegerField() + title = models.TextField() + # mutex identify if it is possible or not dropdown multi-selection + mutex = models.BooleanField(default=True) + + class Meta: + unique_together = [['vers', 'attr', 'name'], + ['vers', 'attr', 'prog']] + + +# TODO: parameters description atom(param1[,param2[...,paramN]]) +# class AtomParam(models.Model): +# atom = models.ForeignKey(Atom) + +# TODO: arguments description atom[:arg1[:arg2[...:argN]]] +# class AtomArg(models.Model): +# atom = models.ForeignKey(Atom) + + +class Atom2(models.Model): + vers = models.ForeignKey(Version2, on_delete=models.CASCADE) + attr = models.ForeignKey(Attribute2, on_delete=models.CASCADE, null=True) + group = models.ForeignKey(AtomsGroup2, on_delete=models.CASCADE, null=True) + name = models.CharField(max_length=32) + prog = models.IntegerField() + title = models.TextField() + desc = models.TextField() + type = models.TextField() + args = models.JSONField(blank=True, null=True) + params = models.JSONField(blank=True, null=True) + deps = models.ManyToManyField('self', symmetrical=False, + related_name='revdeps') + deny = models.ManyToManyField('self', symmetrical=False, + related_name='revdeny') + # is_pseudoid = models.BooleanField() + + class Meta: + unique_together = [['vers', 'name'], + ['vers', 'attr', 'group', 'prog']] + + def entry_type(self): + return json.loads(self.type) + + +class Param2(models.Model): + vers = models.ForeignKey(Version2, on_delete=models.CASCADE) + atom = models.ForeignKey(Atom2, on_delete=models.CASCADE, null=True) + name = models.CharField(max_length=32) + prog = models.IntegerField() + title = models.TextField() + desc = models.TextField() + + class Meta: + unique_together = [['vers', 'atom', 'name'], + ['vers', 'atom', 'prog']] From f3941c3dbf36c6d731e25ca572e96e471eb7a0ed Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Wed, 23 Sep 2026 16:22:19 +0200 Subject: [PATCH 2/9] final step of multi-stage migration --- .../migrations/0005_remove_old_models.py | 25 ++++++ .../migrations/0006_rename_new_models.py | 33 +++++++ django_gem_taxonomy/models.py | 86 +++---------------- 3 files changed, 71 insertions(+), 73 deletions(-) create mode 100644 django_gem_taxonomy/migrations/0005_remove_old_models.py create mode 100644 django_gem_taxonomy/migrations/0006_rename_new_models.py diff --git a/django_gem_taxonomy/migrations/0005_remove_old_models.py b/django_gem_taxonomy/migrations/0005_remove_old_models.py new file mode 100644 index 0000000..26c2685 --- /dev/null +++ b/django_gem_taxonomy/migrations/0005_remove_old_models.py @@ -0,0 +1,25 @@ +# Generated by Django 4.2.30 on 2026-09-23 13:50 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('django_gem_taxonomy', '0004_content_migration'), + ] + + operations = [ + migrations.DeleteModel( + name='Param', + ), + migrations.DeleteModel( + name='Atom', + ), + migrations.DeleteModel( + name='AtomsGroup', + ), + migrations.DeleteModel( + name='Attribute', + ), + ] diff --git a/django_gem_taxonomy/migrations/0006_rename_new_models.py b/django_gem_taxonomy/migrations/0006_rename_new_models.py new file mode 100644 index 0000000..55b076b --- /dev/null +++ b/django_gem_taxonomy/migrations/0006_rename_new_models.py @@ -0,0 +1,33 @@ +# Generated by Django 4.2.30 on 2026-09-23 14:01 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('django_gem_taxonomy', '0005_remove_old_models'), + ] + + operations = [ + migrations.RenameModel( + old_name='Version2', + new_name='Version', + ), + migrations.RenameModel( + old_name='Attribute2', + new_name='Attribute', + ), + migrations.RenameModel( + old_name='AtomsGroup2', + new_name='AtomsGroup', + ), + migrations.RenameModel( + old_name='Atom2', + new_name='Atom', + ), + migrations.RenameModel( + old_name='Param2', + new_name='Param', + ), + ] diff --git a/django_gem_taxonomy/models.py b/django_gem_taxonomy/models.py index 96b415a..e8f587d 100644 --- a/django_gem_taxonomy/models.py +++ b/django_gem_taxonomy/models.py @@ -19,73 +19,13 @@ from django.db import models import json -class Attribute(models.Model): - name = models.CharField(max_length=256, primary_key=True) - prog = models.IntegerField() - title = models.TextField() - - -class AtomsGroup(models.Model): - class Meta: - unique_together = [['attr', 'prog']] - - name = models.CharField(max_length=256, primary_key=True) - prog = models.IntegerField() - title = models.TextField() - attr = models.ForeignKey(Attribute, on_delete=models.CASCADE) - # mutex identify if it is possible or not dropdown multi-selection - mutex = models.BooleanField(default=True) - -# TODO: parameters description atom(param1[,param2[...,paramN]]) -# class AtomParam(models.Model): -# atom = models.ForeignKey(Atom) - -# TODO: arguments description atom[:arg1[:arg2[...:argN]]] -# class AtomArg(models.Model): -# atom = models.ForeignKey(Atom) - - -class Atom(models.Model): - class Meta: - unique_together = [['group', 'prog']] - - name = models.CharField(max_length=32, primary_key=True) - prog = models.IntegerField() - title = models.TextField() - desc = models.TextField() - group = models.ForeignKey(AtomsGroup, on_delete=models.CASCADE, null=True) - attr = models.ForeignKey(Attribute, on_delete=models.CASCADE, null=True) - type = models.TextField() - args = models.JSONField(blank=True, null=True) - params = models.JSONField(blank=True, null=True) - deps = models.ManyToManyField('self', symmetrical=False, - related_name='revdeps') - deny = models.ManyToManyField('self', symmetrical=False, - related_name='revdeny') - # is_pseudoid = models.BooleanField() - - def entry_type(self): - return json.loads(self.type) - - -class Param(models.Model): - class Meta: - unique_together = [['atom', 'name'], ['atom', 'name', 'prog']] - - atom = models.ForeignKey(Atom, on_delete=models.CASCADE, null=True) - name = models.CharField(max_length=32) - title = models.TextField() - desc = models.TextField() - prog = models.IntegerField() - - -class Version2(models.Model): +class Version(models.Model): vers = models.CharField(max_length=16, primary_key=True) desc = models.TextField() is_default = models.BooleanField(default=False) -class Attribute2(models.Model): - vers = models.ForeignKey(Version2, on_delete=models.CASCADE) +class Attribute(models.Model): + vers = models.ForeignKey(Version, on_delete=models.CASCADE) name = models.CharField(max_length=256) prog = models.IntegerField() title = models.TextField() @@ -94,9 +34,9 @@ class Meta: unique_together = [['vers', 'name'], ['vers', 'prog']] -class AtomsGroup2(models.Model): - vers = models.ForeignKey(Version2, on_delete=models.CASCADE) - attr = models.ForeignKey(Attribute2, on_delete=models.CASCADE) +class AtomsGroup(models.Model): + vers = models.ForeignKey(Version, on_delete=models.CASCADE) + attr = models.ForeignKey(Attribute, on_delete=models.CASCADE) name = models.CharField(max_length=256) prog = models.IntegerField() title = models.TextField() @@ -117,10 +57,10 @@ class Meta: # atom = models.ForeignKey(Atom) -class Atom2(models.Model): - vers = models.ForeignKey(Version2, on_delete=models.CASCADE) - attr = models.ForeignKey(Attribute2, on_delete=models.CASCADE, null=True) - group = models.ForeignKey(AtomsGroup2, on_delete=models.CASCADE, null=True) +class Atom(models.Model): + vers = models.ForeignKey(Version, on_delete=models.CASCADE) + attr = models.ForeignKey(Attribute, on_delete=models.CASCADE, null=True) + group = models.ForeignKey(AtomsGroup, on_delete=models.CASCADE, null=True) name = models.CharField(max_length=32) prog = models.IntegerField() title = models.TextField() @@ -142,9 +82,9 @@ def entry_type(self): return json.loads(self.type) -class Param2(models.Model): - vers = models.ForeignKey(Version2, on_delete=models.CASCADE) - atom = models.ForeignKey(Atom2, on_delete=models.CASCADE, null=True) +class Param(models.Model): + vers = models.ForeignKey(Version, on_delete=models.CASCADE) + atom = models.ForeignKey(Atom, on_delete=models.CASCADE, null=True) name = models.CharField(max_length=32) prog = models.IntegerField() title = models.TextField() From c07fe263e0a5aea6ce41af4d38eda72900477235 Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Wed, 23 Sep 2026 16:32:17 +0200 Subject: [PATCH 3/9] update version to 1.5.0 --- .../migrations/0001_initial.py | 37 +++++-------------- .../0002_atom_deny_alter_param_id.py | 23 ++++++++++++ django_gem_taxonomy/version.py | 2 +- 3 files changed, 33 insertions(+), 29 deletions(-) create mode 100644 django_gem_taxonomy/migrations/0002_atom_deny_alter_param_id.py diff --git a/django_gem_taxonomy/migrations/0001_initial.py b/django_gem_taxonomy/migrations/0001_initial.py index 2488040..a707b1e 100644 --- a/django_gem_taxonomy/migrations/0001_initial.py +++ b/django_gem_taxonomy/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.27 on 2026-08-21 15:45 +# Generated by Django 4.2.11 on 2024-11-13 16:45 from django.db import migrations, models import django.db.models.deletion @@ -12,47 +12,31 @@ class Migration(migrations.Migration): ] operations = [ - migrations.CreateModel( - name='Version', - fields=[ - ('vers', models.CharField(max_length=16, primary_key=True, serialize=False)), - ('desc', models.TextField()), - ('is_default', models.BooleanField(default=False)), - ], - ), migrations.CreateModel( name='Attribute', fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=256)), + ('name', models.CharField(max_length=256, primary_key=True, serialize=False)), ('prog', models.IntegerField()), ('title', models.TextField()), - ('vers', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='django_gem_taxonomy.version')), ], - options={ - 'unique_together': {('vers', 'prog'), ('vers', 'name')}, - }, ), migrations.CreateModel( name='AtomsGroup', fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=256)), + ('name', models.CharField(max_length=256, primary_key=True, serialize=False)), ('prog', models.IntegerField()), ('title', models.TextField()), ('mutex', models.BooleanField(default=True)), ('attr', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='django_gem_taxonomy.attribute')), - ('vers', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='django_gem_taxonomy.version')), ], options={ - 'unique_together': {('vers', 'attr', 'prog'), ('vers', 'attr', 'name')}, + 'unique_together': {('attr', 'prog')}, }, ), migrations.CreateModel( name='Atom', fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=32)), + ('name', models.CharField(max_length=32, primary_key=True, serialize=False)), ('prog', models.IntegerField()), ('title', models.TextField()), ('desc', models.TextField()), @@ -60,28 +44,25 @@ class Migration(migrations.Migration): ('args', models.JSONField(blank=True, null=True)), ('params', models.JSONField(blank=True, null=True)), ('attr', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='django_gem_taxonomy.attribute')), - ('deny', models.ManyToManyField(related_name='revdeny', to='django_gem_taxonomy.atom')), ('deps', models.ManyToManyField(related_name='revdeps', to='django_gem_taxonomy.atom')), ('group', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='django_gem_taxonomy.atomsgroup')), - ('vers', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='django_gem_taxonomy.version')), ], options={ - 'unique_together': {('vers', 'name'), ('vers', 'attr', 'group', 'prog')}, + 'unique_together': {('group', 'prog')}, }, ), migrations.CreateModel( name='Param', fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=32)), - ('prog', models.IntegerField()), ('title', models.TextField()), ('desc', models.TextField()), + ('prog', models.IntegerField()), ('atom', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='django_gem_taxonomy.atom')), - ('vers', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='django_gem_taxonomy.version')), ], options={ - 'unique_together': {('vers', 'atom', 'name'), ('vers', 'atom', 'prog')}, + 'unique_together': {('atom', 'name'), ('atom', 'name', 'prog')}, }, ), ] diff --git a/django_gem_taxonomy/migrations/0002_atom_deny_alter_param_id.py b/django_gem_taxonomy/migrations/0002_atom_deny_alter_param_id.py new file mode 100644 index 0000000..5fcdee1 --- /dev/null +++ b/django_gem_taxonomy/migrations/0002_atom_deny_alter_param_id.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.27 on 2026-05-12 17:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('django_gem_taxonomy', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='atom', + name='deny', + field=models.ManyToManyField(related_name='revdeny', to='django_gem_taxonomy.atom'), + ), + migrations.AlterField( + model_name='param', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + ] diff --git a/django_gem_taxonomy/version.py b/django_gem_taxonomy/version.py index 9e0feee..77f1c8e 100644 --- a/django_gem_taxonomy/version.py +++ b/django_gem_taxonomy/version.py @@ -1 +1 @@ -__version__ = '1.4.4' +__version__ = '1.5.0' From 7eba488b4fb37bd06424485b8a6254b10399ae61 Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Wed, 23 Sep 2026 17:22:32 +0200 Subject: [PATCH 4/9] missing '.using(db_alias)' added --- django_gem_taxonomy/migrations/0004_content_migration.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/django_gem_taxonomy/migrations/0004_content_migration.py b/django_gem_taxonomy/migrations/0004_content_migration.py index 57908d8..bcc5b3f 100644 --- a/django_gem_taxonomy/migrations/0004_content_migration.py +++ b/django_gem_taxonomy/migrations/0004_content_migration.py @@ -75,9 +75,9 @@ def dgtaxonomy2dgtaxonomy_two(apps, schema_editor): atom.deny.add(Atom2.objects.using(db_alias).get(vers=vers, name=den.name)) for par_in in Param.objects.using(db_alias).all(): - Param2.objects.create( + Param2.objects.using(db_alias).create( vers=vers, - atom=Atom2.objects.get(vers=vers, name=par_in.atom.name), + atom=Atom2.objects.using(db_alias).get(vers=vers, name=par_in.atom.name), name=par_in.name, title=par_in.title, desc=par_in.desc, @@ -150,8 +150,8 @@ def dgtaxonomy_two2dgtaxonomy(apps, schema_editor): atom.deny.add(Atom.objects.using(db_alias).get(name=den.name)) for par_in in Param2.objects.using(db_alias).all(): - Param.objects.create( - atom=Atom.objects.get(name=par_in.atom.name), + Param.objects.using(db_alias).create( + atom=Atom.objects.using(db_alias).get(name=par_in.atom.name), name=par_in.name, title=par_in.title, desc=par_in.desc, From 8d9b891d40b4b67ae20a7ccb8d49a7d91d2d8c39 Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Wed, 23 Sep 2026 17:23:12 +0200 Subject: [PATCH 5/9] copy 'mutex' field between Param's objects --- django_gem_taxonomy/migrations/0004_content_migration.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/django_gem_taxonomy/migrations/0004_content_migration.py b/django_gem_taxonomy/migrations/0004_content_migration.py index bcc5b3f..5ee4f3d 100644 --- a/django_gem_taxonomy/migrations/0004_content_migration.py +++ b/django_gem_taxonomy/migrations/0004_content_migration.py @@ -39,6 +39,7 @@ def dgtaxonomy2dgtaxonomy_two(apps, schema_editor): name=atg_in.name, prog=atg_in.prog, title=atg_in.title, + mutex=atg_in.mutex, attr=Attribute2.objects.using(db_alias).get(vers=vers, name=atg_in.attr.name) ) @@ -116,6 +117,7 @@ def dgtaxonomy_two2dgtaxonomy(apps, schema_editor): name=atg_in.name, prog=atg_in.prog, title=atg_in.title, + mutex=atg_in.mutex, attr=Attribute.objects.using(db_alias).get(name=atg_in.attr.name) ) for at_in in Atom2.objects.using(db_alias).filter(vers=vers): From 62dadc9769f4ce63cf37a7e04a6a8d7da66da569 Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Wed, 23 Sep 2026 17:24:06 +0200 Subject: [PATCH 6/9] remove nullability of 'atom' field for Param class --- .../migrations/0007_alter_param_atom.py | 19 +++++++++++++++++++ django_gem_taxonomy/models.py | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 django_gem_taxonomy/migrations/0007_alter_param_atom.py diff --git a/django_gem_taxonomy/migrations/0007_alter_param_atom.py b/django_gem_taxonomy/migrations/0007_alter_param_atom.py new file mode 100644 index 0000000..6e9765f --- /dev/null +++ b/django_gem_taxonomy/migrations/0007_alter_param_atom.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.30 on 2026-09-23 15:15 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('django_gem_taxonomy', '0006_rename_new_models'), + ] + + operations = [ + migrations.AlterField( + model_name='param', + name='atom', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='django_gem_taxonomy.atom'), + ), + ] diff --git a/django_gem_taxonomy/models.py b/django_gem_taxonomy/models.py index e8f587d..d199972 100644 --- a/django_gem_taxonomy/models.py +++ b/django_gem_taxonomy/models.py @@ -84,7 +84,7 @@ def entry_type(self): class Param(models.Model): vers = models.ForeignKey(Version, on_delete=models.CASCADE) - atom = models.ForeignKey(Atom, on_delete=models.CASCADE, null=True) + atom = models.ForeignKey(Atom, on_delete=models.CASCADE) name = models.CharField(max_length=32) prog = models.IntegerField() title = models.TextField() From e72df757a83e8661a36127675fff6033363cbf62 Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Wed, 23 Sep 2026 17:29:08 +0200 Subject: [PATCH 7/9] add version entry during migration just if previous version of data already there and revert data migration just if version '4.0' of dataset already exists --- django_gem_taxonomy/migrations/0004_content_migration.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/django_gem_taxonomy/migrations/0004_content_migration.py b/django_gem_taxonomy/migrations/0004_content_migration.py index 5ee4f3d..8d702f4 100644 --- a/django_gem_taxonomy/migrations/0004_content_migration.py +++ b/django_gem_taxonomy/migrations/0004_content_migration.py @@ -16,6 +16,10 @@ def dgtaxonomy2dgtaxonomy_two(apps, schema_editor): # migrate to the new schema that includes version as part of the # unique constraints, the old version has '4.0' as implied + + if Attribute.objects.using(db_alias).all().count() == 0: + return + try: vers = Version2.objects.using(db_alias).get(vers='4.0') except Version2.DoesNotExist: @@ -98,7 +102,10 @@ def dgtaxonomy_two2dgtaxonomy(apps, schema_editor): Atom2= apps.get_model("django_gem_taxonomy", "Atom2") Param2= apps.get_model("django_gem_taxonomy", "Param2") - vers = Version2.objects.using(db_alias).get(vers='4.0') + try: + vers = Version2.objects.using(db_alias).get(vers='4.0') + except Version2.DoesNotExist: + return Param.objects.using(db_alias).delete() Atom.objects.using(db_alias).delete() From b320bfeeec159510722724b48af54fa5ab26db89 Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Wed, 23 Sep 2026 17:35:37 +0200 Subject: [PATCH 8/9] filter data using specific version (4.0) instead of get all entries --- django_gem_taxonomy/migrations/0004_content_migration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/django_gem_taxonomy/migrations/0004_content_migration.py b/django_gem_taxonomy/migrations/0004_content_migration.py index 8d702f4..a59d156 100644 --- a/django_gem_taxonomy/migrations/0004_content_migration.py +++ b/django_gem_taxonomy/migrations/0004_content_migration.py @@ -151,14 +151,14 @@ def dgtaxonomy_two2dgtaxonomy(apps, schema_editor): group=group, ) - for at_in in Atom2.objects.using(db_alias).all(): + for at_in in Atom2.objects.using(db_alias).filter(vers=vers): atom = Atom.objects.using(db_alias).get(name=at_in.name) for dep in at_in.deps.all(): atom.deps.add(Atom.objects.using(db_alias).get(name=dep.name)) for den in at_in.deny.all(): atom.deny.add(Atom.objects.using(db_alias).get(name=den.name)) - for par_in in Param2.objects.using(db_alias).all(): + for par_in in Param2.objects.using(db_alias).filter(vers=vers): Param.objects.using(db_alias).create( atom=Atom.objects.using(db_alias).get(name=par_in.atom.name), name=par_in.name, From 1a0e5b94a51c2d142dc4c4c0c6d06e5b4b89aba2 Mon Sep 17 00:00:00 2001 From: Matteo Nastasi Date: Thu, 24 Sep 2026 16:54:22 +0200 Subject: [PATCH 9/9] in Param model atom is 'nullable' so the code must manage this possibility (even if it never happens) --- .../migrations/0004_content_migration.py | 27 +++++++++++++------ .../migrations/0007_alter_param_atom.py | 19 ------------- django_gem_taxonomy/models.py | 2 +- 3 files changed, 20 insertions(+), 28 deletions(-) delete mode 100644 django_gem_taxonomy/migrations/0007_alter_param_atom.py diff --git a/django_gem_taxonomy/migrations/0004_content_migration.py b/django_gem_taxonomy/migrations/0004_content_migration.py index a59d156..da1ece7 100644 --- a/django_gem_taxonomy/migrations/0004_content_migration.py +++ b/django_gem_taxonomy/migrations/0004_content_migration.py @@ -44,18 +44,20 @@ def dgtaxonomy2dgtaxonomy_two(apps, schema_editor): prog=atg_in.prog, title=atg_in.title, mutex=atg_in.mutex, - attr=Attribute2.objects.using(db_alias).get(vers=vers, name=atg_in.attr.name) + attr=Attribute2.objects.using(db_alias).get( + vers=vers, name=atg_in.attr.name) ) for at_in in Atom.objects.using(db_alias).all(): if at_in.attr is not None: - attr = Attribute2.objects.using(db_alias).get(vers=vers, name=at_in.attr.name) + attr = Attribute2.objects.using(db_alias).get( + vers=vers, name=at_in.attr.name) else: attr = None if at_in.group is not None: - group = AtomsGroup2.objects.using(db_alias).get(vers=vers, attr=attr, - name=at_in.group.name) + group = AtomsGroup2.objects.using(db_alias).get( + vers=vers, attr=attr, name=at_in.group.name) else: group = None @@ -75,14 +77,20 @@ def dgtaxonomy2dgtaxonomy_two(apps, schema_editor): for at_in in Atom.objects.using(db_alias).all(): atom = Atom2.objects.using(db_alias).get(vers=vers, name=at_in.name) for dep in at_in.deps.all(): - atom.deps.add(Atom2.objects.using(db_alias).get(vers=vers, name=dep.name)) + atom.deps.add(Atom2.objects.using(db_alias).get( + vers=vers, name=dep.name)) for den in at_in.deny.all(): - atom.deny.add(Atom2.objects.using(db_alias).get(vers=vers, name=den.name)) + atom.deny.add(Atom2.objects.using(db_alias).get( + vers=vers, name=den.name)) for par_in in Param.objects.using(db_alias).all(): + atom = None + if par_in.atom is not None: + atom = Atom2.objects.using(db_alias).get( + vers=vers, name=par_in.atom.name) Param2.objects.using(db_alias).create( vers=vers, - atom=Atom2.objects.using(db_alias).get(vers=vers, name=par_in.atom.name), + atom=atom, name=par_in.name, title=par_in.title, desc=par_in.desc, @@ -159,8 +167,11 @@ def dgtaxonomy_two2dgtaxonomy(apps, schema_editor): atom.deny.add(Atom.objects.using(db_alias).get(name=den.name)) for par_in in Param2.objects.using(db_alias).filter(vers=vers): + atom = None + if par_in.atom is not None: + atom = Atom.objects.using(db_alias).get(name=par_in.atom.name) Param.objects.using(db_alias).create( - atom=Atom.objects.using(db_alias).get(name=par_in.atom.name), + atom=atom, name=par_in.name, title=par_in.title, desc=par_in.desc, diff --git a/django_gem_taxonomy/migrations/0007_alter_param_atom.py b/django_gem_taxonomy/migrations/0007_alter_param_atom.py deleted file mode 100644 index 6e9765f..0000000 --- a/django_gem_taxonomy/migrations/0007_alter_param_atom.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 4.2.30 on 2026-09-23 15:15 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('django_gem_taxonomy', '0006_rename_new_models'), - ] - - operations = [ - migrations.AlterField( - model_name='param', - name='atom', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='django_gem_taxonomy.atom'), - ), - ] diff --git a/django_gem_taxonomy/models.py b/django_gem_taxonomy/models.py index d199972..e8f587d 100644 --- a/django_gem_taxonomy/models.py +++ b/django_gem_taxonomy/models.py @@ -84,7 +84,7 @@ def entry_type(self): class Param(models.Model): vers = models.ForeignKey(Version, on_delete=models.CASCADE) - atom = models.ForeignKey(Atom, on_delete=models.CASCADE) + atom = models.ForeignKey(Atom, on_delete=models.CASCADE, null=True) name = models.CharField(max_length=32) prog = models.IntegerField() title = models.TextField()