跳转到主要内容

Pagelets是一种在不使用O-wrap的情况下指定模板的方法。

项目描述

Pagelets是Zope 3 UI组件。特别是,它们允许开发者指定内容模板,而无需担心UI O-wrap。

Pagelets

此软件包提供了一种非常灵活的基本实现,可用于编写视图组件,这些组件可以在自定义项目中高度定制。如果您必须编写可重用的组件,例如在框架中需要的组件,则需要此功能。Pagelets是不同的BrowserPages,可以用来替换它们。

这意味着什么?

我们分离了Python视图代码和模板实现。我们还至少将模板分为两个不同的模板——内容模板和布局模板。

本软件包使用了z3c.template并为此模板模式提供了一种实现。此外,该软件包还提供了一个 pagelet 指令,可用于注册页面组件。

页面组件是可以调用并支持更新和渲染模式的视图。

它们是如何工作的

页面组件在render方法中返回不带布局的渲染内容,如果我们调用它,则返回布局代码。有关模板的工作方式,请参阅z3c.template。以下示例仅展示z3c.pagelet.browser模块中基本实现的用法。

BrowserPagelet

基本实现BrowserPagelet提供了内置的__call__和render方法,这些方法提供了不同的模板查找。查看z3c.pagelet.browser模块中的BrowserPagelet类,您可以看到render方法返回一个IContentTemplate,而__call__方法返回一个由z3c.layout包定义的ILayoutTemplate。

>>> import os, tempfile
>>> temp_dir = tempfile.mkdtemp()
>>> import zope.interface
>>> import zope.component
>>> from z3c.pagelet import interfaces
>>> from z3c.pagelet import browser

我们首先定义一个页面模板,用于渲染页面组件的内容。

>>> contentTemplate = os.path.join(temp_dir, 'contentTemplate.pt')
>>> with open(contentTemplate, 'w') as file:
...     _ = file.write('''
...   <div class="content">
...     my template content
...   </div>
... ''')

我们还定义了一个布局模板,用于渲染页面组件的布局。此模板将调用页面组件的render方法。

>>> layoutTemplate = os.path.join(temp_dir, 'layoutTemplate.pt')
>>> with open(layoutTemplate, 'w') as file:
...     _ = file.write('''
...   <html>
...     <body>
...       <div class="layout" tal:content="structure view/render">
...         here comes the content
...       </div>
...     </body>
...   </html>
... ''')

现在注册视图和请求的模板。我们直接从z3c.template包使用TemplateFactory。这通常是通过使用名为z3c:template的ZCML指令来完成的。请注意,我们使用通用接口作为视图基接口来注册模板。这允许我们在下一个示例中注册更具体的模板。

>>> from zope.publisher.interfaces.browser import IDefaultBrowserLayer
>>> from z3c.template.interfaces import IContentTemplate
>>> from z3c.template.template import TemplateFactory
>>> factory = TemplateFactory(contentTemplate, 'text/html')
>>> zope.component.provideAdapter(
...     factory, (zope.interface.Interface, IDefaultBrowserLayer),
...     IContentTemplate)

并使用Interface作为注册基来注册布局模板。

>>> from z3c.template.interfaces import ILayoutTemplate
>>> factory = TemplateFactory(layoutTemplate, 'text/html')
>>> zope.component.provideAdapter(factory,
...     (zope.interface.Interface, IDefaultBrowserLayer), ILayoutTemplate)

现在定义一个视图标记接口。此类标记接口用于让我们注册我们的模板。

>>> class IMyView(zope.interface.Interface):
...     pass

然后我们定义一个继承自BrowserPagelet并实现视图标记接口的视图类。

>>> @zope.interface.implementer(IMyView)
... class MyView(browser.BrowserPagelet):
...     pass

现在测试视图类,提供视图并检查输出。

>>> from zope.publisher.browser import TestRequest
>>> request = TestRequest()
>>> myView = MyView(root, request)
>>> print(myView())
<html>
  <body>
    <div class="layout">
      <div class="content">
        my template content
      </div>
    </div>
  </body>
</html>

您可以看到render方法仅生成内容。

