diff --git a/core/serializers/tutor.py b/core/serializers/tutor.py
index 98a7b982b946b2c096a628bc58fe74bfb2618526..b2eff1e8829433b1806e14b00cbece143231f5e4 100644
--- a/core/serializers/tutor.py
+++ b/core/serializers/tutor.py
@@ -59,4 +59,5 @@ class CorrectorSerializer(DynamicFieldsModelSerializer):
                   'username',
                   'feedback_created',
                   'feedback_validated',
+                  'exercise_groups',
                   'role')
diff --git a/core/tests/test_student_reviewer_viewset.py b/core/tests/test_student_reviewer_viewset.py
index ffbdac9daf21f021598eb6beb1f1260f70cb166c..b351f8661821d7d99cbc8c0fbfaa69c9772ff723 100644
--- a/core/tests/test_student_reviewer_viewset.py
+++ b/core/tests/test_student_reviewer_viewset.py
@@ -93,10 +93,10 @@ class StudentPageTests(APITestCase):
         self.assertEqual(3, len(self.rev_response.data))
 
     @override_config(EXERCISE_MODE=True)
-    def test_tutor_can_only_see_students_when_in_exercise_mode(self):
+    def test_tutor_can_only_see_group_members_when_in_exercise_mode(self):
         force_authenticate(self.request, user=self.tutor)
         response = self.view(self.request)
-        self.assertEqual(3, len(response.data))
+        self.assertEqual(2, len(response.data))
 
     def test_submissions_score_is_included(self):
         res_with_sub = None
diff --git a/core/views/common_views.py b/core/views/common_views.py
index 3ea1f890a625c52a990c64b939ae93303b3e2455..0b6fcbe1855f79d358802ea3dfb97d877db92dd1 100644
--- a/core/views/common_views.py
+++ b/core/views/common_views.py
@@ -81,7 +81,9 @@ class StudentReviewerApiViewSet(viewsets.ReadOnlyModelViewSet):
             return queryset
 
         elif self.request.user.is_tutor() and config.EXERCISE_MODE:
-            return queryset
+            return queryset.filter(
+                user__exercise_groups__in=self.request.user.exercise_groups.all()
+            )
 
         else:
             return []
@@ -328,6 +330,29 @@ class UserAccountViewSet(viewsets.ReadOnlyModelViewSet):
         user.save()
         return Response(status.HTTP_200_OK)
 
+    @action(detail=True, methods=['patch'], permission_classes=(IsReviewer,))
+    def change_groups(self, request, *args, **kwargs):
+        # for some reason only the newly added groups come as a group object
+        groups = [x.get('pk') if type(x) is not str else x for x in request.data]
+        req_user = request.user
+        user = self.get_object()
+        if groups is None:
+            error_msg = "You need to provide an 'groups' field"
+            return Response({'Error': error_msg}, status.HTTP_400_BAD_REQUEST)
+        if req_user.is_student() or req_user.is_tutor():
+            return Response(status.HTTP_403_FORBIDDEN)
+        user.set_groups(groups)
+        user.save()
+        return Response(status.HTTP_200_OK)
+
+    @action(detail=True)
+    def get_groups(self, request, *args, **kwargs):
+        req_user = request.user
+        if req_user.is_student() or req_user.is_tutor():
+            return Response(status.HTTP_403_FORBIDDEN)
+        user = self.get_object()
+        return Response(user.exercise_groups, status=status.HTTP_200_OK)
+
     @action(detail=True, methods=["patch"])
     def change_role(self, request, *args, **kwargs):
         new_role = request.data.get('role')
diff --git a/frontend/src/api.ts b/frontend/src/api.ts
index 0d34730e29fce2d7f3f19d8a872545aecb2ec04b..6ecaedaf34c3edcc84f0596ff0a5ddc4e56b27c3 100644
--- a/frontend/src/api.ts
+++ b/frontend/src/api.ts
@@ -153,6 +153,15 @@ export async function fetchGroups(): Promise<Group[]> {
   return (await ax.get(url)).data
 }
 
