diff --git a/beat/web/attestations/tests.py b/beat/web/attestations/tests.py
index 28e0e2cb0d9b104a4934219a4617fb0278a6629b..58ac0e9db719e5078518cff1b390d6df06d55baf 100644
--- a/beat/web/attestations/tests.py
+++ b/beat/web/attestations/tests.py
@@ -221,7 +221,7 @@ class AttestationsAPIBase(BaseTestCase):
         environment = Environment(name='env1', version='1.0')
         environment.save()
 
-        queue = Queue(name='queue1', memory_limit=1024, time_limit=60, nb_cores_per_slot=1, max_slots_per_user=10)
+        queue = Queue(name='queue1', memory_limit=1024, time_limit=60, cores_per_slot=1, max_slots_per_user=10)
         queue.save()
 
         queue.environments.add(environment)
@@ -280,18 +280,6 @@ class AttestationsAPIBase(BaseTestCase):
         experiment.end_date   = datetime.now()
         experiment.save()
 
-        block            = Block()
-        block.experiment = experiment
-        block.name       = 'block1'
-        block.algorithm  = algorithm1
-        block.save()
-
-        block            = Block()
-        block.experiment = experiment
-        block.name       = 'analyzer1'
-        block.algorithm  = algorithm2
-        block.save()
-
 
     def tearDown(self):
         if os.path.exists(settings.TOOLCHAINS_ROOT):
diff --git a/beat/web/backend/api.py b/beat/web/backend/api.py
index 3316674a7c0da0e5cc4f3d956727c3525414b393..077b39ce93f71cb4f15deb570592ffe4316fcabf 100644
--- a/beat/web/backend/api.py
+++ b/beat/web/backend/api.py
@@ -27,7 +27,6 @@
 
 from django.conf import settings
 from django.db import transaction
-from django.db.models import Q
 from django.utils import six
 
 from rest_framework.decorators import api_view, permission_classes
@@ -510,13 +509,6 @@ def block_finished(request):
         return BadRequestResponse("ERROR: The block '%s' is already marked as 'failed' in the database" % data['block-name'])
 
 
-    # Create or retrieve cached files -- attach to block
-    all_cached_files = []
-    for hash in data['outputs']:
-        cache = CachedFile.objects.get(hash=hash)
-        cache.blocks.add(block)
-        all_cached_files.append(cache)
-
     # Updates all sorts of statistics on these caches (typically only one)
     statistics = None
     if block_state != 'cancelled':
@@ -526,53 +518,53 @@ def block_finished(request):
         if data['statistics']:
             statistics = beat.core.stats.Statistics(data['statistics'])
 
+    update_info = dict()
     if statistics is not None:
-        for cache in all_cached_files:
-            cache.cpu_time = statistics.cpu['user'] + statistics.cpu['system']
-            cache.max_memory = statistics.memory['rss']
-            cache.data_read_size = statistics.data['volume']['read']
-            cache.data_read_nb_blocks = statistics.data['blocks']['read']
-            cache.data_read_time = statistics.data['time']['read']
-            cache.data_written_size = statistics.data['volume']['write']
-            cache.data_written_nb_blocks = statistics.data['blocks']['write']
-            cache.data_written_time = statistics.data['time']['write']
+        update_info.update(dict(
+            cpu_time = statistics.cpu['user'] + statistics.cpu['system'],
+            max_memory = statistics.memory['rss'],
+            data_read_size = statistics.data['volume']['read'],
+            data_read_nb_blocks = statistics.data['blocks']['read'],
+            data_read_time = statistics.data['time']['read'],
+            data_written_size = statistics.data['volume']['write'],
+            data_written_nb_blocks = statistics.data['blocks']['write'],
+            data_written_time = statistics.data['time']['write'],
+            ))
 
     if data.has_key('execution_info') and \
         (data['execution_info'] is not None):
 
         execution_infos = data['execution_info']
 
