跳转到主要内容

实现高级页面模板模式的包。

项目描述

z3c.template

Latest release Supported Python versions https://github.com/zopefoundation/z3c.template/actions/workflows/tests.yml/badge.svg https://coveralls.io/repos/github/zopefoundation/z3c.template/badge.svg?branch=master

此包允许您独立于视图代码注册模板。

在Zope 3中,注册browser:page时,表示和计算都会一起注册。不幸的是,注册将表示和计算紧密结合,以至于无法根据上下文重新注册不同的模板。(您可以覆盖整个注册,但这不是此包的主要目的。)

使用z3c.template,注册在视图和模板之间分开,允许根据皮肤层和视图区分模板。

此外,此包为区分提供特定表示模板和通用布局模板的模板奠定了基础。

Z3C模板

此包允许我们分离视图代码和布局的注册。

模板用于将HTML部分从视图中分离。这在z3中通过页面模板完成。这些页面模板在视图中实现,注册并包含在页面指令中等。但是,它们不使用适配器模式,这使得替换现有模板变得困难。

模板的另一部分是,它们通常将一个部分(来自视图的内容)与另一个部分(用于内容模板的布局)分开。

这个包如何简化模板的使用呢?

可以将模板注册为适配器,以适应上下文和请求,其中上下文是一个视图实现。如果需要模板,则从视图中进行适配。这种适配使得它非常模块化和可插拔。

我们提供了两个基本模板指令来注册生成内容的模板和生成布局的模板。这通常是足够的,但您也可以使用特定接口注册不同类型的模板。如果您视图实现需要在一个以上的模板中分离HTML,这可能很有用。现在让我们看看我们如何使用这些模板。

内容模板

首先,让我们看看我们如何使用模板从视图中生成内容

>>> import os, tempfile
>>> temp_dir = tempfile.mkdtemp()
>>> contentTemplate = os.path.join(temp_dir, 'contentTemplate.pt')
>>> with open(contentTemplate, 'w') as file:
...     _ = file.write('<div>demo content</div>')

注册一个实现接口的视图类

>>> import zope.interface
>>> from z3c.template import interfaces
>>> from zope.pagetemplate.interfaces import IPageTemplate
>>> from zope.publisher.browser import BrowserPage
>>> class IMyView(zope.interface.Interface):
...     pass
>>> @zope.interface.implementer(IMyView)
... class MyView(BrowserPage):
...     template = None
...     def render(self):
...         if self.template is None:
...             template = zope.component.getMultiAdapter(
...                 (self, self.request), interfaces.IContentTemplate)
...             return template(self)
...         return self.template()

让我们调用视图并检查输出

>>> from zope.publisher.browser import TestRequest
>>> request = TestRequest()
>>> view = MyView(root, request)

由于模板尚未注册,渲染视图将失败

>>> print(view.render())
Traceback (most recent call last):
...
zope.interface.interfaces.ComponentLookupError: ......

现在让我们注册模板(通常使用ZCML完成)

>>> from zope import component
>>> from zope.publisher.interfaces.browser import IDefaultBrowserLayer
>>> from z3c.template.template import TemplateFactory

模板工厂允许我们创建ViewPageTemplateFile实例。

>>> factory = TemplateFactory(contentTemplate, 'text/html')
>>> factory
<z3c.template.template.TemplateFactory object at ...>

我们在视图接口和层上注册该工厂。

>>> component.provideAdapter(
...     factory,
...     (zope.interface.Interface, IDefaultBrowserLayer),
...     interfaces.IContentTemplate)
>>> template = component.getMultiAdapter((view, request),
...     interfaces.IPageTemplate)
>>> template
<...ViewPageTemplateFile...>

现在我们为默认层注册了一个已注册的布局模板,我们可以再次调用我们的视图。

>>> print(view.render())
<div>demo content</div>

现在我们在视图的特定接口上注册一个新的模板。

>>> myTemplate = os.path.join(temp_dir, 'myTemplate.pt')
>>> with open(myTemplate, 'w') as file:
...     _ = file.write('<div>My content</div>')
>>> factory = TemplateFactory(myTemplate, 'text/html')
>>> component.provideAdapter(
...     factory,
...     (IMyView, IDefaultBrowserLayer), interfaces.IContentTemplate)
>>> print(view.render())
<div>My content</div>

