diff --git a/djangoldp/models.py b/djangoldp/models.py
index 8976d449d1af3c47848b69a6de53cae1352f37dc..2c5e45118a4130b8eaa889ec7c2c651d8fa8cb95 100644
--- a/djangoldp/models.py
+++ b/djangoldp/models.py
@@ -1,24 +1,23 @@
 import json
+import logging
 import uuid
 from urllib.parse import urlparse
+
 from django.conf import settings
-from django.contrib.auth.models import User
-from django.core.exceptions import ObjectDoesNotExist, ValidationError
 from django.contrib.auth import get_user_model
+from django.core.exceptions import ObjectDoesNotExist, ValidationError
 from django.db import models
 from django.db.models import BinaryField, DateTimeField
 from django.db.models.base import ModelBase
-from django.db.models.signals import post_save
+from django.db.models.signals import post_save, pre_save
 from django.dispatch import receiver
-from django.urls import reverse_lazy, get_resolver
+from django.urls import reverse_lazy, get_resolver, NoReverseMatch
 from django.utils.datastructures import MultiValueDictKeyError
 from django.utils.decorators import classonlymethod
 from rest_framework.utils import model_meta
 
 from djangoldp.fields import LDPUrlField
 from djangoldp.permissions import LDPPermissions
-import logging
-
 
 logger = logging.getLogger('djangoldp')
 
@@ -322,7 +321,8 @@ class Activity(Model):
 
 # temporary database-side storage used for scheduled tasks in the ActivityQueue
 class ScheduledActivity(Activity):
-    failed_attempts = models.PositiveIntegerField(default=0, help_text='a log of how many failed retries have been made sending the activity')
+    failed_attempts = models.PositiveIntegerField(default=0,
+                                                  help_text='a log of how many failed retries have been made sending the activity')
 
     def save(self, *args, **kwargs):
         self.is_finished = False
@@ -361,9 +361,14 @@ if 'djangoldp_account' not in settings.DJANGOLDP_PACKAGES:
             webid = '{0}{1}'.format(settings.BASE_URL, reverse_lazy('user-detail', kwargs={'pk': self.pk}))
         return webid
 
+
     get_user_model().webid = webid
 
 
-@receiver(post_save, sender=User)
-def update_perms(sender, instance, created, **kwargs):
-    LDPPermissions.invalidate_cache()
+@receiver(pre_save)
+def invalidate_caches(instance, **kwargs):
+    if isinstance(instance, Model):
+        from djangoldp.serializers import LDListMixin, LDPSerializer
+        LDPPermissions.invalidate_cache()
+        LDListMixin.to_representation_cache.reset()
+        LDPSerializer.to_representation_cache.reset()
diff --git a/djangoldp/permissions.py b/djangoldp/permissions.py
index c8f71aa846a2df552e5bfa5c1079c1cc98b73b41..1c3046cf64dffdcac4982027f588d71d7397b3c7 100644
--- a/djangoldp/permissions.py
+++ b/djangoldp/permissions.py
@@ -30,7 +30,7 @@ class LDPPermissions(DjangoObjectPermissions):
 
     @classmethod
     def refresh_cache(cls):
-        if time.time() - cls.perms_cache['time'] > 5:
+        if (time.time() - cls.perms_cache['time']) > 5:
             cls.invalidate_cache()
 
     def user_permissions(self, user, obj_or_model, obj=None):
