diff --git a/djangoldp/activities/services.py b/djangoldp/activities/services.py
index 29de522ddf8a75a49dc85c22d04f81224f1b7ee0..15cbe2e44d21236349aa7ce3a517f9ac1dc246fe 100644
--- a/djangoldp/activities/services.py
+++ b/djangoldp/activities/services.py
@@ -1,16 +1,16 @@
 import threading
+import json
+import requests
 from urllib.parse import urlparse
-from django.conf import settings
 from django.contrib.auth import get_user_model
 from django.db.models.signals import post_save, post_delete, m2m_changed
 from django.dispatch import receiver
+from django.conf import settings
 from rest_framework.utils import model_meta
 
 from djangoldp.models import Model, Follower
 from djangoldp.models import Activity as ActivityModel
 
-from .objects import *
-from .verbs import *
 import logging
 
 
@@ -298,7 +298,7 @@ def check_m2m_for_backlinks(sender, instance, action, *args, **kwargs):
         for obj in query_set:
             condition = Model.is_external(obj) and getattr(obj, 'allow_create_backlink', False)
             if action == "post_add":
-                condition = condition and not obj.is_backlink
+                condition = condition and not getattr(obj, 'is_backlink', True)
 
             if condition:
                 targets.append({
diff --git a/djangoldp/activities/verbs.py b/djangoldp/activities/verbs.py
index 313b738f799021162e7f1208e78b4f886af6c404..55568d585a7e7ce535f216ac78d569c2b1f6a903 100644
--- a/djangoldp/activities/verbs.py
+++ b/djangoldp/activities/verbs.py
@@ -1,6 +1,5 @@
 from copy import copy
 
-from django.conf import settings
 from djangoldp.activities import errors
 from djangoldp.activities.objects import ALLOWED_TYPES, Object, Actor
 
diff --git a/djangoldp/permissions.py b/djangoldp/permissions.py
index 5d888fe7f596eca1436b7ead3a1f8ddd6d315fa0..4e1112068df585a163e992293407ed28bab88e76 100644
--- a/djangoldp/permissions.py
+++ b/djangoldp/permissions.py
@@ -1,7 +1,7 @@
 from django.core.exceptions import PermissionDenied
 from django.db.models.base import ModelBase
 from rest_framework.permissions import DjangoObjectPermissions
-from guardian.shortcuts import get_user_perms
+from django.contrib.auth.models import _user_get_all_permissions
 
 
 class LDPPermissions(DjangoObjectPermissions):
@@ -47,12 +47,10 @@ class LDPPermissions(DjangoObjectPermissions):
         perms = set()
 
         if obj is not None and not user.is_anonymous:
-            guardian_perms = get_user_perms(user, obj)
+            # get permissions from all backends and then remove model name from the permissions
             model_name = model._meta.model_name
-
-            # remove model name from the permissions
             forbidden_string = "_" + model_name
-            perms = set([p.replace(forbidden_string, '') for p in guardian_perms])
+            perms = set([p.replace(forbidden_string, '') for p in _user_get_all_permissions(user, obj)])
 
         # apply anon, owner and auth permissions
         if user.is_anonymous:
diff --git a/djangoldp/serializers.py b/djangoldp/serializers.py
index b10fe10956156ba773c0c28ae2555be49e3afce8..e0a62879307d8edda321cb18b61cf5af07fb5764 100644
--- a/djangoldp/serializers.py
+++ b/djangoldp/serializers.py
@@ -7,7 +7,8 @@ from django.conf import settings
 from django.contrib.auth import get_user_model
 from django.core.exceptions import ImproperlyConfigured
 from django.core.exceptions import ValidationError as DjangoValidationError
-from django.core.urlresolvers import get_resolver, resolve, get_script_prefix, Resolver404
+from django.urls import resolve, Resolver404, get_script_prefix
+from django.urls import get_resolver
 from django.db.models import QuerySet
 from django.utils.datastructures import MultiValueDictKeyError
 from django.utils.encoding import uri_to_iri
@@ -514,6 +515,7 @@ class LDPSerializer(HyperlinkedModelSerializer):
         return ret
 
     def get_value(self, dictionary):
+        '''overrides get_value to handle @graph key'''
         try:
             object_list = dictionary["@graph"]
             if self.parent.instance is None:
@@ -549,10 +551,10 @@ class LDPSerializer(HyperlinkedModelSerializer):
         return instance
 
     def attach_related_object(self, instance, validated_data):
+        '''adds m2m relations included in validated_data to the instance'''
         model_class = self.Meta.model
 
         info = model_meta.get_field_info(model_class)
-        many_to_many = {}
         for field_name, relation_info in info.relations.items():
             if relation_info.to_many and relation_info.reverse and not field_name is None:
                 rel = getattr(instance._meta.model, field_name).rel
@@ -561,7 +563,7 @@ class LDPSerializer(HyperlinkedModelSerializer):
                     getattr(instance, field_name).add(related)
 
     def internal_create(self, validated_data, model):
-        validated_data = self.resolve_fk_instances(model, validated_data)
+        validated_data = self.resolve_fk_instances(model, validated_data, True)
 
         # build tuples list of nested_field keys and their values
         nested_fields = []
@@ -617,16 +619,18 @@ class LDPSerializer(HyperlinkedModelSerializer):
 
         return instance
 
-    def resolve_fk_instances(self, model, validated_data):
-        '''iterates over every dict object in validated_data and resolves them into instances (get or create)'''
+    def resolve_fk_instances(self, model, validated_data, create=False):
+        '''
+        iterates over every dict object in validated_data and resolves them into instances (get or create)
+        :param model: the model being operated on
+        :param validated_data: the data passed to the serializer
+        :param create: set to True, foreign keys will be created if they do not exist
+        '''
         nested_fk_fields_name = list(filter(lambda key: isinstance(validated_data[key], dict), validated_data))
         for field_name in nested_fk_fields_name:
             field_dict = validated_data[field_name]
-            try:
-                field_model = getattr(model, field_name).field.rel.model
-            except:
-                # not fk
-                continue
+            field_model = model._meta.get_field(field_name).related_model
+
             slug_field = Model.slug_field(field_model)
             sub_inst = None
             if 'urlid' in field_dict:
@@ -646,7 +650,11 @@ class LDPSerializer(HyperlinkedModelSerializer):
                 kwargs = {slug_field: field_dict[slug_field]}
                 sub_inst = field_model.objects.get(**kwargs)
             if sub_inst is None:
-                sub_inst = self.internal_create(field_dict, field_model)
+                if create:
+                    sub_inst = self.internal_create(field_dict, field_model)
+                else:
+                    continue
+
             validated_data[field_name] = sub_inst
         return validated_data
 
@@ -664,13 +672,9 @@ class LDPSerializer(HyperlinkedModelSerializer):
         if relation_info.to_many:
             value = self.internal_create(validated_data=value, model=relation_info.related_model)
         else:
-            try:
-                reverse_attr_name = instance._meta.fields_map[attr].remote_field.name
-                many = False
-            except:
-                rel = list(filter(lambda field: field.name == attr, instance._meta.fields))[0].rel
-                many = rel.one_to_many
-                reverse_attr_name = rel.related_name
+            rel = instance._meta.get_field(attr)
+            reverse_attr_name = rel.remote_field.name
+            many = rel.one_to_many or rel.many_to_many
             if many:
                 value[reverse_attr_name] = [instance]
                 oldObj = rel.model.object.get(id=value['urlid'])
diff --git a/djangoldp/tests/djangoldp_urls.py b/djangoldp/tests/djangoldp_urls.py
index 4766fedf3b50f759a64578dea5c081adcef9aeb4..2d43fc3dbd7715089a2a5ecd29d69bc29aa0c5cc 100644
--- a/djangoldp/tests/djangoldp_urls.py
+++ b/djangoldp/tests/djangoldp_urls.py
@@ -1,16 +1,15 @@
-from django.conf import settings
-from django.conf.urls import url, include
+from django.conf.urls import re_path
 
 from djangoldp.permissions import LDPPermissions
 from djangoldp.tests.models import Skill, JobOffer, Message, Conversation, Dummy, PermissionlessDummy, Task, DateModel
 from djangoldp.views import LDPViewSet
 
 urlpatterns = [
-    url(r'^messages/', LDPViewSet.urls(model=Message, permission_classes=[LDPPermissions], fields=["@id", "text", "conversation"], nested_fields=['conversation'])),
-    url(r'^conversations/', LDPViewSet.urls(model=Conversation, nested_fields=["message_set"], permission_classes=[LDPPermissions])),
-    url(r'^tasks/', LDPViewSet.urls(model=Task, permission_classes=[LDPPermissions])),
-    url(r'^dates/', LDPViewSet.urls(model=DateModel, permission_classes=[LDPPermissions])),
-    url(r'^dummys/', LDPViewSet.urls(model=Dummy, permission_classes=[LDPPermissions], lookup_field='slug',)),
-    url(r'^permissionless-dummys/', LDPViewSet.urls(model=PermissionlessDummy, permission_classes=[LDPPermissions], lookup_field='slug',)),
+    re_path(r'^messages/', LDPViewSet.urls(model=Message, permission_classes=[LDPPermissions], fields=["@id", "text", "conversation"], nested_fields=['conversation'])),
+    re_path(r'^conversations/', LDPViewSet.urls(model=Conversation, nested_fields=["message_set"], permission_classes=[LDPPermissions])),
+    re_path(r'^tasks/', LDPViewSet.urls(model=Task, permission_classes=[LDPPermissions])),
+    re_path(r'^dates/', LDPViewSet.urls(model=DateModel, permission_classes=[LDPPermissions])),
+    re_path(r'^dummys/', LDPViewSet.urls(model=Dummy, permission_classes=[LDPPermissions], lookup_field='slug',)),
+    re_path(r'^permissionless-dummys/', LDPViewSet.urls(model=PermissionlessDummy, permission_classes=[LDPPermissions], lookup_field='slug',)),
 ]
 
diff --git a/djangoldp/tests/models.py b/djangoldp/tests/models.py
index a1bf7db49b1d4b0e3c6671eba4415b5dc8a2d262..eb4d11be75049641c0783a259c2ce5f2f4012e32 100644
--- a/djangoldp/tests/models.py
+++ b/djangoldp/tests/models.py
@@ -59,8 +59,9 @@ class JobOffer(Model):
 
 class Conversation(models.Model):
     description = models.CharField(max_length=255, blank=True, null=True)
-    author_user = models.ForeignKey(settings.AUTH_USER_MODEL)
-    peer_user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, related_name="peers_conv")
+    author_user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING)
+    peer_user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, related_name="peers_conv",
+                                  on_delete=models.DO_NOTHING)
 
     class Meta(Model.Meta):
         anonymous_perms = ['view']