+export async function fetchUserGroups(userPk: string): Promise<Group[]> {
+  const url = `/api/user/${userPk}/get_groups/`
+  return (await ax.get(url)).data
+}
+
+export async function setGroups (userPk: string, groups: Group[]): Promise<UserAccount> {
+  return (await ax.patch(`/api/user/${userPk}/change_groups/`, groups)).data
+}
+
 export async function deleteSolutionComment (pk: number): Promise<AxiosResponse<void>> {
   const url = `/api/solution-comment/${pk}/`
   return ax.delete(url)
@@ -221,6 +230,10 @@ export async function fetchUsers (): Promise<UserAccount[]> {
   return (await ax.get('api/user/')).data
 }
 
+export async function fetchUser(userPk: string): Promise<UserAccount> {
+  return (await ax.get(`/api/user/${userPk}`)).data
+}
+
 export async function getLabels (): Promise<FeedbackLabel[]> {
   return (await ax.get('/api/label/')).data
 }
diff --git a/frontend/src/components/student_list/StudentList.vue b/frontend/src/components/student_list/StudentList.vue
index 35bf14b9608a91dd097066e669d397896fa561ab..a29fdc40adb7977b5e9781cc2f9b20af2ab9ec4e 100644
--- a/frontend/src/components/student_list/StudentList.vue
+++ b/frontend/src/components/student_list/StudentList.vue
@@ -228,15 +228,15 @@ export default {
       return []
     },
     groups () {
-      return Assignments.state.groups.slice().sort((a, b) => {
-        const matches_a = a.name.match(/(\d+)/)
-        const number_a = Number(matches_a === null ? 0 : matches_a[1])
-
-        const matches_b = b.name.match(/(\d+)/)
-        const number_b = Number(matches_b === null ? 0 : matches_b[1])
-
-        return (number_a<number_b?-1:(number_a>number_b?1:0))
-      })
+      if (Authentication.isTutor) {
+        return Authentication.state.user.exerciseGroups
+      }
+      else if (Authentication.isReviewer) {
+        return Assignments.state.groups
+      }
+      else {
+        return []
+      }
     },
   },
   created () {
diff --git a/frontend/src/components/tutor_list/TutorList.vue b/frontend/src/components/tutor_list/TutorList.vue
index 2a0fe221b2ee84a8de020c570328034ff5be06a6..26bee4c5c7dc38e3cb6dc927a66f1a12f7bfea88 100644
--- a/frontend/src/components/tutor_list/TutorList.vue
+++ b/frontend/src/components/tutor_list/TutorList.vue
@@ -34,6 +34,23 @@
           <span>Free locked submissions</span>
         </v-tooltip>
       </template>
+      <template #item.exerciseGroups="{ item }">
+        <v-select
+          v-model="item.exerciseGroups"
+          item-text="name"
+          item-value="pk"
+          :items="groups"
+          label="Set Groups"
+          single-line
+          return-object
+          multiple
+          chips
+          dense
+          hide-details
+          filled
+          @change="setExerciseGroups($event, item)"
+        />
+      </template>
       <template #item.isActive="{ item }">
         <v-btn
           v-if="canRevokeAccess(item.username)"
@@ -72,11 +89,12 @@
 <script lang="ts">
 import Vue from 'vue'
 import Component from 'vue-class-component'
-import { changeActiveForUser } from '@/api'
+import { changeActiveForUser, setGroups, fetchUserGroups, fetchUser } from '@/api'
 import { actions } from '@/store/actions'
 import { Authentication } from '@/store/modules/authentication'
 import { TutorOverview } from '@/store/modules/tutor-overview'
-import { Tutor, UserAccount } from '@/models'
+import { Group, Tutor, UserAccount } from '@/models'
+import { Assignments } from '@/store/modules/assignments'
 import RoleSelect from './RoleSelect.vue'
 
 @Component({ components: { RoleSelect } })
@@ -102,6 +120,11 @@ export default class TutorList extends Vue {
       align: 'right',
       value: 'reservedSubmissions'
     },
+    {
+      text: 'Exercise Groups',
+      align: 'right',
+      value: 'exerciseGroups'
+    },
     {
       text: 'Has Access',
       align: 'right',
@@ -114,13 +137,43 @@ export default class TutorList extends Vue {
   ]
 
   get tutors () {
-    return TutorOverview.state.tutors.map(tutor => {
+    var tlist =  TutorOverview.state.tutors.map(tutor => {
+      var groups: Group[] = []
+      this.userAccountGroups(tutor).then(function(value) {
+        groups = value // Success!
+      }, (reason) => {
+        this.$notify({
+          title: 'Error',
+          text: `Unable to fetch tutors: ${reason}`,
+          type: 'error'
+      })
+        return []
+      })
       const reservedSubmissions = TutorOverview.state.activeAssignments[tutor.pk]
       return {
         ...tutor,
-        reservedSubmissions: reservedSubmissions ? reservedSubmissions.length : 0
+        reservedSubmissions: reservedSubmissions ? reservedSubmissions.length : 0,
       }
     })
+    return tlist
+  }
+
+  get groups () {
+    return Assignments.state.groups.slice().sort((a, b) => {
+      const matches_a = a.name.match(/(\d+)/)
+      const number_a = Number(matches_a === null ? 0 : matches_a[1])
+
+      const matches_b = b.name.match(/(\d+)/)
+      const number_b = Number(matches_b === null ? 0 : matches_b[1])
+
+      return (number_a<number_b?-1:(number_a>number_b?1:0))
+    })
+  }
+
+
+  async userAccountGroups(tutor: Tutor) {
+    const groups = await (await fetchUser(tutor.pk)).exerciseGroups
+    return groups
   }
 
   changeActiveStatus (tutor: Tutor) {
@@ -135,6 +188,18 @@ export default class TutorList extends Vue {
     })
   }
 
+  setExerciseGroups (groups: Group[], tutor: Tutor){
+    setGroups(tutor.pk, groups).then(() => {
+      TutorOverview.getTutors()
+    }).catch(() => {
+      this.$notify({
+        title: 'Error',
+        text: `Unable to change exercise-groups of ${tutor.username}`,
+        type: 'error'
+      })
+    })
+  }
+
   deleteAssignmentsOfTutor (tutor: Tutor) {
     TutorOverview.deleteActiveAssignmentsOfTutor(tutor)
   }
diff --git a/frontend/src/models.ts b/frontend/src/models.ts
index 5ef5dfd511d752faea05eb33abaebf7545d0088e..6e29640d2eeb148efbc89a79869040f47375d7ad 100644
--- a/frontend/src/models.ts
+++ b/frontend/src/models.ts
@@ -785,6 +785,12 @@ export interface Tutor {
     feedbackValidated?: string
     /**
      *
+     * @type {Group}
+     * @memberof Tutor
+     */
+    exerciseGroups: Group[]
+
+    /**
      * @type {string}
      * @memberof Tutor
      */