z3c.formui


Namez3c.formui JSON
Version 4.1 PyPI version JSON
download
home_pagehttps://github.com/zopefoundation/z3c.formui
SummaryA set of initial UI components for z3c.form.
upload_time2024-10-22 06:56:40
maintainerNone
docs_urlNone
authorStephan Richter, Roger Ineichen and the Zope Community
requires_python>=3.8
licenseZPL 2.1
keywords zope3 form widget
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            This package provides a set of default layouts for the ``z3c.form``
framework. In particular it provides a DIV-based and a TABLE-based layout. The
developer can use either layout by inheriting from a different base layer.

The package also has some support for layout/pagelet templates.


.. contents::

====================
Form User Interfaces
====================

This package provides several useful templates to get a quick start with the
``z3c.form`` package. Previous form frameworks always included default
templates that were implemented in a particular user-interface development
pattern. If you wanted to use an alternative strategy to develop user
interfaces, it was often tedious to do so. This package aims to provide some
options without requiring them for the basic framework.


Layout Template Support
-----------------------

One common pattern in Zope 3 user interface development is the use of layout
templates (see z3c.template). This package provides some mixin classes to the
regular form classes to support layout-based templating.

  >>> from z3c.form import testing
  >>> testing.setupFormDefaults()

Before we can start writing forms, we must have the content to work with:

  >>> import zope.interface
  >>> import zope.schema
  >>> class IPerson(zope.interface.Interface):
  ...
  ...     name = zope.schema.TextLine(
  ...         title='Name',
  ...         required=True)
  ...
  ...     age = zope.schema.Int(
  ...         title='Age',
  ...         description=u"The person's age.",
  ...         min=0,
  ...         default=20,
  ...         required=False)

  >>> from zope.schema.fieldproperty import FieldProperty
  >>> @zope.interface.implementer(IPerson)
  ... class Person(object):
  ...     name = FieldProperty(IPerson['name'])
  ...     age = FieldProperty(IPerson['age'])
  ...
  ...     def __init__(self, name, age):
  ...         self.name = name
  ...         self.age = age
  ...
  ...     def __repr__(self):
  ...         return '<%s %r>' % (self.__class__.__name__, self.name)

Okay, that should suffice for now. Let's now create a working add form:

  >>> from z3c.form import field
  >>> from z3c.formui import form, layout
  >>> class PersonAddForm(form.AddForm):
  ...
  ...     fields = field.Fields(IPerson)
  ...
  ...     def create(self, data):
  ...         return Person(**data)
  ...
  ...     def add(self, object):
  ...         self.context[object.id] = object
  ...
  ...     def nextURL(self):
  ...         return 'index.html'

Let's create a request:

  >>> from z3c.form.testing import TestRequest
  >>> from zope.interface import alsoProvides
  >>> divRequest = TestRequest()

And support the div form layer for our request:

  >>> from z3c.formui.interfaces import IDivFormLayer
  >>> alsoProvides(divRequest, IDivFormLayer)

Now create the form:

  >>> addForm = PersonAddForm(root, divRequest)

Since we have not specified a template yet, we have to do this now. We use our
div based form template:

  >>> import os
  >>> import z3c.formui
  >>> divFormTemplate = os.path.join(os.path.dirname(z3c.formui.__file__),
  ...     'div-form.pt')

  >>> from z3c.template.template import TemplateFactory
  >>> divFormFactory = TemplateFactory(divFormTemplate, 'text/html')

Now register the form (content) template:

  >>> import zope.interface
  >>> import zope.component
  >>> from z3c.template.interfaces import IContentTemplate
  >>> zope.component.provideAdapter(divFormFactory,
  ...     (zope.interface.Interface, IDivFormLayer),
  ...     IContentTemplate)

And let's define a layout template which simply calls the render method. For a
more advanced content/layout render concept see z3c.pagelet.

  >>> import tempfile
  >>> temp_dir = tempfile.mkdtemp()

  >>> myLayout = os.path.join(temp_dir, 'myLayout.pt')
  >>> with open(myLayout, 'w') as file:
  ...     _ = file.write('''<html>
  ...   <body>
  ...     <tal:block content="structure view/render">
  ...       content
  ...     </tal:block>
  ...   </body>
  ... </html>''')
  >>> myLayoutFactory = TemplateFactory(myLayout, 'text/html')

  >>> from z3c.template.interfaces import ILayoutTemplate
  >>> zope.component.provideAdapter(myLayoutFactory,
  ...     (zope.interface.Interface, zope.interface.Interface), ILayoutTemplate)

Now we can get our layout template:

  >>> layout = zope.component.getMultiAdapter((addForm, divRequest),
  ...     ILayoutTemplate)

  >>> layout.__class__.__name__
  'ViewPageTemplateFile'

  >>> os.path.basename(layout.filename)
  'myLayout.pt'


DIV-based Layout
----------------

Let's now render the page. Note the output doesn't contain the layout template:

  >>> addForm.update()
  >>> print(addForm.render())
  <form action="http://127.0.0.1" method="post"
          enctype="multipart/form-data" class="edit-form"
          name="form" id="form">
    <div class="viewspace">
      <div class="required-info">
        <span class="required">*</span> &ndash; required
      </div>
      <div>
        <div id="form-widgets-name-row" class="row required">
          <div class="label">
            <label for="form-widgets-name">
              <span>Name</span>
              <span class="required">*</span>
            </label>
          </div>
          <div class="widget"><input type="text" id="form-widgets-name"
                   name="form.widgets.name"
                   class="text-widget required textline-field" value="" />
          </div>
        </div>
        <div id="form-widgets-age-row" class="row">
          <div class="label">
            <label for="form-widgets-age">
              <span>Age</span>
            </label>
          </div>
          <div class="widget"><input type="text" id="form-widgets-age"
                   name="form.widgets.age" class="text-widget int-field"
                   value="20" />
          </div>
        </div>
      </div>
    </div>
    <div>
      <div class="buttons">
        <input type="submit" id="form-buttons-add"
               name="form.buttons.add"
               class="submit-widget button-field" value="Add" />
      </div>
    </div>
  </form>

But we can call our form which uses the new layout template which renders
the form within the div-form content template:

  >>> print(addForm())
  <html>
    <body>
      <form action="http://127.0.0.1" method="post"
        enctype="multipart/form-data" class="edit-form"
        name="form" id="form">
        <div class="viewspace">
          <div class="required-info">
            <span class="required">*</span>
            &ndash; required
          </div>
          <div>
            <div id="form-widgets-name-row" class="row required">
              <div class="label">
                <label for="form-widgets-name">
                  <span>Name</span>
                  <span class="required">*</span>
                </label>
              </div>
              <div class="widget"><input type="text" id="form-widgets-name"
                   name="form.widgets.name"
                   class="text-widget required textline-field" value="" />
              </div>
            </div>
            <div id="form-widgets-age-row" class="row">
              <div class="label">
                <label for="form-widgets-age">
                  <span>Age</span>
                </label>
              </div>
              <div class="widget"><input type="text" id="form-widgets-age"
                   name="form.widgets.age" class="text-widget int-field"
                   value="20" />
              </div>
            </div>
          </div>
        </div>
        <div>
          <div class="buttons">
            <input type="submit" id="form-buttons-add"
             name="form.buttons.add"
             class="submit-widget button-field" value="Add" />
          </div>
        </div>
      </form>
    </body>
  </html>


Table-based Forms
-----------------

There is a table based layout too. Let's define the template and use them:

  >>> from z3c.formui.interfaces import ITableFormLayer
  >>> tableFormTemplate = os.path.join(os.path.dirname(z3c.formui.__file__),
  ...     'table-form.pt')

  >>> from z3c.template.template import TemplateFactory
  >>> tableFormFactory = TemplateFactory(tableFormTemplate, 'text/html')

Now register the form (content) template:

  >>> zope.component.provideAdapter(tableFormFactory,
  ...     (zope.interface.Interface, ITableFormLayer), IContentTemplate)

Patch the request and call the form again:

  >>> tableRequest = TestRequest()
  >>> alsoProvides(tableRequest, ITableFormLayer)

