diff --git a/beat/web/databases/__init__.py b/beat/web/databases/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..93a15eb13994a4273d1626fb9e0113696b6fb263 100644
--- a/beat/web/databases/__init__.py
+++ b/beat/web/databases/__init__.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.web module of the BEAT platform.              #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+default_app_config = 'beat.web.databases.apps.DatabasesConfig'
diff --git a/beat/web/databases/admin.py b/beat/web/databases/admin.py
index 32daf554d3763a3738303ade03696bbec765717a..06d55ad34a83d5bcc268766c19dcf6d778d4b479 100644
--- a/beat/web/databases/admin.py
+++ b/beat/web/databases/admin.py
@@ -240,6 +240,13 @@ class DatabaseSetTemplateOutputInline(admin.TabularInline):
     model           = DatabaseSetTemplateOutputModel
     extra           = 0
     ordering        = ('name',)
+    readonly_fields = ('name', 'dataformat')
+
+    def has_delete_permission(self, request, obj=None):
+        return False
+
+    def has_add_permission(self, request):
+            return False
 
 
 class DatabaseSetTemplate(admin.ModelAdmin):
@@ -247,11 +254,19 @@ class DatabaseSetTemplate(admin.ModelAdmin):
     list_display        = ('id', 'name')
     search_fields       = ['name']
     list_display_links  = ('id', 'name')
+    readonly_fields = ('name',)
 
     inlines = [
             DatabaseSetTemplateOutputInline,
             ]
 
+    def has_delete_permission(self, request, obj=None):
+        return False
+
+    def has_add_permission(self, request):
+            return False
+
+
 admin.site.register(DatabaseSetTemplateModel, DatabaseSetTemplate)
 
 
@@ -263,6 +278,13 @@ class DatabaseSetOutputInline(admin.TabularInline):
     model           = DatabaseSetOutputModel
     extra           = 0
     ordering        = ('hash',)
+    readonly_fields = ('hash', 'template')
+
+    def has_delete_permission(self, request, obj=None):
+        return False
+
+    def has_add_permission(self, request):
+            return False
 
 
 class DatabaseSet(admin.ModelAdmin):
@@ -271,13 +293,19 @@ class DatabaseSet(admin.ModelAdmin):
     search_fields       = ['name',
                            'template__name',
                            'protocol__database__name',
-                           'protocol__database__short_description',
-                           'protocol__database__description',
                            'protocol__name']
     list_display_links  = ('id', 'name')
+    readonly_fields = ('name', 'template', 'protocol')
 
     inlines = [
             DatabaseSetOutputInline,
             ]
 
+    def has_delete_permission(self, request, obj=None):
+        return False
+
+    def has_add_permission(self, request):
+            return False
+
+
 admin.site.register(DatabaseSetModel, DatabaseSet)