可以直接提供模板。

我们创建一个新的模板。

>>> viewContent = os.path.join(temp_dir, 'viewContent.pt')
>>> with open(viewContent, 'w') as file:
...     _ = file.write('<div>view content</div>')

并创建一个视图

>>> from z3c.template import ViewPageTemplateFile
>>> @zope.interface.implementer(IMyView)
... class MyViewWithTemplate(BrowserPage):
...     template = ViewPageTemplateFile(viewContent)
...     def render(self):
...         if self.template is None:
...             template = zope.component.getMultiAdapter(
...                 (self, self.request), interfaces.IContentTemplate)
...             return template(self)
...         return self.template()
>>> contentView = MyViewWithTemplate(root, request)

如果我们渲染这个视图,我们会得到实现的布局模板而不是注册的模板。

>>> print(contentView.render())
<div>view content</div>

布局模板

首先,我们需要注册一个新的视图类,调用布局模板。注意,这个视图使用__call__方法来调用布局模板

>>> class ILayoutView(zope.interface.Interface):
...     pass
>>> @zope.interface.implementer(ILayoutView)
... class LayoutView(BrowserPage):
...     layout = None
...     def __call__(self):
...         if self.layout is None:
...             layout = zope.component.getMultiAdapter(
...                 (self, self.request), interfaces.ILayoutTemplate)
...             return layout(self)
...         return self.layout()
>>> view2 = LayoutView(root, request)

定义和注册一个新的布局模板

>>> layoutTemplate = os.path.join(temp_dir, 'layoutTemplate.pt')
>>> with open(layoutTemplate, 'w') as file:
...     _ = file.write('<div>demo layout</div>')
>>> factory = TemplateFactory(layoutTemplate, 'text/html')

我们在视图接口和层上注册模板工厂,提供ILayoutTemplate接口。

>>> component.provideAdapter(factory,
...     (zope.interface.Interface, IDefaultBrowserLayer),
...      interfaces.ILayoutTemplate)
>>> layout = component.getMultiAdapter(
...     (view2, request), interfaces.ILayoutTemplate)
>>> layout
<...ViewPageTemplateFile...>

现在我们为默认层注册了一个已注册的布局模板,我们可以再次调用我们的视图。

>>> print(view2())
<div>demo layout</div>

现在我们在视图的特定接口上注册一个新的布局模板。

>>> myLayout = os.path.join(temp_dir, 'myLayout.pt')
>>> with open(myLayout, 'w') as file:
...     _ = file.write('<div>My layout</div>')
>>> factory = TemplateFactory(myLayout, 'text/html')
>>> component.provideAdapter(factory,
...     (ILayoutView, IDefaultBrowserLayer),
...      interfaces.ILayoutTemplate)
>>> print(view2())
<div>My layout</div>

可以直接提供布局模板。

我们创建一个新的模板。

>>> viewLayout = os.path.join(temp_dir, 'viewLayout.pt')
>>> with open(viewLayout, 'w') as file:
...     _ = file.write('''<div>view layout</div>''')
>>> @zope.interface.implementer(ILayoutView)
... class LayoutViewWithLayoutTemplate(BrowserPage):
...     layout = ViewPageTemplateFile(viewLayout)
...     def __call__(self):
...         if self.layout is None:
...             layout = zope.component.getMultiAdapter((self, self.request),
...                 interfaces.ILayoutTemplate)
...             return layout(self)
...         return self.layout()
>>> layoutView = LayoutViewWithLayoutTemplate(root, request)

如果我们渲染这个视图,我们会得到实现的布局模板而不是注册的模板。

>>> print(layoutView())
<div>view layout</div>

由于我们在上面的示例视图中返回布局模板,我们如何从使用的视图中获取内容?这并不是这个包的直接部分,但让我们展示一些可以在使用的布局模板中渲染内容的模式。注意,由于我们为每个特定视图注册了每个布局模板,您可以选择性地选择这个布局模式。这意味着如果您注册了自定义布局模板,您可以将默认的z3宏基于布局注册与这个布局概念结合使用。

最简单的方法是在布局模板中从视图调用内容,就是从方法中调用它。让我们定义一个提供布局模板并提供调用内容方法的视图。