-        for cache in all_cached_files:
-
-            if execution_infos.has_key('linear_execution_time') and \
-                (execution_infos['linear_execution_time'] is not None):
-                cache.linear_execution_time = \
-                    execution_infos['linear_execution_time']
+        if execution_infos.has_key('linear_execution_time') and \
+            (execution_infos['linear_execution_time'] is not None):
+            update_info['linear_execution_time'] = \
+                execution_infos['linear_execution_time']
 
-            if execution_infos.has_key('speed_up_real') and \
-                (execution_infos['speed_up_real'] is not None):
-                cache.speed_up_real = execution_infos['speed_up_real']
+        if execution_infos.has_key('speed_up_real') and \
+            (execution_infos['speed_up_real'] is not None):
+            update_info['speed_up_real'] = execution_infos['speed_up_real']
 
-            if execution_infos.has_key('speed_up_maximal') and \
-                (execution_infos['speed_up_maximal'] is not None):
-                cache.speed_up_maximal = execution_infos['speed_up_maximal']
+        if execution_infos.has_key('speed_up_maximal') and \
+            (execution_infos['speed_up_maximal'] is not None):
+            update_info['speed_up_maximal'] = \
+                execution_infos['speed_up_maximal']
 
-            if execution_infos.has_key('queuing_time') and \
-                (execution_infos['queuing_time'] is not None):
-                cache.queuing_time = execution_infos['queuing_time']
+        if execution_infos.has_key('queuing_time') and \
+            (execution_infos['queuing_time'] is not None):
+            update_info['queuing_time'] = execution_infos['queuing_time']
 
     # Logged messages
     if data.has_key('stdout') and isinstance(data['stdout'], six.string_types):
-        for cache in all_cached_files: cache.stdout = data['stdout']
+        update_info['stdout'] = data['stdout']
     if data.has_key('stderr') and isinstance(data['stderr'], six.string_types):
-        for cache in all_cached_files: cache.stderr = data['stderr']
+        update_info['stderr'] = data['stderr']
     if data.has_key('error-message') and \
         isinstance(data['error-message'], six.string_types):
-        for cache in all_cached_files:
-            cache.error_report = data['error-message']
+        update_info['error_message'] = data['error-message']
 
     # Saves all cached files to the database
-    for cache in all_cached_files: cache.save()
+    CachedFile.objects.filter(hash__in=data['outputs']).update(**update_info)
 
     # Save the results in the database (if applicable)
     if block.analyzer and (block_state == 'processed'):
@@ -607,7 +599,8 @@ def block_finished(request):
         block.status = Block.CACHED
         block.save()
 
-        if (experiment.status in [Experiment.SCHEDULED, Experiment.RUNNING]) and \
+        if (experiment.status in [Experiment.SCHEDULED, Experiment.RUNNING]) \
+            and \
            (experiment.blocks.filter(analyzer=True).exclude(status=Block.CACHED).count() == 0):
             experiment.end_date = datetime.now()
             experiment.status = Experiment.DONE
@@ -627,12 +620,7 @@ def block_finished(request):
             experiment.save()
 
     elif block_state == 'cancelled':
-        block.status = Block.NOT_CACHED
-        block.statistics = None
-        block.linear_execution_time = None
-        block.speed_up_real = None
-        block.speed_up_maximal = None
-        block.queuing_time = None
+        block.status=Block.NOT_CACHED
         block.save()
 
         if experiment.status not in [Experiment.CANCELING, Experiment.FAILED]:
@@ -641,7 +629,7 @@ def block_finished(request):
             experiment.save()
 
         if experiment.status == Experiment.CANCELING:
-            if experiment.blocks.filter(Q(status=Block.PROCESSING)).count() == 0:
+            if experiment.blocks.filter(status=Block.PROCESSING).count() == 0:
                 experiment.end_date = datetime.now()
                 experiment.status = Experiment.FAILED
                 experiment.save()