>>> print(myView.render())
<div class="content">
  my template content
</div>

重定向

当请求是重定向时,页面组件不会麻烦渲染自身及其布局,因为在那种情况下渲染没有意义。让我们创建一个在update方法中执行重定向的视图。

>>> class RedirectingView(MyView):
...     def update(self):
...         self.request.response.redirect('.')

当作为浏览器页面调用时,它将返回一个空字符串。

>>> redirectRequest = TestRequest()
>>> redirectView = RedirectingView(root, redirectRequest)
>>> redirectView() == ''
True

然而,render方法将像往常一样渲染页面组件的模板。

>>> print(redirectView.render())
<div class="content">
  my template content
</div>

PageletRenderer

还有一个标准模式可以调用页面组件的render方法。使用页面组件渲染器,这是一个IContentProvider,使得在没有页面组件的情况下重用现有的布局模板成为可能。如果您想在没有页面组件的情况下重用布局模板,您只需提供另一个内容提供者。这不是很灵活吗?接下来,让我们通过使用页面组件渲染器展示一个示例。

我们使用名为`pagelet`的内容提供者定义一个新的布局模板

>>> providerLayout = os.path.join(temp_dir, 'providerLayout.pt')
>>> with open(providerLayout, 'w') as file:
...     _ = file.write('''
...   <html>
...     <body>
...       <div class="layout" tal:content="structure provider:pagelet">
...         here comes the content
...       </div>
...     </body>
...   </html>
... ''')

并注册它们。现在我们使用在视图中定义的特定接口

>>> factory = TemplateFactory(providerLayout, 'text/html')
>>> zope.component.provideAdapter(factory,
...     (zope.interface.Interface, IDefaultBrowserLayer), ILayoutTemplate)

现在让我们调用视图

>>> try:
...     myView()
... except Exception as e:
...     print(repr(e))
ContentProviderLookupError('pagelet'...)

没错,在我们使用它之前,我们需要注册内容提供者pagelet

>>> from zope.contentprovider.interfaces import IContentProvider
>>> from z3c.pagelet import provider
>>> zope.component.provideAdapter(provider.PageletRenderer,
...     provides=IContentProvider, name='pagelet')

现在让我们再次调用视图

>>> print(myView())
<html>
  <body>
    <div class="layout">
      <div class="content">
        my template content
      </div>
    </div>
  </body>
</html>

上下文特定模板

页面组件还可以通过将它们的上下文对象作为额外的区分器来查找模板,通过(self,self.request,self.context)查找。当您想为某些特定内容对象提供自定义模板时非常有用。让我们看看。

首先,让我们定义一个自定义内容类型并创建一个用于工作的对象

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

让我们使用我们之前定义的视图类。目前,它将使用之前为(视图,请求)定义的布局和内容模板

>>> myView = MyView(content, request)
>>> print(myView())
<html>
  <body>
    <div class="layout">
      <div class="content">
        my template content
      </div>
    </div>
  </body>
</html>

让我们创建特定于上下文的布局和内容模板,并将它们注册到我们的IContent接口

>>> contextLayoutTemplate = os.path.join(temp_dir, 'contextLayoutTemplate.pt')
>>> with open(contextLayoutTemplate, 'w') as file:
...     _ = file.write('''
...   <html>
...     <body>
...       <div class="context-layout" tal:content="structure provider:pagelet">
...         here comes the context-specific content
...       </div>
...     </body>
...   </html>
... ''')
>>> factory = TemplateFactory(contextLayoutTemplate, 'text/html')
>>> zope.component.provideAdapter(
...     factory, (zope.interface.Interface, IDefaultBrowserLayer, IContent),
...     ILayoutTemplate)
>>> contextContentTemplate = os.path.join(temp_dir, 'contextContentTemplate.pt')
>>> with open(contextContentTemplate, 'w') as file:
...     _ = file.write('''
...   <div class="context-content">
...     my context-specific template content
...   </div>
... ''')
>>> factory = TemplateFactory(contextContentTemplate, 'text/html')
>>> zope.component.provideAdapter(
...     factory, (zope.interface.Interface, IDefaultBrowserLayer, IContent),
...     IContentTemplate)