>>> class IFullView(zope.interface.Interface):
...     pass
>>> @zope.interface.implementer(IFullView)
... class FullView(BrowserPage):
...     layout = None
...     def render(self):
...         return u'rendered content'
...     def __call__(self):
...         if self.layout is None:
...             layout = zope.component.getMultiAdapter((self, self.request),
...                 interfaces.ILayoutTemplate)
...             return layout(self)
...         return self.layout()
>>> completeView = FullView(root, request)

现在定义视图的布局并注册它们

>>> completeLayout = os.path.join(temp_dir, 'completeLayout.pt')
>>> with open(completeLayout, 'w') as file:
...     _ = file.write('''
...   <div tal:content="view/render">
...     Full layout
...   </div>
... ''')
>>> factory = TemplateFactory(completeLayout, 'text/html')
>>> component.provideAdapter(factory,
...     (IFullView, IDefaultBrowserLayer), interfaces.ILayoutTemplate)

现在让我们看看布局模板是否可以通过在视图中调用render来调用内容

>>> print(completeView.__call__())
<div>rendered content</div>

内容和布局

现在让我们通过一个实际案例来展示如何结合这两个模板。

>>> class IDocumentView(zope.interface.Interface):
...     pass
>>> @zope.interface.implementer(IDocumentView)
... class DocumentView(BrowserPage):
...     template = None
...     layout = None
...     attr = None
...     def update(self):
...         self.attr = u'content updated'
...     def render(self):
...         if self.template is None:
...             template = zope.component.getMultiAdapter(
...                 (self, self.request), IPageTemplate)
...             return template(self)
...         return self.template()
...     def __call__(self):
...         self.update()
...         if self.layout is None:
...             layout = zope.component.getMultiAdapter((self, self.request),
...                 interfaces.ILayoutTemplate)
...             return layout(self)
...         return self.layout()

定义并注册内容模板...

>>> template = os.path.join(temp_dir, 'template.pt')
>>> with open(template, 'w') as file:
...     _ = file.write('''
...   <div tal:content="view/attr">
...     here comes the value of attr
...   </div>
... ''')
>>> factory = TemplateFactory(template, 'text/html')
>>> component.provideAdapter(factory,
...     (IDocumentView, IDefaultBrowserLayer), IPageTemplate)

并定义和注册布局模板

>>> layout = os.path.join(temp_dir, 'layout.pt')
>>> with open(layout, 'w') as file:
...     _ = file.write('''
... <html>
...   <body>
...     <div tal:content="structure view/render">
...       here comes the rendered content
...     </div>
...   </body>
... </html>
... ''')
>>> factory = TemplateFactory(layout, 'text/html')
>>> component.provideAdapter(factory,
...     (IDocumentView, IDefaultBrowserLayer), interfaces.ILayoutTemplate)

现在调用视图并检查结果

>>> documentView = DocumentView(root, request)
>>> print(documentView())
<html>
  <body>
    <div>
      <div>content updated</div>
    </div>
  </body>
</html>

宏的使用。

>>> macroTemplate = os.path.join(temp_dir, 'macroTemplate.pt')
>>> with open(macroTemplate, 'w') as file:
...     _ = file.write('''
...   <metal:block define-macro="macro1">
...     <div>macro1</div>
...   </metal:block>
...   <metal:block define-macro="macro2">
...     <div>macro2</div>
...     <div tal:content="options/div2">the content of div 2</div>
...   </metal:block>
...   ''')
>>> factory = TemplateFactory(macroTemplate, 'text/html', 'macro1')
>>> print(factory(view, request)())
<div>macro1</div>
>>> m2factory = TemplateFactory(macroTemplate, 'text/html', 'macro2')
>>> print(m2factory(view, request)(div2="from the options"))
<div>macro2</div>
<div>from the options</div>

为什么我们没有使用来自 zope.formlib 包的命名模板?

虽然命名模板允许我们将视图代码与模板注册分离,但它们不能注册到特定层,这使得使用命名模板实现多个皮肤变得不可能。

用例 简单模板

对于最简单的使用,我们提供了一个调用已注册模板的钩子。这样的页面模板可以通过getPageTemplate方法被调用,并返回一个注册的绑定ViewTemplate,类似于ViewPageTemplateFile或NamedTemplate。

getViewTemplate允许我们使用新的模板注册系统,包括所有现有的实现,如zope.formlibzope.viewlet