diff --git a/beat/web/backend/tests.py b/beat/web/backend/tests.py
index be1c7f3d4b48fbb0a2c6e305e0010964353be7a5..77f4fc9e07a2924a06f857b005bf9c163e96529a 100644
--- a/beat/web/backend/tests.py
+++ b/beat/web/backend/tests.py
@@ -153,7 +153,7 @@ class BackendAPIBase(BaseTestCase):
         environment = Environment(name='env1', version='1.0')
         environment.save()
 
-        queue = Queue(name='queue1', memory_limit=1024, time_limit=60, nb_cores_per_slot=1, max_slots_per_user=10)
+        queue = Queue(name='queue1', memory_limit=1024, time_limit=60, cores_per_slot=1, max_slots_per_user=10)
         queue.save()
 
         queue.environments.add(environment)
@@ -578,7 +578,7 @@ class BlockStartedAPI(BackendAPIBase):
 
         self.checkResponse(response, 204)
 
-        experiment = Experiment.objects.get(id=self.experiment.id)
+        experiment.refresh_from_db()
 
         self.assertTrue(experiment.start_date is not None)
         self.assertTrue(experiment.end_date is None)
@@ -675,12 +675,15 @@ class BlockFinishedAPI(BackendAPIBase):
     def test_bad_notification_request_with_unknown_experiment(self):
         self.client.login(username='scheduler', password='1234')
 
+        experiment = Experiment.objects.get(id=self.experiment.id)
+        block = experiment.blocks.get(name='addition1')
+
         response = self.client.put(self.url,
             json.dumps({
                 'experiment-name': 'johndoe/toolchain1/1/unknown',
                 'block-name': 'addition1',
                 'state': 'processed',
-                'outputs': ['deadbeef'],
+                'outputs': [o.encode() for o in block.outputs.values_list('hash', flat=True)],
                 'statistics': None,
             }), content_type='application/json')
 
@@ -778,18 +781,21 @@ class BlockFinishedAPI(BackendAPIBase):
     def test__not_cached_block__processed(self):
         self.client.login(username='scheduler', password='1234')
 
+        experiment = Experiment.objects.get(id=self.experiment.id)
+        block = experiment.blocks.get(name='addition1')
+
         response = self.client.put(self.url,
             json.dumps({
                 'experiment-name': 'johndoe/toolchain1/1/experiment1',
                 'block-name': 'addition1',
                 'state': 'processed',
-                'outputs': ['deadbeef'],
+                'outputs': [o.encode() for o in block.outputs.values_list('hash', flat=True)],
                 'statistics': None,
             }), content_type='application/json')
 
         self.checkResponse(response, 204)
 
-        experiment = Experiment.objects.get(id=self.experiment.id)
+        experiment.refresh_from_db()
 
         self.assertTrue(experiment.start_date is not None)
         self.assertTrue(experiment.end_date is None)
@@ -813,12 +819,15 @@ class BlockFinishedAPI(BackendAPIBase):
     def test__not_cached_block__failed(self):
         self.client.login(username='scheduler', password='1234')
 
+        experiment = Experiment.objects.get(id=self.experiment.id)
+        block = experiment.blocks.get(name='addition1')
+
         response = self.client.put(self.url,
             json.dumps({
                 'experiment-name': 'johndoe/toolchain1/1/experiment1',
                 'block-name': 'addition1',
                 'state': 'failed',
-                'outputs': ['deadbeef'],
+                'outputs': [o.encode() for o in block.outputs.values_list('hash', flat=True)],
                 'statistics': None,
             }), content_type='application/json')
 
@@ -828,12 +837,15 @@ class BlockFinishedAPI(BackendAPIBase):
     def test__not_cached_block__cancelled(self):
         self.client.login(username='scheduler', password='1234')
 
