diff --git a/.isort.cfg b/.isort.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..300d5ee931a1f8fe82f4c959d00c002c95bc05dd
--- /dev/null
+++ b/.isort.cfg
@@ -0,0 +1,7 @@
+[settings]
+# config compatible with Black
+line_length = 88
+multi_line_output = 3
+default_sectiont = "THIRDPARTY"
+include_trailing_comma = true
+known_third_party = django,wagtail
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..dd2c4d85681cc6b9d7d1c8ec82ba4bcdfa3e9eae
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,30 @@
+default_language_version:
+  python: python3.7
+
+repos:
+  - repo: https://github.com/pre-commit/pre-commit-hooks
+    rev: v2.5.0
+    hooks:
+      - id: trailing-whitespace
+        exclude: ^.*\.md$
+      - id: end-of-file-fixer
+      - id: debug-statements
+      - id: mixed-line-ending
+        args: [--fix=lf]
+      - id: detect-private-key
+      - id: check-merge-conflict
+
+  - repo: https://github.com/asottile/seed-isort-config
+    rev: v2.1.0
+    hooks:
+      - id: seed-isort-config
+
+  - repo: https://github.com/timothycrosley/isort
+    rev: 4.3.21
+    hooks:
+      - id: isort
+
+  - repo: https://github.com/psf/black
+    rev: 19.10b0
+    hooks:
+      - id: black
diff --git a/home/migrations/0001_initial.py b/home/migrations/0001_initial.py
index ef46d122577f178feb3028c7d1c5da7dddd6af1c..14920c78f2531c844c5c4d29fb135c12a1cd855f 100644
--- a/home/migrations/0001_initial.py
+++ b/home/migrations/0001_initial.py
@@ -5,18 +5,26 @@ from django.db import migrations, models
 class Migration(migrations.Migration):
 
     dependencies = [
-        ('wagtailcore', '0040_page_draft_title'),
+        ("wagtailcore", "0040_page_draft_title"),
     ]
 
     operations = [
         migrations.CreateModel(
-            name='HomePage',
+            name="HomePage",
             fields=[
-                ('page_ptr', models.OneToOneField(on_delete=models.CASCADE, parent_link=True, auto_created=True, primary_key=True, serialize=False, to='wagtailcore.Page')),
+                (
+                    "page_ptr",
+                    models.OneToOneField(
+                        on_delete=models.CASCADE,
+                        parent_link=True,
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        to="wagtailcore.Page",
+                    ),
+                ),
             ],
-            options={
-                'abstract': False,
-            },
-            bases=('wagtailcore.page',),
+            options={"abstract": False,},
+            bases=("wagtailcore.page",),
         ),
     ]
diff --git a/home/migrations/0002_create_homepage.py b/home/migrations/0002_create_homepage.py
index 039f0f5725454facd4ae99b97bbbfffb1d64ee89..b60860564f7111a12b115c67b4fb1d91ef9abcec 100644
--- a/home/migrations/0002_create_homepage.py
+++ b/home/migrations/0002_create_homepage.py
@@ -4,10 +4,10 @@ from django.db import migrations
 
 def create_homepage(apps, schema_editor):
     # Get models
-    ContentType = apps.get_model('contenttypes.ContentType')
-    Page = apps.get_model('wagtailcore.Page')
-    Site = apps.get_model('wagtailcore.Site')
-    HomePage = apps.get_model('home.HomePage')
+    ContentType = apps.get_model("contenttypes.ContentType")
+    Page = apps.get_model("wagtailcore.Page")
+    Site = apps.get_model("wagtailcore.Site")
+    HomePage = apps.get_model("home.HomePage")
 
     # Delete the default homepage
     # If migration is run multiple times, it may have already been deleted
@@ -15,42 +15,42 @@ def create_homepage(apps, schema_editor):
 
     # Create content type for homepage model
     homepage_content_type, __ = ContentType.objects.get_or_create(
-        model='homepage', app_label='home')
+        model="homepage", app_label="home"
+    )
 
     # Create a new homepage
     homepage = HomePage.objects.create(
         title="Home",
         draft_title="Home",
-        slug='home',
+        slug="home",
         content_type=homepage_content_type,
-        path='00010001',
+        path="00010001",
         depth=2,
         numchild=0,
-        url_path='/home/',
+        url_path="/home/",
     )
 
     # Create a site with the new homepage set as the root
