Giter VIP home page Giter VIP logo

yafowil's Introduction

yafowil

Latest PyPI version Number of PyPI downloads Test yafowil

Yet Another Form WIdget Library

YAFOWIL offers html-form creation and modification at runtime. It is light-weight and provides an extensible, reusable set of blueprints to build flexible forms.

YAFOWIL is independent from any web-framework, but easy to use in your web-framework.

It's all just about rendering widgets and extracting the data returned from the browser per widget. It does not fight with storage.

YAFOWIL vary from most other HTML form packages: Its all just configuration. No subclassing needed any more; no specific schema-framework is necessary.

Yafowil provides a factory where you can fetch your widgets instances from. Or you register your own.

Detailed Documentation

If you're interested to dig deeper: i The detailed YAFOWIL documentation is available. Read it and learn how to create your example application with YAFOWIL forms in 15 minutes.

Source Code

The sources are in a GIT DVCS with its main branches at github.

We'd be happy to see many forks and pull-requests to make YAFOWIL even better.

Contributors

  • Jens W. Klein
  • Robert Niederreiter
  • Johannes Raggam
  • Peter Holzer
  • Attila Olah
  • Florian Friesdorf
  • Daniel Widerin
  • Georg Bernhard
  • Christian Scholz aka MrTopf

yafowil's People

Contributors

agitator avatar attilaolah avatar chaoflow avatar jensens avatar movermeyer avatar rnixx avatar saily avatar thet avatar zworkb avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

yafowil's Issues

Document how to write an addon blueprint

part one as simple code example to be integrated in some integration package
second complete example how to create a yafowil.widget.FOO with entrypoints and whatever needed to sing and dance the whole story

TypeError - 'object.__new__(Unset) is not safe, use Persistence.Persistent.__new__()

after checking out yafowil from github and upgrading to plone 4.4 ftom 4.3, some things have changed which break yafowil.

i get this error:
TypeError: ('object.__new__(Unset) is not safe, use Persistence.Persistent.__new__()', <function _reconstructor at 0x7f372425b9b0>, (<class 'yafowil.utils.Unset'>, <type 'object'>, None))

in this case, i have a yafowil form included in a plone viewlet.

why does yafowil.utils.Unset have anything to do with Persistent?

full traceback:

2013-05-26 18:02:12 ERROR ZODB.Connection Couldn't load state for 0x152b
Traceback (most recent call last):
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/ZODB3-3.10.5-py2.7-linux-x86_64.egg/ZODB/Connection.py", line 860, in setstate
    self._setstate(obj)
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/ZODB3-3.10.5-py2.7-linux-x86_64.egg/ZODB/Connection.py", line 914, in _setstate
    self._reader.setGhostState(obj, p)
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/ZODB3-3.10.5-py2.7-linux-x86_64.egg/ZODB/serialize.py", line 612, in setGhostState
    state = self.getState(pickle)
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/ZODB3-3.10.5-py2.7-linux-x86_64.egg/ZODB/serialize.py", line 605, in getState
    return unpickler.load()
  File "/home/thet/dev/python27/lib/python2.7/copy_reg.py", line 48, in _reconstructor
    obj = object.__new__(cls)