+        experiment = Experiment.objects.get(id=self.experiment.id)
+        block = experiment.blocks.get(name='addition1')
+
         response = self.client.put(self.url,
             json.dumps({
                 'experiment-name': 'johndoe/toolchain1/1/experiment1',
                 'block-name': 'addition1',
                 'state': 'cancelled',
-                'outputs': ['deadbeef'],
+                'outputs': [o.encode() for o in block.outputs.values_list('hash', flat=True)],
                 'statistics': None,
             }), content_type='application/json')
 
@@ -854,7 +866,7 @@ class BlockFinishedAPI(BackendAPIBase):
                 'experiment-name': 'johndoe/toolchain1/1/experiment1',
                 'block-name': 'addition1',
                 'state': 'processed',
-                'outputs': ['deadbeef'],
+                'outputs': [o.encode() for o in block.outputs.values_list('hash', flat=True)],
                 'statistics': None,
             }), content_type='application/json')
 
@@ -875,7 +887,7 @@ class BlockFinishedAPI(BackendAPIBase):
                 'experiment-name': 'johndoe/toolchain1/1/experiment1',
                 'block-name': 'addition1',
                 'state': 'failed',
-                'outputs': ['deadbeef'],
+                'outputs': [o.encode() for o in block.outputs.values_list('hash', flat=True)],
                 'statistics': None,
             }), content_type='application/json')
 
@@ -896,7 +908,7 @@ class BlockFinishedAPI(BackendAPIBase):
                 'experiment-name': 'johndoe/toolchain1/1/experiment1',
                 'block-name': 'addition1',
                 'state': 'cancelled',
-                'outputs': ['deadbeef'],
+                'outputs': [o.encode() for o in block.outputs.values_list('hash', flat=True)],
                 'statistics': None,
             }), content_type='application/json')
 
@@ -917,7 +929,7 @@ class BlockFinishedAPI(BackendAPIBase):
                 'experiment-name': 'johndoe/toolchain1/1/experiment1',
                 'block-name': 'addition1',
                 'state': 'processed',
-                'outputs': ['deadbeef'],
+                'outputs': [o.encode() for o in block.outputs.values_list('hash', flat=True)],
                 'statistics': None,
             }), content_type='application/json')
 
@@ -938,7 +950,7 @@ class BlockFinishedAPI(BackendAPIBase):
                 'experiment-name': 'johndoe/toolchain1/1/experiment1',
                 'block-name': 'addition1',
                 'state': 'failed',
-                'outputs': ['deadbeef'],
+                'outputs': [o.encode() for o in block.outputs.values_list('hash', flat=True)],
                 'statistics': None,
             }), content_type='application/json')
 
@@ -959,7 +971,7 @@ class BlockFinishedAPI(BackendAPIBase):
                 'experiment-name': 'johndoe/toolchain1/1/experiment1',
                 'block-name': 'addition1',
                 'state': 'cancelled',
-                'outputs': ['deadbeef'],
+                'outputs': [o.encode() for o in block.outputs.values_list('hash', flat=True)],
                 'statistics': None,
             }), content_type='application/json')
 
@@ -971,18 +983,21 @@ class BlockFinishedAPI(BackendAPIBase):
 
         self.start_block('addition1')
 
+        experiment = Experiment.objects.get(id=self.experiment.id)
+        block = experiment.blocks.get(name='addition1')
+
         response = self.client.put(self.url,
             json.dumps({
                 'experiment-name': 'johndoe/toolchain1/1/experiment1',
                 'block-name': 'addition1',
                 'state': 'processed',
-                'outputs': ['deadbeef'],
+                'outputs': [o.encode() for o in block.outputs.values_list('hash', flat=True)],
                 'statistics': None,
             }), content_type='application/json')
 
         self.checkResponse(response, 204)
 
-        experiment = Experiment.objects.get(id=self.experiment.id)
+        experiment.refresh_from_db()
 
         self.assertTrue(experiment.start_date is not None)
         self.assertTrue(experiment.end_date is None)
