diff --git a/pages/display.py b/pages/display.py index dfbbdd3..b193e98 100644 --- a/pages/display.py +++ b/pages/display.py @@ -5,8 +5,6 @@ """ import typing -from collections import defaultdict - class Page: """It's python Descriptor.""" @@ -16,48 +14,24 @@ class Page: # Fields stored in DB. See class `pages.models.PageTemplate` STORED = ['name', 'h1', 'keywords', 'description', 'title', 'seo_text'] - def __init__( - self, - context: typing.Dict[str, typing.Any]=None, - *, - _page: 'pages.models.Page'=None, - _name='', - ): + def __init__(self, page: 'pages.models.Page'=None, context: typing.Dict[str, typing.Any]=None): """ Pass context at ctor, but not render method, because client code wants the same context for many different cases. """ + self._page = page + self.key = '' self._context = context or {} - self._page = _page - self._key = _name - - def __set_name__(self, instance, name): - # get `Page` instance to create new `Page` instance with `name` - page = getattr(instance, name) - page_with_name = Page(page._context, _page=page._page, _name=name) - # Set new `Page` instance with name - setattr(instance, name, page_with_name) def __get__(self, instance: 'pages.models.Page', type_): - if instance and self._key in instance.__dict__: - return instance.__dict__[self._key] return Page( - {'page': instance, **self._context}, - _page=instance, - _name=self._key, + instance, + {'page': instance, **instance.__dict__.get(self.key, {})} ) def __set__(self, instance: 'pages.models.Page', value: typing.Dict[str, typing.Any]): - if not isinstance(value, dict): - raise ValueError(f'Value should be a dict') - - if self._key in instance.__dict__: - # merge old context with new one - context = {**instance.__dict__[self._key]._context, **value} - else: - context = {**self._context, **value} - - instance.__dict__[self._key] = Page(context, _page=instance, _name=self._key) + self._page = instance + instance.__dict__[self.key] = value def __getattr__(self, item): if item in self.STORED: @@ -65,6 +39,9 @@ def __getattr__(self, item): else: return super().__getattribute__(item) + def __set_name__(self, owner, name): + self.key = f'_{name}_value' + def render(self, field: str): return ( self._page.template.render_field(field, context=self._context) diff --git a/tests/pages/test_display.py b/tests/pages/test_display.py index 50a4eb8..d5800eb 100644 --- a/tests/pages/test_display.py +++ b/tests/pages/test_display.py @@ -8,6 +8,6 @@ class TestFields(TestCase): def test_attribute_error(self): # noinspection PyTypeChecker - page = display.Page({}) + page = display.Page(None, {}) with self.assertRaises(AttributeError): _ = page.bad_attr diff --git a/tests/pages/test_models.py b/tests/pages/test_models.py index d5129b1..9db5db9 100644 --- a/tests/pages/test_models.py +++ b/tests/pages/test_models.py @@ -132,8 +132,6 @@ def test_display_attribute_uses_template(self): ) self.assertEqual(page.display.h1, 'page h1 - template') - # @todo #SE742:30m Fix display issue with shared context. - # See the test below for details. def test_display_has_unique_context(self): """Two different pages should contain not overlapping display contexts.""" left_template = PageTemplate.objects.create(name='left', h1='{{ tag }}') @@ -146,6 +144,21 @@ def test_display_has_unique_context(self): self.assertNotEqual(left.display.h1, right.display.h1) + def test_display_has_unique_template(self): + """Two different pages should contain not overlapping display contexts.""" + left_template = PageTemplate.objects.create(name='left', h1='{{ tag }}') + right_template = PageTemplate.objects.create( + name='right', h1='different {{ tag }}' + ) + left = Page.objects.create(name='left', template=left_template) + right = Page.objects.create(name='right', template=right_template) + + left.template.h1 = '{{ tag }}' + right.template.h1 = 'different {{ tag }}' + left.display, right.display = {'tag': 'A'}, {'tag': 'A'} + + self.assertNotEqual(left.display.h1, right.display.h1) + class TestCustomPage(TestCase): def test_should_get_only_custom_type_pages(self):