Now our new request should know the table based form template:

  >>> addForm = PersonAddForm(root, tableRequest)
  >>> print(addForm())
  <html>
    <body>
      <form action="http://127.0.0.1" method="post"
        enctype="multipart/form-data" class="edit-form"
        name="form" id="form">
        <div class="viewspace">
          <div class="required-info">
            <span class="required">*</span>
            &ndash; required
          </div>
          <div>
          <table class="form-fields">
                <tr class="row required">
                  <td class="label">
                    <label for="form-widgets-name">
                      <span>Name</span>
                      <span class="required"> * </span>
                    </label>
                  </td>
                  <td class="field">
                    <div class="widget"><input type="text" id="form-widgets-name"
                         name="form.widgets.name"
                         class="text-widget required textline-field" value="" />
                    </div>
                  </td>
                </tr>
                <tr class="row">
                  <td class="label">
                    <label for="form-widgets-age">
                      <span>Age</span>
                    </label>
                  </td>
                  <td class="field">
                    <div class="widget"><input type="text" id="form-widgets-age"
                         name="form.widgets.age" class="text-widget int-field"
                         value="20" />
                    </div>
                  </td>
                </tr>
          </table>
        </div>
      </div>
      <div>
        <div class="buttons">
          <input type="submit" id="form-buttons-add"
         name="form.buttons.add"
         class="submit-widget button-field" value="Add" />
        </div>
      </div>
      </form>
    </body>
  </html>


`AddForm` rendering for `IAdding`
---------------------------------

The `z3c.formui` package also provides a layout-aware version of
`z3c.form.adding.AddForm` which can be used for creating forms for the
`zope.app.container.interfaces.IAdding` mechanism.

Let's check its template support. First, create the form for an `Adding`
instance. We just need to define the ``create()`` method, because the default
``add()`` and ``nextURL()`` methods are already defined using the `Adding`
object.

  >>> from z3c.formui import adding
  >>> class AddingPersonAddForm(adding.AddForm):
  ...
  ...     fields = field.Fields(IPerson)
  ...
  ...     def create(self, data):
  ...         return Person(**data)


Let's now instantiate the "fake" adding component and the add form:

  >>> class Adding(object):
  ...     def __init__(self, context, request):
  ...         self.context = context
  ...         self.request = request
  >>> rootAdding = Adding(root, divRequest)

  >>> addForm = AddingPersonAddForm(rootAdding, divRequest)

First, let's ensure that we can lookup a layout template for the form:

  >>> layout = zope.component.getMultiAdapter(
  ...     (addForm, divRequest), ILayoutTemplate)

  >>> layout.__class__.__name__
  'ViewPageTemplateFile'

Okay, that worked. Let's now render the div-based addform:

  >>> print(addForm())
  <html>
    <body>
      <form action="http://127.0.0.1" method="post"
        enctype="multipart/form-data" class="edit-form"
        name="form" id="form">
        <div class="viewspace">
          <div class="required-info">
            <span class="required">*</span>
            &ndash; required
          </div>
          <div>
            <div id="form-widgets-name-row" class="row required">
              <div class="label">
                <label for="form-widgets-name">
                  <span>Name</span>
                  <span class="required">*</span>
                </label>
              </div>
              <div class="widget"><input type="text" id="form-widgets-name"
                   name="form.widgets.name"
                   class="text-widget required textline-field" value="" />
              </div>
            </div>
            <div id="form-widgets-age-row" class="row">
              <div class="label">
                <label for="form-widgets-age">
                  <span>Age</span>
                </label>
              </div>
              <div class="widget"><input type="text" id="form-widgets-age"
                   name="form.widgets.age" class="text-widget int-field"
                   value="20" />
              </div>
            </div>
          </div>
        </div>
        <div>
          <div class="buttons">
            <input type="submit" id="form-buttons-add"
             name="form.buttons.add"
             class="submit-widget button-field" value="Add" />
          </div>
        </div>
      </form>
    </body>
  </html>

Okay, now we are going to check table layout support.

  >>> rootAdding = Adding(root, tableRequest)
  >>> addForm = AddingPersonAddForm(rootAdding, tableRequest)

Again, the layout should be available:

  >>> layout = zope.component.getMultiAdapter((addForm, tableRequest),
  ...     ILayoutTemplate)

  >>> layout.__class__.__name__
  'ViewPageTemplateFile'

Let's now render the form:

  >>> print(addForm())
  <html>
    <body>
      <form action="http://127.0.0.1" method="post"
        enctype="multipart/form-data" class="edit-form"
        name="form" id="form">
        <div class="viewspace">
          <div class="required-info">
            <span class="required">*</span>
            &ndash; required
          </div>
          <div>
          <table class="form-fields">
                <tr class="row required">
                  <td class="label">
                    <label for="form-widgets-name">
                      <span>Name</span>
                      <span class="required"> * </span>
                    </label>
                  </td>
                  <td class="field">
                    <div class="widget"><input type="text" id="form-widgets-name"
                         name="form.widgets.name"
                         class="text-widget required textline-field" value="" />
                    </div>
                  </td>
                </tr>
                <tr class="row">
                  <td class="label">
                    <label for="form-widgets-age">
                      <span>Age</span>
                    </label>
                  </td>
                  <td class="field">
                    <div class="widget"><input type="text" id="form-widgets-age"
                         name="form.widgets.age" class="text-widget int-field"
                         value="20" />
                    </div>
                  </td>
                </tr>
          </table>
        </div>
      </div>
      <div>
        <div class="buttons">
          <input type="submit" id="form-buttons-add"
         name="form.buttons.add"
         class="submit-widget button-field" value="Add" />
        </div>
      </div>
      </form>
    </body>
  </html>


Form Macros
-----------

Load the configuration, which will make sure that all macros get registered
correctly:

  >>> from zope.configuration import xmlconfig
  >>> import zope.component
  >>> import zope.viewlet
  >>> import zope.security
  >>> import zope.publisher
  >>> import zope.browserresource
  >>> import z3c.macro
  >>> import z3c.template
  >>> import z3c.formui
  >>> xmlconfig.XMLConfig('meta.zcml', zope.component)()
  >>> xmlconfig.XMLConfig('meta.zcml', zope.viewlet)()
  >>> xmlconfig.XMLConfig('meta.zcml', zope.security)()
  >>> xmlconfig.XMLConfig('meta.zcml', zope.publisher)()
  >>> xmlconfig.XMLConfig('meta.zcml', zope.browserresource)()
  >>> xmlconfig.XMLConfig('meta.zcml', z3c.macro)()
  >>> xmlconfig.XMLConfig('meta.zcml', z3c.template)()
  >>> xmlconfig.XMLConfig('configure.zcml', z3c.formui)()

Div IContentTemplate
--------------------

Create some dummy form discriminators for calling div layout templates and
macros and check the div IContentTemplates:

  >>> objects = (addForm, divRequest)
  >>> zope.component.getMultiAdapter(objects, IContentTemplate).filename
  '...div-form.pt'

  >>> objects = (form.DisplayForm(None, None), divRequest)
  >>> zope.component.getMultiAdapter(objects, IContentTemplate, '').filename
  '...div-form-display.pt'

We offer the following named IContentTemplate:

  >>> objects = (form.DisplayForm(None, None), divRequest)
  >>> zope.component.getMultiAdapter(objects, IContentTemplate,
  ...     'display').filename
  '...div-form-display.pt'

  >>> objects = (form.DisplayForm(None, None), divRequest)
  >>> zope.component.getMultiAdapter(objects, IContentTemplate,
  ...     'subform').filename
  '...subform.pt'


Table ILayoutTemplate
---------------------

There is one generic layout template to build sub forms:

  >>> objects = (form.DisplayForm(None, None), divRequest)
  >>> zope.component.getMultiAdapter(objects, ILayoutTemplate,
  ...     'subform').filename
  '...subform-layout.pt'


Div layout macros
-----------------