现在,我们的视图应使用特定于上下文的模板进行渲染

>>> print(myView())
<html>
  <body>
    <div class="context-layout">
      <div class="context-content">
        my context-specific template content
      </div>
    </div>
  </body>
</html>

添加、编辑和显示表单(formlib)

没有基于formlib的实现,页面组件会是什么样子呢?我们提供了基于formlib的基础实现,包括添加、编辑和显示表单。注意:为确保这些类能够定义,您应该已经安装了zope.formlib,因为z3c.pagelet不直接依赖于zope.formlib,因为还有其他表单库。

对于接下来的测试,我们提供了一个通用的表单模板,类似于formlib中使用的模板。此模板在此包中注册为基于formlib混合类的默认模板。

>>> from z3c import pagelet
>>> baseDir = os.path.split(pagelet.__file__)[0]
>>> formTemplate = os.path.join(baseDir, 'form.pt')
>>> factory = TemplateFactory(formTemplate, 'text/html')
>>> zope.component.provideAdapter(
...     factory,
...     (interfaces.IPageletForm, IDefaultBrowserLayer), IContentTemplate)

我们定义了一个新的接口,包括一个文本属性。

>>> import zope.schema
>>> class IDocument(zope.interface.Interface):
...     """A document."""
...     text = zope.schema.TextLine(title=u'Text', description=u'Text attr.')

并定义了一个实现该接口的内容对象。

>>> @zope.interface.implementer(IDocument)
... class Document(object):
...     text = None
>>> document = Document()

PageletAddForm

现在让我们定义一个基于PageletAddForm类的添加表单。

>>> from zope.formlib import form
>>> class MyAddForm(browser.PageletAddForm):
...     form_fields = form.Fields(IDocument)
...     def createAndAdd(self, data):
...         title = data.get('title', u'')
...         doc = Document()
...         doc.title = title
...         root['document'] = doc
...         return doc

现在渲染表单。

>>> addForm = MyAddForm(root, request)
>>> print(addForm())
<html>
  <body>
    <div class="layout">
      <form action="http://127.0.0.1" method="post"
            enctype="multipart/form-data" class="edit-form"
            id="zc.page.browser_form">
        <table class="form-fields">
          <tr>
            <td class="label">
              <label for="form.text">
              <span class="required">*</span><span>Text</span>
              </label>
            </td>
            <td class="field">
              <div class="form-fields-help"
                   id="field-help-for-form.text">Text attr.</div>
              <div class="widget"><input class="textType" id="form.text"
                   name="form.text" size="20" type="text" value=""  /></div>
            </td>
          </tr>
        </table>
      <div class="form-controls">
        <input type="submit" id="form.actions.add" name="form.actions.add"
               value="Add" class="button" />
      </div>
    </form>
  </div>
  </body>
</html>

PageletEditForm

现在让我们定义一个基于PageletEditForm类的编辑表单。

>>> class MyEditForm(browser.PageletEditForm):
...     form_fields = form.Fields(IDocument)

并渲染表单。

>>> document.text = u'foo'
>>> editForm = MyEditForm(document, request)
>>> print(editForm())
<html>
  <body>
    <div class="layout">
      <form action="http://127.0.0.1" method="post"
            enctype="multipart/form-data" class="edit-form"
            id="zc.page.browser_form">
        <table class="form-fields">
            <tr>
              <td class="label">
                <label for="form.text">
                <span class="required">*</span><span>Text</span>
                </label>
              </td>
              <td class="field">
                <div class="form-fields-help"
                     id="field-help-for-form.text">Text attr.</div>
                <div class="widget"><input class="textType" id="form.text"
                     name="form.text" size="20" type="text" value="foo"
                     /></div>
              </td>
            </tr>
        </table>
        <div class="form-controls">
          <input type="submit" id="form.actions.apply"
                 name="form.actions.apply" value="Apply" class="button" />
        </div>
      </form>
    </div>
  </body>
</html>

PageletDisplayForm

现在让我们定义一个基于PageletDisplayForm类的显示表单……

