diff --git a/README.md b/README.md
index 4f46c237ed72bc5e29f4be6003180ba5985ee873..034f93a54642e6d05421b482a8cee246cc0a938f 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,53 @@
+# VIPSWeb
 This is the code for the public webpage for VIPS. It should be adaptable 
 enough to suit many organizations.
-Programming language is Python 2.7 and the web framework is currently Django 1.10
\ No newline at end of file
+
+## Requirements
+The system has been tested and found to run well on Ubuntu >= 18
+* The programming language is Python >= 3.6 
+* The web framework is currently [Django 3.1](https://docs.djangoproject.com/en/3.1/)
+* Apache web server with mod_wsgi compiled for Python 3
+
+## Install and setup
+1. Install and activate a virtual Python environment
+
+```
+python3 -m venv my_venv
+source my_env/bin/activate
+
+```
+
+2. Install all the requirements into the virtual environment. 
+Remember to be in the folder where the requirements.txt file is
+
+```
+pip install -r requirements.txt
+```
+
+3. Copy VIPSWeb/local_settings_sample.py into VIPSWeb/local_settings.py and adapt to your needs
+
+4. Run all database migrations
+
+```
+./manage.py migrate
+```
+
+## Running with mod_wsgi
+The official Django documentation for this [can be found here](https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/modwsgi/) 
+
+* If running on e.g. Ubuntu 18, please remember to install the `libapache2-mod-wsgi-py3` and not the `libapache2-mod-wsgi` package
+* Here's an example mod_wsgi configuration in an Apache2 virtualhost
+
+```
+        WSGIDaemonProcess vipsweb python-path=/opt/VIPSWeb/VIPSWeb python-home=/opt/VIPSWeb/py3dj3
+        WSGIProcessGroup vipsweb
+
+        WSGIScriptAlias / /opt/VIPSWeb/VIPSWeb/VIPSWeb/wsgi.py
+
+        <Directory /opt/VIPSWeb/VIPSWeb/VIPSWeb>
+                <Files wsgi.py>
+                        Require all granted
+                </Files>
+        </Directory>
+
+```
\ No newline at end of file
diff --git a/VIPSWeb/settings.py b/VIPSWeb/settings.py
index f95f5e1f55eda0a780517473d05ffc70b02203c2..ed6c09f7dee2e1c964ec15a7203f874a7c32f9b6 100755
--- a/VIPSWeb/settings.py
+++ b/VIPSWeb/settings.py
@@ -79,13 +79,13 @@ STATICFILES_FINDERS = (
 
 
 
-MIDDLEWARE_CLASSES = (
+MIDDLEWARE = (
     'django.middleware.common.CommonMiddleware',
     'django.contrib.sessions.middleware.SessionMiddleware',
     'django.middleware.csrf.CsrfViewMiddleware',
     'django.contrib.auth.middleware.AuthenticationMiddleware',
     'django.contrib.messages.middleware.MessageMiddleware',
-    'common.middleware.whodid.WhodidMiddleware',
+    #'common.middleware.whodid.WhodidMiddleware',
     'django.middleware.locale.LocaleMiddleware',
     'security.middleware.check_login_middleware.CheckLoginMiddleware'
     # Uncomment the next line for simple clickjacking protection:
@@ -106,6 +106,7 @@ TEMPLATES = [
             'context_processors':[
                 'django.contrib.auth.context_processors.auth',
                 'django.template.context_processors.request',
+                'django.contrib.messages.context_processors.messages',
                 'VIPSWeb.context_processors.settings'
                 ]
         },
@@ -183,7 +184,7 @@ LOGGING = {
 }
 
 try:
-    from local_settings import *
-except ImportError:
-    pass
+    from .local_settings import *
+except ImportError as ex:
+    print(ex)
 
diff --git a/VIPSWeb/templates/base.html b/VIPSWeb/templates/base.html
index 1a1f6e1d65906e18a9e4ef0668db2a3e634ae11c..da90bf70826aa3c1a4b2b2f58369fb9271193be2 100755
--- a/VIPSWeb/templates/base.html
+++ b/VIPSWeb/templates/base.html
@@ -19,7 +19,7 @@
  * 
  */
  
-{% endcomment %}{% load i18n staticfiles template_helper %}<!DOCTYPE html>
+{% endcomment %}{% load i18n static template_helper %}<!DOCTYPE html>
 <html lang="no">
 <head>
 	<meta charset="utf-8">
diff --git a/VIPSWeb/templates/base_with_date_picker.html b/VIPSWeb/templates/base_with_date_picker.html
index 4802711c668908382304343a5c336611a6824c9b..784b35701071448e6bb01ad25e34d34b990d5389 100755
--- a/VIPSWeb/templates/base_with_date_picker.html
+++ b/VIPSWeb/templates/base_with_date_picker.html
@@ -1,5 +1,5 @@
 {% extends "base.html" %}
-{% load staticfiles %}
+{% load static %}
 {% comment %}
 
 /*
diff --git a/VIPSWeb/templates/index.html b/VIPSWeb/templates/index.html
index 7523c55e6ff6da6770601f4b95c316e363d381eb..e46040582cca0ae4f498133b99ddabfe955d6966 100755
--- a/VIPSWeb/templates/index.html
+++ b/VIPSWeb/templates/index.html
@@ -21,7 +21,7 @@
  */
  
 {% endcomment %}
-{% load i18n l10n staticfiles forecast_extras template_helper %}
+{% load i18n l10n static forecast_extras template_helper %}
 {% block title%}{% trans "Welcome" %}{%endblock%}
 {% block customCSS %}
 <link rel="stylesheet" href="{% static "css/3rdparty/ol.css" %}" type="text/css">
diff --git a/VIPSWeb/templates/index_old.html b/VIPSWeb/templates/index_old.html
index ada6a5674b8a6cc99376e0cef9fbfe5f0b141cbe..cd1a9bca2917c7f120ffec88035e3b65992f8717 100755
--- a/VIPSWeb/templates/index_old.html
+++ b/VIPSWeb/templates/index_old.html
@@ -21,7 +21,7 @@
  */
  
 {% endcomment %}
-{% load i18n l10n staticfiles forecast_extras template_helper %}
+{% load i18n l10n static forecast_extras template_helper %}
 {% block title%}{% trans "Welcome" %}{%endblock%}
 {% block customCSS %}
 <link rel="stylesheet" href="{% static "css/3rdparty/ol.css" %}" type="text/css">
diff --git a/VIPSWeb/templatetags/template_helper.py b/VIPSWeb/templatetags/template_helper.py
index 622867c27515991158ce5835e19e92132fdbcc9d..7157562939286e228256e3fdb1050ab4f0e1a40f 100755
--- a/VIPSWeb/templatetags/template_helper.py
+++ b/VIPSWeb/templatetags/template_helper.py
@@ -5,7 +5,7 @@ from django.utils import translation
 from django.conf import settings
 from datetime import datetime
 from dateutil.relativedelta import relativedelta
-from django.core.urlresolvers import reverse
+from django.urls import reverse
 from django.utils.safestring import mark_safe
 
 register = template.Library()
@@ -93,7 +93,7 @@ def generate_frontpage_menu(context):
 		""" 
 	
 	menu_html = ''
-	col_md = 12/len(settings.FRONTPAGE_MENU)
+	col_md = int(12/len(settings.FRONTPAGE_MENU))
 	for col in settings.FRONTPAGE_MENU:
 		menu_html += '<div class="col-md-%s"><h3>%s</h3>' % (col_md,_(col["label"]))
 		menu_html += "<ul class='linkList'>"
diff --git a/VIPSWeb/urls.py b/VIPSWeb/urls.py
index b368352680f3b982a88aaff3617f11c48fe4b022..681c5c57d12edf83cfd960e2c9a0de1dbb94db84 100755
--- a/VIPSWeb/urls.py
+++ b/VIPSWeb/urls.py
@@ -30,7 +30,7 @@ admin.autodiscover()
 # Enabling translation in JavaScript files
 # See https://docs.djangoproject.com/en/1.5/topics/i18n/translation/#internationalization-in-javascript-code
 js_info_dict = {
-    'packages': ['forecasts','messages','VIPSWeb','roughage'],
+    'packages': ['forecasts','vips_messages','VIPSWeb','roughage'],
     'domain': 'djangojs',
 }
 
@@ -63,7 +63,7 @@ else:
         url(r'^applefruitmoth/', include('applefruitmoth.urls', namespace = "applefruitmoth")),
         url(r'^mock/', include('mock.urls', namespace = "mock")),
         # Uncomment the next line to enable the admin:
-        url(r'^admin/', include(admin.site.urls)),
+        url(r'^admin/', admin.site.urls),
         url(r'^tinymce/', include('tinymce.urls')),
         # Static serving of media files
         url(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
diff --git a/VIPSWeb/views.py b/VIPSWeb/views.py
index 1d8589a87efaaec2c5a718721574dda5a4ebfccd..74f2fee79de9e9896d5f2a92231139a040213b02 100755
--- a/VIPSWeb/views.py
+++ b/VIPSWeb/views.py
@@ -100,13 +100,15 @@ def vipslogicproxy(request, path):
     params = request.urlencode()
 
 
-    #print "Params:" + params
+    #print ("Params:" + params)
     try:
         url = "%s://%s/%s" % (settings.VIPSLOGIC_PROTOCOL, settings.VIPSLOGIC_SERVER_NAME,path)
-        #print url
+        #print(url)
     except KeyError:
         return HttpResponseBadRequest("URL must be defined")
-    response = r(url.encode("ascii"), params=params.encode("ascii"))
+    url = url.encode("ascii")
+    params_encoded = params.encode("ascii")
+    response = r(url, params=params_encoded)
     if response.status_code == 200:
         return HttpResponse(response.text, status=int(response.status_code), content_type=response.headers.get('content-type',"text/plain"))
     else:
diff --git a/VIPSWeb/wsgi.py b/VIPSWeb/wsgi.py
index ff8ffae4841294be0c3b1f4021b5a244e343072e..ff95b082818085ce939804325a72d1f6b190c22a 100755
--- a/VIPSWeb/wsgi.py
+++ b/VIPSWeb/wsgi.py
@@ -32,6 +32,7 @@ framework.
 
 """
 import os
+from django.core.wsgi import get_wsgi_application
 
 # We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks
 # if running multiple sites in the same mod_wsgi process. To fix this, use
@@ -42,7 +43,7 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "VIPSWeb.settings")
 # This application object is used by any WSGI server configured to use this
 # file. This includes Django's development server, if the WSGI_APPLICATION
 # setting points here.
-from django.core.wsgi import get_wsgi_application
+
 application = get_wsgi_application()
 
 # Apply WSGI middleware here.
diff --git a/applefruitmoth/templates/applefruitmoth/index.html b/applefruitmoth/templates/applefruitmoth/index.html
index e4bcde15bb2457731deed094c4e94774498a8106..4d2f2218113268e6c719dc2d301d7b74bff8724c 100755
--- a/applefruitmoth/templates/applefruitmoth/index.html
+++ b/applefruitmoth/templates/applefruitmoth/index.html
@@ -1,5 +1,5 @@
 {% extends "base.html" %}
-{% load staticfiles %}
+{% load static %}
 {% comment %}
 
 #
diff --git a/applefruitmoth/urls.py b/applefruitmoth/urls.py
index d10f27647112c8b081813b4a802817cb942b542f..92ccac728202b4f6ced03a8c72aedcbdd283f192 100755
--- a/applefruitmoth/urls.py
+++ b/applefruitmoth/urls.py
@@ -20,6 +20,8 @@ from django.conf.urls import url
 
 from applefruitmoth import views
 
+app_name = "applefruitmoth"
+
 urlpatterns = [
     url(r'^$', views.index, name='index'),
 ]
\ No newline at end of file
diff --git a/calculators/templates/calculators/eil.html b/calculators/templates/calculators/eil.html
index 64063cf88c2c4d5f5d102284f0aa7dfc103d736a..4b8fedb6643d7300fcc5c322a8cf16e62be8332d 100755
--- a/calculators/templates/calculators/eil.html
+++ b/calculators/templates/calculators/eil.html
@@ -1,5 +1,5 @@
 {% extends "base.html" %}
-{% load staticfiles %}
+{% load static %}
 {% comment %}
 
 #
diff --git a/calculators/templates/calculators/index.html b/calculators/templates/calculators/index.html
index a544cb7e29e7c86537aad515ae6a88f7eead1087..aa4e43a0ce95955d23b4f8a2e3a7abdabefd3772 100755
--- a/calculators/templates/calculators/index.html
+++ b/calculators/templates/calculators/index.html
@@ -1,5 +1,5 @@
 {% extends "base.html" %}
-{% load staticfiles %}
+{% load static %}
 {% comment %}
 
 #
diff --git a/calculators/urls.py b/calculators/urls.py
index 210159ff8c15ba1b22b1d229ea9e23558f554d13..1661f2f18d47108c08e4b144d20ff8bb8903cf41 100755
--- a/calculators/urls.py
+++ b/calculators/urls.py
@@ -18,6 +18,8 @@
 from django.conf.urls import url
 from calculators import views
 
+app_name = "calculators"
+
 urlpatterns = [
     # ex: /forecasts/                   
     url(r'^$', views.index, name='index'),
diff --git a/cerealblotchmodels/templates/cerealblotchmodels/barleynetblotchform.html b/cerealblotchmodels/templates/cerealblotchmodels/barleynetblotchform.html
index d1e727ef757b9fc019c43d18e778cfc1eca07062..feb21cdcfc8c5dc90bdc6fd3fb2582076c8f2d8d 100755
--- a/cerealblotchmodels/templates/cerealblotchmodels/barleynetblotchform.html
+++ b/cerealblotchmodels/templates/cerealblotchmodels/barleynetblotchform.html
@@ -1,5 +1,5 @@
 {% extends "base_with_date_picker.html" %}
-{% load staticfiles %}
+{% load static %}
 {% comment %}
 
 #
diff --git a/cerealblotchmodels/templates/cerealblotchmodels/index.html b/cerealblotchmodels/templates/cerealblotchmodels/index.html
index e34160dda9330545103bd47cdbd568d3a8a99602..0f6f6445fcd63e7d0c1f9f22519fbdbc791f13d3 100755
--- a/cerealblotchmodels/templates/cerealblotchmodels/index.html
+++ b/cerealblotchmodels/templates/cerealblotchmodels/index.html
@@ -1,5 +1,5 @@
 {% extends "base.html" %}
-{% load staticfiles %}
+{% load static %}
 {% comment %}
 
 #
diff --git a/cerealblotchmodels/templates/cerealblotchmodels/septoriahumiditymodelform.html b/cerealblotchmodels/templates/cerealblotchmodels/septoriahumiditymodelform.html
index 44792cf03e35a3874ab9010bb59176d07f47f9ce..8eb339989dfc75ff400496ee3bda1e94e3b5707d 100644
--- a/cerealblotchmodels/templates/cerealblotchmodels/septoriahumiditymodelform.html
+++ b/cerealblotchmodels/templates/cerealblotchmodels/septoriahumiditymodelform.html
@@ -1,5 +1,5 @@
 {% extends "base_with_date_picker.html" %}
-{% load staticfiles %}
+{% load static %}
 {% comment %}
 
 #
diff --git a/cerealblotchmodels/templates/cerealblotchmodels/wheatleafblotchform.html b/cerealblotchmodels/templates/cerealblotchmodels/wheatleafblotchform.html
index 1932211037aa8fd1d6b4e1893a86b84893400bcd..dd9c8e9af4d30df2582301fc4771cb1c0fd5d407 100755
--- a/cerealblotchmodels/templates/cerealblotchmodels/wheatleafblotchform.html
+++ b/cerealblotchmodels/templates/cerealblotchmodels/wheatleafblotchform.html
@@ -1,5 +1,5 @@
 {% extends "base_with_date_picker.html" %}
-{% load staticfiles %}
+{% load static %}
 {% comment %}
 
 #
diff --git a/cerealblotchmodels/urls.py b/cerealblotchmodels/urls.py
index fc755c574502d1bd08b2222ba01fbba2b0404217..8a6bd76d63cdb53ffa12ea6a47d1146419095def 100755
--- a/cerealblotchmodels/urls.py
+++ b/cerealblotchmodels/urls.py
@@ -18,6 +18,8 @@
 from django.conf.urls import url
 from cerealblotchmodels import views
 
+app_name = "cerealblotchmodels"
+
 urlpatterns = [
     # ex: /forecasts/                   
     url(r'^$', views.index, name='index'),
diff --git a/common/middleware/whodid.py b/common/middleware/whodid.py
index c0e0fa0f8d8ccf391df2b9e989ce52a19b672042..8a40f0042b4ddcd23925ee697e881f0f360f43e1 100755
--- a/common/middleware/whodid.py
+++ b/common/middleware/whodid.py
@@ -23,11 +23,20 @@ Found here: https://gist.github.com/mindlace/3918300
 """
 from django.db.models import signals
 from django.utils.functional import curry
+from django.http import HttpResponse
  
 class WhodidMiddleware(object):
+    
+    def __init__(self, get_response):
+        self.get_response = get_response
+        
+    def __call__(self, request):
+        self.process_request(request)
+        return self.get_response(request)
+    
     def process_request(self, request):
         if not request.method in ('GET', 'HEAD', 'OPTIONS', 'TRACE'):
-            if hasattr(request, 'user') and request.user.is_authenticated():
+            if hasattr(request, 'user') and request.user.is_authenticated:
                 user = request.user
             else:
                 user = None
@@ -40,7 +49,7 @@ class WhodidMiddleware(object):
         return response
      
     def mark_whodid(self, user, sender, instance, **kwargs):
-        print instance
+        print (instance)
         if not getattr(instance, 'created_by_id', None):
             instance.created_by = user
         if hasattr(instance,'modified_by_id'):
diff --git a/cydiapomonella/templates/cydiapomonella/index.html b/cydiapomonella/templates/cydiapomonella/index.html
index 14494e78d207161f879a31abbda212caec9c2962..a388589964ba2329dc306d9e60b72f84cf620aef 100644
--- a/cydiapomonella/templates/cydiapomonella/index.html
+++ b/cydiapomonella/templates/cydiapomonella/index.html
@@ -1,5 +1,5 @@
 {% extends "base.html" %}
-{% load staticfiles %}
+{% load static %}
 {% comment %}
 
 #
diff --git a/forecasts/migrations/0002_auto_20201123_1434.py b/forecasts/migrations/0002_auto_20201123_1434.py
new file mode 100644
index 0000000000000000000000000000000000000000..5bee98b82be8c28add7ab5966d20c3c643f0ed6d
--- /dev/null
+++ b/forecasts/migrations/0002_auto_20201123_1434.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.0.11 on 2020-11-23 13:34
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('forecasts', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='resultparameterlocal',
+            name='language_code',
+            field=models.CharField(max_length=2, verbose_name='Språkkode'),
+        ),
+    ]
diff --git a/forecasts/models.py b/forecasts/models.py
index fb83dbe6d5d46cc2580de2e85f9f44320548b5ab..7234e0b2c8ec3aeb59d54bda0f93834bfe920f6e 100755
--- a/forecasts/models.py
+++ b/forecasts/models.py
@@ -501,6 +501,9 @@ class MeasurementUnit(models.Model):
     opposite = models.BooleanField(default=False)
     def __unicode__(self):  
         return self.name
+    def __str__(self):  
+        return self.name
+    
     class Meta:
         ordering = ['name']
 
@@ -509,7 +512,7 @@ class ResultParameter(models.Model):
     key = models.CharField(max_length=50)
     name = models.CharField(max_length=200)
     description = models.TextField()
-    measurementunit = models.ForeignKey(MeasurementUnit)
+    measurementunit = models.ForeignKey(MeasurementUnit,on_delete=models.CASCADE)
     local_name = None
     
     
@@ -536,7 +539,7 @@ class ResultParameter(models.Model):
             
         
 class ResultParameterLocal(models.Model):
-    result_parameter = models.ForeignKey(ResultParameter)
+    result_parameter = models.ForeignKey(ResultParameter,on_delete=models.CASCADE)
     local_name = models.CharField(max_length=200)
     language_code = models.CharField(max_length=2, verbose_name=_("Language code"))
 
@@ -548,12 +551,18 @@ class HighChartsType(models.Model):
     name = models.CharField(max_length=63)
     def __unicode__(self):
         return self.name
+    
+    def __str__(self):
+        return self.name
 
 class ModelGraphParameter(models.Model):
     model_id = models.CharField(max_length=10, validators=[validate_model_id_length])
-    resultparameter = models.ForeignKey(ResultParameter)
+    resultparameter = models.ForeignKey(ResultParameter,on_delete=models.CASCADE)
     color_hexcode = models.CharField(max_length=6)
-    highcharts_type = models.ForeignKey(HighChartsType)
+    highcharts_type = models.ForeignKey(HighChartsType,on_delete=models.CASCADE)
     
     def __unicode__(self): 
         return self.model_id + "/" + self.resultparameter.getNamespaceKey()
+    
+    def __str__(self): 
+        return self.model_id + "/" + self.resultparameter.getNamespaceKey()
diff --git a/forecasts/templates/forecasts/detail.html b/forecasts/templates/forecasts/detail.html
index d3b015a6ddf8efc5aa52ff485530838a9bb096ad..37466a56ad920cf13778419a01812fbd1e932b57 100755
--- a/forecasts/templates/forecasts/detail.html
+++ b/forecasts/templates/forecasts/detail.html
@@ -21,7 +21,7 @@
 # @author: Tor-Einar Skog
  
 {% endcomment %}
-{% load i18n l10n staticfiles forecast_extras %}
+{% load i18n l10n static forecast_extras %}
 {% block title%}{% trans "Details" %}{%endblock%}
 {% block content %}
 <h1>{% trans "Details" %}</h1>
diff --git a/forecasts/templates/forecasts/detail_error.html b/forecasts/templates/forecasts/detail_error.html
index 0e9259b9c348602e31dd11f670c894e607d58b0a..37b3dca6a65843850d323e60326c616913d8b49c 100755
--- a/forecasts/templates/forecasts/detail_error.html
+++ b/forecasts/templates/forecasts/detail_error.html
@@ -21,7 +21,7 @@
 # @author: Tor-Einar Skog 
  
 {% endcomment %}
-{% load i18n l10n staticfiles forecast_extras %}
+{% load i18n l10n static forecast_extras %}
 {% block title%}{% trans "Details" %}{%endblock%}
 {% block content %}
 <h1>{% trans "Error with details" %}</h1>
diff --git a/forecasts/templates/forecasts/index.html b/forecasts/templates/forecasts/index.html
index c6360412af9989f4aaca9b8a66724d0ebc714f19..0ecae3aeddd63d46481770a5f3a6f052133b7e95 100755
--- a/forecasts/templates/forecasts/index.html
+++ b/forecasts/templates/forecasts/index.html
@@ -1,5 +1,5 @@
 {% extends "base.html" %}
-{% load staticfiles %}
+{% load static %}
 {% comment %}
 
 #
diff --git a/forecasts/templatetags/forecast_extras.py b/forecasts/templatetags/forecast_extras.py
index a0a020e609b906043ac6255dc7e7d35c2e95686a..5c6104ff3bdde5a9dfc18a9fa25a77e9b088636c 100755
--- a/forecasts/templatetags/forecast_extras.py
+++ b/forecasts/templatetags/forecast_extras.py
@@ -34,7 +34,7 @@ def addfloat(value, arg):
     try:
         return float(value) + float(arg)
     except ValueError:
-        print value
+        print (value)
         return float(arg)
 
 @register.filter(name="distanceto")
diff --git a/forecasts/urls.py b/forecasts/urls.py
index 7a03d4b7a4127f159d92acad13ec687dae3b837f..ef8c0bb0d33bd68f5f977ea1d82cd288c3b06158 100755
--- a/forecasts/urls.py
+++ b/forecasts/urls.py
@@ -20,6 +20,8 @@ from django.views.decorators.cache import cache_page
 
 from forecasts import views
 
+app_name="forecasts"
+
 urlpatterns = [
     # ex: /forecasts/                   
     url(r'^$', views.index, name='index'),
diff --git a/fusarium/templates/fusarium/oat_flowering.html b/fusarium/templates/fusarium/oat_flowering.html
index 39b537a3f40762b139a63847df815bfe34b39593..7bdd13a872021d678f227be96d45e61105317b8d 100755
--- a/fusarium/templates/fusarium/oat_flowering.html
+++ b/fusarium/templates/fusarium/oat_flowering.html
@@ -1,5 +1,5 @@
 {% extends "base.html" %}
-{% load i18n staticfiles %}
+{% load i18n static %}
 {% block title%}{% trans "Oat flowering model" %}{%endblock%}
 {% block extendCSS %}
 
diff --git a/fusarium/urls.py b/fusarium/urls.py
index d0f606a5b778aadcecb95608e6fde472e7c1525b..e7b9cc8e48776a47e7771c8b163dde4813c5bb75 100755
--- a/fusarium/urls.py
+++ b/fusarium/urls.py
@@ -20,6 +20,8 @@ from django.conf.urls import url
 
 from fusarium import views
 
+app_name = "fusarium"
+
 urlpatterns = [
     url(r'^$', views.index, name='index'),
 ]
\ No newline at end of file
diff --git a/information/migrations/0001_initial.py b/information/migrations/0001_initial.py
index 997a00159a03ff8aad42ed4ecb327faf945ee3f5..33f5474900a5416df0ad074f47df4dc26454d05e 100755
--- a/information/migrations/0001_initial.py
+++ b/information/migrations/0001_initial.py
@@ -16,7 +16,7 @@ class Migration(migrations.Migration):
             fields=[
                 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                 ('main_illustration', models.ImageField(upload_to=b'images/information', verbose_name='Hovedillustrasjon', blank=True)),
-                ('parent_information', models.ForeignKey(related_name='children', verbose_name='Informasjon', blank=True, to='information.Information', null=True)),
+                ('parent_information', models.ForeignKey(related_name='children', verbose_name='Informasjon', blank=True, to='information.Information', null=True,on_delete=models.CASCADE)),
             ],
         ),
         migrations.CreateModel(
@@ -24,7 +24,7 @@ class Migration(migrations.Migration):
             fields=[
                 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                 ('attachment', models.FileField(upload_to=b'attachments/information', verbose_name='Vedlegg', blank=True)),
-                ('information', models.ForeignKey(to='information.Information')),
+                ('information', models.ForeignKey(to='information.Information',on_delete=models.CASCADE)),
             ],
         ),
         migrations.CreateModel(
@@ -32,7 +32,7 @@ class Migration(migrations.Migration):
             fields=[
                 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                 ('illustration', models.ImageField(upload_to=b'images/information', verbose_name='Illustrasjon', blank=True)),
-                ('information', models.ForeignKey(to='information.Information')),
+                ('information', models.ForeignKey(to='information.Information',on_delete=models.CASCADE)),
             ],
         ),
         migrations.CreateModel(
@@ -43,7 +43,7 @@ class Migration(migrations.Migration):
                 ('lead_paragraph', models.TextField(verbose_name='Ingress')),
                 ('body', tinymce.models.HTMLField(verbose_name='Br\xf8dtekst')),
                 ('language_code', models.CharField(max_length=2, verbose_name='Spr\xe5kkode')),
-                ('information', models.ForeignKey(to='information.Information')),
+                ('information', models.ForeignKey(to='information.Information',on_delete=models.CASCADE)),
             ],
         ),
     ]
diff --git a/information/migrations/0004_auto_20201123_1434.py b/information/migrations/0004_auto_20201123_1434.py
new file mode 100644
index 0000000000000000000000000000000000000000..c1ae0b348d03c685691a9d29a6ab2c9694b52dfc
--- /dev/null
+++ b/information/migrations/0004_auto_20201123_1434.py
@@ -0,0 +1,60 @@
+# Generated by Django 3.0.11 on 2020-11-23 13:34
+
+from django.db import migrations, models
+import django.db.models.deletion
+import tinymce.models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('information', '0003_auto_20200603_1435'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='information',
+            name='main_illustration',
+            field=models.ImageField(blank=True, upload_to='images/information', verbose_name='Hovedillustrasjon'),
+        ),
+        migrations.AlterField(
+            model_name='information',
+            name='ordering',
+            field=models.IntegerField(default=0, verbose_name='Sortering'),
+        ),
+        migrations.AlterField(
+            model_name='information',
+            name='parent_information',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='information.Information', verbose_name='Informasjon'),
+        ),
+        migrations.AlterField(
+            model_name='informationattachment',
+            name='attachment',
+            field=models.FileField(blank=True, upload_to='attachments/information', verbose_name='Vedlegg'),
+        ),
+        migrations.AlterField(
+            model_name='informationillustration',
+            name='illustration',
+            field=models.ImageField(blank=True, upload_to='images/information', verbose_name='Illustrasjon'),
+        ),
+        migrations.AlterField(
+            model_name='informationlocale',
+            name='body',
+            field=tinymce.models.HTMLField(verbose_name='Brødtekst'),
+        ),
+        migrations.AlterField(
+            model_name='informationlocale',
+            name='headline',
+            field=models.CharField(max_length=200, verbose_name='Overskrift'),
+        ),
+        migrations.AlterField(
+            model_name='informationlocale',
+            name='language_code',
+            field=models.CharField(max_length=2, verbose_name='Språkkode'),
+        ),
+        migrations.AlterField(
+            model_name='informationlocale',
+            name='lead_paragraph',
+            field=models.TextField(verbose_name='Ingress'),
+        ),
+    ]
diff --git a/information/models.py b/information/models.py
index fb4b4211e0cca3e20dbb7700e2a3a74de2292c32..6594c756b70db36f6bbf1a057b1eb5b7efacdde2 100755
--- a/information/models.py
+++ b/information/models.py
@@ -10,22 +10,26 @@ from django.utils import translation
 class Information(models.Model):
     def __unicode__(self):
         return InformationLocale.get_heading_with_fallback(self.id)
-    parent_information = models.ForeignKey('self', blank=True, null=True, related_name='children', verbose_name=_("Parent information"))
+    
+    def __str__(self):
+        return InformationLocale.get_heading_with_fallback(self.id)
+    
+    parent_information = models.ForeignKey('self', blank=True, null=True, related_name='children', verbose_name=_("Parent information"),on_delete=models.CASCADE)
     main_illustration = models.ImageField(upload_to='images/information', blank=True, verbose_name=_("Main illustration") )
     ordering = models.IntegerField(default=0, verbose_name=_("Ordering"))
     class Meta:
         ordering = ["ordering"]
 
 class InformationIllustration(models.Model):
-    information = models.ForeignKey(Information)
+    information = models.ForeignKey(Information,on_delete=models.CASCADE)
     illustration = models.ImageField(upload_to='images/information', blank=True, verbose_name=_("Illustration"))
     
 class InformationAttachment(models.Model):
-    information = models.ForeignKey(Information)
+    information = models.ForeignKey(Information,on_delete=models.CASCADE)
     attachment = models.FileField(upload_to='attachments/information', blank=True, verbose_name=_("Attachment"))
 
 class InformationLocale(models.Model):
-    information = models.ForeignKey(Information)
+    information = models.ForeignKey(Information,on_delete=models.CASCADE)
     headline = models.CharField(max_length=200, verbose_name=_("Headline"))
     lead_paragraph = models.TextField(verbose_name=_("Lead paragraph"))
     body = tinymce_models.HTMLField(verbose_name=_("Body text"))
diff --git a/information/templates/information/detail.html b/information/templates/information/detail.html
index a4e484c154400f7555118ddd46bf9939aab70aa3..a4ed65285b906e48b625850bfd563a29c7cf63a3 100755
--- a/information/templates/information/detail.html
+++ b/information/templates/information/detail.html
@@ -1,5 +1,5 @@
 {% extends "base.html" %}
-{% load i18n staticfiles %}
+{% load i18n static %}
 {% block title%}{{information_locale.headline}}{%endblock%}
 {% block content %}
 <div class="col-md-3">
diff --git a/information/templates/information/index.html b/information/templates/information/index.html
index 3377f184c60d2fcba51457f710f18a62ee4f464a..d7b7cb603afa50412d5b6feebf14c03e66e4638f 100755
--- a/information/templates/information/index.html
+++ b/information/templates/information/index.html
@@ -1,5 +1,5 @@
 {% extends "base.html" %}
-{% load i18n staticfiles %}
+{% load i18n static %}
 {% block title%}{% trans "Information" %}{%endblock%}
 {% block content %}
 <h1>{% trans "Information" %}</h1>
diff --git a/information/urls.py b/information/urls.py
index cd17f675c3ea69988e5f126b639bb8232b740958..cbbea48875ea001a90f86ba14a4eb089ead9215c 100755
--- a/information/urls.py
+++ b/information/urls.py
@@ -20,6 +20,8 @@ from django.conf.urls import url
 
 from information import views
 
+app_name = "information"
+
 urlpatterns = [
     # ex: /messages/                   
     url(r'^$', views.index, name='index'),
diff --git a/information/views.py b/information/views.py
index d5abf1212713f245f9aed58b88e77fec0cec3a97..c330d5ef754376c15f8fefb25f974384dcfa3c6c 100755
--- a/information/views.py
+++ b/information/views.py
@@ -20,7 +20,7 @@ from django.shortcuts import render
 from django.core.exceptions import ObjectDoesNotExist
 from django.http import Http404
 from information.models import InformationLocale, Information
-from django.core.urlresolvers import reverse
+from django.urls import reverse
 
 
 def index(request):
diff --git a/mock/urls.py b/mock/urls.py
index 5ad9f0082bc0c85750606752c7f216f88df7d1fc..76901112a6860351b8028b43b081c6f01b4c2106 100644
--- a/mock/urls.py
+++ b/mock/urls.py
@@ -18,6 +18,9 @@
 
 from django.conf.urls import url
 from mock import views
+
+app_name = "mock"
+
 urlpatterns = [
                        url(r'^zymogridmapclient/$', views.zymogridmapclient, name='zymogridmapclient'),
 ]
diff --git a/observations/templates/observations/detail.html b/observations/templates/observations/detail.html
index 49015d4e5a22fa480407e6adec557dec6ef04ec2..bdce9dd9a0481fdb7add4967649290d231803081 100755
--- a/observations/templates/observations/detail.html
+++ b/observations/templates/observations/detail.html
@@ -1,5 +1,5 @@
 {% extends "base.html" %}
-{% load i18n l10n staticfiles %}
+{% load i18n l10n static %}
 {% comment %}
 
 #
diff --git a/observations/templates/observations/index.html b/observations/templates/observations/index.html
index a8c6b84b3e8ca4d78983151db5c2dd43841b712a..385083b0cb1e63ab1b02559872824e1d2c7119bc 100644
--- a/observations/templates/observations/index.html
+++ b/observations/templates/observations/index.html
@@ -1,5 +1,5 @@
 {% extends "base.html" %}
-{% load staticfiles %}
+{% load static %}
 {% comment %}
 
 #
diff --git a/observations/templates/observations/index_old.html b/observations/templates/observations/index_old.html
index 6919283f5e8ef57c751088477ca92f9f57209cfe..580cb9df26d29bc88a694b7585f908b8badad6b0 100755
--- a/observations/templates/observations/index_old.html
+++ b/observations/templates/observations/index_old.html
@@ -1,5 +1,5 @@
 {% extends "base.html" %}
-{% load staticfiles %}
+{% load static %}
 {% comment %}
 
 #
diff --git a/observations/urls.py b/observations/urls.py
index fd3672f80748ff583997699c9bf45f55a1956ac7..256641870add185a2fb11dd6ca3a79138a090706 100755
--- a/observations/urls.py
+++ b/observations/urls.py
@@ -20,6 +20,8 @@ from django.conf.urls import url
 
 from observations import views
 
+app_name = "observations"
+
 urlpatterns = [
     # ex: /forecasts/                   
     url(r'^$', views.index, name='index'),
diff --git a/organisms/urls.py b/organisms/urls.py
index 467d9059f5a48e548ed0e8ca8a55a70d919ce2bf..5055042aa86db69e5cadfd2155738c001357f041 100755
--- a/organisms/urls.py
+++ b/organisms/urls.py
@@ -20,6 +20,8 @@ from django.conf.urls import url
 
 from organisms import views
 
+app_name = "organisms"
+
 urlpatterns = [
     # ex: /organisms/                   
     url(r'^$', views.index, name='index'),
diff --git a/requirements.txt b/requirements.txt
index c231a49cb1dac1e18c42deee4a70160926608c34..daa73cc54fc3dc48a307d56699b0a1bafda54b8b 100755
--- a/requirements.txt
+++ b/requirements.txt
@@ -18,10 +18,11 @@
 
 # Please add requirements here
 # Read docs for how this works: http://www.pip-installer.org/en/latest/cookbook.html
-Django==1.11
+Django==3.1.3
 Pillow
-requests==2.6.0
+requests==2.25.0
 django-tinymce==2.8.0
-python-dateutil==1.5
+python-dateutil==2.8.1
 django-extensions
-selenium
\ No newline at end of file
+selenium
+python-memcached
diff --git a/roughage/templates/roughage/nutrition.html b/roughage/templates/roughage/nutrition.html
index 9bb409e50ffb24445dedf1bb584642971bb5278b..037bcc959e048c26cbbeb2f2714f0d2e9eed5e88 100755
--- a/roughage/templates/roughage/nutrition.html
+++ b/roughage/templates/roughage/nutrition.html
@@ -20,7 +20,7 @@
 # 
 # @author: Tor-Einar Skog
 {% endcomment %}
-{% load i18n l10n staticfiles template_helper %}
+{% load i18n l10n static template_helper %}
 {% block title%}{% trans "Roughage nutrition model" %}{%endblock%}
 {% block content %}
 <div class="singleBlockContainer">
diff --git a/roughage/templates/roughage/nutrition_calibration.html b/roughage/templates/roughage/nutrition_calibration.html
index adff50896bed0b6228b331f5e3de7628ae0b5fbd..60c0798cc020c18fbe495789676aecca878f5f9e 100644
--- a/roughage/templates/roughage/nutrition_calibration.html
+++ b/roughage/templates/roughage/nutrition_calibration.html
@@ -19,7 +19,7 @@
 # 
 # @author: Tor-Einar Skog
 {% endcomment %}
-{% load i18n l10n staticfiles template_helper %}
+{% load i18n l10n static template_helper %}
 {% block title%}{% trans "Roughage nutrition model" %}{%endblock%}
 {% block content %}
 <div class="singleBlockContainer">
diff --git a/roughage/urls.py b/roughage/urls.py
index b59277f27a7f6198c59db5d7f320648018a27c2c..36a7163778fba5225c80ed79fd08176b72f8ad93 100755
--- a/roughage/urls.py
+++ b/roughage/urls.py
@@ -20,6 +20,8 @@ from django.conf.urls import url
 
 from roughage import views
 
+app_name = "roughage"
+
 urlpatterns = [
     url(r'^nutrition/calibration/$', views.nutrition_calibration),
     url(r'^nutrition/$', views.nutrition)
diff --git a/security/middleware/check_login_middleware.py b/security/middleware/check_login_middleware.py
index 77ddfbf7848b8007500b1e305d8d7834996cd8d2..e8c856a77b16d8181f52d6da8543cb0042686b20 100755
--- a/security/middleware/check_login_middleware.py
+++ b/security/middleware/check_login_middleware.py
@@ -21,11 +21,20 @@ from security.models import VipsLogicUser
 from django.conf import settings
 from datetime import datetime, timedelta
 import json
+from django.http import HttpResponse
 
 
 class CheckLoginMiddleware(object):
     datetime_format = "%Y-%m-%d %H:%M:%S"
     
+    def __init__(self, get_response):
+        self.get_response = get_response
+        
+    def __call__(self, request):
+        self.process_request(request)
+        return self.get_response(request)
+    
+    
     def process_request(self, request):
         # If UUID is provided, login with VIPSLogic
         # VIPSLogicUser exists in session for 24 hours
@@ -58,7 +67,7 @@ class CheckLoginMiddleware(object):
                 if user_uuid != None:
                     found_user = VipsLogicUser.find_by_uuid(user_uuid)
                     if found_user != None:
-                        print "Found user. Logging in."
+                        #print ("Found user. Logging in.")
                         request.session["vips_logic_user"] = found_user
                         request.session["user_uuid"] = user_uuid
                         request.session["last_modified"] = datetime.now().strftime(CheckLoginMiddleware.datetime_format)
diff --git a/security/urls.py b/security/urls.py
index b594c3aea2dbebc9596e33bbfb029f9043171463..4e2f775711b99529740b961d85d8c4fa3f2a3a52 100755
--- a/security/urls.py
+++ b/security/urls.py
@@ -20,6 +20,8 @@ from django.conf.urls import url
 
 from security import views
 
+app_name = "security"
+
 urlpatterns = [
     url(r'^login/(?P<user_uuid>[^/]+)/$', views.login_user_uuid),
     url(r'^login/$', views.login_form),
diff --git a/security/views.py b/security/views.py
index cfadb888f6c9a8d52e99a69cf5c10d33f207c9af..06983135d8d0d71526ea7b7eacf996b8977b300e 100755
--- a/security/views.py
+++ b/security/views.py
@@ -20,7 +20,7 @@
 from django.http import JsonResponse, HttpResponseRedirect
 from django.shortcuts import render
 from django.conf import settings
-from models import VipsLogicUser
+from security.models import VipsLogicUser
 import requests
 
 # Create your views here.
diff --git a/vips_messages/migrations/0006_auto_20201123_1434.py b/vips_messages/migrations/0006_auto_20201123_1434.py
new file mode 100644
index 0000000000000000000000000000000000000000..b972bd5f408a003a9ab09742bf12191ea1ff62ee
--- /dev/null
+++ b/vips_messages/migrations/0006_auto_20201123_1434.py
@@ -0,0 +1,33 @@
+# Generated by Django 3.0.11 on 2020-11-23 13:34
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('vips_messages', '0005_auto_20200603_1435'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='advertisement',
+            name='ad_heading',
+            field=models.CharField(max_length=200, verbose_name='Overskrift'),
+        ),
+        migrations.AlterField(
+            model_name='advertisement',
+            name='exp_date',
+            field=models.DateField(verbose_name='Dato utløpt'),
+        ),
+        migrations.AlterField(
+            model_name='advertisement',
+            name='illustration',
+            field=models.ImageField(blank=True, upload_to='images/advertisement', verbose_name='Illustrasjon'),
+        ),
+        migrations.AlterField(
+            model_name='advertisement',
+            name='pub_date',
+            field=models.DateField(verbose_name='Dato publisert'),
+        ),
+    ]
diff --git a/vips_messages/models.py b/vips_messages/models.py
index 59a5e34cf68c96262b518457da820db53a6efcb3..16803ab6314b9590a858f01e9d8482a7c1d214b5 100755
--- a/vips_messages/models.py
+++ b/vips_messages/models.py
@@ -192,6 +192,8 @@ class Message:
 class Advertisement(models.Model):
     def __unicode__(self):
         return self.ad_heading
+    def __str__(self):
+        return self.ad_heading
     """ Represents an advertisement to be shown on the frontpage """
     ad_heading = models.CharField(max_length=200, verbose_name=_("Headline"))
     ad_text = models.TextField()
diff --git a/vips_messages/templates/messages/index.html b/vips_messages/templates/messages/index.html
index e754391c5600bc8729775b2010c5db28153c9903..6ea530810c5d04406ce135fecf3dd13fafd2d0a6 100755
--- a/vips_messages/templates/messages/index.html
+++ b/vips_messages/templates/messages/index.html
@@ -1,5 +1,5 @@
 {% extends "base_with_date_picker.html" %}
-{% load i18n staticfiles %}
+{% load i18n static %}
 {% block title%}{% trans "Messages" %}{%endblock%}
 {% block extendCSS %}
 <link href="{% static "css/3rdparty/jquery-ui.min.css" %}" rel="stylesheet" media="screen" />
diff --git a/vips_messages/urls.py b/vips_messages/urls.py
index edc66b169439f04d4e324751ed744668f16120b0..38f1f499a52386887390998b318fc32b7ff3200a 100755
--- a/vips_messages/urls.py
+++ b/vips_messages/urls.py
@@ -20,6 +20,8 @@ from django.conf.urls import url
 
 from vips_messages import views
 
+app_name="vips_messages"
+
 urlpatterns = [
     # ex: /messages/                   
     url(r'^$', views.index, name='index'),