TypeError: ('object.__new__(Unset) is not safe, use Persistence.Persistent.__new__()', <function _reconstructor at 0x7f372425b9b0>, (<class 'yafowil.utils.Unset'>, <type 'object'>, None))
2013-05-26 18:02:12 ERROR root Exception while rendering an error message
Traceback (most recent call last):
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/Zope2-2.13.20-py2.7.egg/OFS/SimpleItem.py", line 242, in raise_standardErrorMessage
    v = s(**kwargs)
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/Products.CMFCore-2.2.7-py2.7.egg/Products/CMFCore/FSPythonScript.py", line 127, in __call__
    return Script.__call__(self, *args, **kw)
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/Zope2-2.13.20-py2.7.egg/Shared/DC/Scripts/Bindings.py", line 322, in __call__
    return self._bindAndExec(args, kw, None)
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/Zope2-2.13.20-py2.7.egg/Shared/DC/Scripts/Bindings.py", line 359, in _bindAndExec
    return self._exec(bound_data, args, kw)
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/Products.PythonScripts-2.13.2-py2.7.egg/Products/PythonScripts/PythonScript.py", line 344, in _exec
    result = f(*args, **kw)
  File "Script (Python)", line 35, in standard_error_message
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/Zope2-2.13.20-py2.7.egg/Shared/DC/Scripts/Bindings.py", line 322, in __call__
    return self._bindAndExec(args, kw, None)
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/Zope2-2.13.20-py2.7.egg/Shared/DC/Scripts/Bindings.py", line 359, in _bindAndExec
    return self._exec(bound_data, args, kw)
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/Products.CMFCore-2.2.7-py2.7.egg/Products/CMFCore/FSPageTemplate.py", line 237, in _exec
    result = self.pt_render(extra_context=bound_names)
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/Products.CMFCore-2.2.7-py2.7.egg/Products/CMFCore/FSPageTemplate.py", line 177, in pt_render
    self, source, extra_context
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/Zope2-2.13.20-py2.7.egg/Products/PageTemplates/PageTemplate.py", line 79, in pt_render
    showtal=showtal)
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/zope.pagetemplate-3.6.3-py2.7.egg/zope/pagetemplate/pagetemplate.py", line 132, in pt_render
    strictinsert=0, sourceAnnotations=sourceAnnotations
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/five.pt-2.2.1-py2.7.egg/five/pt/engine.py", line 93, in __call__
    return self.template.render(**kwargs)
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/z3c.pt-2.2.3-py2.7.egg/z3c/pt/pagetemplate.py", line 149, in render
    return base_renderer(**context)
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/Chameleon-2.11-py2.7.egg/chameleon/zpt/template.py", line 257, in render
    return super(PageTemplate, self).render(**vars)
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/Chameleon-2.11-py2.7.egg/chameleon/template.py", line 172, in render
    self._render(stream, econtext, rcontext)
  File "/home/thet-data/data/dev/g24new/g24.buildout/var/chameleon-cache/65a3b5d068f290f28aba98cd3d64c751fc9a7cd6.py", line 1784, in render
    __macro.include(__stream, econtext.copy(), rcontext, __i18n_domain)
  File "/home/thet-data/data/dev/g24new/g24.buildout/var/chameleon-cache/d6835db9b2e978986abd9a726e2c70c1230400f8.py", line 1283, in render_master
    __cache_140158342923408 = _render_content_provider(econtext, __cache_140158342923408)
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/z3c.pt-2.2.3-py2.7.egg/z3c/pt/expressions.py", line 61, in render_content_provider
    return cp.render()
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/plone.app.viewletmanager-2.0.3-py2.7.egg/plone/app/viewletmanager/manager.py", line 155, in render
    return BaseOrderedViewletManager.render(self)
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/plone.app.viewletmanager-2.0.3-py2.7.egg/plone/app/viewletmanager/manager.py", line 86, in render
    return u'\n'.join([viewlet.render() for viewlet in self.viewlets])
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/zope.browserpage-3.12.2-py2.7.egg/zope/browserpage/simpleviewclass.py", line 44, in __call__
    return self.index(*args, **kw)
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/Zope2-2.13.20-py2.7.egg/Products/Five/browser/pagetemplatefile.py", line 125, in __call__
    return self.im_func(im_self, *args, **kw)
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/Zope2-2.13.20-py2.7.egg/Products/Five/browser/pagetemplatefile.py", line 59, in __call__
    sourceAnnotations=getattr(debug_flags, 'sourceAnnotations', 0),
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/zope.pagetemplate-3.6.3-py2.7.egg/zope/pagetemplate/pagetemplate.py", line 132, in pt_render
    strictinsert=0, sourceAnnotations=sourceAnnotations
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/five.pt-2.2.1-py2.7.egg/five/pt/engine.py", line 93, in __call__
    return self.template.render(**kwargs)
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/z3c.pt-2.2.3-py2.7.egg/z3c/pt/pagetemplate.py", line 149, in render
    return base_renderer(**context)
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/Chameleon-2.11-py2.7.egg/chameleon/zpt/template.py", line 257, in render
    return super(PageTemplate, self).render(**vars)
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/Chameleon-2.11-py2.7.egg/chameleon/template.py", line 190, in render
    raise_with_traceback(exc, tb)
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/Chameleon-2.11-py2.7.egg/chameleon/template.py", line 172, in render
    self._render(stream, econtext, rcontext)
  File "/home/thet-data/data/dev/g24new/g24.buildout/var/chameleon-cache/f34e02fbd608df5b5caf08a8850ae6cb14c052eb.py", line 102, in render
    __cache_143452688 = _render_content_provider(econtext, __cache_143452688)
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/z3c.pt-2.2.3-py2.7.egg/z3c/pt/expressions.py", line 58, in render_content_provider
    cp.update()
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/zope.viewlet-3.7.2-py2.7.egg/zope/viewlet/manager.py", line 112, in update
    self._updateViewlets()
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/zope.viewlet-3.7.2-py2.7.egg/zope/viewlet/manager.py", line 118, in _updateViewlets
    viewlet.update()
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/plone.app.layout-2.3.4-py2.7.egg/plone/app/layout/viewlets/common.py", line 187, in update
    self.portal_tabs = portal_tabs_view.topLevelTabs()
  File "/home/thet-data/data/dev/g24new/g24.buildout/src/Products.CMFPlone/Products/CMFPlone/browser/navigation.py", line 204, in topLevelTabs
    for item in rawresult:
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/Products.ZCatalog-2.13.23-py2.7.egg/Products/ZCatalog/Lazy.py", line 190, in __getitem__
    value = data[index] = self._func(self._seq[index])
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/Products.ZCatalog-2.13.23-py2.7.egg/Products/ZCatalog/Catalog.py", line 121, in __getitem__
    r=self._v_result_class(self.data[index]).__of__(aq_parent(self))
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/ZODB3-3.10.5-py2.7-linux-x86_64.egg/ZODB/Connection.py", line 860, in setstate
    self._setstate(obj)
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/ZODB3-3.10.5-py2.7-linux-x86_64.egg/ZODB/Connection.py", line 914, in _setstate
    self._reader.setGhostState(obj, p)
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/ZODB3-3.10.5-py2.7-linux-x86_64.egg/ZODB/serialize.py", line 612, in setGhostState
    state = self.getState(pickle)
  File "/home/thet-data/dotfiles-thet/home/.buildout/eggs/ZODB3-3.10.5-py2.7-linux-x86_64.egg/ZODB/serialize.py", line 605, in getState
    return unpickler.load()
  File "/home/thet/dev/python27/lib/python2.7/copy_reg.py", line 48, in _reconstructor
    obj = object.__new__(cls)