We have different form macros available for IInputForm:


  >>> from z3c.macro.interfaces import IMacroTemplate
  >>> objects = (None, addForm, divRequest)
  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form')
  [...div-form.pt'), ...metal:define-macro': 'form'...


  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'subform')
  [...div-form.pt'), ...define-macro': 'subform'...


  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-label')
  [...div-form.pt'), ...define-macro': 'label'...


  >>> zope.component.getMultiAdapter(
  ...     objects, IMacroTemplate, 'form-required-info')
  [...div-form.pt'), ...define-macro', 'required-info'...


  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-header')
  [...div-form.pt'), ...define-macro': 'header'...


  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-errors')
  [...div-form.pt'), ...define-macro': 'errors'...


  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'widget-rows')
  [...div-form.pt'), ...define-macro': 'widget-rows'...


  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'widget-row')
  [...div-form.pt'), ...define-macro': 'widget-row'...


  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-groups')
  [...div-form.pt'), ...define-macro': 'groups'...


  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-buttons')
  [...div-form.pt'), ...define-macro', 'buttons'...


And we have different form macros available for IDisplayForm:

  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'subform-display')
  [...div-form-display.pt'), ...define-macro': 'subform-display'...


Table IContentTemplate
----------------------

Create some dummy form discriminators for calling table layout templates and
macros and check the div IContentTemplates:

  >>> objects = (addForm, tableRequest)
  >>> zope.component.getMultiAdapter(objects, IContentTemplate, '').filename
  '...table-form.pt'

  >>> objects = (form.DisplayForm(None, None), tableRequest)
  >>> zope.component.getMultiAdapter(objects, IContentTemplate, '').filename
  '...table-form-display.pt'

We offer the following named IContentTemplate:

  >>> objects = (form.DisplayForm(None, None), tableRequest)
  >>> zope.component.getMultiAdapter(objects, IContentTemplate,
  ...     'display').filename
  '...table-form-display.pt'

  >>> objects = (form.DisplayForm(None, None), tableRequest)
  >>> zope.component.getMultiAdapter(objects, IContentTemplate,
  ...     'subform').filename
  '...subform.pt'



Table ILayoutTemplate
---------------------

There is one generic layout template to build sub forms:

  >>> objects = (form.DisplayForm(None, None), tableRequest)
  >>> zope.component.getMultiAdapter(objects, ILayoutTemplate,
  ...     'subform').filename
  '...subform-layout.pt'


Table layout macros
-------------------

We have different form macros available for IInputForm:

  >>> objects = (None, addForm, tableRequest)
  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form')
  [...table-form.pt'), ...metal:define-macro': 'form'...


  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'subform')
  [...table-form.pt'), ...define-macro': 'subform'...


  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-label')
  [...table-form.pt'), ...define-macro': 'label'...


  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-required-info')
  [...table-form.pt'), ...define-macro', 'required-info'...


  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-header')
  [...table-form.pt'), ...define-macro': 'header'...


  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-errors')
  [...table-form.pt'), ...define-macro': 'errors'...


  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-table')
  [...table-form.pt'), ...define-macro', 'formtable'...


  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-row')
  [...table-form.pt'), ...define-macro': 'formrow'...


  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-label-cell')
  [...table-form.pt'), ...define-macro', 'labelcell'...


  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-widget-cell')
  [...table-form.pt'), ...define-macro', 'widgetcell'...


  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-groups')
  [...table-form.pt'), ...define-macro': 'groups'...


  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-buttons')
  [...table-form.pt'), ...define-macro', 'buttons'...


And we have different form macros available for IDisplayForm:

  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'subform-display')
  [...table-form-display.pt'), ...define-macro': 'subform-display'...


Subform
-------

Let's give a quick overview how subform content and layout templates get used:
First define a new form which uses the template getter methods offered
from z3.template

  >>> from z3c.template.template import getPageTemplate, getLayoutTemplate

The ``provider`` TALES expression which is a part of the lookup concept
was already registered by the testing setup, so we don't need to do it
here.

and the TALES expression called ``macro`` which can lookup our macro adapters.
Yes, macros are adapters in our content/layout template concept. See z3c.macro
for more information about the implementation. However, we already registered
the ``macro`` type in the testing setup, as it's needed for rendering form
templates.

and at least we need a pagelet
renderer. By default we use the provider called ``PageletRenderer`` defined
in the z3c.pagelet package. But right now, we don't have a dependency on
this package. So let's implement a simple renderer and use them as a
IContentProvider:

  >>> class PageletRenderer(object):
  ...     zope.component.adapts(zope.interface.Interface,
  ...         zope.publisher.interfaces.browser.IBrowserRequest,
  ...         zope.interface.Interface)
  ...
  ...     def __init__(self, context, request, pagelet):
  ...         self.__updated = False
  ...         self.__parent__ = pagelet
  ...         self.context = context
  ...         self.request = request
  ...
  ...     def update(self):
  ...         pass
  ...
  ...     def render(self):
  ...         return self.__parent__.render()

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

Now define the form:

  >>> class PersonEditForm(form.EditForm):
  ...     """Edit form including layout support. See z3c.formui.form."""
  ...
  ...     template = getPageTemplate('subform')
  ...     layout = getLayoutTemplate('subform')
  ...
  ...     fields = field.Fields(IPerson)

Now we can render the form with our previous created person instance:

  >>> person = Person('Jessy', 6)
  >>> editForm = PersonEditForm(person, divRequest)

Now we call the form which will update and render it:

  >>> print(editForm())
  <div class="viewspace">
    <div class="required-info">
      <span class="required">*</span>
      &ndash; required
    </div>
    <div>
      <div id="form-widgets-name-row" class="row required">
        <div class="label">
          <label for="form-widgets-name">
            <span>Name</span>
            <span class="required">*</span>
          </label>
        </div>
        <div class="widget"><input type="text" id="form-widgets-name"
             name="form.widgets.name"
             class="text-widget required textline-field"
             value="Jessy" />
        </div>
      </div>
      <div id="form-widgets-age-row" class="row">
        <div class="label">
          <label for="form-widgets-age">
            <span>Age</span>
          </label>
        </div>
        <div class="widget"><input type="text" id="form-widgets-age"
           name="form.widgets.age" class="text-widget int-field"
           value="6" />
        </div>
      </div>
    </div>
  </div>
  <div>
    <div class="buttons">
      <input type="submit" id="form-buttons-apply"
             name="form.buttons.apply"
             class="submit-widget button-field" value="Apply" />
    </div>
  </div>

You can see that the form above is a real subform. It doesn't define the form
tag which makes it usable as a subform in parent forms.

Of course this works with table layout based forms too. Let's use our table
request and render the form again:

  >>> editForm = PersonEditForm(person, tableRequest)
  >>> print(editForm())
  <div class="viewspace">
    <div class="required-info">
      <span class="required">*</span>
      &ndash; required
    </div>
    <div>
      <table class="form-fields">
        <tr class="row required">
          <td class="label">
            <label for="form-widgets-name">
              <span>Name</span>
              <span class="required"> * </span>
            </label>
          </td>
          <td class="field">
            <div class="widget"><input type="text" id="form-widgets-name"
                 name="form.widgets.name"
                 class="text-widget required textline-field"
                 value="Jessy" />
            </div>
          </td>
        </tr>
        <tr class="row">
          <td class="label">
            <label for="form-widgets-age">
              <span>Age</span>
            </label>
          </td>
          <td class="field">
            <div class="widget"><input type="text" id="form-widgets-age"
                 name="form.widgets.age" class="text-widget int-field"
                 value="6" />
            </div>
          </td>
        </tr>
      </table>
    </div>
  </div>
  <div>
    <div class="buttons">
      <input type="submit" id="form-buttons-apply"
             name="form.buttons.apply"
            class="submit-widget button-field" value="Apply" />
    </div>
  </div>

Redirection
-----------

The form doesn't bother rendering itself and its layout when
request is a redirection as the rendering doesn't make any sense with
browser requests in that case. Let's create a view that does a
redirection in its update method:

 >>> class RedirectingView(PersonEditForm):
 ...     def update(self):
 ...         super(RedirectingView, self).update()
 ...         self.request.response.redirect('.')

It will return an empty string when called as a browser page.

 >>> redirectView = RedirectingView(person, divRequest)
 >>> redirectView() == ''
 True

However, the ``render`` method will render form's template as usual:

 >>> '<div class="viewspace">' in redirectView.render()
 True

The same thing should work for AddForms:

 >>> class RedirectingAddView(PersonAddForm):
 ...     def update(self):
 ...         super(RedirectingAddView, self).update()
 ...         self.request.response.redirect('.')
 >>> redirectView = RedirectingAddView(person, divRequest)
 >>> redirectView() == ''
 True

No required fields
------------------

If there no required fields in the form, standard templates won't render
the "required-info" hint.

  >>> class IAdditionalInfo(zope.interface.Interface):
  ...
  ...     location = zope.schema.TextLine(title='Location', required=False)
  ...     about = zope.schema.Text(title='About', required=False)

  >>> class AdditionalInfoForm(form.AddForm):
  ...
  ...     fields = field.Fields(IAdditionalInfo)

  >>> additionalInfoForm = AdditionalInfoForm(root, divRequest)
  >>> additionalInfoForm.update()
  >>> '<div class="required-info">' in additionalInfoForm.render()
  False

  >>> additionalInfoForm = AdditionalInfoForm(root, tableRequest)
  >>> additionalInfoForm.update()
  >>> '<div class="required-info">' in additionalInfoForm.render()
  False

Cleanup
-------

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


=======
CHANGES
=======

4.1 (2024-10-22)
----------------

- Add support for Python 3.12, 3.13.

- Drop support for Python 3.7.


4.0 (2023-02-24)
----------------

- Drop support for Python 2.7, 3.4, 3.5, 3.6.

- Add support for Python 3.8, 3.9, 3.10, 3.11.


3.1 (2018-11-15)
----------------

- Add support for Python 3.4, 3.5, 3.6, 3.7.

- Drop support for Python 2.6 and 3.3.


3.0.0 (2015-11-09)
------------------

- Standardize namespace __init__

- Split config, mostly to be able to include in pyramid without browser
  resources and viewlets


3.0.0a2 (2013-02-26)
--------------------

- Added missing version Trove classifiers.


3.0.0a1 (2013-02-25)
--------------------

- Added support for Python 3.3.

- Dropped support for Python 2.4 and 2.5.


2.3.0 (2012-03-15)
------------------

- Feature: Mark a widget row with the "required" class when the widget is
  required. Similarly, when the widget has an error attached, add the "error"
  class to the widget row. That allows you to change the styles of the label
  and the widget if it is reuqired.


2.2.1 (2012-01-09)
------------------

- No longer using deprecated ``zope.testing.doctest`` but built-in
  ``doctest`` instead.

- Fixed tests so they do not break for `z3c.form` 2.5.0.


2.2.0 (2009-12-28)
------------------

- Fixed tests so they do not break for `z3c.form` 2.2.0.

- Using ``requiredInfo`` property (introduced in `z3c.form` 2.0.0) to
  render the information about required fields. This property returns
  an i18n message id making the information translateable.

- Added support for groups containing groups: They get displayed now.

2.1.0 (2009-09-01)
------------------

- Feature: Don't show required info hint if there's no required fields.

- Bug: Don't render add forms when redirecting as well.

- Bug: Fix redirection tests with newer zope.publisher that restricts
  untrusted redirects to different domains.

2.0.0 (2009-06-14)
------------------

- Feature: Added support for context-specific template lookup, introduced in
  `z3c.template` 1.2.0 - templates can now be registered using (view, request,
  context) discriminator.

- Feature: Added support for `z3c.pt` templates using `z3c.ptcompat`
  compatibility package.

- Feature: Added layout support for `IAdding` component based add forms.

- Feature: Added CSS for multi-widget which was added in `z3c.form` 2.0.0.

- Bug: Changed usage of ``template/macros/*`` to ``macro:*``, because the
  first one doesn't work when we override a form template and use the form
  macro, registered with this package.

- Bug: Don't do rendering in form's `__call__` method when request is a
  redirection.

- Bug: Reformatted long-description to render properly on pypi.


1.4.2 (2008-08-26)
------------------

- Bug: Corrected typos and unwanted unicode characters.


1.4.1 (2008-01-23)
------------------

- Bug: Fixed up meta-data and the release.


1.4.0 (2008-01-21)
------------------

- Feature: Added subform content and layout template. This allows you to
  configure real sub forms which do not render the form tag.

- Feature: Improve layout implementation, support built-in layout templates.

- Feature: Use ``IContentTemplate`` instead of ``IPageTemplate`` in layout
  base classes. This will help to prevent running into recursion errors if
  there is a missing layout template.

- Feature: Added form module which offers built-in layout support.

- Bug: Added missing display ``IContentTemplate``, otherwise we can run into a
  recursion in some cases.

- Bug: Renamed table macro argument from ``form-required-info`` to
  ``required-info``. The macro ``form-required-info`` did not exist.

- Bug: Added unit tests for layout support.

- Bug: Added tests for layout macros.

- Bug: Added tests for layout templates.


1.3.0 (2007-08-24)
------------------

- Refactoring: Update CSS classes to reflect latest changes to the widget
  classes in ``z3c.form``.

- Bug: Error view snippets may have a empty ``widget`` attribute values, so we
  cannot rely on accessing the label of the widget. This is the case, if the
  error view sniipet was created from an invariants validation error.

- Bug: The table-form template did not properly render the error at the
  widget, because the ``render()`` method was not called. Thanks to Markus
  Leist for the report.


1.2.0 (2007-07-18)
------------------

- Feature: The row div element now also has an id of the form
  "<widget-id>-row".


1.1.1 (2007-07-04)
------------------

- Refactoring: Split up registrations for simpler management of UI
  components. This also makes it easier to see for developers how to create a
  new template for forms.


1.1.0 (2007-06-29)
------------------

- Feature: Registered all defined macros for each form template. Also, added
  more slots to the templates providing more hooks for customization.

- Feature: Added a macro/slot for the "required info", which explains how
  required fields are marked.

- Feature: Added support for form labels.

- Feature: Added support for groups to templates.


1.0.1 (2007-06-22)
------------------

- Bug: Make sure we use the id for the "for" attribute of the "label"
  element and not the name. This has worked until recently, because the
  name and id were the same, but they are different now.


1.0.0 (2007-05-24)
------------------

- Initial Release

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/zopefoundation/z3c.formui",
    "name": "z3c.formui",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "zope3 form widget",
    "author": "Stephan Richter, Roger Ineichen and the Zope Community",
    "author_email": "zope-dev@zope.dev",
    "download_url": "https://files.pythonhosted.org/packages/31/25/137165cd69dfd55c71211024cacd8429043a7d0ed75bc88f20913adab61c/z3c.formui-4.1.tar.gz",
    "platform": null,
    "description": "This package provides a set of default layouts for the ``z3c.form``\nframework. In particular it provides a DIV-based and a TABLE-based layout. The\ndeveloper can use either layout by inheriting from a different base layer.\n\nThe package also has some support for layout/pagelet templates.\n\n\n.. contents::\n\n====================\nForm User Interfaces\n====================\n\nThis package provides several useful templates to get a quick start with the\n``z3c.form`` package. Previous form frameworks always included default\ntemplates that were implemented in a particular user-interface development\npattern. If you wanted to use an alternative strategy to develop user\ninterfaces, it was often tedious to do so. This package aims to provide some\noptions without requiring them for the basic framework.\n\n\nLayout Template Support\n-----------------------\n\nOne common pattern in Zope 3 user interface development is the use of layout\ntemplates (see z3c.template). This package provides some mixin classes to the\nregular form classes to support layout-based templating.\n\n  >>> from z3c.form import testing\n  >>> testing.setupFormDefaults()\n\nBefore we can start writing forms, we must have the content to work with:\n\n  >>> import zope.interface\n  >>> import zope.schema\n  >>> class IPerson(zope.interface.Interface):\n  ...\n  ...     name = zope.schema.TextLine(\n  ...         title='Name',\n  ...         required=True)\n  ...\n  ...     age = zope.schema.Int(\n  ...         title='Age',\n  ...         description=u\"The person's age.\",\n  ...         min=0,\n  ...         default=20,\n  ...         required=False)\n\n  >>> from zope.schema.fieldproperty import FieldProperty\n  >>> @zope.interface.implementer(IPerson)\n  ... class Person(object):\n  ...     name = FieldProperty(IPerson['name'])\n  ...     age = FieldProperty(IPerson['age'])\n  ...\n  ...     def __init__(self, name, age):\n  ...         self.name = name\n  ...         self.age = age\n  ...\n  ...     def __repr__(self):\n  ...         return '<%s %r>' % (self.__class__.__name__, self.name)\n\nOkay, that should suffice for now. Let's now create a working add form:\n\n  >>> from z3c.form import field\n  >>> from z3c.formui import form, layout\n  >>> class PersonAddForm(form.AddForm):\n  ...\n  ...     fields = field.Fields(IPerson)\n  ...\n  ...     def create(self, data):\n  ...         return Person(**data)\n  ...\n  ...     def add(self, object):\n  ...         self.context[object.id] = object\n  ...\n  ...     def nextURL(self):\n  ...         return 'index.html'\n\nLet's create a request:\n\n  >>> from z3c.form.testing import TestRequest\n  >>> from zope.interface import alsoProvides\n  >>> divRequest = TestRequest()\n\nAnd support the div form layer for our request:\n\n  >>> from z3c.formui.interfaces import IDivFormLayer\n  >>> alsoProvides(divRequest, IDivFormLayer)\n\nNow create the form:\n\n  >>> addForm = PersonAddForm(root, divRequest)\n\nSince we have not specified a template yet, we have to do this now. We use our\ndiv based form template:\n\n  >>> import os\n  >>> import z3c.formui\n  >>> divFormTemplate = os.path.join(os.path.dirname(z3c.formui.__file__),\n  ...     'div-form.pt')\n\n  >>> from z3c.template.template import TemplateFactory\n  >>> divFormFactory = TemplateFactory(divFormTemplate, 'text/html')\n\nNow register the form (content) template:\n\n  >>> import zope.interface\n  >>> import zope.component\n  >>> from z3c.template.interfaces import IContentTemplate\n  >>> zope.component.provideAdapter(divFormFactory,\n  ...     (zope.interface.Interface, IDivFormLayer),\n  ...     IContentTemplate)\n\nAnd let's define a layout template which simply calls the render method. For a\nmore advanced content/layout render concept see z3c.pagelet.\n\n  >>> import tempfile\n  >>> temp_dir = tempfile.mkdtemp()\n\n  >>> myLayout = os.path.join(temp_dir, 'myLayout.pt')\n  >>> with open(myLayout, 'w') as file:\n  ...     _ = file.write('''<html>\n  ...   <body>\n  ...     <tal:block content=\"structure view/render\">\n  ...       content\n  ...     </tal:block>\n  ...   </body>\n  ... </html>''')\n  >>> myLayoutFactory = TemplateFactory(myLayout, 'text/html')\n\n  >>> from z3c.template.interfaces import ILayoutTemplate\n  >>> zope.component.provideAdapter(myLayoutFactory,\n  ...     (zope.interface.Interface, zope.interface.Interface), ILayoutTemplate)\n\nNow we can get our layout template:\n\n  >>> layout = zope.component.getMultiAdapter((addForm, divRequest),\n  ...     ILayoutTemplate)\n\n  >>> layout.__class__.__name__\n  'ViewPageTemplateFile'\n\n  >>> os.path.basename(layout.filename)\n  'myLayout.pt'\n\n\nDIV-based Layout\n----------------\n\nLet's now render the page. Note the output doesn't contain the layout template:\n\n  >>> addForm.update()\n  >>> print(addForm.render())\n  <form action=\"http://127.0.0.1\" method=\"post\"\n          enctype=\"multipart/form-data\" class=\"edit-form\"\n          name=\"form\" id=\"form\">\n    <div class=\"viewspace\">\n      <div class=\"required-info\">\n        <span class=\"required\">*</span> &ndash; required\n      </div>\n      <div>\n        <div id=\"form-widgets-name-row\" class=\"row required\">\n          <div class=\"label\">\n            <label for=\"form-widgets-name\">\n              <span>Name</span>\n              <span class=\"required\">*</span>\n            </label>\n          </div>\n          <div class=\"widget\"><input type=\"text\" id=\"form-widgets-name\"\n                   name=\"form.widgets.name\"\n                   class=\"text-widget required textline-field\" value=\"\" />\n          </div>\n        </div>\n        <div id=\"form-widgets-age-row\" class=\"row\">\n          <div class=\"label\">\n            <label for=\"form-widgets-age\">\n              <span>Age</span>\n            </label>\n          </div>\n          <div class=\"widget\"><input type=\"text\" id=\"form-widgets-age\"\n                   name=\"form.widgets.age\" class=\"text-widget int-field\"\n                   value=\"20\" />\n          </div>\n        </div>\n      </div>\n    </div>\n    <div>\n      <div class=\"buttons\">\n        <input type=\"submit\" id=\"form-buttons-add\"\n               name=\"form.buttons.add\"\n               class=\"submit-widget button-field\" value=\"Add\" />\n      </div>\n    </div>\n  </form>\n\nBut we can call our form which uses the new layout template which renders\nthe form within the div-form content template:\n\n  >>> print(addForm())\n  <html>\n    <body>\n      <form action=\"http://127.0.0.1\" method=\"post\"\n        enctype=\"multipart/form-data\" class=\"edit-form\"\n        name=\"form\" id=\"form\">\n        <div class=\"viewspace\">\n          <div class=\"required-info\">\n            <span class=\"required\">*</span>\n            &ndash; required\n          </div>\n          <div>\n            <div id=\"form-widgets-name-row\" class=\"row required\">\n              <div class=\"label\">\n                <label for=\"form-widgets-name\">\n                  <span>Name</span>\n                  <span class=\"required\">*</span>\n                </label>\n              </div>\n              <div class=\"widget\"><input type=\"text\" id=\"form-widgets-name\"\n                   name=\"form.widgets.name\"\n                   class=\"text-widget required textline-field\" value=\"\" />\n              </div>\n            </div>\n            <div id=\"form-widgets-age-row\" class=\"row\">\n              <div class=\"label\">\n                <label for=\"form-widgets-age\">\n                  <span>Age</span>\n                </label>\n              </div>\n              <div class=\"widget\"><input type=\"text\" id=\"form-widgets-age\"\n                   name=\"form.widgets.age\" class=\"text-widget int-field\"\n                   value=\"20\" />\n              </div>\n            </div>\n          </div>\n        </div>\n        <div>\n          <div class=\"buttons\">\n            <input type=\"submit\" id=\"form-buttons-add\"\n             name=\"form.buttons.add\"\n             class=\"submit-widget button-field\" value=\"Add\" />\n          </div>\n        </div>\n      </form>\n    </body>\n  </html>\n\n\nTable-based Forms\n-----------------\n\nThere is a table based layout too. Let's define the template and use them:\n\n  >>> from z3c.formui.interfaces import ITableFormLayer\n  >>> tableFormTemplate = os.path.join(os.path.dirname(z3c.formui.__file__),\n  ...     'table-form.pt')\n\n  >>> from z3c.template.template import TemplateFactory\n  >>> tableFormFactory = TemplateFactory(tableFormTemplate, 'text/html')\n\nNow register the form (content) template:\n\n  >>> zope.component.provideAdapter(tableFormFactory,\n  ...     (zope.interface.Interface, ITableFormLayer), IContentTemplate)\n\nPatch the request and call the form again:\n\n  >>> tableRequest = TestRequest()\n  >>> alsoProvides(tableRequest, ITableFormLayer)\n\nNow our new request should know the table based form template:\n\n  >>> addForm = PersonAddForm(root, tableRequest)\n  >>> print(addForm())\n  <html>\n    <body>\n      <form action=\"http://127.0.0.1\" method=\"post\"\n        enctype=\"multipart/form-data\" class=\"edit-form\"\n        name=\"form\" id=\"form\">\n        <div class=\"viewspace\">\n          <div class=\"required-info\">\n            <span class=\"required\">*</span>\n            &ndash; required\n          </div>\n          <div>\n          <table class=\"form-fields\">\n                <tr class=\"row required\">\n                  <td class=\"label\">\n                    <label for=\"form-widgets-name\">\n                      <span>Name</span>\n                      <span class=\"required\"> * </span>\n                    </label>\n                  </td>\n                  <td class=\"field\">\n                    <div class=\"widget\"><input type=\"text\" id=\"form-widgets-name\"\n                         name=\"form.widgets.name\"\n                         class=\"text-widget required textline-field\" value=\"\" />\n                    </div>\n                  </td>\n                </tr>\n                <tr class=\"row\">\n                  <td class=\"label\">\n                    <label for=\"form-widgets-age\">\n                      <span>Age</span>\n                    </label>\n                  </td>\n                  <td class=\"field\">\n                    <div class=\"widget\"><input type=\"text\" id=\"form-widgets-age\"\n                         name=\"form.widgets.age\" class=\"text-widget int-field\"\n                         value=\"20\" />\n                    </div>\n                  </td>\n                </tr>\n          </table>\n        </div>\n      </div>\n      <div>\n        <div class=\"buttons\">\n          <input type=\"submit\" id=\"form-buttons-add\"\n         name=\"form.buttons.add\"\n         class=\"submit-widget button-field\" value=\"Add\" />\n        </div>\n      </div>\n      </form>\n    </body>\n  </html>\n\n\n`AddForm` rendering for `IAdding`\n---------------------------------\n\nThe `z3c.formui` package also provides a layout-aware version of\n`z3c.form.adding.AddForm` which can be used for creating forms for the\n`zope.app.container.interfaces.IAdding` mechanism.\n\nLet's check its template support. First, create the form for an `Adding`\ninstance. We just need to define the ``create()`` method, because the default\n``add()`` and ``nextURL()`` methods are already defined using the `Adding`\nobject.\n\n  >>> from z3c.formui import adding\n  >>> class AddingPersonAddForm(adding.AddForm):\n  ...\n  ...     fields = field.Fields(IPerson)\n  ...\n  ...     def create(self, data):\n  ...         return Person(**data)\n\n\nLet's now instantiate the \"fake\" adding component and the add form:\n\n  >>> class Adding(object):\n  ...     def __init__(self, context, request):\n  ...         self.context = context\n  ...         self.request = request\n  >>> rootAdding = Adding(root, divRequest)\n\n  >>> addForm = AddingPersonAddForm(rootAdding, divRequest)\n\nFirst, let's ensure that we can lookup a layout template for the form:\n\n  >>> layout = zope.component.getMultiAdapter(\n  ...     (addForm, divRequest), ILayoutTemplate)\n\n  >>> layout.__class__.__name__\n  'ViewPageTemplateFile'\n\nOkay, that worked. Let's now render the div-based addform:\n\n  >>> print(addForm())\n  <html>\n    <body>\n      <form action=\"http://127.0.0.1\" method=\"post\"\n        enctype=\"multipart/form-data\" class=\"edit-form\"\n        name=\"form\" id=\"form\">\n        <div class=\"viewspace\">\n          <div class=\"required-info\">\n            <span class=\"required\">*</span>\n            &ndash; required\n          </div>\n          <div>\n            <div id=\"form-widgets-name-row\" class=\"row required\">\n              <div class=\"label\">\n                <label for=\"form-widgets-name\">\n                  <span>Name</span>\n                  <span class=\"required\">*</span>\n                </label>\n              </div>\n              <div class=\"widget\"><input type=\"text\" id=\"form-widgets-name\"\n                   name=\"form.widgets.name\"\n                   class=\"text-widget required textline-field\" value=\"\" />\n              </div>\n            </div>\n            <div id=\"form-widgets-age-row\" class=\"row\">\n              <div class=\"label\">\n                <label for=\"form-widgets-age\">\n                  <span>Age</span>\n                </label>\n              </div>\n              <div class=\"widget\"><input type=\"text\" id=\"form-widgets-age\"\n                   name=\"form.widgets.age\" class=\"text-widget int-field\"\n                   value=\"20\" />\n              </div>\n            </div>\n          </div>\n        </div>\n        <div>\n          <div class=\"buttons\">\n            <input type=\"submit\" id=\"form-buttons-add\"\n             name=\"form.buttons.add\"\n             class=\"submit-widget button-field\" value=\"Add\" />\n          </div>\n        </div>\n      </form>\n    </body>\n  </html>\n\nOkay, now we are going to check table layout support.\n\n  >>> rootAdding = Adding(root, tableRequest)\n  >>> addForm = AddingPersonAddForm(rootAdding, tableRequest)\n\nAgain, the layout should be available:\n\n  >>> layout = zope.component.getMultiAdapter((addForm, tableRequest),\n  ...     ILayoutTemplate)\n\n  >>> layout.__class__.__name__\n  'ViewPageTemplateFile'\n\nLet's now render the form:\n\n  >>> print(addForm())\n  <html>\n    <body>\n      <form action=\"http://127.0.0.1\" method=\"post\"\n        enctype=\"multipart/form-data\" class=\"edit-form\"\n        name=\"form\" id=\"form\">\n        <div class=\"viewspace\">\n          <div class=\"required-info\">\n            <span class=\"required\">*</span>\n            &ndash; required\n          </div>\n          <div>\n          <table class=\"form-fields\">\n                <tr class=\"row required\">\n                  <td class=\"label\">\n                    <label for=\"form-widgets-name\">\n                      <span>Name</span>\n                      <span class=\"required\"> * </span>\n                    </label>\n                  </td>\n                  <td class=\"field\">\n                    <div class=\"widget\"><input type=\"text\" id=\"form-widgets-name\"\n                         name=\"form.widgets.name\"\n                         class=\"text-widget required textline-field\" value=\"\" />\n                    </div>\n                  </td>\n                </tr>\n                <tr class=\"row\">\n                  <td class=\"label\">\n                    <label for=\"form-widgets-age\">\n                      <span>Age</span>\n                    </label>\n                  </td>\n                  <td class=\"field\">\n                    <div class=\"widget\"><input type=\"text\" id=\"form-widgets-age\"\n                         name=\"form.widgets.age\" class=\"text-widget int-field\"\n                         value=\"20\" />\n                    </div>\n                  </td>\n                </tr>\n          </table>\n        </div>\n      </div>\n      <div>\n        <div class=\"buttons\">\n          <input type=\"submit\" id=\"form-buttons-add\"\n         name=\"form.buttons.add\"\n         class=\"submit-widget button-field\" value=\"Add\" />\n        </div>\n      </div>\n      </form>\n    </body>\n  </html>\n\n\nForm Macros\n-----------\n\nLoad the configuration, which will make sure that all macros get registered\ncorrectly:\n\n  >>> from zope.configuration import xmlconfig\n  >>> import zope.component\n  >>> import zope.viewlet\n  >>> import zope.security\n  >>> import zope.publisher\n  >>> import zope.browserresource\n  >>> import z3c.macro\n  >>> import z3c.template\n  >>> import z3c.formui\n  >>> xmlconfig.XMLConfig('meta.zcml', zope.component)()\n  >>> xmlconfig.XMLConfig('meta.zcml', zope.viewlet)()\n  >>> xmlconfig.XMLConfig('meta.zcml', zope.security)()\n  >>> xmlconfig.XMLConfig('meta.zcml', zope.publisher)()\n  >>> xmlconfig.XMLConfig('meta.zcml', zope.browserresource)()\n  >>> xmlconfig.XMLConfig('meta.zcml', z3c.macro)()\n  >>> xmlconfig.XMLConfig('meta.zcml', z3c.template)()\n  >>> xmlconfig.XMLConfig('configure.zcml', z3c.formui)()\n\nDiv IContentTemplate\n--------------------\n\nCreate some dummy form discriminators for calling div layout templates and\nmacros and check the div IContentTemplates:\n\n  >>> objects = (addForm, divRequest)\n  >>> zope.component.getMultiAdapter(objects, IContentTemplate).filename\n  '...div-form.pt'\n\n  >>> objects = (form.DisplayForm(None, None), divRequest)\n  >>> zope.component.getMultiAdapter(objects, IContentTemplate, '').filename\n  '...div-form-display.pt'\n\nWe offer the following named IContentTemplate:\n\n  >>> objects = (form.DisplayForm(None, None), divRequest)\n  >>> zope.component.getMultiAdapter(objects, IContentTemplate,\n  ...     'display').filename\n  '...div-form-display.pt'\n\n  >>> objects = (form.DisplayForm(None, None), divRequest)\n  >>> zope.component.getMultiAdapter(objects, IContentTemplate,\n  ...     'subform').filename\n  '...subform.pt'\n\n\nTable ILayoutTemplate\n---------------------\n\nThere is one generic layout template to build sub forms:\n\n  >>> objects = (form.DisplayForm(None, None), divRequest)\n  >>> zope.component.getMultiAdapter(objects, ILayoutTemplate,\n  ...     'subform').filename\n  '...subform-layout.pt'\n\n\nDiv layout macros\n-----------------\n\nWe have different form macros available for IInputForm:\n\n\n  >>> from z3c.macro.interfaces import IMacroTemplate\n  >>> objects = (None, addForm, divRequest)\n  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form')\n  [...div-form.pt'), ...metal:define-macro': 'form'...\n\n\n  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'subform')\n  [...div-form.pt'), ...define-macro': 'subform'...\n\n\n  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-label')\n  [...div-form.pt'), ...define-macro': 'label'...\n\n\n  >>> zope.component.getMultiAdapter(\n  ...     objects, IMacroTemplate, 'form-required-info')\n  [...div-form.pt'), ...define-macro', 'required-info'...\n\n\n  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-header')\n  [...div-form.pt'), ...define-macro': 'header'...\n\n\n  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-errors')\n  [...div-form.pt'), ...define-macro': 'errors'...\n\n\n  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'widget-rows')\n  [...div-form.pt'), ...define-macro': 'widget-rows'...\n\n\n  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'widget-row')\n  [...div-form.pt'), ...define-macro': 'widget-row'...\n\n\n  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-groups')\n  [...div-form.pt'), ...define-macro': 'groups'...\n\n\n  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-buttons')\n  [...div-form.pt'), ...define-macro', 'buttons'...\n\n\nAnd we have different form macros available for IDisplayForm:\n\n  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'subform-display')\n  [...div-form-display.pt'), ...define-macro': 'subform-display'...\n\n\nTable IContentTemplate\n----------------------\n\nCreate some dummy form discriminators for calling table layout templates and\nmacros and check the div IContentTemplates:\n\n  >>> objects = (addForm, tableRequest)\n  >>> zope.component.getMultiAdapter(objects, IContentTemplate, '').filename\n  '...table-form.pt'\n\n  >>> objects = (form.DisplayForm(None, None), tableRequest)\n  >>> zope.component.getMultiAdapter(objects, IContentTemplate, '').filename\n  '...table-form-display.pt'\n\nWe offer the following named IContentTemplate:\n\n  >>> objects = (form.DisplayForm(None, None), tableRequest)\n  >>> zope.component.getMultiAdapter(objects, IContentTemplate,\n  ...     'display').filename\n  '...table-form-display.pt'\n\n  >>> objects = (form.DisplayForm(None, None), tableRequest)\n  >>> zope.component.getMultiAdapter(objects, IContentTemplate,\n  ...     'subform').filename\n  '...subform.pt'\n\n\n\nTable ILayoutTemplate\n---------------------\n\nThere is one generic layout template to build sub forms:\n\n  >>> objects = (form.DisplayForm(None, None), tableRequest)\n  >>> zope.component.getMultiAdapter(objects, ILayoutTemplate,\n  ...     'subform').filename\n  '...subform-layout.pt'\n\n\nTable layout macros\n-------------------\n\nWe have different form macros available for IInputForm:\n\n  >>> objects = (None, addForm, tableRequest)\n  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form')\n  [...table-form.pt'), ...metal:define-macro': 'form'...\n\n\n  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'subform')\n  [...table-form.pt'), ...define-macro': 'subform'...\n\n\n  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-label')\n  [...table-form.pt'), ...define-macro': 'label'...\n\n\n  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-required-info')\n  [...table-form.pt'), ...define-macro', 'required-info'...\n\n\n  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-header')\n  [...table-form.pt'), ...define-macro': 'header'...\n\n\n  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-errors')\n  [...table-form.pt'), ...define-macro': 'errors'...\n\n\n  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-table')\n  [...table-form.pt'), ...define-macro', 'formtable'...\n\n\n  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-row')\n  [...table-form.pt'), ...define-macro': 'formrow'...\n\n\n  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-label-cell')\n  [...table-form.pt'), ...define-macro', 'labelcell'...\n\n\n  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-widget-cell')\n  [...table-form.pt'), ...define-macro', 'widgetcell'...\n\n\n  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-groups')\n  [...table-form.pt'), ...define-macro': 'groups'...\n\n\n  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'form-buttons')\n  [...table-form.pt'), ...define-macro', 'buttons'...\n\n\nAnd we have different form macros available for IDisplayForm:\n\n  >>> zope.component.getMultiAdapter(objects, IMacroTemplate, 'subform-display')\n  [...table-form-display.pt'), ...define-macro': 'subform-display'...\n\n\nSubform\n-------\n\nLet's give a quick overview how subform content and layout templates get used:\nFirst define a new form which uses the template getter methods offered\nfrom z3.template\n\n  >>> from z3c.template.template import getPageTemplate, getLayoutTemplate\n\nThe ``provider`` TALES expression which is a part of the lookup concept\nwas already registered by the testing setup, so we don't need to do it\nhere.\n\nand the TALES expression called ``macro`` which can lookup our macro adapters.\nYes, macros are adapters in our content/layout template concept. See z3c.macro\nfor more information about the implementation. However, we already registered\nthe ``macro`` type in the testing setup, as it's needed for rendering form\ntemplates.\n\nand at least we need a pagelet\nrenderer. By default we use the provider called ``PageletRenderer`` defined\nin the z3c.pagelet package. But right now, we don't have a dependency on\nthis package. So let's implement a simple renderer and use them as a\nIContentProvider:\n\n  >>> class PageletRenderer(object):\n  ...     zope.component.adapts(zope.interface.Interface,\n  ...         zope.publisher.interfaces.browser.IBrowserRequest,\n  ...         zope.interface.Interface)\n  ...\n  ...     def __init__(self, context, request, pagelet):\n  ...         self.__updated = False\n  ...         self.__parent__ = pagelet\n  ...         self.context = context\n  ...         self.request = request\n  ...\n  ...     def update(self):\n  ...         pass\n  ...\n  ...     def render(self):\n  ...         return self.__parent__.render()\n\n  >>> from zope.contentprovider.interfaces import IContentProvider\n  >>> zope.component.provideAdapter(PageletRenderer,\n  ...     provides=IContentProvider, name='pagelet')\n\nNow define the form:\n\n  >>> class PersonEditForm(form.EditForm):\n  ...     \"\"\"Edit form including layout support. See z3c.formui.form.\"\"\"\n  ...\n  ...     template = getPageTemplate('subform')\n  ...     layout = getLayoutTemplate('subform')\n  ...\n  ...     fields = field.Fields(IPerson)\n\nNow we can render the form with our previous created person instance:\n\n  >>> person = Person('Jessy', 6)\n  >>> editForm = PersonEditForm(person, divRequest)\n\nNow we call the form which will update and render it:\n\n  >>> print(editForm())\n  <div class=\"viewspace\">\n    <div class=\"required-info\">\n      <span class=\"required\">*</span>\n      &ndash; required\n    </div>\n    <div>\n      <div id=\"form-widgets-name-row\" class=\"row required\">\n        <div class=\"label\">\n          <label for=\"form-widgets-name\">\n            <span>Name</span>\n            <span class=\"required\">*</span>\n          </label>\n        </div>\n        <div class=\"widget\"><input type=\"text\" id=\"form-widgets-name\"\n             name=\"form.widgets.name\"\n             class=\"text-widget required textline-field\"\n             value=\"Jessy\" />\n        </div>\n      </div>\n      <div id=\"form-widgets-age-row\" class=\"row\">\n        <div class=\"label\">\n          <label for=\"form-widgets-age\">\n            <span>Age</span>\n          </label>\n        </div>\n        <div class=\"widget\"><input type=\"text\" id=\"form-widgets-age\"\n           name=\"form.widgets.age\" class=\"text-widget int-field\"\n           value=\"6\" />\n        </div>\n      </div>\n    </div>\n  </div>\n  <div>\n    <div class=\"buttons\">\n      <input type=\"submit\" id=\"form-buttons-apply\"\n             name=\"form.buttons.apply\"\n             class=\"submit-widget button-field\" value=\"Apply\" />\n    </div>\n  </div>\n\nYou can see that the form above is a real subform. It doesn't define the form\ntag which makes it usable as a subform in parent forms.\n\nOf course this works with table layout based forms too. Let's use our table\nrequest and render the form again:\n\n  >>> editForm = PersonEditForm(person, tableRequest)\n  >>> print(editForm())\n  <div class=\"viewspace\">\n    <div class=\"required-info\">\n      <span class=\"required\">*</span>\n      &ndash; required\n    </div>\n    <div>\n      <table class=\"form-fields\">\n        <tr class=\"row required\">\n          <td class=\"label\">\n            <label for=\"form-widgets-name\">\n              <span>Name</span>\n              <span class=\"required\"> * </span>\n            </label>\n          </td>\n          <td class=\"field\">\n            <div class=\"widget\"><input type=\"text\" id=\"form-widgets-name\"\n                 name=\"form.widgets.name\"\n                 class=\"text-widget required textline-field\"\n                 value=\"Jessy\" />\n            </div>\n          </td>\n        </tr>\n        <tr class=\"row\">\n          <td class=\"label\">\n            <label for=\"form-widgets-age\">\n              <span>Age</span>\n            </label>\n          </td>\n          <td class=\"field\">\n            <div class=\"widget\"><input type=\"text\" id=\"form-widgets-age\"\n                 name=\"form.widgets.age\" class=\"text-widget int-field\"\n                 value=\"6\" />\n            </div>\n          </td>\n        </tr>\n      </table>\n    </div>\n  </div>\n  <div>\n    <div class=\"buttons\">\n      <input type=\"submit\" id=\"form-buttons-apply\"\n             name=\"form.buttons.apply\"\n            class=\"submit-widget button-field\" value=\"Apply\" />\n    </div>\n  </div>\n\nRedirection\n-----------\n\nThe form doesn't bother rendering itself and its layout when\nrequest is a redirection as the rendering doesn't make any sense with\nbrowser requests in that case. Let's create a view that does a\nredirection in its update method:\n\n >>> class RedirectingView(PersonEditForm):\n ...     def update(self):\n ...         super(RedirectingView, self).update()\n ...         self.request.response.redirect('.')\n\nIt will return an empty string when called as a browser page.\n\n >>> redirectView = RedirectingView(person, divRequest)\n >>> redirectView() == ''\n True\n\nHowever, the ``render`` method will render form's template as usual:\n\n >>> '<div class=\"viewspace\">' in redirectView.render()\n True\n\nThe same thing should work for AddForms:\n\n >>> class RedirectingAddView(PersonAddForm):\n ...     def update(self):\n ...         super(RedirectingAddView, self).update()\n ...         self.request.response.redirect('.')\n >>> redirectView = RedirectingAddView(person, divRequest)\n >>> redirectView() == ''\n True\n\nNo required fields\n------------------\n\nIf there no required fields in the form, standard templates won't render\nthe \"required-info\" hint.\n\n  >>> class IAdditionalInfo(zope.interface.Interface):\n  ...\n  ...     location = zope.schema.TextLine(title='Location', required=False)\n  ...     about = zope.schema.Text(title='About', required=False)\n\n  >>> class AdditionalInfoForm(form.AddForm):\n  ...\n  ...     fields = field.Fields(IAdditionalInfo)\n\n  >>> additionalInfoForm = AdditionalInfoForm(root, divRequest)\n  >>> additionalInfoForm.update()\n  >>> '<div class=\"required-info\">' in additionalInfoForm.render()\n  False\n\n  >>> additionalInfoForm = AdditionalInfoForm(root, tableRequest)\n  >>> additionalInfoForm.update()\n  >>> '<div class=\"required-info\">' in additionalInfoForm.render()\n  False\n\nCleanup\n-------\n\n  >>> import shutil\n  >>> shutil.rmtree(temp_dir)\n\n\n=======\nCHANGES\n=======\n\n4.1 (2024-10-22)\n----------------\n\n- Add support for Python 3.12, 3.13.\n\n- Drop support for Python 3.7.\n\n\n4.0 (2023-02-24)\n----------------\n\n- Drop support for Python 2.7, 3.4, 3.5, 3.6.\n\n- Add support for Python 3.8, 3.9, 3.10, 3.11.\n\n\n3.1 (2018-11-15)\n----------------\n\n- Add support for Python 3.4, 3.5, 3.6, 3.7.\n\n- Drop support for Python 2.6 and 3.3.\n\n\n3.0.0 (2015-11-09)\n------------------\n\n- Standardize namespace __init__\n\n- Split config, mostly to be able to include in pyramid without browser\n  resources and viewlets\n\n\n3.0.0a2 (2013-02-26)\n--------------------\n\n- Added missing version Trove classifiers.\n\n\n3.0.0a1 (2013-02-25)\n--------------------\n\n- Added support for Python 3.3.\n\n- Dropped support for Python 2.4 and 2.5.\n\n\n2.3.0 (2012-03-15)\n------------------\n\n- Feature: Mark a widget row with the \"required\" class when the widget is\n  required. Similarly, when the widget has an error attached, add the \"error\"\n  class to the widget row. That allows you to change the styles of the label\n  and the widget if it is reuqired.\n\n\n2.2.1 (2012-01-09)\n------------------\n\n- No longer using deprecated ``zope.testing.doctest`` but built-in\n  ``doctest`` instead.\n\n- Fixed tests so they do not break for `z3c.form` 2.5.0.\n\n\n2.2.0 (2009-12-28)\n------------------\n\n- Fixed tests so they do not break for `z3c.form` 2.2.0.\n\n- Using ``requiredInfo`` property (introduced in `z3c.form` 2.0.0) to\n  render the information about required fields. This property returns\n  an i18n message id making the information translateable.\n\n- Added support for groups containing groups: They get displayed now.\n\n2.1.0 (2009-09-01)\n------------------\n\n- Feature: Don't show required info hint if there's no required fields.\n\n- Bug: Don't render add forms when redirecting as well.\n\n- Bug: Fix redirection tests with newer zope.publisher that restricts\n  untrusted redirects to different domains.\n\n2.0.0 (2009-06-14)\n------------------\n\n- Feature: Added support for context-specific template lookup, introduced in\n  `z3c.template` 1.2.0 - templates can now be registered using (view, request,\n  context) discriminator.\n\n- Feature: Added support for `z3c.pt` templates using `z3c.ptcompat`\n  compatibility package.\n\n- Feature: Added layout support for `IAdding` component based add forms.\n\n- Feature: Added CSS for multi-widget which was added in `z3c.form` 2.0.0.\n\n- Bug: Changed usage of ``template/macros/*`` to ``macro:*``, because the\n  first one doesn't work when we override a form template and use the form\n  macro, registered with this package.\n\n- Bug: Don't do rendering in form's `__call__` method when request is a\n  redirection.\n\n- Bug: Reformatted long-description to render properly on pypi.\n\n\n1.4.2 (2008-08-26)\n------------------\n\n- Bug: Corrected typos and unwanted unicode characters.\n\n\n1.4.1 (2008-01-23)\n------------------\n\n- Bug: Fixed up meta-data and the release.\n\n\n1.4.0 (2008-01-21)\n------------------\n\n- Feature: Added subform content and layout template. This allows you to\n  configure real sub forms which do not render the form tag.\n\n- Feature: Improve layout implementation, support built-in layout templates.\n\n- Feature: Use ``IContentTemplate`` instead of ``IPageTemplate`` in layout\n  base classes. This will help to prevent running into recursion errors if\n  there is a missing layout template.\n\n- Feature: Added form module which offers built-in layout support.\n\n- Bug: Added missing display ``IContentTemplate``, otherwise we can run into a\n  recursion in some cases.\n\n- Bug: Renamed table macro argument from ``form-required-info`` to\n  ``required-info``. The macro ``form-required-info`` did not exist.\n\n- Bug: Added unit tests for layout support.\n\n- Bug: Added tests for layout macros.\n\n- Bug: Added tests for layout templates.\n\n\n1.3.0 (2007-08-24)\n------------------\n\n- Refactoring: Update CSS classes to reflect latest changes to the widget\n  classes in ``z3c.form``.\n\n- Bug: Error view snippets may have a empty ``widget`` attribute values, so we\n  cannot rely on accessing the label of the widget. This is the case, if the\n  error view sniipet was created from an invariants validation error.\n\n- Bug: The table-form template did not properly render the error at the\n  widget, because the ``render()`` method was not called. Thanks to Markus\n  Leist for the report.\n\n\n1.2.0 (2007-07-18)\n------------------\n\n- Feature: The row div element now also has an id of the form\n  \"<widget-id>-row\".\n\n\n1.1.1 (2007-07-04)\n------------------\n\n- Refactoring: Split up registrations for simpler management of UI\n  components. This also makes it easier to see for developers how to create a\n  new template for forms.\n\n\n1.1.0 (2007-06-29)\n------------------\n\n- Feature: Registered all defined macros for each form template. Also, added\n  more slots to the templates providing more hooks for customization.\n\n- Feature: Added a macro/slot for the \"required info\", which explains how\n  required fields are marked.\n\n- Feature: Added support for form labels.\n\n- Feature: Added support for groups to templates.\n\n\n1.0.1 (2007-06-22)\n------------------\n\n- Bug: Make sure we use the id for the \"for\" attribute of the \"label\"\n  element and not the name. This has worked until recently, because the\n  name and id were the same, but they are different now.\n\n\n1.0.0 (2007-05-24)\n------------------\n\n- Initial Release\n",
    "bugtrack_url": null,
    "license": "ZPL 2.1",
    "summary": "A set of initial UI components for z3c.form.",
    "version": "4.1",
    "project_urls": {
        "Homepage": "https://github.com/zopefoundation/z3c.formui"
    },
    "split_keywords": [
        "zope3",
        "form",
        "widget"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "8d0df7d49880b0fa114b2bdca78623ed542870774048b5a923318a2d03504428",
                "md5": "e8eeb89ea3a9ebdb5957585d3d1c1478",
                "sha256": "0c62848fb344a6e2ab2c8eed367d13328185970d7a001d7d12ef6b7608b1a6f3"
            },
            "downloads": -1,
            "filename": "z3c.formui-4.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "e8eeb89ea3a9ebdb5957585d3d1c1478",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 30748,
            "upload_time": "2024-10-22T06:56:38",
            "upload_time_iso_8601": "2024-10-22T06:56:38.143808Z",
            "url": "https://files.pythonhosted.org/packages/8d/0d/f7d49880b0fa114b2bdca78623ed542870774048b5a923318a2d03504428/z3c.formui-4.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "3125137165cd69dfd55c71211024cacd8429043a7d0ed75bc88f20913adab61c",
                "md5": "64f20065da498241618bc8ad2c1fd45e",
                "sha256": "982762ab7b5aa5473706c057b0a667e799b65068a30f68d00dbf980d28dcc613"
            },
            "downloads": -1,
            "filename": "z3c.formui-4.1.tar.gz",
            "has_sig": false,
            "md5_digest": "64f20065da498241618bc8ad2c1fd45e",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 33941,
            "upload_time": "2024-10-22T06:56:40",
            "upload_time_iso_8601": "2024-10-22T06:56:40.549430Z",
            "url": "https://files.pythonhosted.org/packages/31/25/137165cd69dfd55c71211024cacd8429043a7d0ed75bc88f20913adab61c/z3c.formui-4.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-10-22 06:56:40",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "zopefoundation",
    "github_project": "z3c.formui",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "tox": true,
    "lcname": "z3c.formui"
}
        
Elapsed time: 2.18787s