Webhelpers html tag examples

114 views
Skip to first unread message

Ben Sizer

unread,
Aug 30, 2011, 9:09:32 AM8/30/11
to pylons-discuss
I'm putting together a form, and looking for ways to do it, and
although I'm used to just writing the HTML by hand, I'll happily use
something to generate it also. The webhelpers.html.tag stuff looks
like it would work: but are there any examples of people using this
with Pyramid? The docs contain many tiny snippets that demonstrate
what each individual object and function does, but there doesn't
appear to be anywhere giving 1 complete example of a typical usage as
part of a view.

I'm hoping for something similar to http://turbogears.org/2.0/docs/main/FormBasics.html
or https://docs.djangoproject.com/en/dev/topics/forms/#form-objects

--
Ben Sizer

Benjamin Sims

unread,
Aug 30, 2011, 9:42:12 AM8/30/11
to pylons-...@googlegroups.com
If you are looking for a complete form library, there are a few that fit nicely into Pyramid. I've had success with and would recommend deform:


The demo site provides nice examples of its capabilities:


and there is a full example of an integration with Pyramid to get you started:


It will do validation and generation and has various widgets which are easier than building HTML by hand. While it doesn't depend on Pyramid, they are, let's say, good friends.

You might also want to consider pyramid_formalchemy (CRUD generation from SQLAlchemy), pyramid_simpleform and I'm sure there are many others that people could recommend.

Ben




--
You received this message because you are subscribed to the Google Groups "pylons-discuss" group.
To post to this group, send email to pylons-...@googlegroups.com.
To unsubscribe from this group, send email to pylons-discus...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/pylons-discuss?hl=en.


Ben Sizer

unread,
Aug 30, 2011, 10:58:41 AM8/30/11
to pylons-discuss
On Aug 30, 2:42 pm, Benjamin Sims <benjamins...@gmail.com> wrote:
> If you are looking for a complete form library, there are a few that fit
> nicely into Pyramid. I've had success with and would recommend deform:
>
> https://docs.pylonsproject.org/projects/deform/dev/

Hi Ben,

Yeah, deform does look pretty good, and I will give it a go.

It would still be good to get some examples of the webhelper HTML
generation stuff as I expect that can still come in useful.

--
Ben Sizer

cd34

unread,
Aug 30, 2011, 12:08:44 PM8/30/11
to pylons-discuss
Mike Bayer (of Mako and SQLAlchemy) posted this:

http://techspot.zzzeek.org/2008/07/01/better-form-generation-with-mako-and-pylons/

While it discusses Pylons, it does address one possible method without
using a form library (or, using Mako as a form library). The method
isn't limited to Mako, you could do it with Jinja2 or any other
reasonably powerful templating language.

I like deform - it has some very sophisticated widgets that make
developing some rather complex forms easy. The only issue I have
(coming from an SQLAlchemy background and not a zodb background) is
that it requires some magic to bring appstruct over to an object that
SQLAlchemy can use.

http://cd34.com/blog/framework/using-pyramid-with-deform-to-editcreate-records-in-sqlalchemy-backed-database/

WTForms is very Djangolike and works pretty well. We used it as the
form library for Apex. Formalchemy's documentation is in a state of
flux. They swapped maintainers moved the site, some of the links 404.
If you are looking for a form library that is very well tied to your
SQLAlchemy model schemas and has a RESTful interface, it works pretty
well.

If you're just looking to use webhelpers in your existing forms, you
would add it to your subscribers:

something like:

project/lib/subscribers.py:

from pyramid.threadlocal import get_current_request
from project.lib import helpers

def add_renderer_globals(event):
request = event.get('request')
if request is None:
request = get_current_request()
globs = {
'h':helpers,
}
event.update(globs)

project/lib/helpers.py:

from webhelpers.text import plural
from webhelpers.date import distance_of_time_in_words
from webhelpers.date import time_ago_in_words

project/__init__.py:


