diff --git a/README.md b/README.md
index e8525f1c41aa65fa6f64b780ded4280a4012a706..17b3fabed37bd158d1522e0a7949e594db04828d 100644
--- a/README.md
+++ b/README.md
@@ -27,11 +27,14 @@ django-admin startproject myldpserver
 ```
 
 3. Create your django model inside a file myldpserver/myldpserver/models.py
+Note that container_path will be use to resolve instance iri and container iri
+In the future it could also be used to auto configure django router (e.g. urls.py)
 
 ```
-from django.db import models
+from djangoldp.models import Model
 
-class Todo(models.Model):
+class Todo(Model):
+    container_path = "/my-path/"
     name = models.CharField(max_length=255)
     deadline = models.DateTimeField()
 
diff --git a/djangoldp/models.py b/djangoldp/models.py
index 50b31c6c8181e7642d9c5dcc11ac1df41b1490d9..f63ffd4b353cfe20a4f157ebb477cbce7549d56c 100644
--- a/djangoldp/models.py
+++ b/djangoldp/models.py
@@ -1,10 +1,77 @@
 from django.conf import settings
 from django.db import models
+from django.urls import get_resolver
+
+
+class Model(models.Model):
+    container_path = None
+
+    def get_container_path(self):
+        return self.container_path
+
+    def get_absolute_url(self):
+        return Model.resource_id(self)
+
+    def get_container_id(self):
+        return Model.container_id(self)
+
+    @classmethod
+    def resource_id(cls, instance):
+        view_name = '{}-detail'.format(instance._meta.object_name.lower())
+        slug_field = '/{}'.format(get_resolver().reverse_dict[view_name][0][0][1][0])
+        if slug_field.startswith('/'):
+            slug_field = slug_field[1:]
+        return "{}{}".format(cls.container_id(instance), getattr(instance, slug_field))
+
+    @classmethod
+    def container_id(cls, instance):
+        if isinstance(instance, cls):
+            path = instance.container_path
+        else:
+            view_name = '{}-list'.format(instance._meta.object_name.lower())
+            path = get_resolver().reverse(view_name)
+
+        path = cls.__clean_path(path)
+
+        return path
+
+    class Meta:
+        abstract = True
+
+    @classmethod
+    def resolve_id(cls, id):
+        id = cls.__clean_path(id)
+        view, args, kwargs = get_resolver().resolve(id)
+        return view.initkwargs['model'].objects.get(**kwargs)
+
+    @classmethod
+    def resolve_container(cls, path):
+        path = cls.__clean_path(path)
+        view, args, kwargs = get_resolver().resolve(path)
+        return view.initkwargs['model']
+
+    @classmethod
+    def resolve(cls, path):
+        container = cls.resolve_container(path)
+        try:
+            resolve_id = cls.resolve_id(path)
+        except:
+            resolve_id = None
+        return container, resolve_id
+
+    @classmethod
+    def __clean_path(cls, path):
+        if not path.startswith("/"):
+            path = "/{}".format(path)
+        if not path.endswith("/"):
+            path = "{}/".format(path)
+        return path
+
 
 class LDPSource(models.Model):
     container = models.URLField()
     federation = models.CharField(max_length=255)
-    
+
     class Meta:
         rdf_type = 'sib:source'
         ordering = ('federation',)
@@ -12,7 +79,7 @@ class LDPSource(models.Model):
             ('view_source', 'acl:Read'),
             ('control_source', 'acl:Control'),
         )
-    
+
     def __str__(self):
         return "{}: {}".format(self.federation, self.container)
 
@@ -24,6 +91,7 @@ class LDNotification(models.Model):
     type = models.CharField(max_length=255)
     summary = models.TextField()
     date = models.DateTimeField(auto_now_add=True)
