diff --git a/opentech/apply/funds/migrations/0060_prepare_assigned_reviewers_for_data_migration.py b/opentech/apply/funds/migrations/0060_prepare_assigned_reviewers_for_data_migration.py
new file mode 100644
index 0000000000000000000000000000000000000000..bfa578d937545983c378b2352dfcb116be045edf
--- /dev/null
+++ b/opentech/apply/funds/migrations/0060_prepare_assigned_reviewers_for_data_migration.py
@@ -0,0 +1,31 @@
+# Generated by Django 2.0.9 on 2019-02-24 20:08
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('auth', '0009_alter_user_last_name_max_length'),
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('funds', '0059_add_community_review_workflow'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='assignedreviewers',
+            name='type',
+            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='auth.Group'),
+        ),
+        migrations.AlterField(
+            model_name='assignedreviewers',
+            name='reviewer',
+            field=models.ForeignKey(limit_choices_to={'groups__name__in': ['Staff', 'Reviewer', 'Community reviewer', 'Partner']}, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
+        ),
+        migrations.AlterUniqueTogether(
+            name='assignedreviewers',
+            unique_together={('submission', 'role'), ('submission', 'reviewer')},
+        ),
+    ]
diff --git a/opentech/apply/funds/migrations/0061_data_migrate_type_for_assigned_reviewers.py b/opentech/apply/funds/migrations/0061_data_migrate_type_for_assigned_reviewers.py
new file mode 100644
index 0000000000000000000000000000000000000000..c61f59cf408df5d9f601bf50adc39fb52c373cc7
--- /dev/null
+++ b/opentech/apply/funds/migrations/0061_data_migrate_type_for_assigned_reviewers.py
@@ -0,0 +1,52 @@
+# Generated by Django 2.0.9 on 2019-02-24 20:10
+
+from django.db import migrations
+
+# Copied from opentech.apply.users.groups at time of migration to avoid
+# importing and creating a future dependency. Changes to Group names should
+# be handled in another migration
+
+STAFF_GROUP_NAME = 'Staff'
+REVIEWER_GROUP_NAME = 'Reviewer'
+PARTNER_GROUP_NAME = 'Partner'
+COMMUNITY_REVIEWER_GROUP_NAME = 'Community reviewer'
+
+REVIEWER_GROUPS = set([
+    STAFF_GROUP_NAME,
+    REVIEWER_GROUP_NAME,
+    COMMUNITY_REVIEWER_GROUP_NAME,
+    PARTNER_GROUP_NAME,
+])
+
+
+def add_reviewer_type(apps, schema_editor):
+    AssignedReviewer = apps.get_model('funds', 'AssignedReviewers')
+    Group = apps.get_model('auth', 'Group')
+    for assigned in AssignedReviewer.objects.prefetch_related('reviewer__groups'):
+        groups = set(assigned.reviewer.groups.values_list('name', flat=True)) & REVIEWER_GROUPS
+        if len(groups) > 1:
+            if PARTNER_GROUP_NAME in groups and assigned.reviewer in assigned.submission.partners.all():
+                groups = {PARTNER_GROUP_NAME}
+            elif COMMUNITY_REVIEWER_GROUP_NAME in groups:
+                groups = {COMMUNITY_REVIEWER_GROUP_NAME}
+            elif assigned.reviewer.is_staff:
+                groups = {STAFF_GROUP_NAME}
+            else:
+                groups = {REVIEWER_GROUP_NAME}
+        elif not groups:
+            groups = {REVIEWER_GROUP_NAME}
+
+        group = Group.objects.get(name=groups.pop())
+        assigned.type = group
+        assigned.save()
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('funds', '0060_prepare_assigned_reviewers_for_data_migration'),
+    ]
+
+    operations = [
+        migrations.RunPython(add_reviewer_type, migrations.RunPython.noop),
+    ]
diff --git a/opentech/apply/funds/migrations/0062_make_reviewer_type_required.py b/opentech/apply/funds/migrations/0062_make_reviewer_type_required.py
new file mode 100644
index 0000000000000000000000000000000000000000..a487faf759388d0c9bb21e598b1d05085e9d6079
--- /dev/null
+++ b/opentech/apply/funds/migrations/0062_make_reviewer_type_required.py
@@ -0,0 +1,19 @@
+# Generated by Django 2.0.9 on 2019-02-24 20:50
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('funds', '0061_data_migrate_type_for_assigned_reviewers'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='assignedreviewers',
+            name='type',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='auth.Group'),
+        ),
+    ]
diff --git a/opentech/apply/funds/models/submissions.py b/opentech/apply/funds/models/submissions.py
index 5f2a9027306d1e7b2e89eb612be34e6d64d8dd50..6cbad915efb432ed5da2d92eed75df2c41f4e029 100644
--- a/opentech/apply/funds/models/submissions.py
+++ b/opentech/apply/funds/models/submissions.py
@@ -28,7 +28,7 @@ from opentech.apply.stream_forms.files import StreamFieldDataEncoder
 from opentech.apply.stream_forms.models import BaseStreamForm
 
 from .mixins import AccessFormData