config.add_subscriber('project.lib.subscribers.add_renderer_globals',
'pyramid.events.BeforeRender')

In your template (Jinja example, Mako would use ${ } rather than
{{ }}:

<br /><span id="p-{{data.id}}">{{h.plural(data.points,
'point', 'points')}}</span> by <a href="/user/
{{data.author.username}}">{{data.author.username}}</a>
{{h.distance_of_time_in_words(data.posted_date, granularity='hour')}}
ago | <a href="/news/{{data.id}}/
{{data.slug}}">{{h.plural(data.comment_count, 'Comment', 'Comments')}}
</a>

Ben Sizer

unread,
Aug 30, 2011, 12:22:32 PM8/30/11
to pylons-discuss
On Aug 30, 5:08 pm, cd34 <mcd...@gmail.com> wrote:

> If you're just looking to use webhelpers in your existing forms, you
> would add it to your subscribers:
>
> something like:
>
> project/lib/subscribers.py:

I don't have that file or that directory. Should I?

I'm afraid I didn't understand the rest of your example, but thanks
for trying!

--
Ben Sizer

Ben Sizer

unread,
Aug 30, 2011, 12:36:31 PM8/30/11
to pylons-discuss
On Aug 30, 2:42 pm, Benjamin Sims <benjamins...@gmail.com> wrote:
>
> The demo site provides nice examples of its capabilities:
>
> http://deformdemo.repoze.org/

Having explored this a little more, I must admit I find it daunting.
Sure, for simple stuff it's "easier than building HTML by hand", but
for anything non-trivial, it's hard to work out what to do, because
everything's hidden from me.

I have an object which is a few string fields and then a list of
references to Tag objects. The input for the rest of the object is
fine but I can't see any way to handle the tags, because the way the
schema seems to work is that it assumes the object is composed of
everything within it, as opposed to associating with things outside of
it. So I can add new tags to the object, but that is not the behaviour
I want, because the user needs to be limited to existing tags.

And I have no idea if this is something I need to fix with deform, or
colander, or both. Any suggestions?

Do people really find this easier than just emitting the HTML? If I
was doing this by hand I'd grab all the Tag objects, emit "<input
type='checkbox' name='tags' value='%d'>%s</input>" % (tag.id,
tag.name) for each one, then map those IDs back to Tags in the
response handler. That's why I asked about webhelpers, because a
function to generate that HTML for an input tag is quite handy without
obscuring the detail of what is actually going on.

--
Ben Sizer

cd34

unread,
Aug 30, 2011, 12:52:29 PM8/30/11
to pylons-discuss
On Aug 30, 12:22 pm, Ben Sizer <kylo...@gmail.com> wrote:
> On Aug 30, 5:08 pm, cd34 <mcd...@gmail.com> wrote:
>
> > If you're just looking to use webhelpers in your existing forms, you
> > would add it to your subscribers:
>
> > something like:
>
> > project/lib/subscribers.py:

You would create that file in the lib directory (if you desired) and
reference it in your __init__.py. Where the file is actually located
is a matter of personal preference. You could create subscribers.py in
your project directory and adjust the config.add_subscribers line you
added to your __init__.py as needed.

cd34

unread,
Aug 30, 2011, 1:01:59 PM8/30/11
to pylons-discuss
On Aug 30, 12:36 pm, Ben Sizer <kylo...@gmail.com> wrote:
> On Aug 30, 2:42 pm, Benjamin Sims <benjamins...@gmail.com> wrote:
> > The demo site provides nice examples of its capabilities:
>
> >http://deformdemo.repoze.org/
>
> Having explored this a little more, I must admit I find it daunting.
> Sure, for simple stuff it's "easier than building HTML by hand", but
> for anything non-trivial, it's hard to work out what to do, because
> everything's hidden from me.
>
> I have an object which is a few string fields and then a list of
> references to Tag objects. The input for the rest of the object is
> fine but I can't see any way to handle the tags, because the way the
> schema seems to work is that it assumes the object is composed of
> everything within it, as opposed to associating with things outside of
> it. So I can add new tags to the object, but that is not the behaviour
> I want, because the user needs to be limited to existing tags.