>>> class MyDisplayForm(browser.PageletDisplayForm):
...     form_fields = form.Fields(IDocument)

并渲染表单。

>>> document.text = u'foo'
>>> displayForm = MyDisplayForm(document, request)
>>> print(displayForm())
<html>
  <body>
    <div class="layout">
      <form action="http://127.0.0.1" method="post"
            enctype="multipart/form-data" class="edit-form"
            id="zc.page.browser_form">
        <table class="form-fields">
            <tr>
              <td class="label">
                <label for="form.text">
                <span>Text</span>
                </label>
              </td>
              <td class="field">
                <div class="form-fields-help"
                     id="field-help-for-form.text">Text attr.</div>
                <div class="widget">foo</div>
              </td>
            </tr>
        </table>
      </form>
    </div>
  </body>
</html>

清理

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

CHANGES

3.0 (2023-02-09)

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

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

2.1 (2018-11-12)

  • 声明支持Python 3.5、3.6、3.7、PyPy和PyPy3。

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

  • 放弃对python setup.py test的支持。

2.0.0 (2015-11-09)

  • 标准化namespace __init__。

  • 声明支持Python 3.4。

2.0.0a1 (2013-02-28)

  • 添加对Python 3.3的支持。

  • 将废弃的zope.interface.implements使用等效的zope.interface.implementer装饰器。

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

1.3.1 (2012-09-15)

  • zope.component.zcml.IBasicViewInformation更改后修复IPageletDirective

1.3.0 (2011-10-29)

  • 将z3c.pt包含移动到extras_require chameleon。这使得包独立于chameleon及其朋友,并允许将此依赖项包含到您的项目中。

  • 升级到chameleon 2.0模板引擎,并使用最新版本的z3c.pt和z3c.ptcompat包,以调整与chameleon 2.0一起使用。

    请参阅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.2.2 (2011-08-18)

  • 更改页面组件签名中的请求接口,例如(上下文,请求,页面组件)。从IBrowserRequest切换到IHTTPRequest。这允许使用不提供IBrowserRequest的jsonrpc请求的页面组件模式。同时反映在configure.zcml中的更改。

1.2.1 (2010-07-29)

  • zope.app.pagetemplate.metaconfigure.registerType已移动到zope.browserpage,因此现在从那里导入。

  • 放弃对zope.app.security的测试依赖关系,它不再需要。

  • 使用python的doctest而不是已弃用的zope.testing.doctest[unit]

1.2.0 (2009-08-27)

  • 修复测试中的不受信任的重定向到google.com。现在,由较新的zope.publisher版本默认禁止。

  • zope.app.publisher依赖关系更改为新的zope.browserpage,因为它具有更少的依赖关系。

1.1.0 (2009-05-28)

  • 通过要求zope.component >= 3.7,消除了对zope.app.component的依赖。

  • 移除了对zope.formlib的硬依赖:页面组件表单现在仅在zope.formlib可用时定义。测试仍然依赖于zope.formlib,因此它获得了一个测试依赖。

  • 确保长描述在pypi上渲染良好。

1.0.3 (2009-02-27)

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

  • 添加了对特定上下文布局和内容模板查找的支持,使用(view, request, context)判别器。这与z3c.template 1.2.0中引入的特定上下文模板兼容。

  • 当请求是重定向时,不要在页面组件的__call__方法中执行渲染。

  • 将基于sphinx的HTML文档构建部分添加到buildout中。

1.0.2 (2008-01-21)

  • 添加了一个form.zcml,可以包含PageletAddFormPageletEditFormPageletDisplayForm的模板。

1.0.1 (2007-10-08)

  • 为未指定但使用的IPagelet添加了update()render()方法。

  • 修复了当为“*”注册布局模板但没有为页面组件注册内容模板时的无限递归问题。

项目详细信息


下载文件

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

源代码分发

z3c.pagelet-3.0.tar.gz (27.7 kB 查看哈希值)

上传时间 源代码

构建分发

z3c.pagelet-3.0-py3-none-any.whl (24.4 kB 查看哈希值)

上传时间 Python 3

支持者

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误记录 StatusPage StatusPage 状态页面