-    Site.objects.create(
-        hostname='localhost', root_page=homepage, is_default_site=True)
+    Site.objects.create(hostname="localhost", root_page=homepage, is_default_site=True)
 
 
 def remove_homepage(apps, schema_editor):
     # Get models
-    ContentType = apps.get_model('contenttypes.ContentType')
-    HomePage = apps.get_model('home.HomePage')
+    ContentType = apps.get_model("contenttypes.ContentType")
+    HomePage = apps.get_model("home.HomePage")
 
     # Delete the default homepage
     # Page and Site objects CASCADE
-    HomePage.objects.filter(slug='home', depth=2).delete()
+    HomePage.objects.filter(slug="home", depth=2).delete()
 
     # Delete content type for homepage model
-    ContentType.objects.filter(model='homepage', app_label='home').delete()
+    ContentType.objects.filter(model="homepage", app_label="home").delete()
 
 
 class Migration(migrations.Migration):
 
     dependencies = [
-        ('home', '0001_initial'),
+        ("home", "0001_initial"),
     ]
 
     operations = [
diff --git a/home/models.py b/home/models.py
index af7b579be05c75ea1d872e251b1668b0aa7b5734..c73914118e2531bf5d22d9ec8936d15a6d92cba1 100644
--- a/home/models.py
+++ b/home/models.py
@@ -1,5 +1,4 @@
 from django.db import models
-
 from wagtail.core.models import Page
 
 
diff --git a/majak/settings/base.py b/majak/settings/base.py
index 46abed37c3cf259bcf6c49b99b0184926db19009..219f9765218f023a39bf120359392040876bec54 100644
--- a/majak/settings/base.py
+++ b/majak/settings/base.py
@@ -24,75 +24,69 @@ BASE_DIR = os.path.dirname(PROJECT_DIR)
 # Application definition
 
 INSTALLED_APPS = [
-    'home',
-    'search',
-
-    'wagtail.contrib.forms',
-    'wagtail.contrib.redirects',
-    'wagtail.embeds',
-    'wagtail.sites',
-    'wagtail.users',
-    'wagtail.snippets',
-    'wagtail.documents',
-    'wagtail.images',
-    'wagtail.search',
-    'wagtail.admin',
-    'wagtail.core',
-
-    'modelcluster',
-    'taggit',
-
-    'django.contrib.admin',
-    'django.contrib.auth',
-    'django.contrib.contenttypes',
-    'django.contrib.sessions',
-    'django.contrib.messages',
-    'django.contrib.staticfiles',
+    "home",
+    "search",
+    "wagtail.contrib.forms",
+    "wagtail.contrib.redirects",
+    "wagtail.embeds",
+    "wagtail.sites",
+    "wagtail.users",
+    "wagtail.snippets",
+    "wagtail.documents",
+    "wagtail.images",
+    "wagtail.search",
+    "wagtail.admin",
+    "wagtail.core",
+    "modelcluster",
+    "taggit",
+    "django.contrib.admin",
+    "django.contrib.auth",
+    "django.contrib.contenttypes",
+    "django.contrib.sessions",
+    "django.contrib.messages",
+    "django.contrib.staticfiles",
 ]
 
 MIDDLEWARE = [
-    'django.contrib.sessions.middleware.SessionMiddleware',
-    'django.middleware.common.CommonMiddleware',
-    'django.middleware.csrf.CsrfViewMiddleware',
-    'django.contrib.auth.middleware.AuthenticationMiddleware',
-    'django.contrib.messages.middleware.MessageMiddleware',
-    'django.middleware.clickjacking.XFrameOptionsMiddleware',
-    'django.middleware.security.SecurityMiddleware',
-
-    'wagtail.core.middleware.SiteMiddleware',
-    'wagtail.contrib.redirects.middleware.RedirectMiddleware',
+    "django.contrib.sessions.middleware.SessionMiddleware",
+    "django.middleware.common.CommonMiddleware",
+    "django.middleware.csrf.CsrfViewMiddleware",
+    "django.contrib.auth.middleware.AuthenticationMiddleware",
+    "django.contrib.messages.middleware.MessageMiddleware",
+    "django.middleware.clickjacking.XFrameOptionsMiddleware",
+    "django.middleware.security.SecurityMiddleware",
+    "wagtail.core.middleware.SiteMiddleware",
+    "wagtail.contrib.redirects.middleware.RedirectMiddleware",
 ]
 
-ROOT_URLCONF = 'majak.urls'
+ROOT_URLCONF = "majak.urls"
 
 TEMPLATES = [
     {
-        'BACKEND': 'django.template.backends.django.DjangoTemplates',
-        'DIRS': [
-            os.path.join(PROJECT_DIR, 'templates'),
-        ],
-        'APP_DIRS': True,
-        'OPTIONS': {
-            'context_processors': [
-                'django.template.context_processors.debug',
-                'django.template.context_processors.request',
-                'django.contrib.auth.context_processors.auth',
-                'django.contrib.messages.context_processors.messages',
+        "BACKEND": "django.template.backends.django.DjangoTemplates",
+        "DIRS": [os.path.join(PROJECT_DIR, "templates"),],
+        "APP_DIRS": True,
+        "OPTIONS": {
+            "context_processors": [
+                "django.template.context_processors.debug",
+                "django.template.context_processors.request",
+                "django.contrib.auth.context_processors.auth",
+                "django.contrib.messages.context_processors.messages",
             ],
         },
     },
 ]
 
-WSGI_APPLICATION = 'majak.wsgi.application'
+WSGI_APPLICATION = "majak.wsgi.application"
 
 
 # Database
 # https://docs.djangoproject.com/en/3.0/ref/settings/#databases
 
 DATABASES = {
-    'default': {
-        'ENGINE': 'django.db.backends.sqlite3',
-        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
+    "default": {
+        "ENGINE": "django.db.backends.sqlite3",
+        "NAME": os.path.join(BASE_DIR, "db.sqlite3"),
     }
 }
 
@@ -102,26 +96,20 @@ DATABASES = {
 
 AUTH_PASSWORD_VALIDATORS = [
     {
-        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
-    },
-    {
-        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
-    },
-    {
-        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
-    },
-    {
-        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+        "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
     },
+    {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",},
+    {"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",},
+    {"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",},
 ]
 
 
 # Internationalization
 # https://docs.djangoproject.com/en/3.0/topics/i18n/
 
-LANGUAGE_CODE = 'en-us'
+LANGUAGE_CODE = "en-us"
 
-TIME_ZONE = 'UTC'
+TIME_ZONE = "UTC"
 
 USE_I18N = True
 
@@ -134,24 +122,24 @@ USE_TZ = True
 # https://docs.djangoproject.com/en/3.0/howto/static-files/
 
 STATICFILES_FINDERS = [
-    'django.contrib.staticfiles.finders.FileSystemFinder',
-    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
+    "django.contrib.staticfiles.finders.FileSystemFinder",
+    "django.contrib.staticfiles.finders.AppDirectoriesFinder",
 ]
 
 STATICFILES_DIRS = [
-    os.path.join(PROJECT_DIR, 'static'),
+    os.path.join(PROJECT_DIR, "static"),
 ]
 
 # ManifestStaticFilesStorage is recommended in production, to prevent outdated
 # Javascript / CSS assets being served from cache (e.g. after a Wagtail upgrade).
 # See https://docs.djangoproject.com/en/3.0/ref/contrib/staticfiles/#manifeststaticfilesstorage
-STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
+STATICFILES_STORAGE = "django.contrib.staticfiles.storage.ManifestStaticFilesStorage"
 
-STATIC_ROOT = os.path.join(BASE_DIR, 'static')
-STATIC_URL = '/static/'
+STATIC_ROOT = os.path.join(BASE_DIR, "static")
+STATIC_URL = "/static/"
 
-MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
-MEDIA_URL = '/media/'
+MEDIA_ROOT = os.path.join(BASE_DIR, "media")
+MEDIA_URL = "/media/"
 
 
 # Wagtail settings
@@ -160,4 +148,4 @@ WAGTAIL_SITE_NAME = "majak"
 
 # Base URL to use when referring to full URLs within the Wagtail admin backend -
 # e.g. in notification emails. Don't include '/admin' or a trailing slash
-BASE_URL = 'http://example.com'
+BASE_URL = "http://example.com"
diff --git a/majak/settings/dev.py b/majak/settings/dev.py
index a516e938d473c5b46d6b15d6bde9def7004327bf..3ae9e0c6ff57fae8b2ba31d32fcba9f2bae2dd90 100644
--- a/majak/settings/dev.py
+++ b/majak/settings/dev.py
@@ -4,12 +4,12 @@ from .base import *
 DEBUG = True
 
 # SECURITY WARNING: keep the secret key used in production secret!
-SECRET_KEY = '1vu8ve^2vdu%k00s7&)l+^a@b5aht53el96evte(7d)0@!dyk1'
+SECRET_KEY = "1vu8ve^2vdu%k00s7&)l+^a@b5aht53el96evte(7d)0@!dyk1"
 
 # SECURITY WARNING: define the correct hosts in production!
-ALLOWED_HOSTS = ['*'] 
+ALLOWED_HOSTS = ["*"]
 
-EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
+EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
 
 
 try:
diff --git a/majak/urls.py b/majak/urls.py
index 89cfde3db5fca3374e0ee39c0ff58cfad32510dc..2c5155b14ac101fc6a405de63bd1368682c59cf1 100644
--- a/majak/urls.py
+++ b/majak/urls.py
@@ -1,7 +1,6 @@
 from django.conf import settings
 from django.conf.urls import include, url
 from django.contrib import admin
-
 from wagtail.admin import urls as wagtailadmin_urls
 from wagtail.core import urls as wagtail_urls
 from wagtail.documents import urls as wagtaildocs_urls
@@ -9,13 +8,10 @@ from wagtail.documents import urls as wagtaildocs_urls
 from search import views as search_views
 
 urlpatterns = [
-    url(r'^django-admin/', admin.site.urls),
-
-    url(r'^admin/', include(wagtailadmin_urls)),
-    url(r'^documents/', include(wagtaildocs_urls)),
-
-    url(r'^search/$', search_views.search, name='search'),
-
+    url(r"^django-admin/", admin.site.urls),
+    url(r"^admin/", include(wagtailadmin_urls)),
+    url(r"^documents/", include(wagtaildocs_urls)),
+    url(r"^search/$", search_views.search, name="search"),
 ]
 
 
@@ -32,7 +28,6 @@ urlpatterns = urlpatterns + [
     # Wagtail's page serving mechanism. This should be the last pattern in
     # the list:
     url(r"", include(wagtail_urls)),
-
     # Alternatively, if you want Wagtail pages to be served from a subpath
     # of your site, rather than the site root:
     #    url(r"^pages/", include(wagtail_urls)),
diff --git a/search/views.py b/search/views.py
index eeace7c25bab71c68e329d6dbbd3166a35363f31..ef20987c5157a14f0df23c172e2caefd837d985b 100644
--- a/search/views.py
+++ b/search/views.py
@@ -1,13 +1,12 @@
 from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
 from django.shortcuts import render
-
 from wagtail.core.models import Page
 from wagtail.search.models import Query
 
 
 def search(request):
-    search_query = request.GET.get('query', None)
-    page = request.GET.get('page', 1)
+    search_query = request.GET.get("query", None)
+    page = request.GET.get("page", 1)
 
     # Search
     if search_query:
@@ -28,7 +27,8 @@ def search(request):
     except EmptyPage:
         search_results = paginator.page(paginator.num_pages)
 
-    return render(request, 'search/search.html', {
-        'search_query': search_query,
-        'search_results': search_results,
-    })
+    return render(
+        request,
+        "search/search.html",
+        {"search_query": search_query, "search_results": search_results,},
+    )