What you want is to use the deferred decorator and then pass your
values along using .bind()

http://deformdemo.repoze.org/deferred_schema_bindings/


Something like this using

def deferred_csrf_token(node, kw):
csrf_token = kw.get('csrf_token')
return csrf_token

class UserSchema(colander.Schema):
csrf_token = colander.SchemaNode(
colander.String(),
widget = deform.widget.HiddenWidget(),
default = deferred_csrf_token,
)
name = colander.SchemaNode(
colander.String(),
widget = deform.widget.TextInputWidget(size=80),
missing=u'',
)
phone = colander.SchemaNode(
colander.String(),
widget = deform.widget.TextInputWidget(size=80),
missing=u'',
)

@view_config(route_name='profile_index',
renderer='profile_index.jinja2',
permission='authenticated')
def index(request):
schema =
UserSchema().bind(csrf_token=request.session.get_csrf_token())
form = deform.Form(schema, buttons=('Update Profile',))
return {'form':form.render()}

something closer to what you might want:

@colander.deferred
def deferred_users_widget(node, kw):
users = kw.get('user_list', [])
return deform.widget.SelectWidget(values=users)

class FormSchema(colander.Schema):
label = colander.SchemaNode(colander.String(), title='or',
missing=u'',
widget = LabelWidget(),
)
user_id = colander.SchemaNode(colander.String(), title='Username',
widget = deferred_users_widget,
)


In your view:

schema =
HtaccessSchema().bind(user_list=get_ftpusers(self.uid))
form = deform.Form(schema, buttons=('Update',))

Ben Sizer

unread,
Aug 31, 2011, 9:29:06 AM8/31/11
to pylons-discuss
On Aug 30, 2:09 pm, Ben Sizer <kylo...@gmail.com> wrote:
> I'm putting together a form, and looking for ways to do it, and
> although I'm used to just writing the HTML by hand, I'll happily use
> something to generate it also. The webhelpers.html.tag stuff looks
> like it would work: but are there any examples of people using this
> with Pyramid?

Although I'm grateful for the suggestions I've had so far, and have
had some luck with deform, I've still not got a direct answer to this
question; am I asking in the right place?

Having started making forms with deform (and finding that it doesn't
seem to play nicely with anything other than Chameleon - why?
Shouldn't HTML be the lingua franca here, not some template engine?)
and then found out that Chameleon doesn't do template inheritance
without some sort of bizarre macro language (is this not an incredibly
common use case, for menus and the like?), I'm finding myself wanting
to keep things simple and avoid using these big packages that attempt
to solve a problem magically but actually make every edge case 10x
more complicated, and instead just generating the HTML I need, where I
need it.

But nobody wants to talk about generating small bits of HTML, it
seems. :)

--
Ben Sizer

Chris McDonough

unread,
Aug 31, 2011, 11:54:56 AM8/31/11
to pylons-...@googlegroups.com

For the record: https://github.com/mfeif/deform_mako (replaces the
Chameleon templates). Deform is extensible this way, as it mentions in
its docs.

But really the minute someone starts bumping up against
styling/templating problems in Deform that seem to foreshadow a
drip-drip-drip of complaints and recriminations, I usually just tell
them to find something else. ;-) Life's too short.

- C


cd34

unread,
Aug 31, 2011, 9:32:45 PM8/31/11
to pylons-discuss
virtualenv --no-site-packages newenv
source bin/activate
paster create -t pyramid_routesalchemy webhelp
cd webhelp/webhelp

edit webhelp/development.ini and put near the top:

mako.directories = webhelp:templates

edit webhelp/webhelp/__init__.py and put:

