diff --git a/aleksis/core/mixins.py b/aleksis/core/mixins.py
index f6a8c18c483d0833d296abd6310cc2103331ba18..9ca87f71c8629b773589598ce60bfc822ce45506 100644
--- a/aleksis/core/mixins.py
+++ b/aleksis/core/mixins.py
@@ -4,13 +4,19 @@ from datetime import datetime
 from typing import Any, Callable, List, Optional, Tuple, Union
 
 from django.conf import settings
+from django.contrib import messages
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.sites.managers import CurrentSiteManager
 from django.contrib.sites.models import Site
 from django.db import models
 from django.db.models import QuerySet
+from django.forms.forms import BaseForm
 from django.forms.models import ModelForm, ModelFormMetaclass
+from django.http import HttpResponse
 from django.utils.functional import lazy
+from django.utils.translation import gettext as _
+from django.views.generic import CreateView, UpdateView
+from django.views.generic.edit import ModelFormMixin
 
 import reversion
 from easyaudit.models import CRUDEvent
@@ -19,6 +25,8 @@ from jsonstore.fields import JSONField, JSONFieldMixin
 from material.base import Layout, LayoutNode
 from rules.contrib.admin import ObjectPermissionsModelAdmin
 
+from aleksis.core.managers import CurrentSiteManagerWithoutMigrations, SchoolYearRelatedQuerySet
+
 
 class _ExtensibleModelBase(models.base.ModelBase):
     """Ensure predefined behaviour on model creation.
@@ -279,3 +287,56 @@ class BaseModelAdmin(GuardedModelAdmin, ObjectPermissionsModelAdmin):
     """A base class for ModelAdmin combining django-guardian and rules."""
 
     pass
+
+
+class SuccessMessageMixin(ModelFormMixin):
+    success_message: Optional[str] = None
+
+    def form_valid(self, form: BaseForm) -> HttpResponse:
+        print("Hi")
+        if self.success_message:
+            messages.success(self.request, self.success_message)
+        return super().form_valid(form)
+
+
+class AdvancedCreateView(CreateView, SuccessMessageMixin):
+    pass
+
+
+class AdvancedEditView(UpdateView, SuccessMessageMixin):
+    pass
+
+
+class SchoolYearRelatedExtensibleModel(ExtensibleModel):
+    """Add relation to school year."""
+
+    objects = CurrentSiteManagerWithoutMigrations.from_queryset(SchoolYearRelatedQuerySet)()
+
+    school_year = models.ForeignKey(
+        "core.SchoolYear",
+        on_delete=models.CASCADE,
+        related_name="+",
+        verbose_name=_("Linked school year"),
+        blank=True,
+        null=True,
+    )
+
+    class Meta:
+        abstract = True
+
+
+class SchoolYearRelatedExtensibleForm(ExtensibleForm):
+    """Extensible form for school year related data.
+
+    .. warning::
+        This doesn't automatically include the field `school_year` in `fields` or `layout`,
+        it just sets an initial value.
+    """
+
+    def __init__(self, *args, **kwargs):
+        from aleksis.core.models import SchoolYear  # noqa
+
+        if "instance" not in kwargs:
+            kwargs["initial"] = {"school_year": SchoolYear.current}
+
+        super().__init__(*args, **kwargs)