>>> from z3c.template.template import getPageTemplate
>>> class IUseOfViewTemplate(zope.interface.Interface):
...     pass
>>> @zope.interface.implementer(IUseOfViewTemplate)
... class UseOfViewTemplate(object):
...
...     template = getPageTemplate()
...
...     def __init__(self, context, request):
...         self.context = context
...         self.request = request

通过将“template”属性定义为“getPageTemplate”,在调用时执行已注册模板的查找。

>>> simple = UseOfViewTemplate(root, request)
>>> print(simple.template())
<div>demo content</div>

因为我们为任何(“None”)接口注册了演示模板,所以在渲染我们的新视图时,我们看到演示模板。我们还为新视图注册了一个新模板。注意,“macroTemplate”是在本测试中较早创建的。

>>> factory = TemplateFactory(contentTemplate, 'text/html')
>>> component.provideAdapter(factory,
...     (IUseOfViewTemplate, IDefaultBrowserLayer), IPageTemplate)
>>> print(simple.template())
<div>demo content</div>

上下文特定模板

TemplateFactory也可以用于(视图,请求,上下文)查找。当你想要为特定内容对象或类型覆盖模板时,它很有用。

让我们定义一个示例内容类型并为它实例化一个视图。

>>> class IContent(zope.interface.Interface):
...     pass
>>> @zope.interface.implementer(IContent)
... class Content(object):
...     pass
>>> content = Content()
>>> view = UseOfViewTemplate(content, request)

现在,让我们使用TemplateFactory提供(视图,请求,上下文)适配器。

>>> contextTemplate = os.path.join(temp_dir, 'context.pt')
>>> with open(contextTemplate, 'w') as file:
...     _ = file.write('<div>context-specific</div>')
>>> factory = TemplateFactory(contextTemplate, 'text/html')
>>> component.provideAdapter(factory,
...     (IUseOfViewTemplate, IDefaultBrowserLayer, IContent),
...     interfaces.IContentTemplate)

首先,让我们尝试将其作为多适配器获取。

>>> template = zope.component.getMultiAdapter((view, request, content),
...                 interfaces.IContentTemplate)
>>> print(template(view))
<div>context-specific</div>

getPageTemplate和类似的方法将在进行更通用的(视图,请求)查找之前,尝试查找上下文特定的模板,所以我们的视图应该已经使用了我们的上下文特定模板

>>> print(view.template())
<div>context-specific</div>

用例 通过接口的模板

模板也可以在不同的接口上注册,而不是IPageTemplate或ILayoutTemplate。

>>> from z3c.template.template import getViewTemplate
>>> class IMyTemplate(zope.interface.Interface):
...     """My custom tempalte marker."""
>>> factory = TemplateFactory(contentTemplate, 'text/html')
>>> component.provideAdapter(factory,
...     (zope.interface.Interface, IDefaultBrowserLayer), IMyTemplate)

现在定义一个使用这种自定义模板注册的视图

>>> class IMyTemplateView(zope.interface.Interface):
...     pass
>>> @zope.interface.implementer(IMyTemplateView)
... class MyTemplateView(object):
...
...     template = getViewTemplate(IMyTemplate)
...
...     def __init__(self, context, request):
...         self.context = context
...         self.request = request
>>> myTempalteView = MyTemplateView(root, request)
>>> print(myTempalteView.template())
<div>demo content</div>

用例 命名模板

模板也可以按名称注册。在这个例子中,我们使用了一个命名模板和自定义模板标记接口。

>>> class IMyNamedTemplate(zope.interface.Interface):
...     """My custom template marker."""
>>> factory = TemplateFactory(contentTemplate, 'text/html')
>>> component.provideAdapter(factory,
...     (zope.interface.Interface, IDefaultBrowserLayer), IMyNamedTemplate,
...     name='my template')

现在定义一个使用这种自定义命名模板注册的视图

>>> class IMyNamedTemplateView(zope.interface.Interface):
...     pass
>>> @zope.interface.implementer(IMyNamedTemplateView)
... class MyNamedTemplateView(object):
...
...     template = getViewTemplate(IMyNamedTemplate, 'my template')
...
...     def __init__(self, context, request):
...         self.context = context
...         self.request = request
>>> myNamedTempalteView = MyNamedTemplateView(root, request)
>>> print(myNamedTempalteView.template())
<div>demo content</div>