+
     class Meta:
         permissions = (
             ('view_todo', 'Read'),
diff --git a/djangoldp/serializers.py b/djangoldp/serializers.py
index 815be9068ffdc34903d8dae1af4bd06bca64cae6..18a63a1daf9b6d62885e2bee2849ad7dbe7820bc 100644
--- a/djangoldp/serializers.py
+++ b/djangoldp/serializers.py
@@ -16,6 +16,8 @@ from rest_framework.settings import api_settings
 from rest_framework.utils.field_mapping import get_nested_relation_kwargs
 from rest_framework.utils.serializer_helpers import ReturnDict
 
+from djangoldp.models import Model
+
 
 class LDListMixin:
     def to_internal_value(self, data):
@@ -40,9 +42,8 @@ class LDListMixin:
     def get_value(self, dictionary):
         try:
             object_list = dictionary["@graph"]
-            view_name = '{}-list'.format(self.parent.Meta.model._meta.object_name.lower())
-            part_id = '/{}'.format(get_resolver().reverse_dict[view_name][0][0][0], self.parent.instance.pk)
-            obj = next(filter(lambda o: part_id in o['@id'], object_list))
+            container_id = Model.container_id(self.parent.instance)
+            obj = next(filter(lambda o: container_id in o['@id'], object_list))
             list = super().get_value(obj)
             try:
                 list = next(filter(lambda o: list['@id'] == o['@id'], object_list))
@@ -197,9 +198,8 @@ class LDPSerializer(HyperlinkedModelSerializer):
             def get_value(self, dictionary):
                 try:
                     object_list = dictionary["@graph"]
-                    part_id = '/{}'.format(get_resolver().reverse_dict[self.parent_view_name][0][0][0],
-                                           self.parent.instance.pk)
-                    obj = next(filter(lambda o: part_id in o['@id'], object_list))
+                    resource_id = Model.resource_id(self.parent.instance)
+                    obj = next(filter(lambda o: resource_id in o['@id'], object_list))
                     return super().get_value(obj)
                 except KeyError:
                     return super().get_value(dictionary)
@@ -288,10 +288,8 @@ class LDPSerializer(HyperlinkedModelSerializer):
     def get_value(self, dictionary):
         try:
             object_list = dictionary["@graph"]
-            view_name = '{}-list'.format(self.parent.Meta.model._meta.object_name.lower())
-            part_id = '/{}'.format(get_resolver().reverse_dict[view_name][0][0][0],
-                                   self.parent.instance.pk)
-            obj = next(filter(lambda o: part_id in o[self.url_field_name], object_list))
+            container_id = Model.container_path(self.parent.instance)
+            obj = next(filter(lambda o: container_id in o[self.url_field_name], object_list))
             item = super().get_value(obj)
             full_item = None
             if item is empty:
diff --git a/djangoldp/tests/models.py b/djangoldp/tests/models.py
index 13a0aedf31da3efac36fd9c00bb64b4d73923119..2622673f0cd3a3873b2d7e0d592b6a601414b66e 100644
--- a/djangoldp/tests/models.py
+++ b/djangoldp/tests/models.py
@@ -1,6 +1,8 @@
 from django.conf import settings
 from django.db import models
 
+from djangoldp.models import Model
+
 
 class Skill(models.Model):
     title = models.CharField(max_length=255, blank=True, null=True)
@@ -23,3 +25,11 @@ class Message(models.Model):
     author_user = models.ForeignKey(settings.AUTH_USER_MODEL)
 
 
+class Dummy(models.Model):
+    some = models.CharField(max_length=255, blank=True, null=True)
+
+
+class LDPDummy(Model):
+    some = models.CharField(max_length=255, blank=True, null=True)
+    container_path = "ldp-dummys"
+
diff --git a/djangoldp/tests/runner.py b/djangoldp/tests/runner.py
index 1cf8604dbae8eda88317a21cac92b33636febbb5..4e3bcd0417a8ea9e8588f2ec0ae9539615278bb8 100644
--- a/djangoldp/tests/runner.py
+++ b/djangoldp/tests/runner.py
@@ -25,6 +25,7 @@ from django.test.runner import DiscoverRunner
 test_runner = DiscoverRunner(verbosity=1)
 
 failures = test_runner.run_tests([
+    'djangoldp.tests.tests_ldp_model',
     'djangoldp.tests.tests_save',
     'djangoldp.tests.tests_update'])
 if failures:
diff --git a/djangoldp/tests/tests_ldp_model.py b/djangoldp/tests/tests_ldp_model.py
new file mode 100644
index 0000000000000000000000000000000000000000..56bbe55206c0d95c7c90e998ecbda3ad49eb326b
--- /dev/null
+++ b/djangoldp/tests/tests_ldp_model.py
@@ -0,0 +1,39 @@
+import unittest
+
+from django.test import TestCase
+
+from djangoldp.models import Model
+from djangoldp.tests.models import Dummy, LDPDummy
+
+
+class LDPModelTest(TestCase):
+
+    def test_class_not_inheriting_ldp_model(self):
+        dummy = Dummy.objects.create(some="text")
+        self.assertEquals("/dummys/", Model.container_id(dummy))
+        self.assertEquals("/dummys/{}".format(dummy.pk), Model.resource_id(dummy))
+
+    def test_class_inheriting_ldp_model(self):
+        dummy = LDPDummy.objects.create(some="text")
+        self.assertEquals("/ldp-dummys/", dummy.get_container_id())
+        self.assertEquals("/ldp-dummys/{}".format(dummy.pk), dummy.get_absolute_url())
+        self.assertEquals("/ldp-dummys/", Model.container_id(dummy))
+        self.assertEquals("/ldp-dummys/{}".format(dummy.pk), Model.resource_id(dummy))
+
+    def test_from_resolve_id(self):
+        saved_instance = Dummy.objects.create(some="text")
+        result = Model.resolve_id("/dummys/{}".format(saved_instance.pk))
+        self.assertEquals(saved_instance, result)
+
+    def test_resolve_container(self):
+        result = Model.resolve_container("/dummys/")
+        self.assertEquals(Dummy, result)
+
+    @unittest.skip("futur feature: avoid urls.py on apps")
+    def test_auto_url(self):
+        from django.urls import get_resolver
+        dummy = LDPDummy.objects.create(some="text")
+        view_name = '{}-list'.format(dummy._meta.object_name.lower())
+        path = '/{}'.format(get_resolver().reverse_dict[view_name][0][0][0], dummy.pk)
+
+        self.assertEquals(path, dummy.get_absolute_url())
diff --git a/djangoldp/tests/tests_save.py b/djangoldp/tests/tests_save.py
index 1117c928eb4d5f7908c942666a8a718d87b2c79e..7e05496ab21ab8aff4e639bd6d80dcc5c8873ae5 100644
--- a/djangoldp/tests/tests_save.py
+++ b/djangoldp/tests/tests_save.py
@@ -4,7 +4,7 @@ from djangoldp.serializers import LDPSerializer
 from djangoldp.tests.models import Skill, JobOffer
 
 
-class Serializer(TestCase):
+class Save(TestCase):
 
     def test_save_m2m(self):
         skill1 = Skill.objects.create(title="skill1", obligatoire="obligatoire")
diff --git a/djangoldp/tests/tests_update.py b/djangoldp/tests/tests_update.py
index 7b232b4455e057d97544dac01999657d2d104ca4..622b55ef6c6b2374b5650dbbfa994b400ca96853 100644
--- a/djangoldp/tests/tests_update.py
+++ b/djangoldp/tests/tests_update.py
@@ -5,7 +5,7 @@ from djangoldp.serializers import LDPSerializer
 from djangoldp.tests.models import Skill, JobOffer, Thread, Message
 
 
-class Serializer(TestCase):
+class Update(TestCase):
 
     def test_update(self):
         skill = Skill.objects.create(title="to drop", obligatoire="obligatoire")
diff --git a/djangoldp/tests/urls.py b/djangoldp/tests/urls.py
index 0066129ba1d57ef46610773bbcc237d840232f30..392b0e2b7764a2fe37fa87e1b47f3ea9916f66f7 100644
--- a/djangoldp/tests/urls.py
+++ b/djangoldp/tests/urls.py
@@ -1,6 +1,6 @@
 from django.conf import settings
 
-from djangoldp.tests.models import Skill, JobOffer, Message, Thread
+from djangoldp.tests.models import Skill, JobOffer, Message, Thread, Dummy, LDPDummy
 from djangoldp.views import LDPViewSet
 from django.conf.urls import url
 
@@ -11,4 +11,6 @@ urlpatterns = [
     url(r'^messages/', LDPViewSet.urls(model=Message, permission_classes=[], fields=["@id", "text"], nested_fields=[])),
     url(r'^threads/', LDPViewSet.urls(model=Thread, nested_fields=["message_set"], permission_classes=())),
     url(r'^users/', LDPViewSet.urls(model=settings.AUTH_USER_MODEL, permission_classes=[])),
+    url(r'^dummys/', LDPViewSet.urls(model=Dummy, permission_classes=[])),
+    url(r'^ldp-dummys/', LDPViewSet.urls(model=LDPDummy, permission_classes=[])),
 ]
\ No newline at end of file