In the course of this narrative we will demonstrate different usage scenarios. The example test setup contains the following files:
./skins/index.pt
./skins/main_template.pt
./skins/images/logo.png
./skins/about/index.pt
./skins/about/images/logo.png
↳ mount point
To explain this setup, imagine that the index.pt template represents some page in the site (e.g. the front page); it uses main_template.pt as the o-wrap template. The about directory represents some editorial about-section where about/index.pt is the index page. This section provides its own logo.
We begin by registering the directory. This makes the files listed above available as skin components. The ZCML-directive skins makes registration easy:
<configure xmlns="http://pylonshq.com/pyramid">
<include package="pyramid_skins" />
<skins path="skins" />
</configure>
The path parameter indicates a relative path which defines the mount point for the skin registration.
At this point the skin objects are available as utility components. This is the low-level interface:
from zope.component import getUtility
from pyramid_skins.interfaces import ISkinObject
index = getUtility(ISkinObject, name="index")
The component name is available in the name attribute:
index.name
We now move up one layer and consider the skin components as objects.
The SkinObject class itself wraps the low-level utility lookup:
from pyramid_skins import SkinObject
FrontPage = SkinObject("index")
This object is a callable which will render the template to a response (it could be an image, stylesheet or some other resource type). In the case of templates, the first two positional arguments (if given) are mapped to context and request. These symbols are available for use in the template.
response = FrontPage(u"Hello world!")
The index template simply inserts the context value into the body tag of the HTML document:
<html>
<body>
Hello world!
</body>
</html>
The exact same approach works for the logo object:
from pyramid_skins import SkinObject
logo = SkinObject("images/logo.png")
Calling the logo object returns an HTTP response:
200 OK
Instead of global utility skin components, we can provide a request type:
<configure xmlns="http://pylonshq.com/pyramid">
<include package="pyramid_skins" />
<skins path="skins" request_type="pyramid.interfaces.IRequest" />
</configure>
The skin component is now registered as a named adapter on the request:
>>> from pyramid.testing import DummyRequest
>>> request = DummyRequest()
We use the getAdapter call:
>>> from zope.component import getAdapter
>>> getAdapter(request, ISkinObject, name="index")
<pyramid_skins.models.SkinTemplate name="index" path=".../skins/index.pt" at ...>
The call method signature for skin templates is (context, request). This is the same as views. That is, we can use skin template objects directly as view callables:
<view name="frontpage1" view=".FrontPage" />
In Pyramid we can also define a view using a class which provides __init__ and __call__. The call method must return a response. With skin objects, we can express it this way:
class FrontPageView(object):
__call__ = SkinObject("index")
def __init__(self, context, request):
self.context = context
self.request = request
When the __call__ attribute is accessed (as a descriptor), a view callable is returned, bound to the view’s instance dictionary (which in this case has the symbols context and request):
<div id="view-${type(view).__name__.lower()}">
<a href="${request.route_url('search')">Search</a>
<p class="content">
${context}
</p>
</div>
Note that methods are not bound.
<view name=”frontpage2” view=”.FrontPageView” />
While the two patterns are equivalent, using a view allows you to prepare data for the template. Both yield the exact same output when passed 'Hello world!' as the view context:
<html>
<body>
Hello world!
</body>
</html>
The package comes with a renderer factory for skin objects. It looks up a skin object based on view name.
Renders a skin object based on view name.
In your application setup, use the add_renderer method:
config.add_renderer('skin', renderer_factory)
Example view:
@view_config(name='index', renderer='skin')
def index(request):
return {'title': 'My application'}
In some scenarios, it’s useful to be able to discover skin objects at run-time. An example is when you use skins to publish editorial content which is added to the file system.
The discovery parameter takes a boolean argument, e.g. True:
<configure xmlns="http://pylonshq.com/pyramid">
<skins path="skins" discovery="True" />
</configure>
Let’s add a new skin template with the source:
<div>Hello world!</div>
If you prefer imperative configuration over declarative you can use the pyramid_skins.configuration.register_path method for configuration:
Add a skin path to the current configuration state.
If discovery is enabled, the path will automatically be monitored for changes.
The indexes argument is an optional list of view registrations with the provided names.
The request_type option decides the request type for which to make the registration.
Example:
from pyramid.config import Configurator
config = Configurator()
from pyramid_skins.configuration import register_path
register_path(config, path)