@@ -83,7 +84,7 @@ class Resource(Model):
 
 class UserProfile(Model):
     description = models.CharField(max_length=255, blank=True, null=True)
-    user = models.OneToOneField(settings.AUTH_USER_MODEL, related_name='userprofile')
+    user = models.OneToOneField(settings.AUTH_USER_MODEL, related_name='userprofile', on_delete=models.CASCADE)
 
     class Meta(Model.Meta):
         anonymous_perms = ['view']
@@ -95,7 +96,7 @@ class UserProfile(Model):
 class Message(models.Model):
     text = models.CharField(max_length=255, blank=True, null=True)
     conversation = models.ForeignKey(Conversation, on_delete=models.DO_NOTHING)
-    author_user = models.ForeignKey(settings.AUTH_USER_MODEL)
+    author_user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING)
 
     class Meta(Model.Meta):
         anonymous_perms = ['view']
@@ -172,8 +173,9 @@ class Task(models.Model):
 
 class Post(Model):
     content = models.CharField(max_length=255)
-    author = models.ForeignKey(UserProfile, blank=True, null=True)
-    peer_user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, related_name="peers_post")
+    author = models.ForeignKey(UserProfile, blank=True, null=True, on_delete=models.SET_NULL)
+    peer_user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, related_name="peers_post",
+                                  on_delete=models.SET_NULL)
 
     class Meta(Model.Meta):
         auto_author = 'author'
