diff --git a/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py b/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py
index e9110026a1538ff945746d03cbe033aa72acb1b0..f9a2ba99af6b680fa12224b6b0ae313adca1b469 100644
--- a/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py
+++ b/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.2.12 on 2020-05-26 07:14
+# Generated by Django 2.2.12 on 2020-05-26 08:01
 
 from django.conf import settings
 import django.contrib.postgres.fields
@@ -26,6 +26,22 @@ class Migration(migrations.Migration):
                 'abstract': False,
             },
         ),
+        migrations.CreateModel(
+            name='AntennaSet',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
+                ('name', models.CharField(help_text='Human-readable name of this object.', max_length=128)),
+                ('description', models.CharField(help_text='A longer description of this object.', max_length=255)),
+                ('rcus', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), size=128)),
+                ('inputs', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, size=128)),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
         migrations.CreateModel(
             name='Cluster',
             fields=[
@@ -94,7 +110,21 @@ class Migration(migrations.Migration):
                 ('expected_size', models.BigIntegerField(help_text='Expected size of dataproduct size, in bytes. Used for scheduling purposes. NULL if size is unknown (NULLable).', null=True)),
                 ('size', models.BigIntegerField(help_text='Dataproduct size, in bytes. Used for accounting purposes. NULL if size is (yet) unknown (NULLable).', null=True)),
                 ('feedback_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Dataproduct properties, as reported by the producing process.')),
-                ('dataformat', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tmssapp.Dataformat')),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='DataproductArchiveInfo',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
+                ('storage_ticket', models.CharField(help_text='Archive-system identifier.', max_length=128)),
+                ('public_since', models.DateTimeField(help_text='Dataproduct is available for public download since this moment, or NULL if dataproduct is not (NULLable).', null=True)),
+                ('corrupted_since', models.DateTimeField(help_text='Earliest timestamp from which this dataproduct is known to be partially or fully corrupt, or NULL if dataproduct is not known to be corrupt (NULLable).', null=True)),
             ],
             options={
                 'abstract': False,
@@ -112,6 +142,16 @@ class Migration(migrations.Migration):
                 ('version', models.CharField(help_text='Version of this template (with respect to other templates of the same name).', max_length=128)),
                 ('schema', django.contrib.postgres.fields.jsonb.JSONField(help_text='Schema for the configurable parameters needed to use this template.')),
             ],
+        ),
+        migrations.CreateModel(
+            name='DataproductHash',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
+                ('hash', models.CharField(help_text='Hash value.', max_length=128)),
+            ],
             options={
                 'abstract': False,
             },
@@ -128,6 +168,16 @@ class Migration(migrations.Migration):
                 ('version', models.CharField(help_text='Version of this template (with respect to other templates of the same name).', max_length=128)),
                 ('schema', django.contrib.postgres.fields.jsonb.JSONField(help_text='Schema for the configurable parameters needed to use this template.')),
             ],
+        ),
+        migrations.CreateModel(
+            name='DataproductTransform',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
+                ('identity', models.BooleanField(help_text='TRUE if this transform only copies, tars, or losslessly compresses its input, FALSE if the transform changes the data. Allows for efficient reasoning about data duplication.')),
+            ],
             options={
                 'abstract': False,
             },
@@ -141,6 +191,99 @@ class Migration(migrations.Migration):
                 'abstract': False,
             },
         ),
+        migrations.CreateModel(
+            name='DefaultDataproductSpecificationsTemplate',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
+                ('name', models.CharField(max_length=128, unique=True)),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='DefaultGeneratorTemplate',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
+                ('name', models.CharField(max_length=128, unique=True)),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='DefaultSchedulingUnitTemplate',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
+                ('name', models.CharField(max_length=128, unique=True)),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='DefaultSubtaskTemplate',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
+                ('name', models.CharField(max_length=128, unique=True)),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='DefaultTaskTemplate',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
+                ('name', models.CharField(max_length=128, unique=True)),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='DefaultWorkRelationSelectionTemplate',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
+                ('name', models.CharField(max_length=128, unique=True)),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='Filesystem',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
+                ('name', models.CharField(help_text='Human-readable name of this object.', max_length=128)),
+                ('description', models.CharField(help_text='A longer description of this object.', max_length=255)),
+                ('capacity', models.BigIntegerField(help_text='Capacity in bytes')),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
         migrations.CreateModel(
             name='GeneratorTemplate',
             fields=[
@@ -154,9 +297,6 @@ class Migration(migrations.Migration):
                 ('schema', django.contrib.postgres.fields.jsonb.JSONField(help_text='Schema for the configurable parameters needed to use this template.')),
                 ('create_function', models.CharField(help_text='Python function to call to execute the generator.', max_length=128)),
             ],
-            options={
-                'abstract': False,
-            },
         ),
         migrations.CreateModel(
             name='Project',
@@ -171,7 +311,26 @@ class Migration(migrations.Migration):
                 ('private_data', models.BooleanField(default=True, help_text='True if data of this project is sensitive. Sensitive data is not made public.')),
                 ('expert', models.BooleanField(default=False, help_text='Expert projects put more responsibility on the PI.')),
                 ('filler', models.BooleanField(default=False, help_text='Use this project to fill up idle telescope time.')),
-                ('cycle', models.ForeignKey(help_text='Cycle(s) to which this project belongs (NULLable).', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='projects', to='tmssapp.Cycle')),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='ProjectQuota',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('value', models.FloatField(help_text='Resource Quota value')),
+            ],
+        ),
+        migrations.CreateModel(
+            name='ResourceType',
+            fields=[
+                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
+                ('description', models.CharField(help_text='A longer description of this object.', max_length=255)),
+                ('name', models.CharField(help_text='Human-readable name of this object.', max_length=128, primary_key=True, serialize=False)),
             ],
             options={
                 'abstract': False,
@@ -250,8 +409,6 @@ class Migration(migrations.Migration):
                 ('description', models.CharField(help_text='A longer description of this object.', max_length=255)),
                 ('requirements_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Scheduling and/or quality requirements for this run.')),
                 ('generator_instance_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Parameter value that generated this run draft (NULLable).', null=True)),
-                ('copies', models.ForeignKey(help_text='Source reference, if we are a copy (NULLable).', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='copied_from', to='tmssapp.SchedulingUnitDraft')),
-                ('copy_reason', models.ForeignKey(help_text='Reason why source was copied (NULLable).', null=True, on_delete=django.db.models.deletion.PROTECT, to='tmssapp.CopyReason')),
             ],
             options={
                 'abstract': False,
@@ -269,9 +426,6 @@ class Migration(migrations.Migration):
                 ('version', models.CharField(help_text='Version of this template (with respect to other templates of the same name).', max_length=128)),
                 ('schema', django.contrib.postgres.fields.jsonb.JSONField(help_text='Schema for the configurable parameters needed to use this template.')),
             ],
-            options={
-                'abstract': False,
-            },
         ),
         migrations.CreateModel(
             name='StationType',
@@ -294,9 +448,19 @@ class Migration(migrations.Migration):
                 ('specifications_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Final specifications, as input for the controller.')),
                 ('do_cancel', models.DateTimeField(help_text='Timestamp when the subtask has been ordered to cancel (NULLable).', null=True)),
                 ('priority', models.IntegerField(help_text='Absolute priority of this subtask (higher value means more important).')),
-                ('cluster', models.ForeignKey(help_text='Where the Subtask is scheduled to run (NULLable).', null=True, on_delete=django.db.models.deletion.PROTECT, to='tmssapp.Cluster')),
-                ('created_or_updated_by_user', models.ForeignKey(editable=False, help_text='The user who created / updated the subtask.', null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
-                ('schedule_method', models.ForeignKey(help_text='Which method to use for scheduling this Subtask. One of (MANUAL, BATCH, DYNAMIC).', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.ScheduleMethod')),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='SubtaskInput',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
+                ('selection_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Filter to apply to the dataproducts of the producer, to derive input dataproducts when scheduling.')),
             ],
             options={
                 'abstract': False,
@@ -314,6 +478,15 @@ class Migration(migrations.Migration):
                 ('version', models.CharField(help_text='Version of this template (with respect to other templates of the same name).', max_length=128)),
                 ('schema', django.contrib.postgres.fields.jsonb.JSONField(help_text='Schema for the configurable parameters needed to use this template.')),
             ],
+        ),
+        migrations.CreateModel(
+            name='SubtaskOutput',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
+            ],
             options={
                 'abstract': False,
             },
@@ -327,6 +500,34 @@ class Migration(migrations.Migration):
                 'abstract': False,
             },
         ),