@@ -38,18 +38,15 @@ class LDPPermissions(DjangoObjectPermissions):
             Filter user permissions for a model class
         """
 
-        # this may be a permission for the model class, or an instance
         self.refresh_cache()
+        # this may be a permission for the model class, or an instance
         if isinstance(obj_or_model, ModelBase):
             model = obj_or_model
         else:
             obj = obj_or_model
             model = obj_or_model.__class__
 
-        model_name = model._meta.model_name
-        user_key = 'None' if user is None else user.id
-        obj_key = 'None' if obj is None else obj.id
-        perms_cache_key = 'User{}{}{}'.format(user_key, model_name, obj_key)
+        perms_cache_key = self.cache_key(model, obj, user)
         if self.with_cache and perms_cache_key in self.perms_cache:
             return self.perms_cache[perms_cache_key]
 
@@ -95,7 +92,13 @@ class LDPPermissions(DjangoObjectPermissions):
         self.perms_cache[perms_cache_key] = list(perms)
 
         return self.perms_cache[perms_cache_key]
-        # return list(perms)
+
+    def cache_key(self, model, obj, user):
+        model_name = model._meta.model_name
+        user_key = 'None' if user is None else user.id
+        obj_key = 'None' if obj is None else obj.id
+        perms_cache_key = 'User{}{}{}'.format(user_key, model_name, obj_key)
+        return perms_cache_key
 
     def filter_user_perms(self, context, obj_or_model, permissions):
         # Only used on Model.get_permissions to translate permissions to LDP
diff --git a/djangoldp/serializers.py b/djangoldp/serializers.py
index a9eee01c855a0c33e51c3af9db30d3f72537a9b4..28894baf0aa12d83c36472d2b91f2bca97d61423 100644
--- a/djangoldp/serializers.py
+++ b/djangoldp/serializers.py
@@ -1,4 +1,3 @@
-import time
 import uuid
 from collections import OrderedDict, Mapping, Iterable
 from typing import Any
@@ -9,16 +8,17 @@ from django.contrib.auth import get_user_model
 from django.core.exceptions import ImproperlyConfigured
 from django.core.exceptions import ValidationError as DjangoValidationError
 from django.db import transaction
+from django.db.models import QuerySet
 from django.urls import resolve, Resolver404, get_script_prefix
 from django.urls.resolvers import get_resolver
-from django.db.models import QuerySet
 from django.utils.datastructures import MultiValueDictKeyError
 from django.utils.encoding import uri_to_iri
 from rest_framework.exceptions import ValidationError
 from rest_framework.fields import SkipField, empty, ReadOnlyField
 from rest_framework.fields import get_error_detail, set_value
 from rest_framework.relations import HyperlinkedRelatedField, ManyRelatedField, MANY_RELATION_KWARGS, Hyperlink
-from rest_framework.serializers import HyperlinkedModelSerializer, ListSerializer, ModelSerializer, LIST_SERIALIZER_KWARGS
+from rest_framework.serializers import HyperlinkedModelSerializer, ListSerializer, ModelSerializer, \
+    LIST_SERIALIZER_KWARGS
 from rest_framework.settings import api_settings
 from rest_framework.utils import model_meta
 from rest_framework.utils.field_mapping import get_nested_relation_kwargs
@@ -33,26 +33,34 @@ class InMemoryCache:
 
     def __init__(self):
         self.cache = {
-            'time': time.time()
         }
 
-    def invalidate_cache(self):
+    def reset(self):
         self.cache = {
-            'time': time.time()
         }
 
-    def refresh_cache(self):
-        if time.time() - self.cache['time'] > 5:
-            self.invalidate_cache()
+    def has(self, cache_key, vary):
+        if cache_key in self.cache and vary in self.cache[cache_key]:
+            return True
+        else:
+            return cache_key in self.cache
 
-    def has(self, cache_key):
-        return cache_key in self.cache
+    def get(self, cache_key, vary):
+        if self.has(cache_key, vary):
+            return self.cache[cache_key][vary]['value']
+        else:
+            return None
 
-    def get(self, cache_key):
-        return self.cache[cache_key]
+    def set(self, cache_key, vary, value):
+        if cache_key not in self.cache:
+            self.cache[cache_key] = {}
+        self.cache[cache_key][vary] = {'value': value}
 
-    def set(self, cache_key, value):
-        self.cache[cache_key] = value
+    def invalidate(self, cache_key, vary=None):
+        if vary is None:
+            self.cache.pop(cache_key, None)
+        else:
+            self.cache[cache_key].pop(vary, None)
 
 
 class LDListMixin:
@@ -88,7 +96,6 @@ class LDListMixin:
          - Can Add if add permission on contained object's type
          - Can view the container is view permission on container model : container obj are filtered by view permission
         '''
-        self.to_representation_cache.refresh_cache()
         try:
             child_model = getattr(self, self.child_attr).Meta.model
         except AttributeError:
@@ -96,6 +103,8 @@ class LDListMixin:
 
         parent_model = None
 
+        cache_vary = str(self.context['request'].user)
+
         if isinstance(value, QuerySet):
             value = list(value)
 
@@ -104,8 +113,8 @@ class LDListMixin:
                 self.id = '{}{}{}'.format(settings.BASE_URL, Model.resource(parent_model), self.id)
 
             cache_key = self.id
-            if self.with_cache and self.to_representation_cache.has(cache_key):
-                return self.to_representation_cache.get(cache_key)
+            if self.with_cache and self.to_representation_cache.has(cache_key, cache_vary):
+                return self.to_representation_cache.get(cache_key, cache_vary)
 
             filtered_values = value
             container_permissions = Model.get_permissions(child_model, self.context, ['view', 'add'])
@@ -121,8 +130,8 @@ class LDListMixin:
                 self.id = '{}{}{}'.format(settings.BASE_URL, Model.resource(parent_model), self.id)
 
             cache_key = self.id
-            if self.with_cache and self.to_representation_cache.has(cache_key):
-                return self.to_representation_cache.get(cache_key)
+            if self.with_cache and self.to_representation_cache.has(cache_key, cache_vary):
+                return self.to_representation_cache.get(cache_key, cache_vary)
 
             # remove objects from the list which I don't have permission to view
             filtered_values = list(
@@ -132,13 +141,14 @@ class LDListMixin:
             container_permissions.extend(
                 Model.get_permissions(parent_model, self.context, ['view']))
 
-        self.to_representation_cache.set(self.id, {'@id': self.id,
-                '@type': 'ldp:Container',
-                'ldp:contains': super().to_representation(filtered_values),
-                'permissions': container_permissions
-                })
+        self.to_representation_cache.set(self.id, cache_vary, {'@id': self.id,
+                                                               '@type': 'ldp:Container',
+                                                               'ldp:contains': super().to_representation(
+                                                                   filtered_values),
+                                                               'permissions': container_permissions
+                                                               })
 
-        return self.to_representation_cache.get(self.id)
+        return self.to_representation_cache.get(self.id, cache_vary)
 
     def get_attribute(self, instance):
         parent_id_field = self.parent.fields[self.parent.url_field_name]
@@ -268,6 +278,7 @@ class JsonLdRelatedField(JsonLdField):
 
 class JsonLdIdentityField(JsonLdField):
     '''Represents an identity (url) field for a serializer'''
+
     def __init__(self, view_name=None, **kwargs):
         kwargs['read_only'] = True
         kwargs['source'] = '*'
@@ -323,16 +334,16 @@ class LDPSerializer(HyperlinkedModelSerializer):
 
     def to_representation(self, obj):
         # external Models should only be returned with an id (on GET)
-        self.to_representation_cache.refresh_cache()
         if self.context['request'].method == 'GET' and Model.is_external(obj):
             return {'@id': obj.urlid}
 
+        cache_vary = str(self.context['request'].user)
         if self.with_cache and hasattr(obj, 'urlid'):
-            if self.to_representation_cache.has(obj.urlid):
-                data = self.to_representation_cache.get(obj.urlid)
+            if self.to_representation_cache.has(obj.urlid, cache_vary):
+                data = self.to_representation_cache.get(obj.urlid, cache_vary)
             else:
                 data = super().to_representation(obj)
-                self.to_representation_cache.set(obj.urlid, data)
+                self.to_representation_cache.set(obj.urlid, cache_vary, data)
         else:
             data = super().to_representation(obj)
 
@@ -716,7 +727,7 @@ class LDPSerializer(HyperlinkedModelSerializer):
         info = model_meta.get_field_info(instance)
         slug_field = Model.slug_field(instance)
         relation_info = info.relations.get(attr)
-        if slug_field in value :
+        if slug_field in value:
             value = self.update_dict_value_when_id_is_provided(attr, instance, relation_info, slug_field, value)
         else:
             if 'urlid' in value:
diff --git a/djangoldp/tests/perf_result.csv b/djangoldp/tests/perf_result.csv
index 33d2f033490923f81080c5ac54527b537eb97bb3..c6281b8e0e6b9326faa9c8451eaf3f71d72037b9 100644
--- a/djangoldp/tests/perf_result.csv
+++ b/djangoldp/tests/perf_result.csv
@@ -46,4 +46,6 @@ jbl+AC0-T440p,Oct 09 2020 11:56:19,True,True,100,0.003119325637817,0.00560247182
 jbl+AC0-T440p,Oct 09 2020 11:58:22,True,True,100,0.003008058071136,0.005401248931885,0.010658957958222,0.003909242153168,0.000718443393707,0.301162958145142,TRUE,3
 jbl+AC0-T440p,Oct 09 2020 11:59:16,True,True,100,0.003015418052673,0.005526115894318,0.010740044116974,0.00400491476059,0.000724492073059,0.313828229904175,TRUE,4
 jbl+AC0-T440p,Oct 09 2020 12:00:32,True,True,100,0.002969658374786,0.005434756278992,0.018136837482452,0.003030817508698,0.000726938247681,0.320115327835083,TRUE,0
-jbl-T440p,Oct 09 2020 12:21:00,True,True,100,0.0034934663772583007,0.0061032938957214355,0.019232537746429443,0.003091294765472412,0.0007375502586364747,0.36986708641052246,N/A
+jbl+AC0-T440p,Oct 09 2020 12:21:00,True,True,100,0.003493466377258,0.006103293895721,0.01923253774643,0.003091294765472,0.000737550258636,0.369867086410522,TRUE,0
+jbl+AC0-T440p,Oct 15 2020 22:00:10,True,True,100,0.003004941940308,0.00546817779541,0.018348352909088,0.003068554401398,0.000729415416718,0.320573329925537,TRUE,0
+jbl+AC0-T440p,Oct 15 2020 22:15:26,True,True,100,0.003350086212158,0.005898218154907,0.011625332832337,0.004264788627625,0.000795011520386,0.319289922714233,TRUE,1
diff --git a/djangoldp/tests/runner.py b/djangoldp/tests/runner.py
index 63cb160784dac942a2224dde6902205eff4f676c..5b432cc4db424c8966e2d98c7501ae946a78123f 100644
--- a/djangoldp/tests/runner.py
+++ b/djangoldp/tests/runner.py
@@ -25,7 +25,8 @@ failures = test_runner.run_tests([
     'djangoldp.tests.tests_sources',
     'djangoldp.tests.tests_pagination',
     'djangoldp.tests.tests_inbox',
-    'djangoldp.tests.tests_backlinks_service'
+    'djangoldp.tests.tests_backlinks_service',
+    'djangoldp.tests.tests_cache'
 
 ])
 if failures:
diff --git a/djangoldp/tests/tests_cache.py b/djangoldp/tests/tests_cache.py
new file mode 100644
index 0000000000000000000000000000000000000000..ff4aed657bae7be0b803a4b863e2add45fb52d56
--- /dev/null
+++ b/djangoldp/tests/tests_cache.py
@@ -0,0 +1,73 @@
+from django.contrib.auth import get_user_model
+from django.test import TestCase
+from rest_framework.test import APIRequestFactory, APIClient
+from rest_framework.utils import json
+
+from djangoldp.models import Model
+from djangoldp.serializers import LDPSerializer, LDListMixin
+from djangoldp.tests.models import Skill, JobOffer, Invoice, LDPDummy, Resource, Post, Circle, Project, Conversation
+
+
+class TestCache(TestCase):
+
+    def setUp(self):
+        self.factory = APIRequestFactory()
+        self.client = APIClient()
+        self.user = get_user_model().objects.create_user(username='john', email='jlennon@beatles.com',
+                                                         password='glass onion')
+        self.client.force_authenticate(self.user)
+        LDListMixin.to_representation_cache.reset()
+        LDPSerializer.to_representation_cache.reset()
+
+    def tearDown(self):
+        pass
+
+    def test_save_fk_graph_with_nested(self):
+        response = self.client.get('/batchs/', content_type='application/ld+json')
+        self.assertEqual(response.status_code, 200)
+
+        post = {
+            '@graph': [
+                {
+                    'http://happy-dev.fr/owl/#title': "title",
+                    'http://happy-dev.fr/owl/#invoice': {
+                        '@id': "_.123"
+                    }
+                },
+                {
+                    '@id': "_.123",
+                    'http://happy-dev.fr/owl/#title': "title 2"
+                }
+            ]
+        }
+
+        response = self.client.post('/batchs/', data=json.dumps(post), content_type='application/ld+json')
+        self.assertEqual(response.status_code, 201)
+
+        response = self.client.get('/batchs/', content_type='application/ld+json')
+        self.assertIn('ldp:contains', response.data)
+        self.assertEquals(response.data['ldp:contains'][0]['title'], "title")
+        self.assertEquals(response.data['ldp:contains'][0]['invoice']['title'], "title 2")
+
+    def test_update_with_new_fk_relation(self):
+        conversation = Conversation.objects.create(author_user=self.user,
+                                                   description="conversation description")
+        response = self.client.get('/conversations/{}/'.format(conversation.pk), content_type='application/ld+json')
+        body = [
+            {
+                '@id': "/conversations/{}/".format(conversation.pk),
+                'http://happy-dev.fr/owl/#description': "conversation update",
+                'http://happy-dev.fr/owl/#peer_user': {
+                    '@id': 'http://happy-dev.fr/users/{}'.format(self.user.pk),
+                }
+            }
+        ]
+        response = self.client.put('/conversations/{}/'.format(conversation.pk), data=json.dumps(body),
+                                   content_type='application/ld+json')
+        self.assertEqual(response.status_code, 200)
+
+        response = self.client.get('/conversations/{}/'.format(conversation.pk), content_type='application/ld+json')
+        self.assertIn('peer_user', response.data)
+        self.assertEquals('conversation update', response.data['description'])
+        self.assertIn('@id', response.data['peer_user'])
+
diff --git a/djangoldp/tests/tests_get.py b/djangoldp/tests/tests_get.py
index 96baf36e5e287d835eba632d20eea34f95f19cd9..7ae5c76f069a003f351bb4806c3e18651a7f8dff 100644
--- a/djangoldp/tests/tests_get.py
+++ b/djangoldp/tests/tests_get.py
@@ -11,8 +11,8 @@ class TestGET(APITestCase):
     def setUp(self):
         self.factory = APIRequestFactory()
         self.client = APIClient()
-        LDListMixin.to_representation_cache.invalidate_cache()
-        LDPSerializer.to_representation_cache.invalidate_cache()
+        LDListMixin.to_representation_cache.reset()
+        LDPSerializer.to_representation_cache.reset()
 
     def tearDown(self):
         pass
diff --git a/djangoldp/tests/tests_perf_get.py b/djangoldp/tests/tests_perf_get.py
index 4b1ca1eda35c450348f5a6b2b9c76a21f565624a..1120e1cccce09c7c2650e02fbc1be540ec633d5a 100644
--- a/djangoldp/tests/tests_perf_get.py
+++ b/djangoldp/tests/tests_perf_get.py
@@ -18,6 +18,7 @@ class TestPerformanceGET(APITestCase):
     result_line = []
     withAuth = True
     withPermsCache = True
+    # fixtures = ['test_ten_1000.json',]
 
     @classmethod
     def setUpClass(cls):
diff --git a/djangoldp/tests/tests_save.py b/djangoldp/tests/tests_save.py
index 92255e46536e3b3147266aa179b9a32a9153757a..bebcb4acf940bce37ddd2de2b706da3f34fbdff6 100644
--- a/djangoldp/tests/tests_save.py
+++ b/djangoldp/tests/tests_save.py
@@ -16,8 +16,8 @@ class Save(TestCase):
         self.user = get_user_model().objects.create_user(username='john', email='jlennon@beatles.com',
                                                          password='glass onion')
         self.client.force_authenticate(self.user)
-        LDListMixin.to_representation_cache.invalidate_cache()
-        LDPSerializer.to_representation_cache.invalidate_cache()
+        LDListMixin.to_representation_cache.reset()
+        LDPSerializer.to_representation_cache.reset()
 
     def tearDown(self):
         pass
diff --git a/djangoldp/tests/tests_update.py b/djangoldp/tests/tests_update.py
index ea293c3a4118af7889fcca3224e71e66002b88a7..a4b6ef01f8d0351be43322f36c9e1badba4dc873 100644
--- a/djangoldp/tests/tests_update.py
+++ b/djangoldp/tests/tests_update.py
@@ -17,8 +17,8 @@ class Update(TestCase):
         self.user = get_user_model().objects.create_user(username='john', email='jlennon@beatles.com',
                                                          password='glass onion')
         self.client.force_authenticate(user=self.user)
-        LDListMixin.to_representation_cache.invalidate_cache()
-        LDPSerializer.to_representation_cache.invalidate_cache()
+        LDListMixin.to_representation_cache.reset()
+        LDPSerializer.to_representation_cache.reset()
 
     def tearDown(self):
         pass