I've been thinking about how to solve this problem, and what I've come
up with so far is this:

- Schemas may be bound to a set of values.  When a schema is bound, 
  it is cloned, and any "deferred" values it has will be resolved.

- A deferred value is a callable that accepts the schema node being
  bound and a set of arbitrary keyword arguments.  It should
  return a value appropriate for its usage (a widget, a missing value,
  a validator, etc).

- Deferred values are not resolved until the schema is bound.

- Schemas are bound via "SomeSchema().bind(**kw)".  The values in "kw"
  are passed to each deferred value along with the schema node being
  bound.

Here's an example:

"""
@colander.deferred
def deferred_date_validator(node, **kw):
    max_date = kw.get('max_date')
    if max_date is None:
        max_date = datetime.date.today()
    return colander.Range(min=datetime.date.min, max=max_date)

@colander.deferred
def deferred_date_description(node, **kw):
    max_date = kw.get('max_date')
    if max_date is None:
        max_date = datetime.date.today()
    return 'Blog post date (no earlier than %s)' % max_date.ctime()

@colander.deferred
def deferred_date_missing(node, **kw):
    default_date = kw.get('default_date')
    if default_date is None:
        default_date = datetime.date.today()
    return default_date

@colander.deferred
def deferred_body_validator(node, **kw):
    max_bodylen = kw.get('max_bodylen')
    if max_bodylen is None:
        max_bodylen = 1 << 18
    return colander.Length(max=max_bodylen)

@colander.deferred
def deferred_body_description(node, **kw):
    max_bodylen = kw.get('max_bodylen')
    if max_bodylen is None:
        max_bodylen = 1 << 18
    return 'Blog post body (no longer than %s bytes)' % max_bodylen

@colander.deferred
def deferred_body_widget(node, **kw):
    body_type = kw.get('body_type')
    if body_type == 'richtext':
        widget = deform.widget.RichTextWidget()
    else:
        widget = deform.widget.TextAreaWidget()
    return widget

@colander.deferred
def deferred_category_validator(node, **kw):
    categories = kw.get('categories', [])
    return colander.OneOf([ x[0] for x in categories ])

@colander.deferred
def deferred_category_widget(node, **kw):
    categories = kw.get('categories', [])
    return deform.widget.RadioChoiceWidget(values=categories)

class BlogPostSchema(Schema):
    title = SchemaNode(
        colander.String(),
        title = 'Title',
        description = 'Blog post title',
        validator = colander.Length(min=5, max=100),
        widget = deform.widget.TextInputWidget(),
        )
    date = SchemaNode(
        colander.Date(),
        title = 'Date',
        missing = deferred_date_missing,
        description = deferred_date_description,
        validator = deferred_date_validator,
        widget = deform.widget.DateInputWidget(),
        )
    body = SchemaNode(
        colander.String(),
        title = 'Body',
        description = deferred_body_description,
        validator = deferred_body_validator,
        widget = deferred_body_widget,
        )
    category = SchemaNode(
        colander.String(),
        title = 'Category',
        description = 'Blog post category',
        validator = deferred_category_validator,
        widget = deferred_category_widget,
        )
        
schema = BlogPostSchema().bind(
    max_date = datetime.date.max,
    max_bodylen = 5000,
    body_type = 'richtext',
    default_date = datetime.date.today(),
    )
form = deform.Form(schema)
"""

This proposal does not deal with conditional inclusion or exclusion of
schema nodes, only resolving deferred schema properties.

Comments are appreciated.

- C




On Tue, 2010-09-07 at 20:37 +0800, Tim Hoffman wrote:
> Bummer ;-)
> 
> 
> I don't think I have a developed an application in the last 10 years
> that hasn't has to do this.
> I was quite surprised when I discovered this feature was missing in
> formish, but it seems to be missing in quite a
> few other form libs like wtforms as well.
> 
> I suppose at least they all have declarative method of defining the
> schema, but it does mean the actual schema definition is a bit more
> obscured.
> 
> Cheers
> 
> T
> 
> 
> 
> 
> On Tue, Sep 7, 2010 at 8:20 PM, Chris McDonough <chr...@plope.com> wrote:
> > Hi Tim,
> >
> > Sorry, there is no built-in solution that will allow you to use
> > declarative-module-scope code only.  You'll need to generate schemas and
> > widgets at render time.
> >
> > - C
> >
> >
> > On Tue, 2010-09-07 at 12:56 +0800, Tim Hoffman wrote:
> >> Hi Chris.
> >>
> >> Am just starting too look at deform in some detail and I have question.
> >>
> >> One of the things I have struggled with formish has been the fact I
> >> can't easily
> >> define a source of values for widgets like checkbox or validators such
> >> as OneOf to
> >> only be resolved late at render time. And I can't see how I would go
> >> about it with deform.
> >>
> >> In your example http://docs.repoze.org/deform/app.html you have
> >>  colors = (('red', 'Red'), ('green', 'Green'), ('blue', 'Blue')) used
> >> as values for
> >>
> >> widget.RadioChoiceWidget values and for the validator OneOf
> >>
> >> So in my contrived example I would like the set of possible values
> >> for color is dependent on the
> >> user and some other factor.  Looking at the code for SelectWidget and
> >> RadioChoice widget
> >> it appears they won't take a callable and lazily render those values
> >> at render time.
> >>
> >>
> >> With formish I basically constructed the form structure (schema)
> >> but only applied widget definitions  and validators just before render
> >> time. So that I could use things
> >> like the current context to determine values for validation or
> >> choices.  This was a bit of a hack.
> >>
> >>
> >> So do you have a strategy or suggestion on how to approach this use case ?
> >>
> >> I suppose I could work with imperative schema definition performed
> >> late, but I much prefer to work with classes.
> >> (I currently generate Formish (structures) directly from UML).
> >>
> >> Cheers
> >>
> >> Tim
> >> _______________________________________________
> >> Repoze-dev mailing list
> >> Repoze-dev@lists.repoze.org
> >> http://lists.repoze.org/listinfo/repoze-dev
> >>
> >
> >
> >
> 


_______________________________________________
Repoze-dev mailing list
Repoze-dev@lists.repoze.org
http://lists.repoze.org/listinfo/repoze-dev

Reply via email to