Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#240 Page views and Page context classes #262

Merged
merged 6 commits into from
Feb 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion catalog/newcontext/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
# @todo #207:15m Annotate methods of Context implementations.

from . import category, products, tags
from .context import Context, Contexts, ModelContext, Products, Tags
from .context import Context, ModelContext, Products, Tags
2 changes: 1 addition & 1 deletion catalog/newcontext/category.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from catalog.models import AbstractCategory
from catalog.newcontext.context import Context as BaseContext
from pages.newcontext import Context as BaseContext


class Context(BaseContext):
Expand Down
22 changes: 2 additions & 20 deletions catalog/newcontext/context.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import abc
import collections

from catalog import typing


class Context(abc.ABC):
from catalog import typing

@abc.abstractmethod
def context(self) -> typing.Dict[str, typing.Any]:
...
from pages.newcontext import Context


class ModelContext(abc.ABC):
Expand All @@ -27,19 +22,6 @@ def context(self) -> typing.Dict[str, typing.Any]:
Context.register(ModelContext)


class Contexts(Context):

# use context as list, not as args pack (`*context`)
# to move attention on homogeneous nature of args
def __init__(self, contexts: typing.List[Context]):
self.contexts = contexts or []

def context(self):
return dict(collections.ChainMap(
*[ctx.context() for ctx in self.contexts]
))


class Tags(ModelContext):

def context(self):
Expand Down
42 changes: 42 additions & 0 deletions pages/display.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""
Views processing db based templates.
This view are outside of MTV concept.
Responsible only for rendering given context data with db preserved text template.
"""
import typing

from pages import models


class Fields:
# Fields stored in DB. See class `pages.models.PageTemplate`
STORED = ['name', 'h1', 'keywords', 'description', 'title', 'seo_text']

def __init__(self, page_display: 'Page'):
self.page_display = page_display

def __getattr__(self, item):
if item in self.STORED:
return self.page_display.render(item)
else:
return super().__getattribute__(item)


class Page:
# @todo #240:30m Create usage doc for page view.

def __init__(self, page: models.Page, context: typing.Dict[str, typing.Any]):
"""
Pass context at ctor, but not render method,
because client code wants the same context for many different cases.
"""
self.page = page
self.context = context
self.fields = Fields(self)

def render(self, field: str):
return (
self.page.template.render_field(field, self.context)
if self.page.template
else getattr(self.page, field)
)
10 changes: 5 additions & 5 deletions pages/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ class Meta:
def __str__(self):
return self.name

def render(self, field, context):
return render_str(field, context)
def render_field(self, field: str, context: dict) -> str:
return render_str(getattr(self, field), context)


class PageQuerySet(mptt.querysets.TreeQuerySet):
Expand Down Expand Up @@ -211,9 +211,9 @@ def display_attribute(self, name):
if not self.template:
return getattr(self, name) or self.name

return self.template.render(
getattr(self.template, name),
self.get_template_render_context(),
return self.template.render_field(
field=name,
context=self.get_template_render_context(),
)

@property
Expand Down
3 changes: 3 additions & 0 deletions pages/newcontext/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import base, pages
from .base import Context, Contexts
from .pages import Page
23 changes: 23 additions & 0 deletions pages/newcontext/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import abc
import collections
import typing


class Context(abc.ABC):

@abc.abstractmethod
def context(self) -> typing.Dict[str, typing.Any]:
...


class Contexts(Context):

# use context as list, not as args pack (`*context`)
# to move attention on homogeneous nature of args
def __init__(self, contexts: typing.List[Context]):
self.contexts = contexts or []

def context(self):
return dict(collections.ChainMap(
*[ctx.context() for ctx in self.contexts]
))
12 changes: 12 additions & 0 deletions pages/newcontext/pages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from .base import Context


class Page(Context):

def __init__(self, page):
self.page = page

def context(self):
return {
'page': self.page,
}
7 changes: 5 additions & 2 deletions pages/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
from django.template import engines


def render_str(text, data):
# @todo #240:30m Create TextView class.
# Instead of render_str method.
# And inherit `pages.db_views.Page` from this class.
def render_str(template: str, context: dict):
django_engine = engines['django']
return django_engine.from_string(text).render(data)
return django_engine.from_string(template).render(context)


def save_custom_pages():
Expand Down
13 changes: 13 additions & 0 deletions tests/pages/test_display.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from django.test import TestCase

from pages import display


class TestFields(TestCase):
fixtures = []

def test_attribute_error(self):
# noinspection PyTypeChecker
page = display.Page(None, {})
with self.assertRaises(AttributeError):
_ = page.fields.bad_attr
24 changes: 17 additions & 7 deletions tests/pages/test_models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.test import TestCase

from pages import display
from pages.models import ModelPage, CustomPage, FlatPage, Page, PageTemplate

from tests.models import MockEntity, MockEntityWithSync
Expand Down Expand Up @@ -100,34 +101,43 @@ def test_slug_should_auto_generate(self):

self.assertTrue(page.slug)

# @todo #240:30m Improve DB templates (and views) tests.
# Move them to separated module.
# Rename theirs `test_display` prefix.
# Separate them on small pieces.
# Add test for `display.Page` with passing context.
def test_display_seo_fields(self):
page_with_custom_fields = Page.objects.create(
name='some page', slug='test', h1='test h1'
)
self.assertEqual(page_with_custom_fields.display_h1, 'test h1')
page_view = display.Page(page_with_custom_fields, {})
self.assertEqual(page_view.fields.h1, 'test h1')

custom_page_template = PageTemplate.objects.create(
name='test',
h1='{{ page.name }} - купить в СПб',
)

page_with_template = Page.objects.create(
page = Page.objects.create(
name='different page', template=custom_page_template
)

self.assertEqual(page_with_template.display_h1, 'different page - купить в СПб')
page_view = display.Page(page, {'page': page})

self.assertEqual(page_view.fields.h1, 'different page - купить в СПб')

def test_display_attribute_uses_template(self):
custom_page_template = PageTemplate.objects.create(
template = PageTemplate.objects.create(
name='test',
h1='{{ page.h1 }} - template',
)
page_with_template = Page.objects.create(
page = Page.objects.create(
name='different page',
h1='page h1',
template=custom_page_template,
template=template,
)
self.assertEqual(page_with_template.display_h1, 'page h1 - template')
page_view = display.Page(page, {'page': page})
self.assertEqual(page_view.fields.h1, 'page h1 - template')


class TestCustomPage(TestCase):
Expand Down