diff --git a/aleksis/apps/untis/util/mysql/importers/common_data.py b/aleksis/apps/untis/util/mysql/importers/common_data.py
index 181ce7645b5c0d8831035d8053f687dc54d02aa0..001f1d2b85b744fff20b396a95466869b67e2972 100644
--- a/aleksis/apps/untis/util/mysql/importers/common_data.py
+++ b/aleksis/apps/untis/util/mysql/importers/common_data.py
@@ -9,7 +9,7 @@ from aleksis.apps.chronos import models as chronos_models
 from aleksis.core import models as core_models
 
 from .... import models as mysql_models
-from ..util import run_default_filter, untis_colour_to_hex, untis_split_first, sync_m2m, connect_untis_fields, \
+from ..util import run_default_filter, untis_colour_to_hex, untis_split_first, connect_untis_fields, \
     TQDM_DEFAULTS
 
 logger = logging.getLogger(__name__)
@@ -213,7 +213,8 @@ def import_classes(
             new_group.save()
 
         if config.UNTIS_IMPORT_MYSQL_UPDATE_GROUPS_OVERWRITE_OWNERS:
-            sync_m2m(owners, new_group.owners)
+            new_group.owners.set(owners)
+            logger.info("  Group owners set")
         else:
             new_group.owners.add(*owners)
             logger.info("  Group owners added")
diff --git a/aleksis/apps/untis/util/mysql/importers/events.py b/aleksis/apps/untis/util/mysql/importers/events.py
index 51b285bcff119acc1561b4e5514d08ef21f03d94..e0b845af3f1a93808870036824c5b6798807ac1f 100644
--- a/aleksis/apps/untis/util/mysql/importers/events.py
+++ b/aleksis/apps/untis/util/mysql/importers/events.py
@@ -12,7 +12,7 @@ from ..util import (
     move_weekday_to_range,
     get_first_period,
     get_last_period,
-    connect_untis_fields, sync_m2m, TQDM_DEFAULTS,
+    connect_untis_fields, TQDM_DEFAULTS,
 )
 
 logger = logging.getLogger(__name__)
@@ -120,9 +120,9 @@ def import_events(time_periods_ref, teachers_ref, classes_ref, rooms_ref):
             logger.info("  Time range and title updated")
 
         # Sync m2m-fields
-        sync_m2m(groups, new_event.groups)
-        sync_m2m(teachers, new_event.teachers)
-        sync_m2m(rooms, new_event.rooms)
+        new_event.groups.set(groups)
+        new_event.teachers.set(teachers)
+        new_event.rooms.set(rooms)
 
         existing_events.append(import_ref)
         ref[import_ref] = new_event
diff --git a/aleksis/apps/untis/util/mysql/importers/lessons.py b/aleksis/apps/untis/util/mysql/importers/lessons.py
index 3594be2bbb3d12c4529318691f39112741fb0c3e..9f95dccabb4c5b32c4f4a7582788c2036f32f213 100644
--- a/aleksis/apps/untis/util/mysql/importers/lessons.py
+++ b/aleksis/apps/untis/util/mysql/importers/lessons.py
@@ -14,7 +14,6 @@ from ..util import (
     untis_split_third,
     untis_date_to_date,
     get_term,
-    sync_m2m,
     compare_m2m,
     connect_untis_fields, TQDM_DEFAULTS,
 )
@@ -159,7 +158,8 @@ def import_lessons(
                         logger.info("    Course group created")
 
                     # Update parent groups
-                    sync_m2m(course_classes, course_group.parent_groups)
+                    course_group.parent_groups.set(course_classes)
+                    logger.info("    Course groups set")
 
                     # Update name
                     if course_group.name != group_name:
@@ -169,7 +169,7 @@ def import_lessons(
                         changed = True
 
                 # Update owners
-                sync_m2m(teachers, course_group.owners)
+                course_group.owners.set(teachers)
 
                 # Update import ref
                 if (
@@ -223,10 +223,10 @@ def import_lessons(
                 logger.info("    New lesson created")
 
             # Sync groups
-            sync_m2m(groups, lesson.groups)
+            lesson.groupsset(groups)
 
             # Sync teachers
-            sync_m2m(teachers, lesson.teachers)
+            lesson.teachers.set(teachers)
 
             # All times for this course
             old_lesson_periods_qs = chronos_models.LessonPeriod.objects.filter(
diff --git a/aleksis/apps/untis/util/mysql/importers/substitutions.py b/aleksis/apps/untis/util/mysql/importers/substitutions.py
index 32a9bb9e21782abae0c350e2a3dec94397239317..eaaf47a9aee83b8fa7682c1397db4faf32d86794 100644
--- a/aleksis/apps/untis/util/mysql/importers/substitutions.py
+++ b/aleksis/apps/untis/util/mysql/importers/substitutions.py
@@ -9,7 +9,6 @@ from ..util import (
     run_default_filter,
     untis_split_first,
     untis_date_to_date,
-    sync_m2m,
     get_term, TQDM_DEFAULTS,
 )
 from .... import models as mysql_models
@@ -135,7 +134,8 @@ def import_substitutions(
                     logger.info("  Substitution created")
 
                 # Sync teachers
-                sync_m2m(teachers, substitution.teachers)
+                substitution.teachers.set(teachers)
+                logger.info("   Substitution teachers set")
 
                 # Update values
                 if (
diff --git a/aleksis/apps/untis/util/mysql/util.py b/aleksis/apps/untis/util/mysql/util.py
index ac16e9f9f8cf1fdfc6f0a702b745148d04587763..9d81e85c61cf02e627c90ed0528c56086f69f7b5 100644
--- a/aleksis/apps/untis/util/mysql/util.py
+++ b/aleksis/apps/untis/util/mysql/util.py
@@ -1,14 +1,14 @@
 import logging
-from datetime import date, datetime
-from typing import Optional, Union, List
+from datetime import date
+from typing import Optional, Union, Sequence, Callable, Any
 
 from django.db.models import QuerySet, Model
+from django.utils import timezone
 
-from aleksis.apps.untis import models as mysql_models
+from ... import models as mysql_models
 
 DB_NAME = "untis"
-
-logger = logging.getLogger(__name__)
+UNTIS_DATE_FORMAT = "%Y%m%d"
 
 TQDM_DEFAULTS = {
  "disable": None,
@@ -16,32 +16,33 @@ TQDM_DEFAULTS = {
  "dynamic_ncols": True,
 }
 
+logger = logging.getLogger(__name__)
+
+
 def run_using(obj: QuerySet) -> QuerySet:
+    """ Seed QuerySet with using() database from global DB_NAME """
     return obj.using(DB_NAME)
 
 
-def get_term(date: Optional[date] = None) -> mysql_models.Terms:
+def get_term(for_date: Optional[date] = None) -> mysql_models.Terms:
     """ Get term valid for the provided date """
 
-    if not date:
-        date = datetime.now()
+    if not for_date:
+        for_date = timezone.now().date()
 
-    terms = run_using(mysql_models.Terms.objects).filter(
-        datefrom__lte=date_to_untis_date(date), dateto__gte=date_to_untis_date(date)
+    term = run_using(mysql_models.Terms.objects).get(
+        datefrom__lte=date_to_untis_date(for_date), dateto__gte=date_to_untis_date(for_date)
     )
 
-    if not terms.exists():
-        raise Exception("Term needed")
-
-    return terms[0]
+    return term
 
 
 def run_default_filter(
-    qs: QuerySet, date: Optional[date] = None, filter_term: bool = True, filter_deleted: bool = True
+    qs: QuerySet, for_date: Optional[date] = None, filter_term: bool = True, filter_deleted: bool = True
 ) -> QuerySet:
     """ Add a default filter in order to select the correct term """
 
-    term = get_term(date)
+    term = get_term(for_date)
     term_id, schoolyear_id, school_id, version_id = (
         term.term_id,
         term.schoolyear_id,
@@ -64,48 +65,48 @@ def run_default_filter(
     return qs
 
 
-def clean_array(a: list, conv=None) -> list:
-    b = []
-    for el in a:
-        if el != "" and el != "0":
-            if conv is not None:
-                el = conv(el)
-            b.append(el)
-    return b
+def clean_array(seq: Sequence, conv: Callable[[Any], Any] = lambda el: el) -> Sequence:
+    """ Convert a sequence using a converter function, stripping all
+    elements that are boolean False after conversion.
+
+    >>> clean_array(["a", "", "b"])
+    ['a', 'b']
+
+    >>> clean_array(["8", "", "12", "0"], int)
+    [8, 12]
+    """
 
+    filtered = filter(lambda el: bool(el), map(lambda el: conv(el) if el, seq))
+    return type(a)(filtered)
 
-def untis_split_first(s: str, conv=None) -> list:
+
+def untis_split_first(s: str, conv: Callable[[Any], Any] = lambda el: el) -> Sequence:
     return clean_array(s.split(","), conv=conv)
 
 
-def untis_split_second(s: str, conv=None) -> list:
+def untis_split_second(s: str, conv: Callable[[Any], Any] = lambda el: el) -> Sequence:
     return clean_array(s.split("~"), conv=conv)
 
 
-def untis_split_third(s: str, conv=None) -> list:
+def untis_split_third(s: str, conv: Callable[[Any], Any] = lambda el: el) -> Sequence:
     return clean_array(s.split(";"), conv=conv)
 
 
-UNTIS_DATE_FORMAT = "%Y%m%d"
-
-
 def untis_date_to_date(untis: int) -> date:
     """ Converts a UNTIS date to a python date """
     return datetime.strptime(str(untis), UNTIS_DATE_FORMAT).date()
 
 
-def date_to_untis_date(date: date) -> int:
+def date_to_untis_date(from_date: date) -> int:
     """ Converts a python date to a UNTIS date """
-    return int(date.strftime(UNTIS_DATE_FORMAT))
+    return int(from_date.strftime(UNTIS_DATE_FORMAT))
 
 
 def untis_colour_to_hex(colour: int) -> str:
-    # Convert UNTIS number to HEX
-    hex_bgr = str(hex(colour)).replace("0x", "")
+    """ Convert a numerical colour in BGR order to a standard hex RGB string """
 
-    # Add beginning zeros if len < 6
-    if len(hex_bgr) < 6:
-        hex_bgr = "0" * (6 - len(hex_bgr)) + hex_bgr
+    # Convert UNTIS number to HEX
+    hex_bgr = str(hex(colour))[2:].zfill(6)
 
     # Change BGR to RGB
     hex_rgb = hex_bgr[4:6] + hex_bgr[2:4] + hex_bgr[0:2]
@@ -114,41 +115,32 @@ def untis_colour_to_hex(colour: int) -> str:
     return "#" + hex_rgb
 
 
-def sync_m2m(new_items: Union[List[Model], QuerySet], m2m_qs: QuerySet):
-    """ Sync m2m field """
-
-    # Add items
-    for item in new_items:
-        if item not in m2m_qs.all():
-            m2m_qs.add(item)
-            logger.info("  Many-to-many sync: item added")
-
-    # Delete items
-    for item in m2m_qs.all():
-        if item not in new_items:
-            m2m_qs.remove(item)
-            logger.info("  Many-to-many sync: item removed")
-
-
 def compare_m2m(
     a: Union[List[Model], QuerySet], b: Union[List[Model], QuerySet]
 ) -> bool:
     """ Compare if content of two m2m fields is equal """
 
-    ids_a = sorted([i.id for i in a])
-    ids_b = sorted([i.id for i in b])
-    return ids_a == ids_b
+    return set(a) == set(b)
 
 
 def connect_untis_fields(obj: Model, attr: str, limit: int) -> List[str]:
-    """ Connects data from multiple DB fields """
+    """ Connects data from multiple DB fields
+
+    Untis splits structured data, like lists, as comma-separated string into
+    multiple, numbered database fields, like:
+
+      field1 = "This,is,a,nice"
+      field2 = "list,of,words"
+
+    This function joins these fields, then splits them into the original list.
+    """
 
     all_data = []
 
     for i in range(1, limit + 1):
         attr_name = "{}{}".format(attr, i)
         raw_data = getattr(obj, attr_name, "")
-        if raw_data not in ("", None):
+        if raw_data:
             data = untis_split_first(raw_data)
             all_data += data