用例 命名布局模板

我们也可以按名称注册一个新的布局模板,并在视图中使用它

>>> from z3c.template.template import getLayoutTemplate
>>> editLayout = os.path.join(temp_dir, 'editLayout.pt')
>>> with open(editLayout, 'w') as file:
...     _ = file.write('''
...   <div>Edit layout</div>
...   <div tal:content="view/render">content</div>
... ''')
>>> factory = TemplateFactory(editLayout, 'text/html')
>>> component.provideAdapter(factory,
...     (zope.interface.Interface, IDefaultBrowserLayer),
...      interfaces.ILayoutTemplate, name='edit')

现在定义一个使用这种自定义命名模板注册的视图

>>> class MyEditView(BrowserPage):
...
...     layout = getLayoutTemplate('edit')
...
...     def render(self):
...         return u'edit content'
...
...     def __call__(self):
...         if self.layout is None:
...             layout = zope.component.getMultiAdapter((self, self.request),
...                 interfaces.ILayoutTemplate)
...             return layout(self)
...         return self.layout()
>>> myEditView = MyEditView(root, request)
>>> print(myEditView())
<div>Edit layout</div>
<div>edit content</div>

清理

>>> import shutil
>>> shutil.rmtree(temp_dir)

页面组件

参见z3c.pagelet,了解另一种基于模板的布局生成实现。

模板指令

展示如何使用模板指令。注册指令的元配置。

>>> import sys
>>> from zope.configuration import xmlconfig
>>> import z3c.template
>>> context = xmlconfig.file('meta.zcml', z3c.template)

页面模板

我们需要一个自定义的内容模板

>>> import os, tempfile
>>> temp_dir = tempfile.mkdtemp()
>>> content_file = os.path.join(temp_dir, 'content.pt')
>>> with open(content_file, 'w') as file:
...     _ = file.write('''<div>content</div>''')

和一个接口

>>> import zope.interface
>>> class IView(zope.interface.Interface):
...     """Marker interface"""

和一个视图类

>>> from zope.publisher.browser import TestRequest
>>> @zope.interface.implementer(IView)
... class View(object):
...     def __init__(self, context, request):
...         self.context = context
...         self.request = request
>>> request = TestRequest()
>>> view = View(object(), request)

在伪包custom下使它们可用

>>> sys.modules['custom'] = type(
...     'Module', (),
...     {'IView': IView})()

并在z3c:template指令中将它们注册为模板

>>> context = xmlconfig.string("""
... <configure
...     xmlns:z3c="http://namespaces.zope.org/z3c">
...   <z3c:template
...       template="%s"
...       for="custom.IView"
...       />
... </configure>
... """ % content_file, context=context)

让我们获取模板

>>> import zope.component
>>> from z3c.template.interfaces import IContentTemplate
>>> template = zope.component.queryMultiAdapter(
...     (view, request),
...     interface=IContentTemplate)

并检查它们

>>> from z3c.template.template import ViewPageTemplateFile
>>> isinstance(template, ViewPageTemplateFile)
True
>>> isinstance(template.content_type, str)
True
>>> print(template(view))
<div>content</div>

错误

如果我们尝试使用一个不存在的模板路径,我们会得到一个错误

>>> context = xmlconfig.string("""
... <configure
...     xmlns:z3c="http://namespaces.zope.org/z3c">
...   <z3c:template
...       template="this_file_does_not_exist"
...       for="custom.IView"
...       />
... </configure>
... """, context=context)
Traceback (most recent call last):
...
ConfigurationError: ('No such file', '...this_file_does_not_exist')
File "<string>", line 4.2-7.8

布局模板

定义一个布局模板

>>> layout_file = os.path.join(temp_dir, 'layout.pt')
>>> with open(layout_file, 'w') as file:
...     _ = file.write('''<div>layout</div>''')

并在z3c:layout指令中将它们注册为布局模板

>>> context = xmlconfig.string("""
... <configure
...     xmlns:z3c="http://namespaces.zope.org/z3c">
...   <z3c:layout
...       template="%s"
...       for="custom.IView"
...       />
... </configure>
... """ % layout_file, context=context)

让我们获取模板