config.add_subscriber('webhelp.subscribers.add_renderer_globals',
'pyramid.events.BeforeRender')
config.add_route('webhelper', '/webhelper')
config.scan()

above:

return config.make_wsgi_app()

edit webhelp/webhelp/views.py, add:

from pyramid.view import view_config

@view_config(route_name='webhelper', renderer='webhelper.mako')
def webhelper(request):
return {'data':'data', 'email1_count':1, 'email2_count':2}

create webhelp/webhelp/subscribers.py:

from pyramid.threadlocal import get_current_request
from pyramid.exceptions import ConfigurationError
from pyramid.url import route_url
from webhelp import helpers

def add_renderer_globals(event):
request = event.get('request')
if request is None:
request = get_current_request()
globs = {
'h':helpers,
}
event.update(globs)

create webhelp/webhelp/helpers.py:

from webhelpers.text import plural
from webhelpers.html.tags import form

create webhelp/webhelp/templates/webhelper.mako:

<p>
here's my ${data}
</p>
You have ${h.plural(email1_count,'message','messages')}.<br/>
You have ${h.plural(email2_count,'message','messages')}.<br/>
${h.form('asdf')}

Briefly, any import in helpers will be exposed to your template as
h.whatever. If you just want to use normal webhelper rather than a
form library you can. Also, I use deform with both Mako and Jinja2,
and unless you need to change the widgets, it doesn't really matter
much that the templates use chameleon. I've written several projects
and except for one minor case, haven't modified the templates.

Mark

unread,
Sep 1, 2011, 8:33:07 AM9/1/11
to pylons-discuss
Hi Ben,

I totally feel you. I had this same issue with Pylon's FormEncode
initially. I had a hard time customizing it for my needs and was
unable to get it working properly with AJAX. In the end I just gave
up and designed my form the old-fashion way, in my opinion, the best
way.

You are right. Keep things simple. I don't like magic and often
times they cause more grief than joy. These packages like Deform and
Chameleon are good to have, but not necessary when it comes to
building a web application.

Stay hungry, Stay foolish

-Mark Huang

Ben Sizer

unread,
Sep 8, 2011, 7:44:14 AM9/8/11
to pylons-discuss
On Aug 31, 4:54 pm, Chris McDonough <chr...@plope.com> wrote:
> On Wed, 2011-08-31 at 06:29 -0700, Ben Sizer wrote:
>
> For the record:  https://github.com/mfeif/deform_mako(replaces the
> Chameleon templates).  Deform is extensible this way, as it mentions in
> its docs.
>
> But really the minute someone starts bumping up against
> styling/templating problems in Deform that seem to foreshadow a
> drip-drip-drip of complaints and recriminations, I usually just tell
> them to find something else.  ;-)  Life's too short.

Yes, you're right, and I am sorry for any implicit and unintentional
criticism of the hard work that has gone into Deform and other
systems! This is exactly why I just wanted information on webhelpers,
and why it was a little frustrating for people to say, "use deform
instead". I know they are just trying to help when they suggest new
systems to approach the issue but often it just changes the problem
from "tedious but simple" to "short but complex".

So, again (not directed to you, Chris): are there any examples of
people using the webhelpers.html.tag stuff with Pyramid? :)

--
Ben Sizer

Ben Sizer

unread,
Sep 8, 2011, 7:51:05 AM9/8/11
to pylons-discuss
On Sep 1, 2:32 am, cd34 <mcd...@gmail.com> wrote:
> Also, I use deform with both Mako and Jinja2,
> and unless you need to change the widgets, it doesn't really matter
> much that the templates use chameleon. I've written several projects
> and except for one minor case, haven't modified the templates.

That's what I had hoped would be the case, due to the similar syntax.
But when I actually tried it, the change from Chameleon to Mako
started escaping all the HTML that I was injecting into the template,
including rendered forms, which made a lot of my code useless. :)

Thanks also for your example code. Whether or not I use deform, I will
probably switch to Mako and so the summary of all the things I need to
change is very helpful.