-from .utils import LIMIT_TO_STAFF, LIMIT_TO_STAFF_AND_REVIEWERS, LIMIT_TO_PARTNERS, WorkflowHelpers
+from .utils import LIMIT_TO_STAFF, LIMIT_TO_REVIEWER_GROUPS, LIMIT_TO_PARTNERS, WorkflowHelpers
 from ..blocks import ApplicationCustomFormFieldsBlock, NAMED_BLOCKS
 from ..workflow import (
     active_statuses,
@@ -744,7 +744,11 @@ class AssignedReviewers(models.Model):
     reviewer = models.ForeignKey(
         settings.AUTH_USER_MODEL,
         on_delete=models.CASCADE,
-        limit_choices_to=LIMIT_TO_STAFF_AND_REVIEWERS,
+        limit_choices_to=LIMIT_TO_REVIEWER_GROUPS,
+    )
+    type = models.ForeignKey(
+        'auth.Group',
+        on_delete=models.PROTECT,
     )
     submission = models.ForeignKey(
         ApplicationSubmission,
@@ -761,7 +765,7 @@ class AssignedReviewers(models.Model):
     objects = AssignedReviewersQuerySet.as_manager()
 
     class Meta:
-        unique_together = ('submission', 'role')
+        unique_together = (('submission', 'role'), ('submission', 'reviewer'))
 
     def __str__(self):
         return f'{self.reviewer} as {self.role}'
diff --git a/opentech/apply/funds/models/utils.py b/opentech/apply/funds/models/utils.py
index c6c8ecb6153718edac9468dd0848c472888d23d1..6ff742f0168dbd106fe18007ba48bfbb537f5b83 100644
--- a/opentech/apply/funds/models/utils.py
+++ b/opentech/apply/funds/models/utils.py
@@ -19,9 +19,15 @@ from ..workflow import WORKFLOWS
 
 LIMIT_TO_STAFF = {'groups__name': STAFF_GROUP_NAME}
 LIMIT_TO_REVIEWERS = {'groups__name': REVIEWER_GROUP_NAME}
-LIMIT_TO_STAFF_AND_REVIEWERS = {'groups__name__in': [STAFF_GROUP_NAME, REVIEWER_GROUP_NAME]}
 LIMIT_TO_PARTNERS = {'groups__name': PARTNER_GROUP_NAME}
 LIMIT_TO_COMMUNITY_REVIEWERS = {'groups__name': COMMUNITY_REVIEWER_GROUP_NAME}
+LIMIT_TO_REVIEWER_GROUPS = {'groups__name__in': [
+    STAFF_GROUP_NAME,
+    REVIEWER_GROUP_NAME,
+    COMMUNITY_REVIEWER_GROUP_NAME,
+    PARTNER_GROUP_NAME,
+]}
+
 
 
 def admin_url(page):
diff --git a/opentech/apply/review/migrations/0017_add_temp_author_field.py b/opentech/apply/review/migrations/0017_add_temp_author_field.py
new file mode 100644
index 0000000000000000000000000000000000000000..7a5e44a4a21586842ed31bf1b084817298aca6d5
--- /dev/null
+++ b/opentech/apply/review/migrations/0017_add_temp_author_field.py
@@ -0,0 +1,25 @@
+# Generated by Django 2.0.9 on 2019-02-24 03:14
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('review', '0016_review_visibility'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='review',
+            name='author_temp',
+            field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='review', to='funds.AssignedReviewers', null=True),
+        ),
+        migrations.AddField(
+            model_name='reviewopinion',
+            name='author_temp',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='opinions', to='funds.AssignedReviewers', null=True),
+        ),
+
+    ]
diff --git a/opentech/apply/review/migrations/0018_migrate_author_data.py b/opentech/apply/review/migrations/0018_migrate_author_data.py
new file mode 100644
index 0000000000000000000000000000000000000000..d92a25e3c5ed6ed87fced9690045a6461801b9c7
--- /dev/null
+++ b/opentech/apply/review/migrations/0018_migrate_author_data.py
@@ -0,0 +1,84 @@
+# Generated by Django 2.0.9 on 2019-02-24 03:16
+
+from django.db import migrations
+from django.core.exceptions import ObjectDoesNotExist
+
+# Copied from opentech.apply.users.groups at time of migration to avoid
+# importing and creating a future dependency. Changes to Group names should
+# be handled in another migration
+
+STAFF_GROUP_NAME = 'Staff'
+REVIEWER_GROUP_NAME = 'Reviewer'
+PARTNER_GROUP_NAME = 'Partner'
+COMMUNITY_REVIEWER_GROUP_NAME = 'Community reviewer'
+
+REVIEWER_GROUPS = set([
+    STAFF_GROUP_NAME,
+    REVIEWER_GROUP_NAME,
+    COMMUNITY_REVIEWER_GROUP_NAME,
+    PARTNER_GROUP_NAME,
+])
+
+
+def add_to_assigned_reviewers(apps, schema_editor):
+    Review = apps.get_model('review', 'Review')
+    AssignedReviewer = apps.get_model('funds', 'AssignedReviewers')
+    Group = apps.get_model('auth', 'Group')
+    for review in Review.objects.select_related('author'):
+        groups = set(review.author.groups.values_list('name', flat=True)) & REVIEWER_GROUPS
+        if len(groups) > 1:
+            if PARTNER_GROUP_NAME in groups and review.author in review.submission.partners.all():
+                groups = {PARTNER_GROUP_NAME}
+            elif COMMUNITY_REVIEWER_GROUP_NAME in groups:
+                groups = {COMMUNITY_REVIEWER_GROUP_NAME}
+            elif review.author.is_staff:
+                groups = {STAFF_GROUP_NAME}
+            else:
+                groups = {REVIEWER_GROUP_NAME}
+        elif not groups:
+            groups = {REVIEWER_GROUP_NAME}
+
+        group = Group.objects.get(name=groups.pop())
+
+        assignment, _ = AssignedReviewer.objects.update_or_create(
+            submission=review.submission,
+            reviewer=review.author,
+            type=group,
+        )
+        review.author_temp = assignment
+        review.save()
+        for opinion in review.opinions.select_related('author'):
+            opinion_assignment, _ = AssignedReviewer.objects.update_or_create(
+                submission=review.submission,
+                reviewer=opinion.author,
+                type=Group.objects.get(name=STAFF_GROUP_NAME),
+            )
+            opinion.author_temp = opinion_assignment
+            opinion.save()
+
+
+def add_to_review_and_opinion(apps, schema_editor):
+    AssignedReviewer = apps.get_model('funds', 'AssignedReviewers')
+    for assigned in AssignedReviewer.objects.all():
+        try:
+            assigned.review.author = assigned.reviewer
+            assigned.review.save()
+        except ObjectDoesNotExist:
+            pass
+        if assigned.opinions.exists():
+            for opinion in assigned.opinions.all():
+                opinion.author = assigned.reviewer
+                opinion.save()
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('review', '0017_add_temp_author_field'),
+        ('funds', '0062_make_reviewer_type_required'),
+        ('users', '0010_add_community_reviewer_group'),
+    ]
+
+    operations = [
+        migrations.RunPython(add_to_assigned_reviewers, add_to_review_and_opinion),
+    ]
diff --git a/opentech/apply/review/migrations/0019_replace_existing_author_field.py b/opentech/apply/review/migrations/0019_replace_existing_author_field.py
new file mode 100644
index 0000000000000000000000000000000000000000..a5de51876c95f40b2c9854838c8603c315166307
--- /dev/null
+++ b/opentech/apply/review/migrations/0019_replace_existing_author_field.py
@@ -0,0 +1,42 @@
+# Generated by Django 2.0.9 on 2019-02-24 03:38
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('review', '0018_migrate_author_data'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='review',
+            name='author',
+        ),
+        migrations.RenameField(
+            model_name='review',
+            old_name='author_temp',
+            new_name='author',
+        ),
+        migrations.AlterField(
+            model_name='review',
+            name='author',
+            field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='review', to='funds.AssignedReviewers'),
+        ),
+        migrations.RemoveField(
+            model_name='reviewopinion',
+            name='author',
+        ),
+        migrations.RenameField(
+            model_name='reviewopinion',
+            old_name='author_temp',
+            new_name='author',
+        ),
+        migrations.AlterField(
+            model_name='reviewopinion',
+            name='author',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='opinions', to='funds.AssignedReviewers'),
+        ),
+    ]
diff --git a/opentech/apply/review/models.py b/opentech/apply/review/models.py
index b604831a529c97d20a1fed04ddaa25e6f26b2b8d..c7de2c3599a36904b150895466eae8002d8248bf 100644
--- a/opentech/apply/review/models.py
+++ b/opentech/apply/review/models.py
@@ -121,9 +121,10 @@ class ReviewQuerySet(models.QuerySet):
 class Review(ReviewFormFieldsMixin, BaseStreamForm, AccessFormData, models.Model):
     submission = models.ForeignKey('funds.ApplicationSubmission', on_delete=models.CASCADE, related_name='reviews')
     revision = models.ForeignKey('funds.ApplicationRevision', on_delete=models.SET_NULL, related_name='reviews', null=True)
-    author = models.ForeignKey(
-        settings.AUTH_USER_MODEL,
-        on_delete=models.PROTECT,
+    author = models.OneToOneField(
+        'funds.AssignedReviewers',
+        related_name='review',
+        on_delete=models.CASCADE,
     )
 
     form_data = JSONField(default=dict, encoder=DjangoJSONEncoder)
@@ -190,8 +191,9 @@ def update_submission_reviewers_list(sender, **kwargs):
 class ReviewOpinion(models.Model):
     review = models.ForeignKey(Review, on_delete=models.CASCADE, related_name='opinions')
     author = models.ForeignKey(
-        settings.AUTH_USER_MODEL,
-        on_delete=models.PROTECT,
+        'funds.AssignedReviewers',
+        related_name='opinions',
+        on_delete=models.CASCADE,
     )
     opinion = models.IntegerField(choices=OPINION_CHOICES)