>>> from z3c.template.interfaces import ILayoutTemplate
>>> layout = zope.component.queryMultiAdapter((view, request),
...     interface=ILayoutTemplate)

并检查它们

>>> isinstance(layout, ViewPageTemplateFile)
True
>>> isinstance(layout.content_type, str)
True
>>> print(layout(view))
<div>layout</div>

上下文特定模板

大多数视图都有一个对象作为它们的上下文,能够注册上下文特定的模板通常非常有用。我们可以使用ZCML指令的context参数来做这件事。

让我们定义一些内容类型

>>> class IContent(zope.interface.Interface):
...     pass
>>> @zope.interface.implementer(IContent)
... class Content(object):
...     pass
>>> sys.modules['custom'].IContent = IContent

现在,我们可以为这个类注册一个模板。让我们创建一个并注册

>>> context_file = os.path.join(temp_dir, 'context.pt')
>>> with open(context_file, 'w') as file:
...     _ = file.write('''<div>i'm context-specific</div>''')
>>> context = xmlconfig.string("""
... <configure
...     xmlns:z3c="http://namespaces.zope.org/z3c">
...   <z3c:template
...       template="%s"
...       for="custom.IView"
...       context="custom.IContent"
...       />
... </configure>
... """ % context_file, context=context)

现在我们可以使用(视图,请求,上下文)区分器来查找它

>>> content = Content()
>>> view = View(content, request)
>>> template = zope.component.queryMultiAdapter((view, request, content),
...     interface=IContentTemplate)
>>> print(template(view))
<div>i'm context-specific</div>

相同的操作也适用于布局注册指令

>>> context_layout_file = os.path.join(temp_dir, 'context_layout.pt')
>>> with open(context_layout_file, 'w') as file:
...     _ = file.write('''<div>context-specific layout</div>''')
>>> context = xmlconfig.string("""
... <configure
...     xmlns:z3c="http://namespaces.zope.org/z3c">
...   <z3c:layout
...       template="%s"
...       for="custom.IView"
...       context="custom.IContent"
...       />
... </configure>
... """ % context_layout_file, context=context)
>>> layout = zope.component.queryMultiAdapter((view, request, content),
...     interface=ILayoutTemplate)
>>> print(layout(view))
<div>context-specific layout</div>

命名模板

可以通过名称注册模板。让我们注册一个名为edit的页面

>>> editTemplate = os.path.join(temp_dir, 'edit.pt')
>>> with open(editTemplate, 'w') as file:
...     _ = file.write('''<div>edit</div>''')
>>> context = xmlconfig.string("""
... <configure
...     xmlns:z3c="http://namespaces.zope.org/z3c">
...   <z3c:template
...       name="edit"
...       template="%s"
...       for="custom.IView"
...       />
... </configure>
... """ % editTemplate, context=context)

并调用它

>>> from z3c.template.interfaces import ILayoutTemplate
>>> template = zope.component.queryMultiAdapter(
...     (view, request),
...     interface=IContentTemplate, name='edit')
>>> print(template(view))
<div>edit</div>

自定义模板

或者,你可以定义自己的接口并为它们注册模板

>>> from zope.pagetemplate.interfaces import IPageTemplate
>>> class IMyTemplate(IPageTemplate):
...     """My template"""

将模板接口作为自定义模块类提供。

>>> sys.modules['custom'].IMyTemplate = IMyTemplate

定义一个新的模板

>>> interfaceTemplate = os.path.join(temp_dir, 'interface.pt')
>>> with open(interfaceTemplate, 'w') as file:
...     _ = file.write('''<div>interface</div>''')
>>> context = xmlconfig.string("""
... <configure
...     xmlns:z3c="http://namespaces.zope.org/z3c">
...   <z3c:template
...       template="%s"
...       for="custom.IView"
...       provides="custom.IMyTemplate"
...       />
... </configure>
... """ % interfaceTemplate, context=context)

让我们看看我们是否通过新的接口获得模板

>>> from z3c.template.interfaces import ILayoutTemplate
>>> template = zope.component.queryMultiAdapter((view, request),
...     interface=IMyTemplate,)
>>> print(template(view))
<div>interface</div>

清理

现在我们需要清理自定义模块。

>>> del sys.modules['custom']

变更

4.0 (2023-03-01)

  • 添加对Python 3.11的支持。

  • 放弃对Python 2.7、3.5和3.6的支持。