diff --git a/beat/web/databases/apps.py b/beat/web/databases/apps.py
new file mode 100644
index 0000000000000000000000000000000000000000..fd94461ab2849a1702de37129f7232f0690b97ad
--- /dev/null
+++ b/beat/web/databases/apps.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.web module of the BEAT platform.              #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+from ..common.apps import CommonAppConfig
+from django.utils.translation import ugettext_lazy as _
+
+class DatabasesConfig(CommonAppConfig):
+    name = 'beat.web.databases'
+    verbose_name = _('Databases')
+
+    def ready(self):
+        super(DatabasesConfig, self).ready()
+        from . import signals
diff --git a/beat/web/databases/migrations/0002_auto_20160329_1733.py b/beat/web/databases/migrations/0002_auto_20160329_1733.py
new file mode 100644
index 0000000000000000000000000000000000000000..4264e2be61b2b94c75157c8caa64c1cbb84b9111
--- /dev/null
+++ b/beat/web/databases/migrations/0002_auto_20160329_1733.py
@@ -0,0 +1,91 @@
+# -*- coding: utf-8 -*-
+
+###############################################################################
+#                                                                             #
+# Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/           #
+# Contact: beat.support@idiap.ch                                              #
+#                                                                             #
+# This file is part of the beat.web module of the BEAT platform.              #
+#                                                                             #
+# Commercial License Usage                                                    #
+# Licensees holding valid commercial BEAT licenses may use this file in       #
+# accordance with the terms contained in a written agreement between you      #
+# and Idiap. For further information contact tto@idiap.ch                     #
+#                                                                             #
+# Alternatively, this file may be used under the terms of the GNU Affero      #
+# Public License version 3 as published by the Free Software and appearing    #
+# in the file LICENSE.AGPL included in the packaging of this file.            #
+# The BEAT platform is distributed in the hope that it will be useful, but    #
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY  #
+# or FITNESS FOR A PARTICULAR PURPOSE.                                        #
+#                                                                             #
+# You should have received a copy of the GNU Affero Public License along      #
+# with the BEAT platform. If not, see http://www.gnu.org/licenses/.           #
+#                                                                             #
+###############################################################################
+
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+from ...common.models import get_declaration
+
+from ..models import validate_database
+
+import logging
+logger = logging.getLogger(__name__)
+
+
+def refresh_databases(apps, schema_editor):
+    '''Refreshes each database so datasets/outputs are recreated'''
+
+    Database = apps.get_model("databases", "Database")
+    DatabaseSetOutput = apps.get_model("databases", "DatabaseSetOutput")
+
+    Database.declaration = property(get_declaration)
+    Database.fullname = lambda self: '%s/%d' % (self.name, self.version)
+
+    print('')
+
+    for db in Database.objects.order_by('id'):
+        print("Refreshing protocols for database `%s'..." % db.fullname())
+        core = validate_database(db.declaration)
+        core.name = db.fullname()
+        for proto in db.protocols.all():
+            for set in proto.sets.all():
+                for output in set.template.outputs.all():
+                    try:
+                        DatabaseSetOutput(template=output, set=set,
+                            hash=core.hash_output(proto.name, set.name,
+                              output.name)).save()
+                    except KeyError:
+                        logger.warn('Database output %s/%d.%s.%s.%s does ' \
+                            'not exist' % (db.name, db.version, proto.name,
+                              set.name, output.name))
+                        continue
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('dataformats', '0001_initial'),
+        ('databases', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.RenameModel('DatabaseOutput', 'DatabaseSetTemplateOutput'),
+        migrations.AlterUniqueTogether(
+            name='databasesettemplateoutput',
+            unique_together=set([('template', 'name')]),
+        ),
+        migrations.CreateModel(
+            name='DatabaseSetOutput',
+            fields=[
+                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('hash', models.CharField(unique=True, max_length=64)),
+                ('set', models.ForeignKey(related_name='outputs', to='databases.DatabaseSet')),
+                ('template', models.ForeignKey(related_name='instances', to='databases.DatabaseSetTemplateOutput')),
+            ],
+        ),
+        migrations.RunPython(refresh_databases),
+    ]
diff --git a/beat/web/databases/models.py b/beat/web/databases/models.py
index 64fa485e86db3dd3b7c87e94840781effe04aaa3..1351621e1bd87ed7b2fcf9ba9271d3c145ff8def 100755
--- a/beat/web/databases/models.py
+++ b/beat/web/databases/models.py
@@ -31,7 +31,6 @@ import simplejson
 
 from django.db import models
 from django.conf import settings
-from django.dispatch import receiver
 from django.core.urlresolvers import reverse
 
 import beat.core.database
@@ -263,9 +262,6 @@ class Database(Versionable):
             result.extend(database_protocol.all_needed_dataformats())
         return list(set(result))
 
-    def core(self):
-        return validate_database(self.declaration)
-
     #_____ Properties __________
 
     description = property(get_description, set_description)
@@ -414,7 +410,7 @@ class DatabaseSetTemplateOutput(models.Model):
         related_name='database_outputs', on_delete=models.CASCADE)
 
     class Meta:
-        unique_together = ('template', 'name', 'dataformat')
+        unique_together = ('template', 'name')
 
     def __str__(self):
         return self.fullname()
@@ -441,7 +437,7 @@ class DatabaseSetOutput(models.Model):
             self.set.protocol.database.fullname(),
             self.set.protocol.name,
             self.set.name,
-            self.name,
+            self.template.name,
             )
 
     def all_referenced_dataformats(self):
diff --git a/beat/web/databases/signals.py b/beat/web/databases/signals.py
index cf8ebad4207de36fef3802ff4d086292dc5881e9..474c719ba27387a6b57fd02ec0f8e73e1a585dc3 100644
--- a/beat/web/databases/signals.py
+++ b/beat/web/databases/signals.py
@@ -27,6 +27,7 @@
 
 
 from django.db import models
+from django.dispatch import receiver
 
 from ..dataformats.models import DataFormat
 
@@ -34,6 +35,8 @@ from .models import Database, DatabaseProtocol, DatabaseSet
 from .models import DatabaseSetTemplate, DatabaseSetTemplateOutput
 from .models import DatabaseSetOutput
 
+from .models import validate_database
+
 
 @receiver(models.signals.post_delete, sender=Database)
 def auto_delete_file_on_delete(sender, instance, **kwargs):
@@ -86,7 +89,9 @@ def refresh_protocols(sender, instance, **kwargs):
     """Refreshes changed protocols"""
 
     try:
-        core = instance.core()
+
+        core = validate_database(instance.declaration)
+        core.name = instance.fullname()
 
         protocols = DatabaseProtocol.objects.filter(
                 database__name=instance.name,
@@ -94,13 +99,13 @@ def refresh_protocols(sender, instance, **kwargs):
             )
 
         existing = set((k.name, k.set_template_basename()) for k in protocols)
-        new_objects = set((k['name'], k['template']) for k in core.protocols()])
+        new_objects = set([(k,v['template']) for k,v in core.protocols.items()])
 
         for protocol_name, template in existing - new_objects:
             # notice: no need to worry, this will clean-up all the rest
             protocols.get(name__iexact=protocol_name).delete()
 
-        json_protocols = dict([(k['name'], k) for k in core.protocols()]])
+        json_protocols = dict([(k,v) for k,v in core.protocols.items()])
 
         for protocol_name, template in new_objects - existing:
             protocol = DatabaseProtocol(name=protocol_name, database=instance)
@@ -135,72 +140,72 @@ def refresh_protocols(sender, instance, **kwargs):
                         )
                     dataset.save()
 
-                    # Create the database set template output
-                    for output_name, format_name in set_attr['outputs'].items():
-                        if len(format_name.split('/')) != 3:
-                            raise SyntaxError(
-                                "Dataformat should be named following the " \
-                                "style `<username>/<format>/<version>', the " \
-                                "value `%s' is not valid" % (format_name,)
-                                )
-                        (author, name, version) = format_name.split('/')
-                        dataformats = DataFormat.objects.filter(
-                            author__username=author,
-                            name=name,
-                            version=version,
+                # Create the database set template output
+                for output_name, format_name in set_attr['outputs'].items():
+                    if len(format_name.split('/')) != 3:
+                        raise SyntaxError(
+                            "Dataformat should be named following the " \
+                            "style `<username>/<format>/<version>', the " \
+                            "value `%s' is not valid" % (format_name,)
                             )
+                    (author, name, version) = format_name.split('/')
+                    dataformats = DataFormat.objects.filter(
+                        author__username=author,
+                        name=name,
+                        version=version,
+                        )
 
-                        # TODO: Remove this when validation works (see comments)
-                        if len(dataformats) != 1:
-                            raise SyntaxError(
-                                "Could not find dataformat named `%s' to set" \
-                                "output `%s' of template `%s' for protocol" \
-                                "`%s' of database `%s'", (
-                                  format_name,
-                                  output_name,
-                                  dataset_template.name,
-                                  protocol_name,
-                                  instance.name,
-                                  )
-                                )
-                            return
+                    # TODO: Remove this when validation works (see comments)
+                    if len(dataformats) != 1:
+                        raise SyntaxError(
+                            "Could not find dataformat named `%s' to set" \
+                            "output `%s' of template `%s' for protocol" \
+                            "`%s' of database `%s'", (
+                              format_name,
+                              output_name,
+                              dataset_template.name,
+                              protocol_name,
+                              instance.name,
+                              )
+                            )
+                        return
 
+                    database_template_output = \
+                        DatabaseSetTemplateOutput.objects.filter(
+                            name=output_name,
+                            template=dataset_template,
+                            dataformat=dataformats[0],
+                            )
+
+                    if not database_template_output: # create
                         database_template_output = \
-                            DatabaseSetTemplateOutput.objects.filter(
+                            DatabaseSetTemplateOutput(
                                 name=output_name,
                                 template=dataset_template,
                                 dataformat=dataformats[0],
                                 )
+                        database_template_output.save()
 
-                        if not database_template_output: # create
-                            database_template_output = \
-                                DatabaseSetTemplateOutput(
-                                    name=output_name,
-                                    template=dataset_template,
-                                    dataformat=dataformats[0],
-                                    )
-                            database_template_output.save()
-
-                        else:
-                            database_template_output = \
-                                database_template_output[0]
-
-                        # Create the database template output
-                        hash = core.hash_output(protocol.name,
-                            dataset.name, output_name)
-                        dataset_output = \
-                            DatabaseSetOutput.objects.filter(hash=hash)
-
-                        if not dataset_output: # create
-                            dataset_output = DatabaseSetOutput(
-                                template=database_template_output,
-                                set=dataset,
-                                hash=hash,
-                                )
-                            dataset_output.save()
+                    else:
+                        database_template_output = \
+                            database_template_output[0]
+
+                    # Create the database set output
+                    hash = core.hash_output(protocol.name,
+                        dataset.name, output_name)
+                    dataset_output = \
+                        DatabaseSetOutput.objects.filter(hash=hash)
+
+                    if not dataset_output: # create
+                        dataset_output = DatabaseSetOutput(
+                            template=database_template_output,
+                            set=dataset,
+                            hash=hash,
+                            )
+                        dataset_output.save()
 
     except Exception:
-        instance.delete() #do we need this or is it auto-rolled back?
+        instance.delete()
         raise