+        migrations.CreateModel(
+            name='SubtaskStateLog',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
+                ('user_identifier', models.CharField(editable=False, help_text='The ID of the user who changed the state of the subtask.', max_length=128, null=True)),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='SubtaskTemplate',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
+                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
+                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
+                ('name', models.CharField(help_text='Human-readable name of this object.', max_length=128)),
+                ('description', models.CharField(help_text='A longer description of this object.', max_length=255)),
+                ('version', models.CharField(help_text='Version of this template (with respect to other templates of the same name).', max_length=128)),
+                ('schema', django.contrib.postgres.fields.jsonb.JSONField(help_text='Schema for the configurable parameters needed to use this template.')),
+                ('queue', models.BooleanField(default=False)),
+                ('realtime', models.BooleanField(default=False)),
+            ],
+        ),
         migrations.CreateModel(
             name='SubtaskType',
             fields=[
@@ -367,8 +568,6 @@ class Migration(migrations.Migration):
                 ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
                 ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
                 ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
-                ('dataformats', models.ManyToManyField(blank=True, to='tmssapp.Dataformat')),
-                ('datatype', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tmssapp.Datatype')),
             ],
             options={
                 'abstract': False,
@@ -384,91 +583,167 @@ class Migration(migrations.Migration):
                 ('name', models.CharField(help_text='Human-readable name of this object.', max_length=128)),
                 ('description', models.CharField(help_text='A longer description of this object.', max_length=255)),
                 ('specifications_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Specifications for this task.')),
-                ('copies', models.ForeignKey(help_text='Source reference, if we are a copy (NULLable).', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='copied_from', to='tmssapp.TaskDraft')),
-                ('copy_reason', models.ForeignKey(help_text='Reason why source was copied (NULLable).', null=True, on_delete=django.db.models.deletion.PROTECT, to='tmssapp.CopyReason')),
-                ('scheduling_unit_draft', models.ForeignKey(help_text='Scheduling Unit draft to which this task draft belongs.', on_delete=django.db.models.deletion.CASCADE, related_name='task_drafts', to='tmssapp.SchedulingUnitDraft')),
             ],
             options={
                 'abstract': False,
             },
         ),
         migrations.CreateModel(
-            name='TaskTemplate',
+            name='TaskRelationBlueprint',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                 ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
                 ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
                 ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
-                ('name', models.CharField(help_text='Human-readable name of this object.', max_length=128)),
-                ('description', models.CharField(help_text='A longer description of this object.', max_length=255)),
-                ('version', models.CharField(help_text='Version of this template (with respect to other templates of the same name).', max_length=128)),
-                ('schema', django.contrib.postgres.fields.jsonb.JSONField(help_text='Schema for the configurable parameters needed to use this template.')),
-                ('validation_code_js', models.CharField(help_text='JavaScript code for additional (complex) validation.', max_length=128)),
+                ('selection_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Filter for selecting dataproducts from the output role.')),
             ],
             options={
                 'abstract': False,
             },
         ),
         migrations.CreateModel(
-            name='WorkRelationSelectionTemplate',
+            name='TaskRelationDraft',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                 ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
                 ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
                 ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
-                ('name', models.CharField(help_text='Human-readable name of this object.', max_length=128)),
-                ('description', models.CharField(help_text='A longer description of this object.', max_length=255)),
-                ('version', models.CharField(help_text='Version of this template (with respect to other templates of the same name).', max_length=128)),
-                ('schema', django.contrib.postgres.fields.jsonb.JSONField(help_text='Schema for the configurable parameters needed to use this template.')),
+                ('selection_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Filter for selecting dataproducts from the output role.')),
             ],
             options={
                 'abstract': False,
             },
         ),
         migrations.CreateModel(
-            name='TaskRelationDraft',
+            name='TaskTemplate',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                 ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
                 ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
                 ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
-                ('selection_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Filter for selecting dataproducts from the output role.')),
-                ('consumer', models.ForeignKey(help_text='Task Draft that has the input connector.', on_delete=django.db.models.deletion.CASCADE, related_name='consumed_by', to='tmssapp.TaskDraft')),
-                ('dataformat', models.ForeignKey(help_text='Selected data format to use. One of (MS, HDF5).', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.Dataformat')),
-                ('input', models.ForeignKey(help_text='Input connector of consumer.', on_delete=django.db.models.deletion.CASCADE, related_name='inputs_task_relation_draft', to='tmssapp.TaskConnectors')),
-                ('output', models.ForeignKey(help_text='Output connector of producer.', on_delete=django.db.models.deletion.CASCADE, related_name='outputs_task_relation_draft', to='tmssapp.TaskConnectors')),
-                ('producer', models.ForeignKey(help_text='Task Draft that has the output connector. NOTE: The producer does typically, but not necessarily, belong to the same Scheduling Unit (or even the same Project) as the consumer.', on_delete=django.db.models.deletion.CASCADE, related_name='produced_by', to='tmssapp.TaskDraft')),
-                ('selection_template', models.ForeignKey(help_text='Schema used for selection_doc.', on_delete=django.db.models.deletion.CASCADE, to='tmssapp.WorkRelationSelectionTemplate')),
+                ('name', models.CharField(help_text='Human-readable name of this object.', max_length=128)),
+                ('description', models.CharField(help_text='A longer description of this object.', max_length=255)),
+                ('version', models.CharField(help_text='Version of this template (with respect to other templates of the same name).', max_length=128)),
+                ('schema', django.contrib.postgres.fields.jsonb.JSONField(help_text='Schema for the configurable parameters needed to use this template.')),
+                ('validation_code_js', models.CharField(help_text='JavaScript code for additional (complex) validation.', max_length=128)),
             ],
-            options={
-                'abstract': False,
-            },
         ),
         migrations.CreateModel(
-            name='TaskRelationBlueprint',
+            name='WorkRelationSelectionTemplate',
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                 ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
                 ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
                 ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
-                ('selection_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Filter for selecting dataproducts from the output role.')),
-                ('consumer', models.ForeignKey(help_text='Task Blueprint that has the input connector.', on_delete=django.db.models.deletion.CASCADE, related_name='consumed_by', to='tmssapp.TaskBlueprint')),
-                ('dataformat', models.ForeignKey(help_text='Selected data format to use.', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.Dataformat')),
-                ('draft', models.ForeignKey(help_text='Task Relation Draft which this work request instantiates.', on_delete=django.db.models.deletion.CASCADE, related_name='related_task_relation_blueprint', to='tmssapp.TaskRelationDraft')),
-                ('input', models.ForeignKey(help_text='Input connector of consumer.', on_delete=django.db.models.deletion.CASCADE, related_name='inputs_task_relation_blueprint', to='tmssapp.TaskConnectors')),
-                ('output', models.ForeignKey(help_text='Output connector of producer.', on_delete=django.db.models.deletion.CASCADE, related_name='outputs_task_relation_blueprint', to='tmssapp.TaskConnectors')),
-                ('producer', models.ForeignKey(help_text='Task Blueprint that has the output connector.', on_delete=django.db.models.deletion.CASCADE, related_name='produced_by', to='tmssapp.TaskBlueprint')),
-                ('selection_template', models.ForeignKey(help_text='Schema used for selection_doc.', on_delete=django.db.models.deletion.CASCADE, to='tmssapp.WorkRelationSelectionTemplate')),
+                ('name', models.CharField(help_text='Human-readable name of this object.', max_length=128)),
+                ('description', models.CharField(help_text='A longer description of this object.', max_length=255)),
+                ('version', models.CharField(help_text='Version of this template (with respect to other templates of the same name).', max_length=128)),
+                ('schema', django.contrib.postgres.fields.jsonb.JSONField(help_text='Schema for the configurable parameters needed to use this template.')),
             ],
-            options={
-                'abstract': False,
-            },
+        ),
+        migrations.AddConstraint(
+            model_name='workrelationselectiontemplate',
+            constraint=models.UniqueConstraint(fields=('name', 'version'), name='WorkRelationSelectionTemplate_unique_name_version'),
+        ),
+        migrations.AddConstraint(
+            model_name='tasktemplate',
+            constraint=models.UniqueConstraint(fields=('name', 'version'), name='TaskTemplate_unique_name_version'),
+        ),
+        migrations.AddField(
+            model_name='taskrelationdraft',
+            name='consumer',
+            field=models.ForeignKey(help_text='Task Draft that has the input connector.', on_delete=django.db.models.deletion.CASCADE, related_name='consumed_by', to='tmssapp.TaskDraft'),
+        ),
+        migrations.AddField(
+            model_name='taskrelationdraft',
+            name='dataformat',
+            field=models.ForeignKey(help_text='Selected data format to use. One of (MS, HDF5).', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.Dataformat'),
+        ),
+        migrations.AddField(
+            model_name='taskrelationdraft',
+            name='input',
+            field=models.ForeignKey(help_text='Input connector of consumer.', on_delete=django.db.models.deletion.CASCADE, related_name='inputs_task_relation_draft', to='tmssapp.TaskConnectors'),
+        ),
+        migrations.AddField(
+            model_name='taskrelationdraft',
+            name='output',
+            field=models.ForeignKey(help_text='Output connector of producer.', on_delete=django.db.models.deletion.CASCADE, related_name='outputs_task_relation_draft', to='tmssapp.TaskConnectors'),
+        ),
+        migrations.AddField(
+            model_name='taskrelationdraft',
+            name='producer',
+            field=models.ForeignKey(help_text='Task Draft that has the output connector. NOTE: The producer does typically, but not necessarily, belong to the same Scheduling Unit (or even the same Project) as the consumer.', on_delete=django.db.models.deletion.CASCADE, related_name='produced_by', to='tmssapp.TaskDraft'),
+        ),
+        migrations.AddField(
+            model_name='taskrelationdraft',
+            name='selection_template',
+            field=models.ForeignKey(help_text='Schema used for selection_doc.', on_delete=django.db.models.deletion.CASCADE, to='tmssapp.WorkRelationSelectionTemplate'),
+        ),
+        migrations.AddField(
+            model_name='taskrelationblueprint',
+            name='consumer',
+            field=models.ForeignKey(help_text='Task Blueprint that has the input connector.', on_delete=django.db.models.deletion.CASCADE, related_name='consumed_by', to='tmssapp.TaskBlueprint'),
+        ),
+        migrations.AddField(
+            model_name='taskrelationblueprint',
+            name='dataformat',
+            field=models.ForeignKey(help_text='Selected data format to use.', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.Dataformat'),
+        ),
+        migrations.AddField(
+            model_name='taskrelationblueprint',
+            name='draft',
+            field=models.ForeignKey(help_text='Task Relation Draft which this work request instantiates.', on_delete=django.db.models.deletion.CASCADE, related_name='related_task_relation_blueprint', to='tmssapp.TaskRelationDraft'),
+        ),
+        migrations.AddField(
+            model_name='taskrelationblueprint',
+            name='input',
+            field=models.ForeignKey(help_text='Input connector of consumer.', on_delete=django.db.models.deletion.CASCADE, related_name='inputs_task_relation_blueprint', to='tmssapp.TaskConnectors'),
+        ),
+        migrations.AddField(
+            model_name='taskrelationblueprint',
+            name='output',
+            field=models.ForeignKey(help_text='Output connector of producer.', on_delete=django.db.models.deletion.CASCADE, related_name='outputs_task_relation_blueprint', to='tmssapp.TaskConnectors'),
+        ),
+        migrations.AddField(
+            model_name='taskrelationblueprint',
+            name='producer',
+            field=models.ForeignKey(help_text='Task Blueprint that has the output connector.', on_delete=django.db.models.deletion.CASCADE, related_name='produced_by', to='tmssapp.TaskBlueprint'),
+        ),
+        migrations.AddField(
+            model_name='taskrelationblueprint',
+            name='selection_template',
+            field=models.ForeignKey(help_text='Schema used for selection_doc.', on_delete=django.db.models.deletion.CASCADE, to='tmssapp.WorkRelationSelectionTemplate'),
+        ),
+        migrations.AddField(
+            model_name='taskdraft',
+            name='copies',
+            field=models.ForeignKey(help_text='Source reference, if we are a copy (NULLable).', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='copied_from', to='tmssapp.TaskDraft'),
+        ),
+        migrations.AddField(
+            model_name='taskdraft',
+            name='copy_reason',
+            field=models.ForeignKey(help_text='Reason why source was copied (NULLable).', null=True, on_delete=django.db.models.deletion.PROTECT, to='tmssapp.CopyReason'),
+        ),
+        migrations.AddField(
+            model_name='taskdraft',
+            name='scheduling_unit_draft',
+            field=models.ForeignKey(help_text='Scheduling Unit draft to which this task draft belongs.', on_delete=django.db.models.deletion.CASCADE, related_name='task_drafts', to='tmssapp.SchedulingUnitDraft'),
         ),
         migrations.AddField(
             model_name='taskdraft',
             name='specifications_template',
             field=models.ForeignKey(help_text='Schema used for requirements_doc.', on_delete=django.db.models.deletion.CASCADE, to='tmssapp.TaskTemplate'),
         ),
+        migrations.AddField(
+            model_name='taskconnectors',
+            name='dataformats',
+            field=models.ManyToManyField(blank=True, to='tmssapp.Dataformat'),
+        ),
+        migrations.AddField(
+            model_name='taskconnectors',
+            name='datatype',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tmssapp.Datatype'),
+        ),
         migrations.AddField(
             model_name='taskconnectors',
             name='input_of',
@@ -499,72 +774,79 @@ class Migration(migrations.Migration):
             name='specifications_template',
             field=models.ForeignKey(help_text='Schema used for specifications_doc (IMMUTABLE).', on_delete=django.db.models.deletion.CASCADE, to='tmssapp.TaskTemplate'),
         ),
-        migrations.CreateModel(
-            name='SubtaskTemplate',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
-                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
-                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
-                ('name', models.CharField(help_text='Human-readable name of this object.', max_length=128)),
-                ('description', models.CharField(help_text='A longer description of this object.', max_length=255)),
-                ('version', models.CharField(help_text='Version of this template (with respect to other templates of the same name).', max_length=128)),
-                ('schema', django.contrib.postgres.fields.jsonb.JSONField(help_text='Schema for the configurable parameters needed to use this template.')),
-                ('queue', models.BooleanField(default=False)),
-                ('realtime', models.BooleanField(default=False)),
-                ('type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tmssapp.SubtaskType')),
-            ],
-            options={
-                'abstract': False,
-            },
+        migrations.AddField(
+            model_name='subtasktemplate',
+            name='type',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tmssapp.SubtaskType'),
         ),
-        migrations.CreateModel(
-            name='SubtaskStateLog',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
-                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
-                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
-                ('user_identifier', models.CharField(editable=False, help_text='The ID of the user who changed the state of the subtask.', max_length=128, null=True)),
-                ('new_state', models.ForeignKey(editable=False, help_text='Subtask state after update (see Subtask State Machine).', on_delete=django.db.models.deletion.PROTECT, related_name='is_new_state_of', to='tmssapp.SubtaskState')),
-                ('old_state', models.ForeignKey(editable=False, help_text='Subtask state before update (see Subtask State Machine).', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='is_old_state_of', to='tmssapp.SubtaskState')),
-                ('subtask', models.ForeignKey(editable=False, help_text='Subtask to which this state change refers.', on_delete=django.db.models.deletion.CASCADE, to='tmssapp.Subtask')),
-                ('user', models.ForeignKey(editable=False, help_text='The user who changed the state of the subtask.', null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
-            ],
-            options={
-                'abstract': False,
-            },
+        migrations.AddField(
+            model_name='subtaskstatelog',
+            name='new_state',
+            field=models.ForeignKey(editable=False, help_text='Subtask state after update (see Subtask State Machine).', on_delete=django.db.models.deletion.PROTECT, related_name='is_new_state_of', to='tmssapp.SubtaskState'),
         ),
-        migrations.CreateModel(
-            name='SubtaskOutput',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
-                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
-                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
-                ('subtask', models.ForeignKey(help_text='Subtask to which this output specification refers.', on_delete=django.db.models.deletion.CASCADE, related_name='outputs', to='tmssapp.Subtask')),
-            ],
-            options={
-                'abstract': False,
-            },
+        migrations.AddField(
+            model_name='subtaskstatelog',
+            name='old_state',
+            field=models.ForeignKey(editable=False, help_text='Subtask state before update (see Subtask State Machine).', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='is_old_state_of', to='tmssapp.SubtaskState'),
         ),
-        migrations.CreateModel(
-            name='SubtaskInput',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
-                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
-                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
-                ('selection_doc', django.contrib.postgres.fields.jsonb.JSONField(help_text='Filter to apply to the dataproducts of the producer, to derive input dataproducts when scheduling.')),
-                ('dataproducts', models.ManyToManyField(help_text='The Dataproducts resulting from application of the filter at time of scheduling Although the dataproducts are simply the result of applying the filter on immutable data, the filter application could change over time. We thus store the result of this filtering directly to retain which input was specified for the task..', to='tmssapp.Dataproduct')),
-                ('producer', models.ForeignKey(help_text='The SubtaskOutput producing the input dataproducts for this SubtaskInput.', on_delete=django.db.models.deletion.PROTECT, related_name='consumers', to='tmssapp.SubtaskOutput')),
-                ('selection_template', models.ForeignKey(help_text='Schema used for selection_doc.', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.SubtaskInputSelectionTemplate')),
-                ('subtask', models.ForeignKey(help_text='Subtask to which this input specification refers.', on_delete=django.db.models.deletion.CASCADE, related_name='inputs', to='tmssapp.Subtask')),
-                ('task_relation_blueprint', models.ForeignKey(help_text='Task Relation Blueprint which this Subtask Input implements (NULLable).', null=True, on_delete=django.db.models.deletion.SET_NULL, to='tmssapp.TaskRelationBlueprint')),
-            ],
-            options={
-                'abstract': False,
-            },
+        migrations.AddField(
+            model_name='subtaskstatelog',
+            name='subtask',
+            field=models.ForeignKey(editable=False, help_text='Subtask to which this state change refers.', on_delete=django.db.models.deletion.CASCADE, to='tmssapp.Subtask'),
+        ),
+        migrations.AddField(
+            model_name='subtaskstatelog',
+            name='user',
+            field=models.ForeignKey(editable=False, help_text='The user who changed the state of the subtask.', null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL),
+        ),
+        migrations.AddField(
+            model_name='subtaskoutput',
+            name='subtask',
+            field=models.ForeignKey(help_text='Subtask to which this output specification refers.', on_delete=django.db.models.deletion.CASCADE, related_name='outputs', to='tmssapp.Subtask'),
+        ),
+        migrations.AddConstraint(
+            model_name='subtaskinputselectiontemplate',
+            constraint=models.UniqueConstraint(fields=('name', 'version'), name='SubtaskInputSelectionTemplate_unique_name_version'),
+        ),
+        migrations.AddField(
+            model_name='subtaskinput',
+            name='dataproducts',
+            field=models.ManyToManyField(help_text='The Dataproducts resulting from application of the filter at time of scheduling Although the dataproducts are simply the result of applying the filter on immutable data, the filter application could change over time. We thus store the result of this filtering directly to retain which input was specified for the task..', to='tmssapp.Dataproduct'),
+        ),
+        migrations.AddField(
+            model_name='subtaskinput',
+            name='producer',
+            field=models.ForeignKey(help_text='The SubtaskOutput producing the input dataproducts for this SubtaskInput.', on_delete=django.db.models.deletion.PROTECT, related_name='consumers', to='tmssapp.SubtaskOutput'),
+        ),
+        migrations.AddField(
+            model_name='subtaskinput',
+            name='selection_template',
+            field=models.ForeignKey(help_text='Schema used for selection_doc.', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.SubtaskInputSelectionTemplate'),
+        ),
+        migrations.AddField(
+            model_name='subtaskinput',
+            name='subtask',
+            field=models.ForeignKey(help_text='Subtask to which this input specification refers.', on_delete=django.db.models.deletion.CASCADE, related_name='inputs', to='tmssapp.Subtask'),
+        ),
+        migrations.AddField(
+            model_name='subtaskinput',
+            name='task_relation_blueprint',
+            field=models.ForeignKey(help_text='Task Relation Blueprint which this Subtask Input implements (NULLable).', null=True, on_delete=django.db.models.deletion.SET_NULL, to='tmssapp.TaskRelationBlueprint'),
+        ),
+        migrations.AddField(
+            model_name='subtask',
+            name='cluster',
+            field=models.ForeignKey(help_text='Where the Subtask is scheduled to run (NULLable).', null=True, on_delete=django.db.models.deletion.PROTECT, to='tmssapp.Cluster'),
+        ),
+        migrations.AddField(
+            model_name='subtask',
+            name='created_or_updated_by_user',
+            field=models.ForeignKey(editable=False, help_text='The user who created / updated the subtask.', null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL),
+        ),
+        migrations.AddField(
+            model_name='subtask',
+            name='schedule_method',
+            field=models.ForeignKey(help_text='Which method to use for scheduling this Subtask. One of (MANUAL, BATCH, DYNAMIC).', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.ScheduleMethod'),
         ),
         migrations.AddField(
             model_name='subtask',
@@ -581,6 +863,20 @@ class Migration(migrations.Migration):
             name='task_blueprint',
             field=models.ForeignKey(help_text='Task Blueprint to which this Subtask belongs.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='subtasks', to='tmssapp.TaskBlueprint'),
         ),
+        migrations.AddConstraint(
+            model_name='schedulingunittemplate',
+            constraint=models.UniqueConstraint(fields=('name', 'version'), name='SchedulingUnitTemplate_unique_name_version'),
+        ),
+        migrations.AddField(
+            model_name='schedulingunitdraft',
+            name='copies',
+            field=models.ForeignKey(help_text='Source reference, if we are a copy (NULLable).', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='copied_from', to='tmssapp.SchedulingUnitDraft'),
+        ),
+        migrations.AddField(
+            model_name='schedulingunitdraft',
+            name='copy_reason',
+            field=models.ForeignKey(help_text='Reason why source was copied (NULLable).', null=True, on_delete=django.db.models.deletion.PROTECT, to='tmssapp.CopyReason'),
+        ),
         migrations.AddField(
             model_name='schedulingunitdraft',
             name='requirements_template',
@@ -616,174 +912,102 @@ class Migration(migrations.Migration):
             name='project',
             field=models.ForeignKey(help_text='Project to which this scheduling set belongs.', on_delete=django.db.models.deletion.PROTECT, related_name='scheduling_sets', to='tmssapp.Project'),
         ),
-        migrations.CreateModel(
-            name='ResourceType',
-            fields=[
-                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
-                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
-                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
-                ('description', models.CharField(help_text='A longer description of this object.', max_length=255)),
-                ('name', models.CharField(help_text='Human-readable name of this object.', max_length=128, primary_key=True, serialize=False)),
-                ('resource_unit', models.ForeignKey(help_text='Unit of current resource.', on_delete=django.db.models.deletion.PROTECT, related_name='resource_types', to='tmssapp.ResourceUnit')),
-            ],
-            options={
-                'abstract': False,
-            },
+        migrations.AddField(
+            model_name='resourcetype',
+            name='resource_unit',
+            field=models.ForeignKey(help_text='Unit of current resource.', on_delete=django.db.models.deletion.PROTECT, related_name='resource_types', to='tmssapp.ResourceUnit'),
         ),
-        migrations.CreateModel(
-            name='ProjectQuota',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('value', models.FloatField(help_text='Resource Quota value')),
-                ('project', models.ForeignKey(help_text='Project to wich this quota belongs.', on_delete=django.db.models.deletion.PROTECT, related_name='project_quota', to='tmssapp.Project')),
-                ('resource_type', models.ForeignKey(help_text='Resource type.', on_delete=django.db.models.deletion.PROTECT, related_name='resource_type', to='tmssapp.ResourceType')),
-            ],
+        migrations.AddField(
+            model_name='projectquota',
+            name='project',
+            field=models.ForeignKey(help_text='Project to wich this quota belongs.', on_delete=django.db.models.deletion.PROTECT, related_name='project_quota', to='tmssapp.Project'),
         ),
-        migrations.CreateModel(
-            name='Filesystem',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
-                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
-                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
-                ('name', models.CharField(help_text='Human-readable name of this object.', max_length=128)),
-                ('description', models.CharField(help_text='A longer description of this object.', max_length=255)),
-                ('capacity', models.BigIntegerField(help_text='Capacity in bytes')),
-                ('cluster', models.ForeignKey(help_text='Cluster hosting this filesystem.', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.Cluster')),
-            ],
-            options={
-                'abstract': False,
-            },
+        migrations.AddField(
+            model_name='projectquota',
+            name='resource_type',
+            field=models.ForeignKey(help_text='Resource type.', on_delete=django.db.models.deletion.PROTECT, related_name='resource_type', to='tmssapp.ResourceType'),
         ),
-        migrations.CreateModel(
-            name='DefaultWorkRelationSelectionTemplate',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
-                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
-                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
-                ('name', models.CharField(max_length=128, unique=True)),
-                ('template', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tmssapp.WorkRelationSelectionTemplate')),
-            ],
-            options={
-                'abstract': False,
-            },
+        migrations.AddField(
+            model_name='project',
+            name='cycle',
+            field=models.ForeignKey(help_text='Cycle(s) to which this project belongs (NULLable).', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='projects', to='tmssapp.Cycle'),
         ),
-        migrations.CreateModel(
-            name='DefaultTaskTemplate',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
-                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
-                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
-                ('name', models.CharField(max_length=128, unique=True)),
-                ('template', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tmssapp.TaskTemplate')),
-            ],
-            options={
-                'abstract': False,
-            },
+        migrations.AddConstraint(
+            model_name='generatortemplate',
+            constraint=models.UniqueConstraint(fields=('name', 'version'), name='GeneratorTemplate_unique_name_version'),
         ),
-        migrations.CreateModel(
-            name='DefaultSubtaskTemplate',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
-                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
-                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
-                ('name', models.CharField(max_length=128, unique=True)),
-                ('template', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tmssapp.SubtaskTemplate')),
-            ],
-            options={
-                'abstract': False,
-            },
+        migrations.AddField(
+            model_name='filesystem',
+            name='cluster',
+            field=models.ForeignKey(help_text='Cluster hosting this filesystem.', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.Cluster'),
         ),
-        migrations.CreateModel(
-            name='DefaultSchedulingUnitTemplate',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
-                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
-                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
-                ('name', models.CharField(max_length=128, unique=True)),
-                ('template', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tmssapp.SchedulingUnitTemplate')),
-            ],
-            options={
-                'abstract': False,
-            },
+        migrations.AddField(
+            model_name='defaultworkrelationselectiontemplate',
+            name='template',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tmssapp.WorkRelationSelectionTemplate'),
         ),
-        migrations.CreateModel(
-            name='DefaultGeneratorTemplate',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
-                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
-                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
-                ('name', models.CharField(max_length=128, unique=True)),
-                ('template', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tmssapp.GeneratorTemplate')),
-            ],
-            options={
-                'abstract': False,
-            },
+        migrations.AddField(
+            model_name='defaulttasktemplate',
+            name='template',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tmssapp.TaskTemplate'),
         ),
-        migrations.CreateModel(
-            name='DefaultDataproductSpecificationsTemplate',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
-                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
-                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
-                ('name', models.CharField(max_length=128, unique=True)),
-                ('template', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tmssapp.DataproductSpecificationsTemplate')),
-            ],
-            options={
-                'abstract': False,
-            },
+        migrations.AddField(
+            model_name='defaultsubtasktemplate',
+            name='template',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tmssapp.SubtaskTemplate'),
         ),
-        migrations.CreateModel(
-            name='DataproductTransform',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
-                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
-                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
-                ('identity', models.BooleanField(help_text='TRUE if this transform only copies, tars, or losslessly compresses its input, FALSE if the transform changes the data. Allows for efficient reasoning about data duplication.')),
-                ('input', models.ForeignKey(help_text='A dataproduct that was the input of a transformation.', on_delete=django.db.models.deletion.PROTECT, related_name='inputs', to='tmssapp.Dataproduct')),
-                ('output', models.ForeignKey(help_text='A dataproduct that was produced from the input dataproduct.', on_delete=django.db.models.deletion.PROTECT, related_name='outputs', to='tmssapp.Dataproduct')),
-            ],
-            options={
-                'abstract': False,
-            },
+        migrations.AddField(
+            model_name='defaultschedulingunittemplate',
+            name='template',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tmssapp.SchedulingUnitTemplate'),
         ),
-        migrations.CreateModel(
-            name='DataproductHash',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
-                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
-                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
-                ('hash', models.CharField(help_text='Hash value.', max_length=128)),
-                ('algorithm', models.ForeignKey(help_text='Algorithm used (MD5, AES256).', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.Algorithm')),
-                ('dataproduct', models.ForeignKey(help_text='The dataproduct to which this hash refers.', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.Dataproduct')),
-            ],
-            options={
-                'abstract': False,
-            },
+        migrations.AddField(
+            model_name='defaultgeneratortemplate',
+            name='template',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tmssapp.GeneratorTemplate'),
         ),
-        migrations.CreateModel(
-            name='DataproductArchiveInfo',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
-                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
-                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
-                ('storage_ticket', models.CharField(help_text='Archive-system identifier.', max_length=128)),
-                ('public_since', models.DateTimeField(help_text='Dataproduct is available for public download since this moment, or NULL if dataproduct is not (NULLable).', null=True)),
-                ('corrupted_since', models.DateTimeField(help_text='Earliest timestamp from which this dataproduct is known to be partially or fully corrupt, or NULL if dataproduct is not known to be corrupt (NULLable).', null=True)),
-                ('dataproduct', models.ForeignKey(help_text='A dataproduct residing in the archive.', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.Dataproduct')),
-            ],
-            options={
-                'abstract': False,
-            },
+        migrations.AddField(
+            model_name='defaultdataproductspecificationstemplate',
+            name='template',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tmssapp.DataproductSpecificationsTemplate'),
+        ),
+        migrations.AddField(
+            model_name='dataproducttransform',
+            name='input',
+            field=models.ForeignKey(help_text='A dataproduct that was the input of a transformation.', on_delete=django.db.models.deletion.PROTECT, related_name='inputs', to='tmssapp.Dataproduct'),
+        ),
+        migrations.AddField(
+            model_name='dataproducttransform',
+            name='output',
+            field=models.ForeignKey(help_text='A dataproduct that was produced from the input dataproduct.', on_delete=django.db.models.deletion.PROTECT, related_name='outputs', to='tmssapp.Dataproduct'),
+        ),
+        migrations.AddConstraint(
+            model_name='dataproductspecificationstemplate',
+            constraint=models.UniqueConstraint(fields=('name', 'version'), name='DataproductSpecificationsTemplate_unique_name_version'),
+        ),
+        migrations.AddField(
+            model_name='dataproducthash',
+            name='algorithm',
+            field=models.ForeignKey(help_text='Algorithm used (MD5, AES256).', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.Algorithm'),
+        ),
+        migrations.AddField(
+            model_name='dataproducthash',
+            name='dataproduct',
+            field=models.ForeignKey(help_text='The dataproduct to which this hash refers.', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.Dataproduct'),
+        ),
+        migrations.AddConstraint(
+            model_name='dataproductfeedbacktemplate',
+            constraint=models.UniqueConstraint(fields=('name', 'version'), name='DataproductFeedbackTemplate_unique_name_version'),
+        ),
+        migrations.AddField(
+            model_name='dataproductarchiveinfo',
+            name='dataproduct',
+            field=models.ForeignKey(help_text='A dataproduct residing in the archive.', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.Dataproduct'),
+        ),
+        migrations.AddField(
+            model_name='dataproduct',
+            name='dataformat',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tmssapp.Dataformat'),
         ),
         migrations.AddField(
             model_name='dataproduct',
@@ -800,22 +1024,10 @@ class Migration(migrations.Migration):
             name='specifications_template',
             field=models.ForeignKey(help_text='Schema used for specifications_doc.', on_delete=django.db.models.deletion.CASCADE, to='tmssapp.DataproductSpecificationsTemplate'),
         ),
-        migrations.CreateModel(
-            name='AntennaSet',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('tags', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, default=list, help_text='User-defined search keywords for object.', size=8)),
-                ('created_at', models.DateTimeField(auto_now_add=True, help_text='Moment of object creation.')),
-                ('updated_at', models.DateTimeField(auto_now=True, help_text='Moment of last object update.')),
-                ('name', models.CharField(help_text='Human-readable name of this object.', max_length=128)),
-                ('description', models.CharField(help_text='A longer description of this object.', max_length=255)),
-                ('rcus', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), size=128)),
-                ('inputs', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=128), blank=True, size=128)),
-                ('station_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tmssapp.StationType')),
-            ],
-            options={
-                'abstract': False,
-            },
+        migrations.AddField(
+            model_name='antennaset',
+            name='station_type',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tmssapp.StationType'),
         ),
         migrations.AddIndex(
             model_name='taskrelationdraft',
@@ -829,6 +1041,10 @@ class Migration(migrations.Migration):
             model_name='taskconnectors',
             index=django.contrib.postgres.indexes.GinIndex(fields=['tags'], name='tmssapp_tas_tags_0ebd6d_gin'),
         ),
+        migrations.AddConstraint(
+            model_name='subtasktemplate',
+            constraint=models.UniqueConstraint(fields=('name', 'version'), name='SubtaskTemplate_unique_name_version'),
+        ),
         migrations.AddIndex(
             model_name='subtaskstatelog',
             index=django.contrib.postgres.indexes.GinIndex(fields=['tags'], name='tmssapp_sub_tags_70e4a2_gin'),
diff --git a/SAS/TMSS/src/tmss/tmssapp/models/scheduling.py b/SAS/TMSS/src/tmss/tmssapp/models/scheduling.py
index 26d42fd17f3090417360a4dcf45d475d06586a2a..b067845f2a6c8c6219f35fa8bfdcc622779d918b 100644
--- a/SAS/TMSS/src/tmss/tmssapp/models/scheduling.py
+++ b/SAS/TMSS/src/tmss/tmssapp/models/scheduling.py
@@ -7,7 +7,7 @@ import logging
 logger = logging.getLogger(__name__)
 
 from django.db.models import ForeignKey, CharField, DateTimeField, BooleanField, IntegerField, BigIntegerField, \
-    ManyToManyField, CASCADE, SET_NULL, PROTECT
+    ManyToManyField, CASCADE, SET_NULL, PROTECT, UniqueConstraint
 from django.contrib.postgres.fields import ArrayField, JSONField
 from django.contrib.auth.models import User
 from .specification import AbstractChoice, BasicCommon, Template, NamedCommon # , <TaskBlueprint
@@ -100,6 +100,10 @@ class SubtaskTemplate(Template):
     queue = BooleanField(default=False)
     realtime = BooleanField(default=False)
 
+    class Meta:
+        # TODO: move up to the abstract base class and replace with django 3.0 UniqueConstraint(... name='%*class)s_unique_name_version)
+        constraints = [UniqueConstraint(fields=['name', 'version'], name='SubtaskTemplate_unique_name_version')]
+
 
 class DefaultSubtaskTemplate(BasicCommon):
     name = CharField(max_length=128, unique=True)
@@ -107,7 +111,9 @@ class DefaultSubtaskTemplate(BasicCommon):
 
 
 class DataproductSpecificationsTemplate(Template):
-    pass
+    class Meta:
+        # TODO: move up to the abstract base class and replace with django 3.0 UniqueConstraint(... name='%*class)s_unique_name_version)
+        constraints = [UniqueConstraint(fields=['name', 'version'], name='DataproductSpecificationsTemplate_unique_name_version')]
 
 
 class DefaultDataproductSpecificationsTemplate(BasicCommon):
@@ -116,13 +122,17 @@ class DefaultDataproductSpecificationsTemplate(BasicCommon):
 
 
 class SubtaskInputSelectionTemplate(Template):
-    pass
+    class Meta:
+        # TODO: move up to the abstract base class and replace with django 3.0 UniqueConstraint(... name='%*class)s_unique_name_version)
+        constraints = [UniqueConstraint(fields=['name', 'version'], name='SubtaskInputSelectionTemplate_unique_name_version')]
 
 # todo: so we need to specify a default?
 
 
 class DataproductFeedbackTemplate(Template):
-    pass
+    class Meta:
+        # TODO: move up to the abstract base class and replace with django 3.0 UniqueConstraint(... name='%*class)s_unique_name_version)
+        constraints = [UniqueConstraint(fields=['name', 'version'], name='DataproductFeedbackTemplate_unique_name_version')]
 
 # todo: do we need to specify a default?
 
diff --git a/SAS/TMSS/src/tmss/tmssapp/models/specification.py b/SAS/TMSS/src/tmss/tmssapp/models/specification.py
index 02954067d35179b3e6862e9d70ba82976f50ab80..e3dcaedbf4456e7878d6b0dfe5010322ce9fcbcb 100644
--- a/SAS/TMSS/src/tmss/tmssapp/models/specification.py
+++ b/SAS/TMSS/src/tmss/tmssapp/models/specification.py
@@ -2,7 +2,7 @@
 This file contains the database models
 """
 
-from django.db.models import Model, CharField, DateTimeField, BooleanField, ForeignKey, CASCADE, IntegerField, FloatField, SET_NULL, PROTECT, ManyToManyField
+from django.db.models import Model, CharField, DateTimeField, BooleanField, ForeignKey, CASCADE, IntegerField, FloatField, SET_NULL, PROTECT, ManyToManyField, UniqueConstraint
 from django.contrib.postgres.fields import ArrayField, JSONField
 from django.contrib.postgres.indexes import GinIndex
 from enum import Enum
@@ -152,12 +152,18 @@ class Template(NamedCommon):
 
     class Meta:
         abstract = True
+        # TODO: remove all <class>_unique_name_version UniqueConstraint's from the subclasses and replace by this line below when we start using django 3.0
+        # constraints = [UniqueConstraint(fields=['name', 'version'], name='%(class)s_unique_name_version')]
 
 # concrete models
 
 class GeneratorTemplate(Template):
     create_function = CharField(max_length=128, help_text='Python function to call to execute the generator.')
 
+    class Meta:
+        # TODO: move up to the abstract base class and replace with django 3.0 UniqueConstraint(... name='%*class)s_unique_name_version)
+        constraints = [UniqueConstraint(fields=['name', 'version'], name='GeneratorTemplate_unique_name_version')]
+
 
 class DefaultGeneratorTemplate(BasicCommon):
     name = CharField(max_length=128, unique=True)
@@ -165,7 +171,9 @@ class DefaultGeneratorTemplate(BasicCommon):
 
 
 class SchedulingUnitTemplate(Template):
-    pass
+    class Meta:
+        # TODO: move up to the abstract base class and replace with django 3.0 UniqueConstraint(... name='%*class)s_unique_name_version)
+        constraints = [UniqueConstraint(fields=['name', 'version'], name='SchedulingUnitTemplate_unique_name_version')]
 
 
 class DefaultSchedulingUnitTemplate(BasicCommon):
@@ -176,6 +184,9 @@ class DefaultSchedulingUnitTemplate(BasicCommon):
 class TaskTemplate(Template):
     validation_code_js = CharField(max_length=128, help_text='JavaScript code for additional (complex) validation.')
 
+    class Meta:
+        # TODO: move up to the abstract base class and replace with django 3.0 UniqueConstraint(... name='%*class)s_unique_name_version)
+        constraints = [UniqueConstraint(fields=['name', 'version'], name='TaskTemplate_unique_name_version')]
 
 class DefaultTaskTemplate(BasicCommon):
     name = CharField(max_length=128, unique=True)
@@ -183,7 +194,9 @@ class DefaultTaskTemplate(BasicCommon):
 
 
 class WorkRelationSelectionTemplate(Template):
-    pass
+    class Meta:
+        # TODO: move up to the abstract base class and replace with django 3.0 UniqueConstraint(... name='%*class)s_unique_name_version)
+        constraints = [UniqueConstraint(fields=['name', 'version'], name='WorkRelationSelectionTemplate_unique_name_version')]
 
 
 class DefaultWorkRelationSelectionTemplate(BasicCommon):
diff --git a/SAS/TMSS/test/t_tmssapp_specification_REST_API.py b/SAS/TMSS/test/t_tmssapp_specification_REST_API.py
index dd47d53adc23ba97c2e61c52da3b48db63455abe..57833cffd1de3fdf0fdb3ba5e56c0e2c562f9d5d 100755
--- a/SAS/TMSS/test/t_tmssapp_specification_REST_API.py
+++ b/SAS/TMSS/test/t_tmssapp_specification_REST_API.py
@@ -69,30 +69,35 @@ class GeneratorTemplateTestCase(unittest.TestCase):
     def test_generator_template_POST_and_GET(self):
 
         # POST and GET a new item and assert correctness
-        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/generator_template/', test_data_creator.GeneratorTemplate(), 201, test_data_creator.GeneratorTemplate())
+        test_data = test_data_creator.GeneratorTemplate()
+        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/generator_template/', test_data, 201, test_data)
         url = r_dict['url']
-        GET_OK_and_assert_equal_expected_response(self, url, test_data_creator.GeneratorTemplate())
+        GET_OK_and_assert_equal_expected_response(self, url, test_data)
 
     def test_generator_template_PUT_invalid_raises_error(self):
-        PUT_and_assert_expected_response(self, BASE_URL + '/generator_template/9876789876/', test_data_creator.GeneratorTemplate(), 404, {})
+        test_data = test_data_creator.GeneratorTemplate()
+        PUT_and_assert_expected_response(self, BASE_URL + '/generator_template/9876789876/', test_data, 404, {})
 
     def test_generator_template_PUT(self):
 
         # POST new item, verify
-        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/generator_template/', test_data_creator.GeneratorTemplate(), 201, test_data_creator.GeneratorTemplate())
+        test_data = test_data_creator.GeneratorTemplate()
+        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/generator_template/', test_data, 201, test_data)
         url = r_dict['url']
-        GET_OK_and_assert_equal_expected_response(self, url, test_data_creator.GeneratorTemplate())
+        GET_OK_and_assert_equal_expected_response(self, url, test_data)
 
         # PUT new values, verify
-        PUT_and_assert_expected_response(self, url, test_data_creator.GeneratorTemplate("generatortemplate2"), 200, test_data_creator.GeneratorTemplate("generatortemplate2"))
-        GET_OK_and_assert_equal_expected_response(self, url, test_data_creator.GeneratorTemplate("generatortemplate2"))
+        test_data2 = test_data_creator.GeneratorTemplate("generatortemplate2")
+        PUT_and_assert_expected_response(self, url, test_data2, 200, test_data2)
+        GET_OK_and_assert_equal_expected_response(self, url, test_data2)
 
     def test_generator_template_PATCH(self):
 
         # POST new item, verify
-        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/generator_template/', test_data_creator.GeneratorTemplate(), 201, test_data_creator.GeneratorTemplate())
+        test_data = test_data_creator.GeneratorTemplate()
+        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/generator_template/', test_data, 201, test_data)
         url = r_dict['url']
-        GET_OK_and_assert_equal_expected_response(self, url, test_data_creator.GeneratorTemplate())
+        GET_OK_and_assert_equal_expected_response(self, url, test_data)
 
         test_patch = {"version": 'v6.28318530718',
                       "schema": {"mykey": "my better value"}}
@@ -106,9 +111,10 @@ class GeneratorTemplateTestCase(unittest.TestCase):
     def test_generator_template_DELETE(self):
 
         # POST new item, verify
-        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/generator_template/', test_data_creator.GeneratorTemplate(), 201, test_data_creator.GeneratorTemplate())
+        test_data = test_data_creator.GeneratorTemplate()
+        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/generator_template/', test_data, 201, test_data)
         url = r_dict['url']
-        GET_OK_and_assert_equal_expected_response(self, url, test_data_creator.GeneratorTemplate())
+        GET_OK_and_assert_equal_expected_response(self, url, test_data)
 
         # DELETE and check it's gone
         DELETE_and_assert_gone(self, url)
@@ -135,30 +141,35 @@ class SchedulingUnitTemplateTestCase(unittest.TestCase):
     def test_scheduling_unit_template_POST_and_GET(self):
 
         # POST and GET a new item and assert correctness
-        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/scheduling_unit_template/', test_data_creator.SchedulingUnitTemplate(), 201, test_data_creator.SchedulingUnitTemplate())
+        test_data = test_data_creator.SchedulingUnitTemplate()
+        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/scheduling_unit_template/', test_data, 201, test_data)
         url = r_dict['url']
-        GET_OK_and_assert_equal_expected_response(self, url+'?format=json', test_data_creator.SchedulingUnitTemplate())
+        GET_OK_and_assert_equal_expected_response(self, url+'?format=json', test_data)
 
     def test_scheduling_unit_template_PUT_invalid_raises_error(self):
-        PUT_and_assert_expected_response(self, BASE_URL + '/scheduling_unit_template/9876789876/', test_data_creator.SchedulingUnitTemplate(), 404, {})
+        test_data = test_data_creator.SchedulingUnitTemplate()
+        PUT_and_assert_expected_response(self, BASE_URL + '/scheduling_unit_template/9876789876/', test_data, 404, {})
 
     def test_scheduling_unit_template_PUT(self):
 
         # POST new item, verify
-        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/scheduling_unit_template/', test_data_creator.SchedulingUnitTemplate(), 201, test_data_creator.SchedulingUnitTemplate())
+        test_data = test_data_creator.SchedulingUnitTemplate()
+        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/scheduling_unit_template/', test_data, 201, test_data)
         url = r_dict['url']
-        GET_OK_and_assert_equal_expected_response(self, url, test_data_creator.SchedulingUnitTemplate())
+        GET_OK_and_assert_equal_expected_response(self, url, test_data)
 
         # PUT new values, verify
-        PUT_and_assert_expected_response(self, url, test_data_creator.SchedulingUnitTemplate("schedulingunittemplate2"), 200, test_data_creator.SchedulingUnitTemplate("schedulingunittemplate2"))
-        GET_OK_and_assert_equal_expected_response(self, url, test_data_creator.SchedulingUnitTemplate("schedulingunittemplate2"))
+        test_data2 = test_data_creator.SchedulingUnitTemplate("schedulingunittemplate2")
+        PUT_and_assert_expected_response(self, url, test_data2, 200, test_data2)
+        GET_OK_and_assert_equal_expected_response(self, url, test_data2)
 
     def test_scheduling_unit_template_PATCH(self):
 
         # POST new item, verify
-        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/scheduling_unit_template/', test_data_creator.SchedulingUnitTemplate(), 201, test_data_creator.SchedulingUnitTemplate())
+        test_data = test_data_creator.SchedulingUnitTemplate()
+        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/scheduling_unit_template/', test_data, 201, test_data)
         url = r_dict['url']
-        GET_OK_and_assert_equal_expected_response(self, url, test_data_creator.SchedulingUnitTemplate())
+        GET_OK_and_assert_equal_expected_response(self, url, test_data)
 
         test_patch = {"version": 'v6.28318530718',
                       "schema": {"mykey": "my better value"}}
@@ -172,9 +183,10 @@ class SchedulingUnitTemplateTestCase(unittest.TestCase):
     def test_scheduling_unit_template_DELETE(self):
 
         # POST new item, verify
-        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/scheduling_unit_template/', test_data_creator.SchedulingUnitTemplate(), 201, test_data_creator.SchedulingUnitTemplate())
+        test_data = test_data_creator.SchedulingUnitTemplate()
+        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/scheduling_unit_template/', test_data, 201, test_data)
         url = r_dict['url']
-        GET_OK_and_assert_equal_expected_response(self, url, test_data_creator.SchedulingUnitTemplate())
+        GET_OK_and_assert_equal_expected_response(self, url, test_data)
 
         # DELETE and check it's gone
         DELETE_and_assert_gone(self, url)
@@ -201,31 +213,33 @@ class TaskTemplateTestCase(unittest.TestCase):
 
     def test_task_template_POST_and_GET(self):
         # POST and GET a new item and assert correctness
-        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/task_template/', test_data_creator.TaskTemplate(), 201,
-                                                   test_data_creator.TaskTemplate())
+        test_data = test_data_creator.TaskTemplate()
+        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/task_template/', test_data, 201, test_data)
         url = r_dict['url']
-        GET_OK_and_assert_equal_expected_response(self, url + '?format=json', test_data_creator.TaskTemplate())
+        GET_OK_and_assert_equal_expected_response(self, url + '?format=json', test_data)
 
     def test_task_template_PUT_invalid_raises_error(self):
-        PUT_and_assert_expected_response(self, BASE_URL + '/task_template/9876789876/', test_data_creator.TaskTemplate(), 404, {})
+        test_data = test_data_creator.TaskTemplate()
+        PUT_and_assert_expected_response(self, BASE_URL + '/task_template/9876789876/', test_data, 404, {})
 
     def test_task_template_PUT(self):
         # POST new item, verify
-        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/task_template/', test_data_creator.TaskTemplate(), 201,
-                                                   test_data_creator.TaskTemplate())
+        test_data = test_data_creator.TaskTemplate()
+        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/task_template/', test_data, 201, test_data)
         url = r_dict['url']
-        GET_OK_and_assert_equal_expected_response(self, url, test_data_creator.TaskTemplate())
+        GET_OK_and_assert_equal_expected_response(self, url, test_data)
 
         # PUT new values, verify
-        PUT_and_assert_expected_response(self, url, test_data_creator.TaskTemplate("tasktemplate2"), 200, test_data_creator.TaskTemplate("tasktemplate2"))
-        GET_OK_and_assert_equal_expected_response(self, url, test_data_creator.TaskTemplate("tasktemplate2"))
+        test_data2 = test_data_creator.TaskTemplate("tasktemplate2")
+        PUT_and_assert_expected_response(self, url, test_data2, 200, test_data2)
+        GET_OK_and_assert_equal_expected_response(self, url, test_data2)
 
     def test_task_template_PATCH(self):
         # POST new item, verify
-        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/task_template/', test_data_creator.TaskTemplate(), 201,
-                                                   test_data_creator.TaskTemplate())
+        test_data = test_data_creator.TaskTemplate()
+        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/task_template/', test_data, 201, test_data)
         url = r_dict['url']
-        GET_OK_and_assert_equal_expected_response(self, url, test_data_creator.TaskTemplate())
+        GET_OK_and_assert_equal_expected_response(self, url, test_data)
 
         test_patch = {"version": 'v6.28318530718',
                       "schema": {"mykey": "my better value"},
@@ -238,10 +252,10 @@ class TaskTemplateTestCase(unittest.TestCase):
 
     def test_task_template_DELETE(self):
         # POST new item, verify
-        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/task_template/', test_data_creator.TaskTemplate(), 201,
-                                                   test_data_creator.TaskTemplate())
+        test_data = test_data_creator.TaskTemplate()
+        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/task_template/', test_data, 201, test_data)
         url = r_dict['url']
-        GET_OK_and_assert_equal_expected_response(self, url, test_data_creator.TaskTemplate())
+        GET_OK_and_assert_equal_expected_response(self, url, test_data)
 
         # DELETE and check it's gone
         DELETE_and_assert_gone(self, url)
@@ -268,30 +282,35 @@ class WorkRelationSelectionTemplateTestCase(unittest.TestCase):
     def test_work_relation_selection_template_POST_and_GET(self):
 
         # POST and GET a new item and assert correctness
-        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/work_relation_selection_template/', test_data_creator.WorkRelationSelectionTemplate(), 201, test_data_creator.WorkRelationSelectionTemplate())
+        test_data = test_data_creator.WorkRelationSelectionTemplate()
+        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/work_relation_selection_template/', test_data, 201, test_data)
         url = r_dict['url']
-        GET_OK_and_assert_equal_expected_response(self, url+'?format=json', test_data_creator.WorkRelationSelectionTemplate())
+        GET_OK_and_assert_equal_expected_response(self, url+'?format=json', test_data)
 
     def test_work_relation_selection_template_PUT_invalid_raises_error(self):
-        PUT_and_assert_expected_response(self, BASE_URL + '/work_relation_selection_template/9876789876/', test_data_creator.WorkRelationSelectionTemplate(), 404, {})
+        test_data = test_data_creator.WorkRelationSelectionTemplate()
+        PUT_and_assert_expected_response(self, BASE_URL + '/work_relation_selection_template/9876789876/', test_data, 404, {})
 
     def test_work_relation_selection_template_PUT(self):
 
         # POST new item, verify
-        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/work_relation_selection_template/', test_data_creator.WorkRelationSelectionTemplate(), 201, test_data_creator.WorkRelationSelectionTemplate())
+        test_data = test_data_creator.WorkRelationSelectionTemplate()
+        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/work_relation_selection_template/', test_data, 201, test_data)
         url = r_dict['url']
-        GET_OK_and_assert_equal_expected_response(self, url, test_data_creator.WorkRelationSelectionTemplate())
+        GET_OK_and_assert_equal_expected_response(self, url, test_data)
 
         # PUT new values, verify
-        PUT_and_assert_expected_response(self, url, test_data_creator.WorkRelationSelectionTemplate("workrelationselectiontemplate2"), 200, test_data_creator.WorkRelationSelectionTemplate("workrelationselectiontemplate2"))
-        GET_OK_and_assert_equal_expected_response(self, url, test_data_creator.WorkRelationSelectionTemplate("workrelationselectiontemplate2"))
+        test_data2 = test_data_creator.WorkRelationSelectionTemplate("workrelationselectiontemplate2")
+        PUT_and_assert_expected_response(self, url, test_data2, 200, test_data2)
+        GET_OK_and_assert_equal_expected_response(self, url, test_data2)
 
     def test_work_relation_selection_template_PATCH(self):
 
         # POST new item, verify
-        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/work_relation_selection_template/', test_data_creator.WorkRelationSelectionTemplate(), 201, test_data_creator.WorkRelationSelectionTemplate())
+        test_data = test_data_creator.WorkRelationSelectionTemplate()
+        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/work_relation_selection_template/', test_data, 201, test_data)
         url = r_dict['url']
-        GET_OK_and_assert_equal_expected_response(self, url, test_data_creator.WorkRelationSelectionTemplate())
+        GET_OK_and_assert_equal_expected_response(self, url, test_data)
 
         test_patch = {"version": 'v6.28318530718',
                       "schema": {"mykey": "my better value"},
@@ -306,9 +325,10 @@ class WorkRelationSelectionTemplateTestCase(unittest.TestCase):
     def test_work_relation_selection_template_DELETE(self):
 
         # POST new item, verify
-        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/work_relation_selection_template/', test_data_creator.WorkRelationSelectionTemplate(), 201, test_data_creator.WorkRelationSelectionTemplate())
+        test_data = test_data_creator.WorkRelationSelectionTemplate()
+        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/work_relation_selection_template/', test_data, 201, test_data)
         url = r_dict['url']
-        GET_OK_and_assert_equal_expected_response(self, url, test_data_creator.WorkRelationSelectionTemplate())
+        GET_OK_and_assert_equal_expected_response(self, url, test_data)
 
         # DELETE and check it's gone
         DELETE_and_assert_gone(self, url)
@@ -387,7 +407,8 @@ class TaskConnectorsTestCase(unittest.TestCase):
     def test_task_connectors_POST_existing_outputs_works(self):
 
         # First POST a new item to reference
-        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/task_template/', test_data_creator.TaskTemplate(), 201, test_data_creator.TaskTemplate())
+        test_data = test_data_creator.TaskTemplate()
+        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/task_template/', test_data, 201, test_data)
         url = r_dict['url']
 
         # POST a new item with correct reference
@@ -476,9 +497,8 @@ class TaskConnectorsTestCase(unittest.TestCase):
 
 class DefaultTemplates(unittest.TestCase):
     def test_default_generator_template_POST(self):
-        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/generator_template/',
-                                                   test_data_creator.GeneratorTemplate(), 201,
-                                                   test_data_creator.GeneratorTemplate())
+        test_data = test_data_creator.GeneratorTemplate()
+        r_dict = POST_and_assert_expected_response(self, BASE_URL + '/generator_template/', test_data, 201, test_data)
         url = r_dict['url']
 
         test_data_1 = dict(test_data_creator.DefaultTemplates())
@@ -486,9 +506,10 @@ class DefaultTemplates(unittest.TestCase):
         POST_and_assert_expected_response(self, BASE_URL + '/default_generator_template/', test_data_1, 201, test_data_1)
 
     def test_default_scheduling_unit_template_POST(self):
+        test_data = test_data_creator.SchedulingUnitTemplate()
         r_dict = POST_and_assert_expected_response(self, BASE_URL + '/scheduling_unit_template/',
-                                                   test_data_creator.SchedulingUnitTemplate(), 201,
-                                                   test_data_creator.SchedulingUnitTemplate())
+                                                   test_data, 201,
+                                                   test_data)
         url = r_dict['url']
 
         test_data_1 = dict(test_data_creator.DefaultTemplates())
@@ -496,9 +517,10 @@ class DefaultTemplates(unittest.TestCase):
         POST_and_assert_expected_response(self, BASE_URL + '/default_scheduling_unit_template/', test_data_1, 201, test_data_1)
 
     def test_default_task_template_POST(self):
+        test_data = test_data_creator.TaskTemplate()
         r_dict = POST_and_assert_expected_response(self, BASE_URL + '/task_template/',
-                                                   test_data_creator.TaskTemplate(), 201,
-                                                   test_data_creator.TaskTemplate())
+                                                   test_data, 201,
+                                                   test_data)
         url = r_dict['url']
 
         test_data_1 = dict(test_data_creator.DefaultTemplates())
@@ -506,9 +528,10 @@ class DefaultTemplates(unittest.TestCase):
         POST_and_assert_expected_response(self, BASE_URL + '/default_task_template/', test_data_1, 201, test_data_1)
 
     def test_default_work_relation_selection_template_POST(self):
+        test_data = test_data_creator.WorkRelationSelectionTemplate()
         r_dict = POST_and_assert_expected_response(self, BASE_URL + '/work_relation_selection_template/',
-                                                   test_data_creator.WorkRelationSelectionTemplate(), 201,
-                                                   test_data_creator.WorkRelationSelectionTemplate())
+                                                   test_data, 201,
+                                                   test_data)
         url = r_dict['url']
 
         test_data_1 = dict(test_data_creator.DefaultTemplates())
@@ -518,74 +541,78 @@ class DefaultTemplates(unittest.TestCase):
     def test_default_generator_template_PROTECT_behavior_on_template_deleted(self):
 
         # POST with dependency
+        test_data = test_data_creator.GeneratorTemplate()
         template_url = POST_and_assert_expected_response(self, BASE_URL + '/generator_template/',
-                                                test_data_creator.GeneratorTemplate(), 201,
-                                                test_data_creator.GeneratorTemplate())['url']
-        test_data = dict(test_data_creator.DefaultTemplates("defaulttemplate2"))
-        test_data['template'] = template_url
+                                                test_data, 201,
+                                                test_data)['url']
+        test_data2 = dict(test_data_creator.DefaultTemplates("defaulttemplate2"))
+        test_data2['template'] = template_url
         POST_and_assert_expected_response(self, BASE_URL + '/default_generator_template/',
-                                          test_data, 201, test_data)
+                                          test_data2, 201, test_data2)
 
         # Try to DELETE dependency, verify that was not successful
         # Unfortunately we don't get a nice error in json, but a Django debug page on error 500...
         response = requests.delete(template_url, auth=AUTH)
         self.assertEqual(500, response.status_code)
         self.assertTrue("ProtectedError" in str(response.content))
-        GET_OK_and_assert_equal_expected_response(self, template_url, test_data_creator.GeneratorTemplate())
+        GET_OK_and_assert_equal_expected_response(self, template_url, test_data)
 
     def test_default_scheduling_unit_template_PROTECT_behavior_on_template_deleted(self):
 
         # POST with dependency
+        test_data = test_data_creator.SchedulingUnitTemplate()
         template_url = POST_and_assert_expected_response(self, BASE_URL + '/scheduling_unit_template/',
-                                                test_data_creator.SchedulingUnitTemplate(), 201,
-                                                test_data_creator.SchedulingUnitTemplate())['url']
-        test_data = dict(test_data_creator.DefaultTemplates("defaulttemplate2"))
-        test_data['template'] = template_url
+                                                test_data, 201,
+                                                test_data)['url']
+        test_data2 = dict(test_data_creator.DefaultTemplates("defaulttemplate2"))
+        test_data2['template'] = template_url
         POST_and_assert_expected_response(self, BASE_URL + '/default_scheduling_unit_template/',
-                                          test_data, 201, test_data)
+                                          test_data2, 201, test_data2)
 
         # Try to DELETE dependency, verify that was not successful
         # Unfortunately we don't get a nice error in json, but a Django debug page on error 500...
         response = requests.delete(template_url, auth=AUTH)
         self.assertEqual(500, response.status_code)
         self.assertTrue("ProtectedError" in str(response.content))
-        GET_OK_and_assert_equal_expected_response(self, template_url, test_data_creator.SchedulingUnitTemplate())
+        GET_OK_and_assert_equal_expected_response(self, template_url, test_data)
 
     def test_default_task_template_PROTECT_behavior_on_template_deleted(self):
 
         # POST with dependency
+        test_data = test_data_creator.TaskTemplate()
         template_url = POST_and_assert_expected_response(self, BASE_URL + '/task_template/',
-                                                test_data_creator.TaskTemplate(), 201,
-                                                test_data_creator.TaskTemplate())['url']
-        test_data = dict(test_data_creator.DefaultTemplates("defaulttemplate2"))
-        test_data['template'] = template_url
+                                                test_data, 201,
+                                                test_data)['url']
+        test_data2 = dict(test_data_creator.DefaultTemplates("defaulttemplate2"))
+        test_data2['template'] = template_url
         POST_and_assert_expected_response(self, BASE_URL + '/default_task_template/',
-                                          test_data, 201, test_data)
+                                          test_data2, 201, test_data2)
 
         # Try to DELETE dependency, verify that was not successful
         # Unfortunately we don't get a nice error in json, but a Django debug page on error 500...
         response = requests.delete(template_url, auth=AUTH)
         self.assertEqual(500, response.status_code)
         self.assertTrue("ProtectedError" in str(response.content))
-        GET_OK_and_assert_equal_expected_response(self, template_url, test_data_creator.TaskTemplate())
+        GET_OK_and_assert_equal_expected_response(self, template_url, test_data)
 
     def test_default_work_relation_selection_template_PROTECT_behavior_on_template_deleted(self):
 
         # POST with dependency
+        test_data = test_data_creator.WorkRelationSelectionTemplate()
         template_url = POST_and_assert_expected_response(self, BASE_URL + '/work_relation_selection_template/',
-                                                test_data_creator.WorkRelationSelectionTemplate(), 201,
-                                                test_data_creator.WorkRelationSelectionTemplate())['url']
-        test_data = dict(test_data_creator.DefaultTemplates("defaulttemplate2"))
-        test_data['template'] = template_url
+                                                test_data, 201,
+                                                test_data)['url']
+        test_data2 = dict(test_data_creator.DefaultTemplates("defaulttemplate2"))
+        test_data2['template'] = template_url
         POST_and_assert_expected_response(self, BASE_URL + '/default_work_relation_selection_template/',
-                                          test_data, 201, test_data)
+                                          test_data2, 201, test_data2)
 
         # Try to DELETE dependency, verify that was not successful
         # Unfortunately we don't get a nice error in json, but a Django debug page on error 500...
         response = requests.delete(template_url, auth=AUTH)
         self.assertEqual(500, response.status_code)
         self.assertTrue("ProtectedError" in str(response.content))
-        GET_OK_and_assert_equal_expected_response(self, template_url, test_data_creator.WorkRelationSelectionTemplate())
+        GET_OK_and_assert_equal_expected_response(self, template_url, test_data)
 
 
 class CycleTestCase(unittest.TestCase):
diff --git a/SAS/TMSS/test/t_tmssapp_specification_django_API.py b/SAS/TMSS/test/t_tmssapp_specification_django_API.py
index 150b3cecf71e413a8bf4f6427cfa63f94aa59599..1b7c1fd39a891016230ac23d2e9f767579708e8d 100755
--- a/SAS/TMSS/test/t_tmssapp_specification_django_API.py
+++ b/SAS/TMSS/test/t_tmssapp_specification_django_API.py
@@ -39,6 +39,7 @@ from lofar.sas.tmss.test.tmss_database_unittest_setup import *
 from lofar.sas.tmss.test.tmss_test_data_django_models import *
 
 from django.db.utils import IntegrityError
+from django.core.exceptions import ValidationError
 
 
 class GeneratorTemplateTest(unittest.TestCase):
@@ -130,6 +131,21 @@ class TaskTemplateTest(unittest.TestCase):
         self.assertLess(before, entry.updated_at)
         self.assertGreater(after, entry.updated_at)
 
+    def test_TaskTemplate_name_version_unique(self):
+        test_data = TaskTemplate_test_data(name="my_name", version="1")
+        entry1 = models.TaskTemplate.objects.create(**test_data)
+
+        with self.assertRaises(IntegrityError):
+            entry2 = models.TaskTemplate.objects.create(**test_data)
+
+        test_data2 = dict(**test_data)
+        test_data2['version'] = "2"
+        entry2 = models.TaskTemplate.objects.create(**test_data2)
+
+        with self.assertRaises(IntegrityError):
+            entry2.version = '1'
+            entry2.save()
+
 
 class WorkRelationSelectionTemplateTest(unittest.TestCase):
     def test_WorkRelationSelectionTemplate_gets_created_with_correct_creation_timestamp(self):
diff --git a/SAS/TMSS/test/tmss_test_data_django_models.py b/SAS/TMSS/test/tmss_test_data_django_models.py
index 742128326aff23ea336e96fb604db8313adc40e8..d44e462b481bc4132e59689957d3a9691af0bf6b 100644
--- a/SAS/TMSS/test/tmss_test_data_django_models.py
+++ b/SAS/TMSS/test/tmss_test_data_django_models.py
@@ -34,10 +34,13 @@ from datetime import datetime
 import uuid
 import json
 
-def GeneratorTemplate_test_data(name="my_GeneratorTemplate") -> dict:
+def GeneratorTemplate_test_data(name="my_GeneratorTemplate", version:str=None) -> dict:
+    if version is None:
+        version = str(uuid.uuid4())
+
     return {"name": name,
             "description": 'My one observation',
-            "version": 'v0.314159265359',
+            "version": version,
             "schema": {"mykey": "my value"},
             "create_function": 'Funky',
             "tags": ["TMSS", "TESTING"]}
@@ -47,25 +50,34 @@ def DefaultGeneratorTemplate_test_data(name=None, template=None) -> dict:
             'template': template,
             'tags':[]}
 
-def SchedulingUnitTemplate_test_data(name="my_SchedulingUnitTemplate") -> dict:
+def SchedulingUnitTemplate_test_data(name="my_SchedulingUnitTemplate", version:str=None) -> dict:
+    if version is None:
+        version = str(uuid.uuid4())
+
     return {"name": name,
             "description": 'My SchedulingUnitTemplate description',
-            "version": 'v0.314159265359',
+            "version": version,
             "schema": {"mykey": "my value"},
             "tags": ["TMSS", "TESTING"]}
 
-def TaskTemplate_test_data(name="my TaskTemplate") -> dict:
+def TaskTemplate_test_data(name="my TaskTemplate", version:str=None) -> dict:
+    if version is None:
+        version = str(uuid.uuid4())
+
     return  {"validation_code_js":"",
               "name": name,
               "description": 'My TaskTemplate description',
-              "version": 'v0.314159265359',
+              "version": version,
               "schema": {"mykey": "my value"},
               "tags": ["TMSS", "TESTING"]}
 
-def WorkRelationSelectionTemplate_test_data(name="my_WorkRelationSelectionTemplate") -> dict:
+def WorkRelationSelectionTemplate_test_data(name="my_WorkRelationSelectionTemplate", version:str=None) -> dict:
+    if version is None:
+        version = str(uuid.uuid4())
+
     return  {"name": name,
                "description": 'My WorkRelationSelectionTemplate description',
-               "version": 'v0.314159265359',
+               "version": version,
                "schema": {"mykey": "my value"},
                "tags": ["TMSS", "TESTING"]}
 
@@ -197,30 +209,39 @@ def TaskRelationBlueprint_test_data() -> dict:
             "consumer": models.TaskBlueprint.objects.create(**TaskBlueprint_test_data())}
 
 
-def SubtaskTemplate_test_data(schema: object=None) -> dict:
+def SubtaskTemplate_test_data(schema: object=None, version:str=None) -> dict:
     if schema is None:
         schema = {}
 
+    if version is None:
+        version = str(uuid.uuid4())
+
     return {"type": models.SubtaskType.objects.get(value='copy'),
             "name": "observation",
             "description": 'My one observation',
-            "version": 'v0.314159265359',
+            "version": version,
             "schema": schema,
             "realtime": True,
             "queue": False,
             "tags": ["TMSS", "TESTING"]}
 
-def DataproductSpecificationsTemplate_test_data() -> dict:
+def DataproductSpecificationsTemplate_test_data(version:str=None) -> dict:
+    if version is None:
+        version = str(uuid.uuid4())
+
     return {"name": "data",
             "description": 'My one date',
-            "version": 'v0.314159265359',
+            "version": version,
             "schema": {"mykey": "my value"},
             "tags": ["TMSS", "TESTING"]}
 
-def DataproductFeedbackTemplate_test_data() -> dict:
+def DataproductFeedbackTemplate_test_data(version:str=None) -> dict:
+    if version is None:
+        version = str(uuid.uuid4())
+
     return {"name": "data",
             "description": 'My one date',
-            "version": 'v0.314159265359',
+            "version": version,
             "schema": {"mykey": "my value"},
             "tags": ["TMSS", "TESTING"]}
 
@@ -336,10 +357,13 @@ def DataproductHash_test_data() -> dict:
             "hash": "myhash_1",
             "tags": ['tmss', 'testing']}
 
-def SubtaskInputSelectionTemplate_test_data() -> dict:
+def SubtaskInputSelectionTemplate_test_data(version:str=None) -> dict:
+    if version is None:
+        version = str(uuid.uuid4())
+
     return {"name": "data",
             "description": 'My one date',
-            "version": 'v0.314159265359',
+            "version": version,
             "schema": {"mykey": "my value"},
             "tags": ["TMSS", "TESTING"]}
 
diff --git a/SAS/TMSS/test/tmss_test_data_rest.py b/SAS/TMSS/test/tmss_test_data_rest.py
index 771aaa9d4896206e734436d43871352bae189850..17c3e9e8170cf8d1fe70e32e63c8b678d102228a 100644
--- a/SAS/TMSS/test/tmss_test_data_rest.py
+++ b/SAS/TMSS/test/tmss_test_data_rest.py
@@ -45,33 +45,45 @@ class TMSSRESTTestDataCreator():
     #######################################################
     
     
-    def GeneratorTemplate(self, name="generatortemplate"):
+    def GeneratorTemplate(self, name="generatortemplate", version:str=None) -> dict:
+        if version is None:
+            version = str(uuid.uuid4())
+
         return {"name": name,
                 "description": 'My one observation',
-                "version": 'v0.314159265359',
+                "version": version,
                 "schema": {"mykey": "my value"},
                 "create_function": 'Funky',
                 "tags": ["TMSS", "TESTING"]}
     
-    def SchedulingUnitTemplate(self, name="schedulingunittemplate1"):
+    def SchedulingUnitTemplate(self, name="schedulingunittemplate1", version:str=None) -> dict:
+        if version is None:
+            version = str(uuid.uuid4())
+
         return { "name": name,
                  "description": 'My description',
-                 "version": 'v0.314159265359',
+                 "version": version,
                  "schema": {"mykey": "my value"},
                  "tags": ["TMSS", "TESTING"]}
     
-    def TaskTemplate(self, name="tasktemplate1"):
+    def TaskTemplate(self, name="tasktemplate1", version:str=None) -> dict:
+        if version is None:
+            version = str(uuid.uuid4())
+
         return {"name": name,
                 "description": 'My one observation',
-                "version": 'v0.314159265359',
+                "version": version,
                 "schema": {"mykey": "my value"},
                 "tags": ["TMSS", "TESTING"],
                 "validation_code_js": "???"}
     
-    def WorkRelationSelectionTemplate(self, name="workrelationselectiontemplate1"):
+    def WorkRelationSelectionTemplate(self, name="workrelationselectiontemplate1", version:str=None) -> dict:
+        if version is None:
+            version = str(uuid.uuid4())
+
         return {"name": name,
                 "description": 'My one observation',
-                "version": 'v0.314159265359',
+                "version": version,
                 "schema": {"mykey": "my value"},
                 "tags": ["TMSS", "TESTING"]}
     
@@ -294,7 +306,10 @@ class TMSSRESTTestDataCreator():
                 "producer": producer_url,
                 "consumer": consumer_url}
     
-    def SubtaskTemplate(self, name="subtask_template_1", schema=None, subtask_type_url: str=None):
+    def SubtaskTemplate(self, name="subtask_template_1", schema=None, subtask_type_url: str=None, version:str=None) -> dict:
+        if version is None:
+            version = str(uuid.uuid4())
+
         if schema is None:
             schema = {}
 
@@ -304,23 +319,29 @@ class TMSSRESTTestDataCreator():
         return {"type": subtask_type_url,
                        "name": name,
                        "description": 'My one observation',
-                       "version": 'v0.314159265359',
+                       "version": version,
                        "schema": schema,
                        "realtime": True,
                        "queue": False,
                        "tags": ["TMSS", "TESTING"]}
     
-    def DataproductSpecificationsTemplate(self, name="my_DataproductSpecificationsTemplate"):
+    def DataproductSpecificationsTemplate(self, name="my_DataproductSpecificationsTemplate", version:str=None) -> dict:
+        if version is None:
+            version = str(uuid.uuid4())
+
         return  {"name": name,
                  "description": 'My one date',
-                 "version": 'v0.314159265359',
+                 "version": version,
                  "schema": {"mykey": "my value"},
                  "tags": ["TMSS", "TESTING"]}
     
-    def DataproductFeedbackTemplate(self, name="my_DataproductFeedbackTemplate"):
+    def DataproductFeedbackTemplate(self, name="my_DataproductFeedbackTemplate", version:str=None) -> dict:
+        if version is None:
+            version = str(uuid.uuid4())
+
         return  {"name": name,
                  "description": 'My one date',
-                 "version": 'v0.314159265359',
+                 "version": version,
                  "schema": {"mykey": "my value"},
                  "tags": ["TMSS", "TESTING"]}
     
@@ -440,10 +461,13 @@ class TMSSRESTTestDataCreator():
                 "corrupted_since": datetime.utcnow().isoformat(),
                 "tags": ['tmss', 'testing']}
     
-    def SubtaskInputSelectionTemplate(self, name="my_SubtaskInputSelectionTemplate"):
+    def SubtaskInputSelectionTemplate(self, name="my_SubtaskInputSelectionTemplate", version:str=None) -> dict:
+        if version is None:
+            version = str(uuid.uuid4())
+
         return  {"name": name,
                  "description": 'My one date',
-                 "version": 'v0.314159265359',
+                 "version": version,
                  "schema": {"mykey": "my value"},
                  "tags": ["TMSS", "TESTING"]}