TypeError: ('object.__new__(Unset) is not safe, use Persistence.Persistent.__new__()', <function _reconstructor at 0x7f372425b9b0>, (<class 'yafowil.utils.Unset'>, <type 'object'>, None))

 - Expression: "provider:plone.portaltop"
 - Filename:   ... theme/sunburst/skins/sunburst_templates/main_template.pt
 - Location:   (62:40)
 - Source:     ... l:replace="structure provider:plone.portaltop" />
                                        ^^^^^^^^^^^^^^^^^^^^^^^^
 - Expression: "provider:plone.portalheader"
 - Filename:   ... 3.4-py2.7.egg/plone/app/layout/viewlets/portal_header.pt
 - Location:   (2:32)
 - Source:     ... :replace="structure provider:plone.portalheader" />
                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 - Arguments:  repeat: {...} (0)
               template: <ViewPageTemplateFile - at 0x7f37144ff450>
               views: <ViewMapper - at 0x7f370c186290>
               modules: <instance - at 0x2703a70>
               args: <tuple - at 0x7f37242ee050>
               here: <ImplicitAcquisitionWrapper Plone at 0x82d5aa0>
               user: <ImplicitAcquisitionWrapper - at 0x8360a00>
               nothing: <NoneType - at 0x84b1e0>
               container: <ImplicitAcquisitionWrapper Plone at 0x82d5aa0>
               request: <instance - at 0x7f370c0e7e18>
               wrapped_repeat: <SafeMapping - at 0x7f370c0c9628>
               traverse_subpath: <list - at 0x7f370c197320>
               default: <object - at 0x7f3724257500>
               loop: {...} (0)
               context: <ImplicitAcquisitionWrapper Plone at 0x82d5aa0>
               view: <SimpleViewletClass from /home/thet-data/dotfiles-thet/home/.buildout/eggs/plone.app.layout-2.3.4-py2.7.egg/plone/app/layout/viewlets/portal_header.pt plone.header at 0x7f370c186250>
               translate: <function translate at 0x7f370c18f578>
               root: <ImplicitAcquisitionWrapper Zope at 0x6a54e60>
               options: {...} (0)
               target_language: <NoneType - at 0x84b1e0>