--
Ben Sizer

Robert Forkel

unread,
Sep 8, 2011, 8:28:46 AM9/8/11
to pylons-...@googlegroups.com
You'll have to use a filter to include rendered deform forms in a mako
template, like so

${form.render()|n}

The n-filter prevents escaping.
regards
robert

Mike Orr

unread,
Sep 8, 2011, 1:28:41 PM9/8/11
to pylons-...@googlegroups.com
On Tue, Aug 30, 2011 at 9:36 AM, Ben Sizer <kyl...@gmail.com> wrote:
> On Aug 30, 2:42 pm, Benjamin Sims <benjamins...@gmail.com> wrote:
>>
>> The demo site provides nice examples of its capabilities:
>>
>> http://deformdemo.repoze.org/
>
> Having explored this a little more, I must admit I find it daunting.
> Sure, for simple stuff it's "easier than building HTML by hand", but
> for anything non-trivial, it's hard to work out what to do, because
> everything's hidden from me.

The first question is, do you have specific requirements for how the
forms should look and feel, or do you just want quick functioning
forms that you can prettify with CSS? The second question is, do you
prefer a minimal library that keeps you close to the HTML, or an
object-oriented system that generates a form in one step? These
questions indicate what kind of library you'd prefer, and they also
explain why there are so many vastly different form libraries.

I have only used WebHelpers/FormEncode/Mako since all of my forms so
far are in Pylons. and because I'm the WebHelpers maintainer so I'm
quite familiar with it. :) But I do want to try Deform as soon as I
have occasion to. I had mentioned earlier about making a form library
comparison demo but I never got around to it. In the meantime, Ben
Sims and cd34 have listed the most common alternatives.

> Do people really find this easier than just emitting the HTML? If I
> was doing this by hand I'd grab all the Tag objects, emit "<input
> type='checkbox' name='tags' value='%d'>%s</input>" % (tag.id,
> tag.name) for each one, then map those IDs back to Tags in the
> response handler. That's why I asked about webhelpers, because a
> function to generate that HTML for an input tag is quite handy without
> obscuring the detail of what is actually going on.

The advantage of WebHelpers tags is it's close to the HTML, so if
you're used to making forms by hand it's an easy and flexible
alternative. The helpers are more convenient than hand-coding if you
happen to have a boolean variable meaning "checked", or a boolean
meaning "use the multipart POST format because I have file uploads",
etc. If you're shadowing a database record, check out the ModelTags
object. I've never had a problem with late-binding SELECT options and
the like, because the helper is called when the template is rendered,
and so all the necessary state information should be available.

Re the 'id' attributes, there are differences of opinion on how those
should be set. WebHelpers offers one opinion by default; you can
override it if your CSS or Javascript needs something else.

> Although I'm grateful for the suggestions I've had so far, and have
had some luck with deform, I've still not got a direct answer to this
question; am I asking in the right place?

Yes. All of us make forms, so all of us would benefit from discussions
of the various form alternatives in Pyramid apps, and hearing how well
they've worked in actual deployments.

Chris M wrote:
> But really the minute someone starts bumping up against
styling/templating problems in Deform that seem to foreshadow a
drip-drip-drip of complaints and recriminations, I usually just tell
them to find something else. ;-)

That's an important point, and it dovetails with my questions above.
If the object-oriented form generators don't do what you want out of
the box or with simple customizations, then you're probably better off
with a lower-level tool.

--
Mike Orr <slugg...@gmail.com>

Mike Orr

unread,
Sep 8, 2011, 2:16:20 PM9/8/11
to pylons-...@googlegroups.com

It's important to understand how this smart escaping works. It was
built into the late generation of template engines to avoid injecting
unexpected HTML markup into templates, especially those coming
directly from user input. Site developers should escape all input
data, but sometimes they forget, or markup is found in a database
field where nobody anticipated it, etc. Mako, Pyramid, and WebHelpers
have all converged on the MarkupSafe package, which replaced their
previous individual systems. I haven't used Chameleon so I don't know
how it's configured in Pyramid, but I assume it should be escaping
like Mako.