diff --git a/djangoldp/tests/settings_default.py b/djangoldp/tests/settings_default.py
index db0e8609fe45891062ac76d64a632dcbabc78e84..6ecb12a9129cab2b3adf3b5e017daa9fc5b2c5f2 100644
--- a/djangoldp/tests/settings_default.py
+++ b/djangoldp/tests/settings_default.py
@@ -14,6 +14,8 @@ INSTALLED_APPS=('django.contrib.auth',
                'django.contrib.contenttypes',
                'django.contrib.sessions',
                'django.contrib.admin',
+               'django.contrib.messages',
+               'django.contrib.staticfiles',
                'guardian',
                'djangoldp',
                'djangoldp.tests',
@@ -32,11 +34,37 @@ REST_FRAMEWORK = {
 
 AUTH_USER_MODEL='tests.User'
 ANONYMOUS_USER_NAME = None
+
+MIDDLEWARE = [
+    'django.middleware.security.SecurityMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.middleware.common.CommonMiddleware',
+    'django.middleware.csrf.CsrfViewMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+    'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
 AUTHENTICATION_BACKENDS=(
    'django.contrib.auth.backends.ModelBackend', 'guardian.backends.ObjectPermissionBackend')
 
 ROOT_URLCONF='djangoldp.urls'
 
+TEMPLATES = [
+    {
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'DIRS': [],
+        'APP_DIRS': True,
+        'OPTIONS': {
+            'context_processors': [
+                'django.template.context_processors.debug',
+                'django.template.context_processors.request',
+                'django.contrib.auth.context_processors.auth',
+                'django.contrib.messages.context_processors.messages',
+            ],
+        },
+    },
+]
+
 LDP_RDF_CONTEXT={
    "@context": {
        "@vocab": "http://happy-dev.fr/owl/#",
diff --git a/djangoldp/tests/tests_save.py b/djangoldp/tests/tests_save.py
index 5ec9830d3e558f4950fabd9f36c0dd27d94424c4..d1ab7b30e1f4a4a8b0454f023239ca1ada46d257 100644
--- a/djangoldp/tests/tests_save.py
+++ b/djangoldp/tests/tests_save.py
@@ -328,6 +328,20 @@ class Save(TestCase):
         saved_post = Post.objects.get(pk=1)
         self.assertEqual(saved_post.urlid, "http://happy-dev.fr/posts/1/")
 
+    def test_save_invalid_nested_user(self):
+        body = {
+            '@id': "./",
+            'content': "post update",
+            'peer_user': {'none': None},
+            '@context': {
+                "@vocab": "http://happy-dev.fr/owl/#",
+            }
+        }
+
+        response = self.client.post('/posts/', data=json.dumps(body),
+                                    content_type='application/ld+json')
+        self.assertEqual(response.status_code, 400)
+
     def test_nested_container_user_federated(self):
         project = Project.objects.create()
         body = {
diff --git a/djangoldp/tests/tests_update.py b/djangoldp/tests/tests_update.py
index 7157b72ef0b6b8c79f39b58b4df93953a663c87b..17d5702ceece7558d75adc4325ba363c105eaf43 100644
--- a/djangoldp/tests/tests_update.py
+++ b/djangoldp/tests/tests_update.py
@@ -572,6 +572,7 @@ class Update(TestCase):
                                    content_type='application/ld+json')
         self.assertEqual(response.status_code, 200)
         self.assertIn('userprofile', response.data)
+        self.assertIsNotNone(response.data['userprofile'])
 
     def test_m2m_user_link_remove_existing_link(self):
         ext_user = get_user_model().objects.create(username=str(uuid.uuid4()), urlid='http://external.user/user/1')
diff --git a/djangoldp/urls.py b/djangoldp/urls.py
index 49db770cbe66344c89f35c0102653fb6342b35a0..424f1afbbf55c0ffe0ffe7fc0e661ca31bcee70a 100644
--- a/djangoldp/urls.py
+++ b/djangoldp/urls.py
@@ -1,7 +1,7 @@
 from importlib import import_module
 
 from django.conf import settings
-from django.conf.urls import url, include
+from django.conf.urls import re_path, include
 
 from djangoldp.models import LDPSource, Model
 from djangoldp.permissions import LDPPermissions
@@ -19,21 +19,21 @@ def __clean_path(path):
 
 
 urlpatterns = [
-    url(r'^sources/(?P<federation>\w+)/', LDPSourceViewSet.urls(model=LDPSource, fields=['federation', 'urlid'],
+    re_path(r'^sources/(?P<federation>\w+)/', LDPSourceViewSet.urls(model=LDPSource, fields=['federation', 'urlid'],
                                                                 permission_classes=[LDPPermissions], )),
-    url(r'^\.well-known/webfinger/?$', WebFingerView.as_view()),
-    url(r'^inbox/$', InboxView.as_view()),
+    re_path(r'^\.well-known/webfinger/?$', WebFingerView.as_view()),
+    re_path(r'^inbox/$', InboxView.as_view()),
 ]
 
 for package in settings.DJANGOLDP_PACKAGES:
     try:
         import_module('{}.models'.format(package))
-        urlpatterns.append(url(r'^', include('{}.djangoldp_urls'.format(package))))
+        urlpatterns.append(re_path(r'^', include('{}.djangoldp_urls'.format(package))))
     except ModuleNotFoundError:
         pass
 
 if 'djangoldp_account' not in settings.DJANGOLDP_PACKAGES:
-    urlpatterns.append(url(r'^users/', LDPViewSet.urls(model=settings.AUTH_USER_MODEL, permission_classes=[])))
+    urlpatterns.append(re_path(r'^users/', LDPViewSet.urls(model=settings.AUTH_USER_MODEL, permission_classes=[])))
 
 # fetch a list of all models which subclass DjangoLDP Model
 model_classes = {cls.__name__: cls for cls in Model.__subclasses__()}
@@ -43,11 +43,11 @@ for class_name in model_classes:
     model_class = model_classes[class_name]
     # the path is the url for this model
     path = __clean_path(model_class.get_container_path())
-    # urls_fct will be a method which generates urls for a ViewSet (defined in LDPViewSet)
+    # urls_fct will be a method which generates urls for a ViewSet (defined in LDPViewSetGenerator)
     urls_fct = model_class.get_view_set().urls
-    urlpatterns.append(url(r'^' + path, include(
+    urlpatterns.append(re_path(r'^' + path,
         urls_fct(model=model_class,
                  lookup_field=Model.get_meta(model_class, 'lookup_field', 'pk'),
                  permission_classes=Model.get_meta(model_class, 'permission_classes', [LDPPermissions]),
                  fields=Model.get_meta(model_class, 'serializer_fields', []),
-                 nested_fields=model_class.nested.fields()))))
+                 nested_fields=model_class.nested.fields())))
diff --git a/djangoldp/views.py b/djangoldp/views.py
index d08943348bd13ef0945b26563b3fbda86eb4eb21..6e12304e21c2d655e5b9593b9b577302ee4eafe2 100644
--- a/djangoldp/views.py
+++ b/djangoldp/views.py
@@ -1,11 +1,10 @@
-import copy
 import json
 from django.apps import apps
 from django.conf import settings
-from django.conf.urls import url, include
+from django.conf.urls import re_path, include
 from django.contrib.auth import get_user_model
 from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist
-from django.core.urlresolvers import get_resolver
+from django.urls import get_resolver
 from django.db import IntegrityError, transaction
 from django.http import JsonResponse, Http404
 from django.shortcuts import get_object_or_404
@@ -14,7 +13,6 @@ from django.views import View
 from pyld import jsonld
 from rest_framework import status
 from rest_framework.authentication import SessionAuthentication
-from rest_framework.exceptions import ValidationError
 from rest_framework.permissions import AllowAny
 from rest_framework.parsers import JSONParser
 from rest_framework.renderers import JSONRenderer
@@ -328,9 +326,9 @@ class LDPViewSetGenerator(ModelViewSet):
         detail_expr = cls.get_detail_expr(**kwargs)
 
         urls = [
-            url('^$', cls.as_view(cls.list_actions, **kwargs), name='{}-list'.format(model_name)),
-            url('^' + detail_expr + '$', cls.as_view(cls.detail_actions, **kwargs),
-                name='{}-detail'.format(model_name)),
+            re_path('^$', cls.as_view(cls.list_actions, **kwargs), name='{}-list'.format(model_name)),
+            re_path('^' + detail_expr + '$', cls.as_view(cls.detail_actions, **kwargs),
+                    name='{}-detail'.format(model_name)),
         ]
 
         # append nested fields to the urls list
@@ -346,7 +344,7 @@ class LDPViewSetGenerator(ModelViewSet):
                 urls_fct = kwargs['view_set'].nested_urls # our custom view_set may override nested_urls
             else:
                 urls_fct = cls.nested_urls
-            urls.append(url('^' + detail_expr + field + '/', urls_fct(field, **kwargs)))
+            urls.append(re_path('^' + detail_expr + field + '/', urls_fct(field, **kwargs)))
 
         return include(urls)
 
@@ -496,11 +494,11 @@ class LDPViewSet(LDPViewSetGenerator):
         response["Access-Control-Allow-Credentials"] = 'true'
         response["Accept-Post"] = "application/ld+json"
         if response.status_code in [201, 200] and '@id' in response.data:
-            response["Location"] = response.data['@id']
+            response["Location"] = str(response.data['@id'])
         else:
             pass
         response["Accept-Post"] = "application/ld+json"
-        if request.user.is_authenticated():
+        if request.user.is_authenticated:
             try:
                 response['User'] = request.user.webid()
             except AttributeError:
diff --git a/setup.cfg b/setup.cfg
index a07d9b5852954d89c8983e8e0bc50730d1d8ce6b..453495b3d13692a7c70a2e20f49fea36e8767071 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -10,14 +10,14 @@ license = MIT
 [options]
 packages = find:
 setup_requires =
-    django~=1.11
+    django~=2.2
 install_requires =
-    django~=1.11
+    django~=2.2
     django_rest_framework
     requests
     validators~=0.12
     pyld==1.0.5
-    django-guardian==2.0.0
+    django-guardian==2.3.0
 
 [options.extras_require]
 dev =