DRAFT: Random Improvent Proposal Ideas

This is a work in progress for improvement ideas, coming up as I work with YAFOWIL.

  • factory widget properties: allow namespaces:

Currently, if you want to create a label, an error div and a text field with a factory and want the label contain the textfield, you do it like so:

factory(
    'label:error:text',
    props={
        'label': _('label_minute', u'Minute'),
        'position': 'inner-before'
    }
)


the error blueprint also accepts a position property and it is not clear to which one it belongs.
I propose to allow nested dicts:

factory(
    'label:error:text',
    props={
        'label': {
            'text': _('label_minute', u'Minute'),
            'position': 'inner-before'
        }
    }
)

this should also be possible to support with YAML.

  • ...

missing fieldsetname in rendered fieldset id for structural fieldsets

the yaml structure

[...]
widgets:
- myAwesomeFieldset:
    factory: fieldset
    props:
        structural: True
    widgets:
[...]

renders to

<fieldset id="fieldset-FORMNAME">

instead of

<fieldset id="fieldset-FORMNAME-myAwesomeFieldset">

(the fieldset name is rendered for non-structural fieldsets)

i consider this beeing a bug, since i see no reason, why the fieldset name isn't included in the fieldset id for structural fieldsets.

let data-attribute be optional for attr_value

attr_value must be called with the attribute-key, the widget instance and a data-dict. this data-dict is not always available, like in cssid: db9619f#L1R152

the 'structural' attribute would be better queried by attr_value('structural', widget, data) to cover callables too, but the data-dict is not available.

attr_value('structural', widget, {}) might be possible, but that seems like a misuse of the API. passing the data-dict to all cssid instances is another option. but maybe the data-dict is not always needed, like in the cssid case. so the call to attr(widget, data) in attr_value would be ok without data. in this case, the attr_value function signature should look like:
attr_value(key, widget, data={}, default=None)

Zope messages are not respected in error messages

Rendering Zope i18n messages in custom error messages is not supported.

In https://github.com/bluedynamics/yafowil/blob/master/src/yafowil/common.py#L1304 error messages are converted to string and all message information (domain, mapping, etc.) is lost.

Steps to reproduce:

  1. Create a form with a custom error message:

    form['SearchableText'] = factory(
    'error:text',
    props={'required': True,
    'required_message': Message('label_query_toshort', default='Query is too short. $numchars needed!',
    domain="mydomain",
    mapping={'numchars': 3}),
    })

  2. render the form (with error triggered)

  3. You see the message-id instead of the translated message.

