diff --git a/aleksis/core/mixins.py b/aleksis/core/mixins.py
index 5b2db3447db3d1b2208a623c7487e88bb689675b..7a77754f1c1475c4356506aac577e1a00bb45bd2 100644
--- a/aleksis/core/mixins.py
+++ b/aleksis/core/mixins.py
@@ -16,7 +16,7 @@ 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
+from django.views.generic.edit import DeleteView, ModelFormMixin
 
 import reversion
 from easyaudit.models import CRUDEvent
@@ -375,6 +375,16 @@ class AdvancedEditView(UpdateView, SuccessMessageMixin):
     pass
 
 
+class AdvancedDeleteView(DeleteView):
+    success_message: Optional[str] = None
+
+    def delete(self, request, *args, **kwargs):
+        r = super().delete(request, *args, **kwargs)
+        if self.success_message:
+            messages.success(self.request, self.success_message)
+        return r
+
+
 class SchoolTermRelatedExtensibleModel(ExtensibleModel):
     """Add relation to school term."""
 
diff --git a/aleksis/core/templates/core/pages/delete.html b/aleksis/core/templates/core/pages/delete.html
new file mode 100644
index 0000000000000000000000000000000000000000..db64ee8fc3b977ed9cb74333e1a25834ce3e58b7
--- /dev/null
+++ b/aleksis/core/templates/core/pages/delete.html
@@ -0,0 +1,25 @@
+{% extends "core/base.html" %}
+{% load data_helpers i18n %}
+
+{% block browser_title %}
+  {% verbose_name_object object as object_name %}
+  {% blocktrans with object_name=object_name %}Delete {{ object_name }}{% endblocktrans %}
+{% endblock %}
+
+{% block content %}
+  {% verbose_name_object object as object_name %}
+
+  <p class="flow-text">
+    {% blocktrans with object_name=object_name object=object %}
+      Do you really want to delete the {{ object_name }} "{{ object }}"?
+    {% endblocktrans %}
+  </p>
+
+  <form method="post" action="">
+    {% csrf_token %}
+    <button type="submit" class="btn red waves-effect waves-light">
+      <i class="material-icons left">delete</i>
+      {% trans "Delete" %}
+    </button>
+  </form>
+{% endblock %}
diff --git a/aleksis/core/templatetags/data_helpers.py b/aleksis/core/templatetags/data_helpers.py
index f7393c73fd86eba6f861f87e321bf8dc5cbce6fd..ab2309f260dd6b039cc722bae86fa71637967a1f 100644
--- a/aleksis/core/templatetags/data_helpers.py
+++ b/aleksis/core/templatetags/data_helpers.py
@@ -3,6 +3,7 @@ from typing import Any, Optional, Union
 
 from django import template
 from django.contrib.contenttypes.models import ContentType
+from django.db.models import Model
 
 register = template.Library()
 
@@ -33,6 +34,17 @@ def verbose_name(app_label: str, model: str, field: Optional[str] = None) -> str
         return ct._meta.verbose_name.title()
 
 
+@register.simple_tag
+def verbose_name_object(model: Model, field: Optional[str] = None) -> str:
+    """Get a verbose name of a model or a field by a model or an instance of a model."""
+    if field:
+        # Field
+        return model._meta.get_field(field).verbose_name.title()
+    else:
+        # Whole model
+        return model._meta.verbose_name.title()
+
+
 @register.simple_tag
 def parse_json(value: Optional[str] = None) -> Union[dict, None]:
     """Template tag for parsing JSON from a string."""