3.2 (2022-03-25)

  • 添加对Python 3.8、3.9和3.10的支持。

  • 放弃对Python 3.4的支持。

3.1.0 (2019-02-05)

  • 适配测试以符合 zope.configuration >= 4.2

  • 添加对Python 3.7的支持。

3.0.0 (2017-10-18)

  • 添加对PyPy的支持。

  • 添加对Python 3.4、3.5和3.6的支持。

  • 放弃对Python 2.6和3.3的支持。

  • 让绑定的页面模板具有 __self____func__ 属性,使其更像Python 3的绑定方法。(im_funcim_self 仍然可用。)请参阅 问题3

  • 由于可能的渲染问题,依赖Chameleon >= 3.0、z3c.pt >= 2.1和z3c.ptcompat >= 2.1.0。请参阅 PR 2

2.0.0 (2015-11-09)

  • 标准化命名空间 __init__

2.0.0a2 (2013-02-25)

  • 确保模板的内容类型是原生字符串而不是强制字节。

2.0.0a1 (2013-02-22)

  • 添加了对Python 3.3的支持。

  • 将过时的 zope.interface.implements 使用方式替换为等效的 zope.interface.implementer 装饰器。

  • 放弃对Python 2.4和2.5的支持。

1.4.1 (2012-02-15)

  • 由于z3c.pt可用但z3c.ptcompat未包含,因此删除了用于从z3c.pt使用ViewPageTemplateFile的钩子。如以下说明中建议的那样。

1.4.0 (2011-10-29)

  • 将z3c.pt包含移动到extras_require chameleon。这使得该软件包独立于chameleon及其相关软件,并允许在您自己的项目中包含这些依赖项。

  • 升级到chameleon 2.0模板引擎,并使用与chameleon 2.0兼容的最新z3c.pt和z3c.ptcompat软件包。

    参阅z3c.ptcompat软件包的说明。

    将z3c.ptcompat实现更新为使用基于组件的模板引擎配置,直接插入Zope Toolkit框架。

    z3c.ptcompat软件包不再提供模板类或ZCML指令;您应直接从ZTK代码库导入。

    请注意,PREFER_Z3C_PT 环境选项已被弃用;现在,这通过组件配置进行管理。

    另外请注意,chameleon CHAMELEON_CACHE环境值已从True/False更改为路径。如果您不喜欢使用缓存,请跳过此属性。在buildout环境部分中定义的None或False不起作用。至少在chameleon <= 2.5.4中是这样。

    注意:您需要包含z3c.ptcompat中的configure.zcml文件以启用z3c.pt模板引擎。configure.zcml将插件模板引擎。另外,请删除任何自定义构建的钩子,这些钩子将在您的测试或其他地方导入z3c.ptcompat。

1.3.0 (2011-10-28)

  • 更新到z3c.ptcompat 1.0(因此,更新到z3c.pt 2.x系列)。

  • 使用Python的 doctest 模块代替已弃用的 zope.testing.doctest

1.2.1 (2009-08-22)

  • 修正了 ITemplateDirective.name 的描述。

  • zcml.txt 添加到 long_description 以在PyPI上显示。

  • 删除了zpkg辅助文件和zcml缩写。

1.2.0 (2009-02-26)

  • 添加了对上下文特定模板的支持。现在,模板可以使用(视图、请求、上下文)三元组进行注册和查找。为此,将 context 参数传递给ZCML指令。现在,getPageTemplate 和朋友将首先尝试查找上下文特定模板,然后回退到(视图、请求)查找。

  • 允许使用 z3c.pt 通过 z3c.ptcompat 兼容层。

  • 将模板kwargs转发到宏的选项中

  • 将软件包的邮件列表地址更改为zope-dev at zope.org,而不是已退休的地址。

1.1.0 (2007-10-08)

  • 添加了一个 IContentTemplate 接口,用于 <z3c:template>

1.0.0 (2007-??-??)

  • 初始发布。

项目详细信息


下载文件

下载适用于您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。

源分发

z3c.template-4.0.tar.gz (30.2 kB 查看哈希值)

上传时间

构建分发

z3c.template-4.0-py3-none-any.whl (22.9 kB 查看哈希值)

上传时间 Python 3

由以下提供支持