@@ -1009,18 +1024,21 @@ class BlockFinishedAPI(BackendAPIBase):
 
         self.start_block('addition1')
 
+        experiment = Experiment.objects.get(id=self.experiment.id)
+        block = experiment.blocks.get(name='addition1')
+
         response = self.client.put(self.url,
             json.dumps({
                 'experiment-name': 'johndoe/toolchain1/1/experiment1',
                 'block-name': 'addition1',
                 'state': 'failed',
-                'outputs': ['deadbeef'],
+                'outputs': [o.encode() for o in block.outputs.values_list('hash', flat=True)],
                 'statistics': None,
             }), content_type='application/json')
 
         self.checkResponse(response, 204)
 
-        experiment = Experiment.objects.get(id=self.experiment.id)
+        experiment.refresh_from_db()
 
         self.assertTrue(experiment.start_date is not None)
         self.assertTrue(experiment.end_date is not None)
@@ -1046,18 +1064,21 @@ class BlockFinishedAPI(BackendAPIBase):
 
         self.start_block('addition1')
 
+        experiment = Experiment.objects.get(id=self.experiment.id)
+        block = experiment.blocks.get(name='addition1')
+
         response = self.client.put(self.url,
             json.dumps({
                 'experiment-name': 'johndoe/toolchain1/1/experiment1',
                 'block-name': 'addition1',
                 'state': 'cancelled',
-                'outputs': ['deadbeef'],
+                'outputs': [o.encode() for o in block.outputs.values_list('hash', flat=True)],
                 'statistics': None,
             }), content_type='application/json')
 
         self.checkResponse(response, 204)
 
-        experiment = Experiment.objects.get(id=self.experiment.id)
+        experiment.refresh_from_db()
 
         self.assertTrue(experiment.creation_date is not None)
         self.assertTrue(experiment.start_date is not None)
@@ -1073,18 +1094,21 @@ class BlockFinishedAPI(BackendAPIBase):
         self.start_block('addition1')
         self.start_block('addition2')
 
+        experiment = Experiment.objects.get(id=self.experiment.id)
+        block = experiment.blocks.get(name='addition1')
+
         response = self.client.put(self.url,
             json.dumps({
                 'experiment-name': 'johndoe/toolchain1/1/experiment1',
                 'block-name': 'addition1',
                 'state': 'cancelled',
-                'outputs': ['deadbeef'],
+                'outputs': [o.encode() for o in block.outputs.values_list('hash', flat=True)],
                 'statistics': None,
             }), content_type='application/json')
 
         self.checkResponse(response, 204)
 
-        experiment = Experiment.objects.get(id=self.experiment.id)
+        experiment.refresh_from_db()
 
         self.assertTrue(experiment.start_date is not None)
         self.assertTrue(experiment.end_date is not None)
diff --git a/beat/web/experiments/tests.py b/beat/web/experiments/tests.py
index de67b02555b79d4d1083787b683affcb6202c50f..8b08fe78915cde54b7bc92ae26332af3c619eb46 100644
--- a/beat/web/experiments/tests.py
+++ b/beat/web/experiments/tests.py
@@ -207,7 +207,7 @@ class ExperimentTestBase(BaseTestCase):
         environment = Environment(name='env1', version='1.0')
         environment.save()
 
-        queue = Queue(name='queue1', memory_limit=1024, time_limit=60, nb_cores_per_slot=1, max_slots_per_user=10)
+        queue = Queue(name='queue1', memory_limit=1024, time_limit=60, cores_per_slot=1, max_slots_per_user=10)
         queue.save()
 
         queue.environments.add(environment)
@@ -548,7 +548,7 @@ class ExperimentCreationAPI(ExperimentTestBase):
         self.assertTrue(experiment.start_date is None)
         self.assertTrue(experiment.end_date is None)
         self.assertEqual(experiment.status, Experiment.PENDING)
-        self.assertEqual(experiment.blocks.count(), 0)
+        self.assertEqual(experiment.blocks.count(), 3)
 
 
 #----------------------------------------------------------
