diff --git a/greg/migrations/0001_initial.py b/greg/migrations/0001_initial.py
index c83c5a082bb0d243204f99a32b486b5e633eadf4..941e50e061e2a3d74e5594582f4805dd1e8c9506 100644
--- a/greg/migrations/0001_initial.py
+++ b/greg/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 3.2.5 on 2021-08-12 10:52
+# Generated by Django 3.2.5 on 2021-08-19 12:47
 
 import datetime
 import dirtyfields.dirtyfields
@@ -157,9 +157,6 @@ class Migration(migrations.Migration):
                 ('role', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='person_roles', to='greg.role')),
                 ('unit', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='unit_person_role', to='greg.organizationalunit')),
             ],
-            options={
-                'abstract': False,
-            },
             bases=(dirtyfields.dirtyfields.DirtyFieldsMixin, models.Model),
         ),
         migrations.CreateModel(
@@ -211,6 +208,10 @@ class Migration(migrations.Migration):
             model_name='sponsor',
             constraint=models.UniqueConstraint(fields=('feide_id',), name='unique_feide_id'),
         ),
+        migrations.AddConstraint(
+            model_name='personrole',
+            constraint=models.UniqueConstraint(fields=('person_id', 'role_id', 'unit_id', 'start_date', 'end_date'), name='person_role_unique'),
+        ),
         migrations.AddConstraint(
             model_name='personconsent',
             constraint=models.UniqueConstraint(fields=('person', 'consent'), name='person_consent_unique'),
diff --git a/greg/models.py b/greg/models.py
index f8fddf6448f16c4e89b0deecff230b1b61a7f3dc..2a81c1e1a699aebf8ae86d9734edc7f5067d2712 100644
--- a/greg/models.py
+++ b/greg/models.py
@@ -103,6 +103,14 @@ class PersonRole(BaseModel):
         "Sponsor", on_delete=models.PROTECT, related_name="sponsor_role"
     )
 
+    class Meta:
+        constraints = [
+            models.UniqueConstraint(
+                fields=["person_id", "role_id", "unit_id", "start_date", "end_date"],
+                name="person_role_unique",
+            )
+        ]
+
     def __repr__(self):
         return "{}(id={!r}, person={!r}, role={!r})".format(
             self.__class__.__name__, self.pk, self.person, self.role
diff --git a/greg/tests/api/test_person.py b/greg/tests/api/test_person.py
index d0736f280d4bdd51fb6a73a62829325b50c01bb2..f919737287e992b60c3bf061023a1702e5ee9849 100644
--- a/greg/tests/api/test_person.py
+++ b/greg/tests/api/test_person.py
@@ -11,6 +11,8 @@ from greg.models import (
     Role,
     OrganizationalUnit,
     Consent,
+    Person,
+    PersonRole,
 )
 
 
@@ -350,3 +352,28 @@ def test_remove_person(
         reverse("person_identity-list", kwargs={"person_id": person_foo.id})
     )
     assert len(response.json()["results"]) == 0
+
+
+@pytest.mark.django_db
+def test_add_duplicate_role_fails(
+    client, person_foo: Person, person_foo_role: PersonRole
+):
+    url = reverse("person_role-list", kwargs={"person_id": person_foo.id})
+    roles_for_person = client.get(url).json()["results"]
+
+    assert len(roles_for_person) == 1
+
+    role_data = {
+        "role": person_foo_role.role_id,
+        "start_date": person_foo_role.start_date,
+        "end_date": person_foo_role.end_date,
+        "registered_by": person_foo_role.registered_by,
+        "unit": person_foo_role.unit_id,
+    }
+    response = client.post(url, role_data)
+    # If the role cannot be create the return code is 400
+    assert response.status_code == status.HTTP_400_BAD_REQUEST
+
+    # Check that there is still only one role attached to the person
+    roles_for_person = client.get(url).json()["results"]
+    assert len(roles_for_person) == 1
diff --git a/greg/tests/api/test_sponsor.py b/greg/tests/api/test_sponsor.py
index 8c4906a4b711d7049811ac2ca290807fbe26ef23..06e5eb0c74045772401d143b3e2f7eb1171e88d4 100644
--- a/greg/tests/api/test_sponsor.py
+++ b/greg/tests/api/test_sponsor.py
@@ -3,25 +3,6 @@ from rest_framework import status
 
 from rest_framework.reverse import reverse
 
-from greg.models import Role, Sponsor, OrganizationalUnit, PersonRole, Person
-
-
-@pytest.fixture
-def person_foo_role(
-    person_foo: Person,
-    role_test_guest: Role,
-    sponsor_guy: Sponsor,
-    unit_foo: OrganizationalUnit,
-) -> PersonRole:
-    return PersonRole.objects.create(
-        person=person_foo,
-        role=role_test_guest,
-        start_date="2021-08-02",
-        end_date="2021-08-06",
-        registered_by=sponsor_guy,
-        unit=unit_foo,
-    )
-
 
 @pytest.mark.django_db
 def test_add_sponsor(client):
diff --git a/greg/tests/conftest.py b/greg/tests/conftest.py
index 41edf5695e8067d408c302f5bd2302c04e75bbbd..63646efdd9947cd04f5ec31079c855447e8b73b2 100644
--- a/greg/tests/conftest.py
+++ b/greg/tests/conftest.py
@@ -6,7 +6,14 @@ from django.contrib.auth import get_user_model
 
 import pytest
 
-from greg.models import Person, Sponsor, PersonIdentity, Role, OrganizationalUnit
+from greg.models import (
+    Person,
+    Sponsor,
+    PersonIdentity,
+    Role,
+    OrganizationalUnit,
+    PersonRole,
+)
 
 # faker spams the logs with localisation warnings
 # see https://github.com/joke2k/faker/issues/753
@@ -82,3 +89,20 @@ def role_test_guest() -> Role:
 @pytest.fixture
 def unit_foo() -> OrganizationalUnit:
     return OrganizationalUnit.objects.create(orgreg_id="12345", name_en="foo_unit")
+
+
+@pytest.fixture
+def person_foo_role(
+    person_foo: Person,
+    role_test_guest: Role,
+    sponsor_guy: Sponsor,
+    unit_foo: OrganizationalUnit,
+) -> PersonRole:
+    return PersonRole.objects.create(
+        person=person_foo,
+        role=role_test_guest,
+        start_date="2021-08-02",
+        end_date="2021-08-06",
+        registered_by=sponsor_guy,
+        unit=unit_foo,
+    )