Merge pull request #3419 from FinnStutzenstein/ViewCaching
Index and Webclient View caching
This commit is contained in:
commit
c269ae5cf8
@ -85,6 +85,8 @@ Core:
|
|||||||
- Added custom translations in config [#3383].
|
- Added custom translations in config [#3383].
|
||||||
- Added dynamic webpage title [#3404].
|
- Added dynamic webpage title [#3404].
|
||||||
- Added 'go to top'-link [#3404].
|
- Added 'go to top'-link [#3404].
|
||||||
|
- Added caching for the index views. When using a Webserver for serving
|
||||||
|
static files see the example configuration in the PR [#3419].
|
||||||
|
|
||||||
Mediafiles:
|
Mediafiles:
|
||||||
- Fixed reloading of PDF on page change [#3274].
|
- Fixed reloading of PDF on page change [#3274].
|
||||||
|
@ -3,11 +3,10 @@ import uuid
|
|||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
from typing import Any, Dict, List # noqa
|
from typing import Any, Dict, List, cast # noqa
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.staticfiles import finders
|
|
||||||
from django.db.models import F
|
from django.db.models import F
|
||||||
from django.http import Http404, HttpResponse
|
from django.http import Http404, HttpResponse
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
@ -54,7 +53,7 @@ from .models import (
|
|||||||
|
|
||||||
# Special Django views
|
# Special Django views
|
||||||
|
|
||||||
class IndexView(utils_views.CSRFMixin, utils_views.View):
|
class IndexView(utils_views.CSRFMixin, utils_views.IndexView):
|
||||||
"""
|
"""
|
||||||
The primary view for OpenSlides using AngularJS.
|
The primary view for OpenSlides using AngularJS.
|
||||||
|
|
||||||
@ -62,28 +61,20 @@ class IndexView(utils_views.CSRFMixin, utils_views.View):
|
|||||||
You can override it by simply adding a custom 'templates/index.html' file
|
You can override it by simply adding a custom 'templates/index.html' file
|
||||||
to the custom staticfiles directory. See STATICFILES_DIRS in settings.py.
|
to the custom staticfiles directory. See STATICFILES_DIRS in settings.py.
|
||||||
"""
|
"""
|
||||||
|
template_name = 'templates/index.html'
|
||||||
def get(self, *args, **kwargs):
|
|
||||||
with open(finders.find('templates/index.html')) as f:
|
|
||||||
content = f.read()
|
|
||||||
return HttpResponse(content)
|
|
||||||
|
|
||||||
|
|
||||||
class ProjectorView(utils_views.View):
|
class ProjectorView(utils_views.IndexView):
|
||||||
"""
|
"""
|
||||||
The primary view for OpenSlides projector using AngularJS.
|
The primary view for OpenSlides projector using AngularJS.
|
||||||
|
|
||||||
The projector container template is 'openslides/core/static/templates/projector-container.html'.
|
The projector container template is 'openslides/core/static/templates/projector-container.html'.
|
||||||
This container is for controlling the projector resolution.
|
This container is for controlling the projector resolution.
|
||||||
"""
|
"""
|
||||||
|
template_name = 'templates/projector-container.html'
|
||||||
def get(self, *args, **kwargs):
|
|
||||||
with open(finders.find('templates/projector-container.html')) as f:
|
|
||||||
content = f.read()
|
|
||||||
return HttpResponse(content)
|
|
||||||
|
|
||||||
|
|
||||||
class RealProjectorView(utils_views.View):
|
class RealProjectorView(utils_views.IndexView):
|
||||||
"""
|
"""
|
||||||
The original view without resolutioncontrol for OpenSlides projector using AngularJS.
|
The original view without resolutioncontrol for OpenSlides projector using AngularJS.
|
||||||
|
|
||||||
@ -92,11 +83,7 @@ class RealProjectorView(utils_views.View):
|
|||||||
file to the custom staticfiles directory. See STATICFILES_DIRS in
|
file to the custom staticfiles directory. See STATICFILES_DIRS in
|
||||||
settings.py.
|
settings.py.
|
||||||
"""
|
"""
|
||||||
|
template_name = 'templates/projector.html'
|
||||||
def get(self, *args, **kwargs):
|
|
||||||
with open(finders.find('templates/projector.html')) as f:
|
|
||||||
content = f.read()
|
|
||||||
return HttpResponse(content)
|
|
||||||
|
|
||||||
|
|
||||||
class WebclientJavaScriptView(utils_views.View):
|
class WebclientJavaScriptView(utils_views.View):
|
||||||
@ -105,10 +92,15 @@ class WebclientJavaScriptView(utils_views.View):
|
|||||||
AngularJS app for the requested realm (site or projector). Also code
|
AngularJS app for the requested realm (site or projector). Also code
|
||||||
for plugins is appended. The result is not uglified.
|
for plugins is appended. The result is not uglified.
|
||||||
"""
|
"""
|
||||||
def get(self, *args, **kwargs):
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||||
angular_modules = []
|
super().__init__(*args, **kwargs)
|
||||||
|
self.cache = {} # type: Dict[str, str]
|
||||||
|
self.init_cache('site')
|
||||||
|
self.init_cache('projector')
|
||||||
|
|
||||||
|
def init_cache(self, realm: str) -> None:
|
||||||
|
angular_modules = [] # type: List[str]
|
||||||
js_files = [] # type: List[str]
|
js_files = [] # type: List[str]
|
||||||
realm = kwargs.get('realm') # Result is 'site' or 'projector'
|
|
||||||
for app_config in apps.get_app_configs():
|
for app_config in apps.get_app_configs():
|
||||||
# Add the angular app if the module has one.
|
# Add the angular app if the module has one.
|
||||||
if getattr(app_config, 'angular_{}_module'.format(realm), False):
|
if getattr(app_config, 'angular_{}_module'.format(realm), False):
|
||||||
@ -187,8 +179,11 @@ class WebclientJavaScriptView(utils_views.View):
|
|||||||
"""
|
"""
|
||||||
}());
|
}());
|
||||||
""")
|
""")
|
||||||
|
self.cache[realm] = content
|
||||||
|
|
||||||
return HttpResponse(content, content_type='application/javascript')
|
def get(self, *args: Any, **kwargs: Any) -> HttpResponse:
|
||||||
|
realm = cast(str, kwargs.get('realm')) # Result is 'site' or 'projector'
|
||||||
|
return HttpResponse(self.cache[realm], content_type='application/javascript')
|
||||||
|
|
||||||
|
|
||||||
# Viewsets for the REST API
|
# Viewsets for the REST API
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
from typing import Any, Dict, List # noqa
|
from typing import Any, Dict, List # noqa
|
||||||
|
|
||||||
|
from django.contrib.staticfiles import finders
|
||||||
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
|
from django.http import HttpResponse
|
||||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||||
from django.views.generic.base import View
|
from django.views.generic.base import View
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
@ -45,3 +48,22 @@ class APIView(_APIView):
|
|||||||
# Add the http-methods and delete the method "method_call"
|
# Add the http-methods and delete the method "method_call"
|
||||||
get = post = put = patch = delete = head = options = trace = method_call
|
get = post = put = patch = delete = head = options = trace = method_call
|
||||||
del method_call
|
del method_call
|
||||||
|
|
||||||
|
|
||||||
|
class IndexView(View):
|
||||||
|
"""
|
||||||
|
A view to serve a single cached template file. Subclasses has to provide 'template_name'.
|
||||||
|
"""
|
||||||
|
template_name = None # type: str
|
||||||
|
|
||||||
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
if self.template_name is None:
|
||||||
|
raise ImproperlyConfigured("'template_name' is not provided")
|
||||||
|
|
||||||
|
with open(finders.find(self.template_name)) as template:
|
||||||
|
self.template = template.read()
|
||||||
|
|
||||||
|
def get(self, *args: Any, **kwargs: Any) -> HttpResponse:
|
||||||
|
return HttpResponse(self.template)
|
||||||
|
@ -169,10 +169,11 @@ class ProjectorAPI(TestCase):
|
|||||||
class WebclientJavaScriptView(TestCase):
|
class WebclientJavaScriptView(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.request = MagicMock()
|
self.request = MagicMock()
|
||||||
self.view_instance = views.WebclientJavaScriptView()
|
|
||||||
self.view_instance.request = self.request
|
|
||||||
|
|
||||||
@patch('django.contrib.auth.models.Permission.objects.all')
|
@patch('django.contrib.auth.models.Permission.objects.all')
|
||||||
def test_permissions_as_constant(self, mock_all):
|
def test_permissions_as_constant(self, mock_all):
|
||||||
self.view_instance.get()
|
self.view_instance = views.WebclientJavaScriptView()
|
||||||
self.assertEqual(mock_all.call_count, 1)
|
self.view_instance.request = self.request
|
||||||
|
response = self.view_instance.get(realm='site')
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(mock_all.call_count, 2)
|
||||||
|
Loading…
Reference in New Issue
Block a user