The system escapes all strings except those which have been explicitly
marked as preformatted. The marker is an '.__html__' method on the
object, which is called instead of '.__str__'. Normal string objects
do not have this method, but the subclasses ``markupsafe.Markup`` and
``webhelpers.html.literal`` do. The marker is a method rather than a
base class so that any library can provide its own implementation
without depending on anything external. So, using WebHelpers:

literal(u"My <strong>safe</strong> string.") => mark the
string preformatted as-is
escape(u"My <strong>unsfae</strong> string.") => escape the
string and mark it as preformatted

Mako implements the escaper as a default filter, meaning it's applied
by default to every value that's inserted into the template. The
"${var | n}" syntax disables the default filter, which allows the
string to remain as-is even if it's not marked. So you can do this.
But in principle it's better to mark the string as early as possible,
to maintain the distinction of safe vs unsafe strings throughout the
program. The WebHelpers form helpers automatically produce marked
strings. Other libraries should do the same (now that MarkupSafe has
become the de facto standard in Pylons/WSGI circles), but maybe they
don't.

Mako automatically marks the return values from its '%def' methods,
because literal HTML in the template is presumed to be safe. This has
the side effect of escaping any unmarked strings embedded in the
return value.

Normally when marked and unmarked strings are mixed in an expression,
the unmarked strings are escaped and the final result is marked.
Markup/literal do this by overriding the '+' operator to take control
of the evaluation. However, in a few unavoidable cases due to Python's
language rules, an unmarked component takes control of the expression,
and the final result is a regular string (unmarked), which will
therefore be escaped when used in a template. The solution to this is
to wrap the expression in `Markup()` or `literal()`, or to use the
convenience functions which take care of this ; e.g.

webhelpers.html.lit_sub(my_re_regex, ...)

instead of:

my_re_regex.sub(...)

--
Mike Orr <slugg...@gmail.com>

Mike Orr

unread,
Sep 8, 2011, 2:33:59 PM9/8/11
to pylons-...@googlegroups.com
Two other things about auto-escaping.

The Pyramid and Mako default is to convert ``None`` to ``u'None'``.
The Pylons default (i.e., Pylons' Mako configuration) is to convert
``None`` to ``u''``. To get the None-to-"" conversion, you have to
replace the default filter with ``markupsafe.escape_silent``. I don't
remember exactly where you do this in Pyramid apps.

The other thing is, a non-string object can also provide an
'.__html__' method. That way it can be used directly as a template
variable, and it can provide its preferred HTML rendering. It can
return an ordinary string; it doesn't have to mark it. (Marking it
would cause an infinite recursion. :) The implementations of
Markup/literal themselves return 'self' when '.__html__' is called.

--
Mike Orr <slugg...@gmail.com>

cd34

unread,
Sep 8, 2011, 9:00:38 PM9/8/11
to pylons-discuss
On Sep 8, 7:44 am, Ben Sizer <kylo...@gmail.com> wrote:
> So, again (not directed to you, Chris): are there any examples of
> people using the webhelpers.html.tag stuff with Pyramid? :)

http://groups.google.com/group/pylons-discuss/msg/d711428e505072b5

This enables webhelpers in a very minimal project.

Mike Orr

unread,
Sep 9, 2011, 1:50:59 PM9/9/11
to pylons-...@googlegroups.com

You don't *need* a BeforeRenderer unless you want to create an 'h'
variable for the helpers. You can import the helpers directly into any
template or view that needs them. At least if the template engine lets
you do Python imports as Mako does.

Also, Akhet has this preconfigured, so you can either start a project
with that or copy the code from it.

--
Mike Orr <slugg...@gmail.com>

Reply all
Reply to author
Forward
0 new messages