diff --git a/aleksis/core/mixins.py b/aleksis/core/mixins.py
index f65941f8592fa08946bc457364313cc5e4cb8ebb..856187d79a5735616a5ba3f2abb65a8fc82e8f7d 100644
--- a/aleksis/core/mixins.py
+++ b/aleksis/core/mixins.py
@@ -1,5 +1,5 @@
 from datetime import datetime
-from typing import Any, Callable, Optional, Union
+from typing import Any, Callable, List, Optional, Tuple, Union
 
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.sites.managers import CurrentSiteManager
@@ -7,6 +7,7 @@ from django.contrib.sites.models import Site
 from django.db import models
 from django.db.models import QuerySet
 from django.forms.models import ModelForm, ModelFormMetaclass
+from django.utils.functional import lazy
 
 import reversion
 from easyaudit.models import CRUDEvent
@@ -176,6 +177,30 @@ class ExtensibleModel(models.Model):
 
         cls._safe_add(field, name)
 
+    @classmethod
+    def syncable_fields(cls) -> List[models.Field]:
+        """ Collect all fields that can be synced on a model """
+
+        return [
+            field
+            for field in cls._meta.fields
+            if (field.editable and not field.auto_created and not field.is_relation)
+        ]
+
+    @classmethod
+    def syncable_fields_choices(cls) -> Tuple[Tuple[str, str]]:
+        """ Collect all fields that can be synced on a model """
+
+        return tuple(
+            [(field.name, field.verbose_name or field.name) for field in cls.syncable_fields()]
+        )
+
+    @classmethod
+    def syncable_fields_choices_lazy(cls) -> Callable[[], Tuple[Tuple[str, str]]]:
+        """ Collect all fields that can be synced on a model """
+
+        return lazy(cls.syncable_fields_choices, tuple)
+
     class Meta:
         abstract = True
 
diff --git a/aleksis/core/models.py b/aleksis/core/models.py
index b2bcf3968b51fd01a91d54996af6eee0aab36477..c72f6e1430d994343f0f1436d7f73ba844570103 100644
--- a/aleksis/core/models.py
+++ b/aleksis/core/models.py
@@ -192,7 +192,9 @@ class Person(ExtensibleModel):
             )
             admin.save()
 
-    def auto_select_primary_group(self, pattern: Optional[str] = None, force: bool = False) -> None:
+    def auto_select_primary_group(
+        self, pattern: Optional[str] = None, field: Optional[str] = None, force: bool = False
+    ) -> None:
         """Auto-select the primary group among the groups the person is member of.
 
         Uses either the pattern passed as argument, or the pattern configured system-wide.
@@ -201,10 +203,11 @@ class Person(ExtensibleModel):
         a primary group, unless force is True.
         """
         pattern = pattern or get_site_preferences()["account__primary_group_pattern"]
+        field = field or get_site_preferences()["account__primary_group_field"]
 
         if pattern:
             if force or not self.primary_group:
-                self.primary_group = self.member_of.filter(name__regex=pattern).first()
+                self.primary_group = self.member_of.filter(**{f"{field}__regex": pattern}).first()
 
 
 class DummyPerson(Person):
@@ -325,7 +328,7 @@ class PersonGroupThrough(ExtensibleModel):
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
 
-        for field in self.group.additional_fields:
+        for field in self.group.additional_fields.all():
             field_class = getattr(jsonstore, field.field_type)
             field_name = slugify(field.title).replace("-", "_")
             field_instance = field_class(verbose_name=field.title)
diff --git a/aleksis/core/preferences.py b/aleksis/core/preferences.py
index 77322db0f5f64d3e79f0756805208fc54cc5e6e8..6693aff1fba605180b6f83e7741fe1cb005267fe 100644
--- a/aleksis/core/preferences.py
+++ b/aleksis/core/preferences.py
@@ -5,6 +5,7 @@ from django.utils.translation import gettext_lazy as _
 from dynamic_preferences.preferences import Section
 from dynamic_preferences.types import ChoicePreference, FilePreference, StringPreference
 
+from .models import Person
 from .registries import person_preferences_registry, site_preferences_registry
 from .util.notifications import get_notification_choices_lazy
 
@@ -149,6 +150,18 @@ class PrimaryGroupPattern(StringPreference):
     verbose_name = _("Regular expression to match primary group, e.g. '^Class .*'")
 
 
+@site_preferences_registry.register
+class PrimaryGroupField(ChoicePreference):
+    section = account
+    name = "primary_group_field"
+    default = "name"
+    required = False
+    verbose_name = _("Field on person to match primary group against")
+
+    def get_choices(self):
+        return Person.syncable_fields_choices()
+
+
 @site_preferences_registry.register
 class SchoolName(StringPreference):
     section = school