@@ -816,24 +816,24 @@ class ExperimentStartingAPI(ExperimentTestBase):
         self.assertFalse(block.analyzer)
         self.assertEqual(0, block.results.count())
 
-        hashes = block.hashes.all()
-        self.assertEqual(0, hashes.count())
+        hashes = block.outputs.all()
+        self.assertEqual(1, hashes.count())
 
         block = experiment.blocks.get(name='addition2')
         self.assertEqual(Block.NOT_CACHED, block.status)
         self.assertFalse(block.analyzer)
         self.assertEqual(0, block.results.count())
 
-        hashes = block.hashes.all()
-        self.assertEqual(0, hashes.count())
+        hashes = block.outputs.all()
+        self.assertEqual(1, hashes.count())
 
         block = experiment.blocks.get(name='analysis')
         self.assertEqual(Block.NOT_CACHED, block.status)
         self.assertTrue(block.analyzer)
         self.assertEqual(0, block.results.count())
 
-        hashes = block.hashes.all()
-        self.assertEqual(0, hashes.count())
+        hashes = block.outputs.all()
+        self.assertEqual(1, hashes.count())
 
 
     def test_start_team_shared_experiment(self):
@@ -1102,11 +1102,9 @@ class ResultsAPI(ExperimentTestBase):
         self.experiment.status = Experiment.FAILED
         self.experiment.save()
 
-        cached_file = CachedFile()
-        cached_file.hash = 'deadbeef123456'
+        cached_file = block.first_cache()
         cached_file.error_report = 'ERROR REPORT'
         cached_file.save()
-        cached_file.blocks.add(block)
 
         self.client.login(username='johndoe', password='1234')
 
@@ -1190,7 +1188,7 @@ class ResultsAPI(ExperimentTestBase):
         analysis_block.save()
 
         db_result             = Result()
-        db_result.block       = analysis_block
+        db_result.cache       = analysis_block.first_cache()
         db_result.name        = 'out_float'
         db_result.type        = 'float32'
         db_result.primary     = True
@@ -1198,7 +1196,7 @@ class ResultsAPI(ExperimentTestBase):
         db_result.save()
 
         db_result             = Result()
-        db_result.block       = analysis_block
+        db_result.cache       = analysis_block.first_cache()
         db_result.name        = 'out_text'
         db_result.type        = 'string'
         db_result.primary     = True
@@ -1235,7 +1233,7 @@ class ResultsAPI(ExperimentTestBase):
         analysis_block.save()
 
         db_result             = Result()
-        db_result.block       = analysis_block
+        db_result.cache       = analysis_block.first_cache()
         db_result.name        = 'out_float'
         db_result.type        = 'float32'
         db_result.primary     = True
@@ -1243,7 +1241,7 @@ class ResultsAPI(ExperimentTestBase):
         db_result.save()
 
         db_result             = Result()
-        db_result.block       = analysis_block
+        db_result.cache       = analysis_block.first_cache()
         db_result.name        = 'out_text'
         db_result.type        = 'string'
         db_result.primary     = True
diff --git a/beat/web/reports/tests.py b/beat/web/reports/tests.py
index d9cbe6e6af909a4e3808811b03d529a551510df3..26839c9b5ade337706fa3f1786f5753e436dd654 100644
--- a/beat/web/reports/tests.py
+++ b/beat/web/reports/tests.py
@@ -511,7 +511,7 @@ class ReportTestCase(APITestCase):
         environment = Environment(name='env1', version='1.0')
         environment.save()
 
-        queue = Queue(name='queue1', memory_limit=1024, time_limit=60, nb_cores_per_slot=1, max_slots_per_user=10)
+        queue = Queue(name='queue1', memory_limit=1024, time_limit=60, cores_per_slot=1, max_slots_per_user=10)
         queue.save()
 
         queue.environments.add(environment)