Prevent rendering of extracted output on validation error

Take this form:

form = factory(u'*json:form',
    name='create-deck',
    props={
        'action': '%s/create-deck-ajax' % self.nodeurl,
    },
    custom={'json': ([], [self.json], [], [])},
)
form['tags'] = factory(
    'field:*tolist:text',
    props={
        'placeholder': self.tr(_(u'Optional tags')),
    },
    custom={'tolist': ([self.tolist], [], [], [])},
)


def tolist(self, widget, data):
    """Convert the tags to a list."""
    separator = ',' in data.extracted and ',' or None
    return [tag.strip() for tag in data.extracted.split(separator)]

Now, this works nicely, but if there's a validation error somewhere else, the form gets re-rendered with the extracted values, which, in case of tags, is now a list, so the output will be something like:

<input type="text" placeholder="Optional tags" value="[u'some', u'values']" />

which is definitely not what I want. I'd like to display the original values (the ones that the user has entered).

Any suggestions?

Documentation build broken (RTD)

At ReadThe Docs the build is broken. @rnixx and I discussed to move this to our own server, since RTD build process is much harder to follow than just to build our selfs. The demo is already over there.

Mechanism to ignore errors on extraction

Implement ignore_errors on widget.extract and extend Controller to pass ignore_errors if set on active action.

Use case is preloading or updating forms via JS.

Relates to #35

Deprecate Factory Builders

In the early times of yafowil, builders felt an appropriate concept for pre-building compound widgets from blueprints. Practice showed that the inconsistency with attribute callables cause more problems than it solves.

Deprecate builders as of yafowil 2.4
Remove builders as of yafowil 3.0

new widget attribute for html attributes

while trying to implement every possible html(5) attribute in yafowil (see: https://github.com/bluedynamics/yafowil/tree/html5-attributes ) i found that a seperate namespace for those attributes would be neccassary.
mixing yafowil specific widget attributes like next, handler, error_class, etc and html specific attributes like id, name, class, size, ... in one dictionary might lead into namespace clashes.
therefore i suggest to put yafowil specific attributes and html attributes into seperate variables:

widgets:
- title:
    factory: label:field:error:text
    value: expr:context.get('title', '')
    attrs:
        label: i18n:Title
        required: True
    props:
        required_message: No title given
        error_class: blabla

Document prefixed properties

add section about propertied behaviour i.e. prefixed properties, including defaults and prefixed defaults to documentation.

How should the `writer=` override work?

I wanted to add the following test:

If ``persist_writer`` is provided by the widget, passing ``writer`` overrides it::

    >>> form['yet_another_field'] = factory(
    ...     'text',
    ...     props={
    ...         'persist': True,
    ...         'persist_writer': write_mapping_writer
    ...     })
    >>> data = form.extract(request={
    ...     'form.yet_another_field': 'value',
    ... })
    >>> model = MixedModel()
    >>> data['yet_another_field'].write(model, writer=attribute_writer)
    >>> model.yet_another_field
    'value'

But this fails:

File "/home/jean/repos/git/yafowil/src/yafowil/persistence.rst", line 182, in persistence.rst
Failed example:
    model.yet_another_field
Exception raised:
    Traceback (most recent call last):
      File "/usr/lib/python2.7/doctest.py", line 1315, in __run
        compileflags, 1) in test.globs
      File "<doctest persistence.rst[63]>", line 1, in <module>
        model.yet_another_field
    AttributeError: 'MixedModel' object has no attribute 'yet_another_field'

If I understand correctly, it works like this:

  • if the form has self.persist_writer, that is used throughout.
  • if writer= is passed, that is used for child nodes that don't have self.persist_writer set.
  • but writer= is not used for nodes that already have self.persist_writer set.

That seems surprising. I would expect writer= to always govern if it's passed. So I think I'm probably misunderstanding. If you have some time, could you let me know if I am?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.