From cc38634519d3e6f5fb6510cddc430886bc91da49 Mon Sep 17 00:00:00 2001 From: Michael van Tellingen Date: Wed, 31 May 2017 11:59:25 +0200 Subject: [PATCH] Add adapters.get_segment_adapter to set the segment adapter on the request object. Previously the segment adapter was set globally with the request as attribute. This causes thread safety issues. Instead we now initialise the segment adapter for each request and set it as attribute --- README.rst | 17 ++++++++- src/wagtail_personalisation/adapters.py | 37 ++++++++++++++----- src/wagtail_personalisation/app_settings.py | 9 ----- .../templatetags/datalayer_tags.py | 18 +++++---- src/wagtail_personalisation/wagtail_hooks.py | 9 +++-- 5 files changed, 59 insertions(+), 31 deletions(-) delete mode 100644 src/wagtail_personalisation/app_settings.py diff --git a/README.rst b/README.rst index d2cda70..4753a81 100644 --- a/README.rst +++ b/README.rst @@ -51,7 +51,22 @@ Next, include the ``wagtail_personalisation`` and ] Make sure that ``django.contrib.sessions.middleware.SessionMiddleware`` has -been added in first. +been added in first, this is a prerequisite for this project. + + +Changing segments adapter +------------------------- +To change the segments adapter, first make a new one based on the +``BaseSegmentsAdapter`` + +.. code-block:: python + + class YourSegmentsAdapter(BaseSegmentsAdapter): + # Add your own logic here + +Add the ``PERSONALISATION_SEGMENTS_ADAPTER`` setting to your settings.py and +choose your own adapter. + Sandbox ------- diff --git a/src/wagtail_personalisation/adapters.py b/src/wagtail_personalisation/adapters.py index ee571c7..55afea3 100644 --- a/src/wagtail_personalisation/adapters.py +++ b/src/wagtail_personalisation/adapters.py @@ -1,6 +1,8 @@ from __future__ import absolute_import, unicode_literals +from django.conf import settings from django.db.models import F +from django.utils.module_loading import import_string from wagtail_personalisation.models import Segment from wagtail_personalisation.rules import AbstractBaseRule @@ -10,6 +12,15 @@ from wagtail_personalisation.utils import create_segment_dictionary class BaseSegmentsAdapter(object): """Base segments adapter.""" + def __init__(self, request): + """Prepare the request session for segment storage. + + :param request: The http request + :type request: django.http.HttpRequest + + """ + self.request = request + def setup(self): """Prepare the adapter for segment storage.""" return None @@ -64,15 +75,8 @@ class BaseSegmentsAdapter(object): class SessionSegmentsAdapter(BaseSegmentsAdapter): """Segment adapter that uses Django's session backend.""" - def setup(self, request): - """Prepare the request session for segment storage. - - :param request: The http request - :type request: django.http.HttpRequest - - """ - self.request = request - + def __init__(self, request): + super(SessionSegmentsAdapter, self).__init__(request) self.request.session.setdefault('segments', []) def get_all_segments(self): @@ -166,3 +170,18 @@ class SessionSegmentsAdapter(BaseSegmentsAdapter): self.request.session['segments'] = new_segments self.update_visit_count() + + +SEGMENT_ADAPTER_CLASS = import_string(getattr( + settings, + 'PERSONALISATION_SEGMENTS_ADAPTER', + 'wagtail_personalisation.adapters.SessionSegmentsAdapter')) + + +def get_segment_adapter(request): + """Return the Segment Adapter for the given request""" + try: + return request.segment_adapter + except AttributeError: + request.segment_adapter = SEGMENT_ADAPTER_CLASS(request) + return request.segment_adapter diff --git a/src/wagtail_personalisation/app_settings.py b/src/wagtail_personalisation/app_settings.py deleted file mode 100644 index cfcf84d..0000000 --- a/src/wagtail_personalisation/app_settings.py +++ /dev/null @@ -1,9 +0,0 @@ -from django.conf import settings -from django.utils.module_loading import import_string - -# Create a setting for the segments adapter to allow -# overwriting of the provided adapter's functionality. -segments_adapter = import_string(getattr( - settings, - 'PERSONALISATION_SEGMENTS_ADAPTER', - 'wagtail_personalisation.adapters.SessionSegmentsAdapter'))() diff --git a/src/wagtail_personalisation/templatetags/datalayer_tags.py b/src/wagtail_personalisation/templatetags/datalayer_tags.py index f5d7a64..ccca56f 100644 --- a/src/wagtail_personalisation/templatetags/datalayer_tags.py +++ b/src/wagtail_personalisation/templatetags/datalayer_tags.py @@ -1,16 +1,18 @@ from django.template import Library -from wagtail_personalisation.app_settings import segments_adapter +from wagtail_personalisation.adapters import get_segment_adapter register = Library() -@register.inclusion_tag('wagtail_personalisation/tags/datalayer.html') -def render_datalayer(): +@register.inclusion_tag('wagtail_personalisation/tags/datalayer.html', takes_context=True) +def render_datalayer(context): """Render the sessions active segments in a data layer script tag.""" - segments = segments_adapter.get_all_segments() - segment_names = [item['encoded_name'] for item in segments] + request = context.get('request') + if request: + segments = get_segment_adapter(request).get_all_segments() + segment_names = [item['encoded_name'] for item in segments] - return { - 'segments': segment_names - } + return { + 'segments': segment_names + } diff --git a/src/wagtail_personalisation/wagtail_hooks.py b/src/wagtail_personalisation/wagtail_hooks.py index 47843c3..cb66a82 100644 --- a/src/wagtail_personalisation/wagtail_hooks.py +++ b/src/wagtail_personalisation/wagtail_hooks.py @@ -11,7 +11,7 @@ from wagtail.wagtailadmin.widgets import Button, ButtonWithDropdownFromHook from wagtail.wagtailcore import hooks from wagtail_personalisation import admin_urls -from wagtail_personalisation.app_settings import segments_adapter +from wagtail_personalisation.adapters import get_segment_adapter from wagtail_personalisation.models import PersonalisablePage, Segment from wagtail_personalisation.utils import impersonate_other_page @@ -87,8 +87,8 @@ def segment_user(page, request, serve_args, serve_kwargs): :type request: django.http.HttpRequest """ - segments_adapter.setup(request) - segments_adapter.refresh() + adapter = get_segment_adapter(request) + adapter.refresh() @hooks.register('before_serve_page') @@ -105,8 +105,9 @@ def serve_variation(page, request, serve_args, serve_kwargs): """ user_segments = [] + adapter = get_segment_adapter(request) - for segment in segments_adapter.get_all_segments(): + for segment in adapter.get_all_segments(): try: user_segment = Segment.objects.get(pk=segment['id'], status='enabled')