Skip to content

Commit cf30c57

Browse files
author
Andy Babic
committed
Add docs
1 parent f83dff0 commit cf30c57

File tree

1 file changed

+147
-0
lines changed

1 file changed

+147
-0
lines changed

docs/guides/defining-template-context.md

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,150 @@ context:
5757
```
5858
5959
You can define a list or a dict or anything that [`PyYAML`](http://pyyaml.org/wiki/PyYAMLDocumentation) allows you to create in `yaml` format without creating a custom objects.
60+
61+
62+
## Modifying template contexts with Python
63+
64+
While most objects can be faked with `yaml`, Django has a few common constructs that are difficult to replicate. For example: `Form` and `Paginator` instances. To help with this, django-pattern library allows you to register any number of 'context modifiers'. Context modifiers are simply Python functions that accept the `context` dictionary generated from the `yaml` file, and can make additions or updates to it as necessary. For convenience, they also receive the current `HttpRequest` as `request`.
65+
66+
Context modifiers can easily be registered using the `register_context_modifier` decorator. Here is a simple example:
67+
68+
```python
69+
70+
# myproject/core/pattern_contexts.py
71+
72+
from pattern_library import register_context_modifier
73+
from myproject.core.forms import SearchForm, SignupForm
74+
75+
@register_context_modifier
76+
def add_common_forms(context, request):
77+
if 'search_form' not in context:
78+
context["search_form"] = SearchForm()
79+
if 'signup_form' not in context:
80+
context["signup_form"] = SignupForm()
81+
82+
```
83+
84+
Context modifiers are also great for reducing the amount of template tag patching that is needed. The following examples are from a Wagtail project:
85+
86+
```python
87+
88+
# myproject/core/pattern_contexts.py
89+
90+
from django.core.paginator import Paginator
91+
from wagtail.images import get_image_model
92+
from pattern_library import register_context_modifier
93+
94+
95+
@register_context_modifier
96+
def add_page_images(context, request):
97+
"""
98+
Replace some common 'image' field values on pages with real `Image`
99+
instances, so that the {% image %} template tag will work.
100+
"""
101+
Image = get_image_model()
102+
if "page" in context:
103+
if "hero_image" in context["page"]:
104+
context["hero_image"] = Image.objects.all().order("?").first()
105+
if "main_image" in context["page"]:
106+
context["main_image"] = Image.objects.all().order("?").first()
107+
108+
109+
@register_context_modifier
110+
def replicate_pagination(context, request):
111+
"""
112+
Replace lists of items using the 'page_obj.object_list' key
113+
with a real Paginator page, and add a few other pagination-related
114+
things to the context (like Django's `ListView` does).
115+
"""
116+
object_list = context.pop('page_obj.object_list', None)
117+
if object_list is None:
118+
return
119+
120+
original_length = len(object_list)
121+
122+
# add dummy items to force pagination
123+
for i in range(50):
124+
object_list.append(None)
125+
126+
# paginate and add ListView-like values
127+
paginator = Paginator(object_list, original_length)
128+
context.update(
129+
paginator=paginator,
130+
page_obj=paginator.page(1),
131+
is_paginated=True,
132+
object_list=object_list
133+
)
134+
```
135+
136+
### Registering a context modifier for a specific template
137+
138+
By default, context modifiers are applied to all pattern library templates. If you only wish for a context modifier to be applied to a specific pattern, you can use the ``template`` parameter to indicate this. For example:
139+
140+
```python
141+
142+
# myproject/accounts/pattern_contexts.py
143+
144+
from pattern_library import register_context_modifier
145+
from my_app.accounts.forms import SubscribeForm
146+
147+
148+
@register_context_modifier(template="patterns/subscribe/form.html")
149+
def add_subscribe_form(context, request):
150+
"""
151+
Adds an unbount form to 'form.html'
152+
"""
153+
context["form"] = SubscribeForm()
154+
155+
156+
@register_context_modifier(template="patterns/subscribe/form_invalid.html")
157+
def add_invalid_subscribe_form(context, request):
158+
"""
159+
Adds a bound form with invalid data to 'form_invalid.html'
160+
"""
161+
context["form"] = SubscribeForm(data={
162+
"email": 'invalid-email',
163+
"name": ''
164+
})
165+
```
166+
167+
### Controlling the order in which context modifiers are applied
168+
169+
By default, context modifiers are applied in the order they were registered (which can be difficult to predict accross multiple apps), with generic context modifiers being applied first, followed by template-specific ones. If you need to control the order in which a series of context modifiers are applied, you can use the `order` parameter to do this.
170+
171+
In the following example, a generic context modifier is registered with an `order` value of `1`, while others recieve the default value of `0`. Because `1` is higher than `0`, the generic context modifier will be applied **after** the others.
172+
173+
```python
174+
175+
# myproject/sums/pattern_contexts.py
176+
177+
178+
from pattern_library import register_context_modifier
179+
180+
181+
@register_context_modifier(template='patterns/sums/single_number.html')
182+
def add_single_number(context, request):
183+
context['first_number'] = 933
184+
185+
186+
@register_context_modifier(template='patterns/sums/two_numbers.html')
187+
def add_two_numbers(context, request):
188+
context['first_number'] = 125
189+
context['second_number'] = 22
190+
191+
192+
@register_context_modifier(template='patterns/sums/three_numbers.html')
193+
def add_three_numbers(context, request):
194+
context['first_number'] = 125
195+
context['second_number'] = 22
196+
context['third_number'] = 9
197+
198+
199+
@register_context_modifier(order=1)
200+
def add_total(context, request):
201+
if 'total' not in context:
202+
first_num = context.get('first_number', 0)
203+
second_num = context.get('second_number', 0)
204+
third_num = context.get('third_number', 0)
205+
context['total'] = first_num + second_num + third_num
206+
```

0 commit comments

Comments
 (0)