diff --git a/aleksis/core/models.py b/aleksis/core/models.py
index eb92cb5d714aa3d538e9a407677b06f9cdc664ce..bd3c017371dc2daeef2c855dca290c8d50b6577d 100644
--- a/aleksis/core/models.py
+++ b/aleksis/core/models.py
@@ -14,6 +14,7 @@ from django.utils.translation import gettext_lazy as _
 from image_cropping import ImageCropField, ImageRatioField
 from phonenumber_field.modelfields import PhoneNumberField
 from polymorphic.models import PolymorphicModel
+import reversion
 
 from .mixins import ExtensibleModel, PureDjangoModel
 from .tasks import send_notification
@@ -23,6 +24,7 @@ from .util.model_helpers import ICONS
 from constance import config
 
 
+@reversion.register()
 class School(ExtensibleModel):
     """A school that will have many other objects linked to it.
     AlekSIS has multi-tenant support by linking all objects to a school,
@@ -54,6 +56,7 @@ class School(ExtensibleModel):
         verbose_name_plural = _("Schools")
 
 
+@reversion.register()
 class SchoolTerm(ExtensibleModel):
     """ Information about a term (limited time frame) that data can
     be linked to.
@@ -86,6 +89,7 @@ class SchoolTerm(ExtensibleModel):
         verbose_name_plural = _("School terms")
 
 
+@reversion.register()
 class Person(ExtensibleModel):
     """ A model describing any person related to a school, including, but not
     limited to, students, teachers and guardians (parents).
@@ -246,6 +250,7 @@ class Person(ExtensibleModel):
                 self.primary_group = self.member_of.filter(name__regex=pattern).first()
 
 
+@reversion.register()
 class Group(ExtensibleModel):
     """Any kind of group of persons in a school, including, but not limited
     classes, clubs, and the like.
@@ -303,6 +308,7 @@ class Group(ExtensibleModel):
         dj_group.save()
 
 
+@reversion.register()
 class Activity(ExtensibleModel):
     user = models.ForeignKey("Person", on_delete=models.CASCADE, related_name="activities")
 
@@ -319,6 +325,7 @@ class Activity(ExtensibleModel):
         verbose_name_plural = _("Activities")
 
 
+@reversion.register()
 class Notification(ExtensibleModel):
     sender = models.CharField(max_length=100, verbose_name=_("Sender"))
     recipient = models.ForeignKey("Person", on_delete=models.CASCADE, related_name="notifications")
@@ -398,7 +405,12 @@ class AnnouncementQuerySet(models.QuerySet):
 
         return announcements_for_person
 
+    class Meta:
+        verbose_name = _("Announcement Queryset")
+        verbose_name_plural = _("Announcements Querysets")
+
 
+@reversion.register()
 class Announcement(ExtensibleModel):
     objects = models.Manager.from_queryset(AnnouncementQuerySet)()
 
@@ -437,6 +449,7 @@ class Announcement(ExtensibleModel):
         verbose_name_plural = _("Announcements")
 
 
+@reversion.register()
 class AnnouncementRecipient(ExtensibleModel):
     announcement = models.ForeignKey(Announcement, on_delete=models.CASCADE, related_name="recipients")
 
@@ -465,6 +478,7 @@ class AnnouncementRecipient(ExtensibleModel):
         verbose_name_plural = _("Announcement recipients")
 
 
+@reversion.register()
 class DashboardWidget(PolymorphicModel, PureDjangoModel):
     """ Base class for dashboard widgets on the index page
 
@@ -529,6 +543,7 @@ class DashboardWidget(PolymorphicModel, PureDjangoModel):
         verbose_name_plural = _("Dashboard Widgets")
 
 
+@reversion.register()
 class CustomMenu(ExtensibleModel):
     id = models.CharField(max_length=100, verbose_name=_("Menu ID"), primary_key=True)
     name = models.CharField(max_length=150, verbose_name=_("Menu name"))
@@ -552,6 +567,7 @@ class CustomMenu(ExtensibleModel):
         verbose_name_plural = _("Custom menus")
 
 
+@reversion.register()
 class CustomMenuItem(ExtensibleModel):
     menu = models.ForeignKey(
         CustomMenu, models.CASCADE, verbose_name=_("Menu"), related_name="items"
@@ -569,6 +585,8 @@ class CustomMenuItem(ExtensibleModel):
         verbose_name = _("Custom menu item")
         verbose_name_plural = _("Custom menu items")
 
+
+@reversion.register()
 class GroupType(ExtensibleModel):
     name = models.CharField(verbose_name=_("Title of type"), max_length=50)
     description = models.CharField(verbose_name=_("Description"), max_length=500)
@@ -578,6 +596,7 @@ class GroupType(ExtensibleModel):
         verbose_name_plural = _("Group types")
 
 
+@reversion.register()
 class GlobalPermissions(ExtensibleModel):
     class Meta:
         managed = False
diff --git a/aleksis/core/settings.py b/aleksis/core/settings.py
index eb24f076df6a414052e2e5a29d9dfbf8af5c7083..5dd638bb5991b1dc738d4cdc542dedeffc13aed7 100644
--- a/aleksis/core/settings.py
+++ b/aleksis/core/settings.py
@@ -75,6 +75,7 @@ INSTALLED_APPS = [
     "image_cropping",
     "maintenance_mode",
     "menu_generator",
+    "reversion",
     "phonenumber_field",
     "debug_toolbar",
     "django_select2",
diff --git a/pyproject.toml b/pyproject.toml
index b0e9c84ee4820832417255a8f290c762fe270206..118325ec2e47142790983685cc36bb77531ccf03 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -75,6 +75,7 @@ celery-haystack = {version="^0.3.1", optional=true}
 django-dbbackup = "^3.3.0"
 spdx-license-list = "^0.4.0"
 license-expression = "^1.2"
+django-reversion = "^3.0.7"
 
 [tool.poetry.extras]
 ldap = ["django-auth-ldap"]