Posts Tagged With “pylons”

In Place Link Editing

Comment

I've been working on a new project that has some content management features lately. It's a pretty big project and will take months to complete. I was messing with in-place editing with prototype and scriptaculous and found that I couldn't use the default editor for managing links. Normally when you're doing in-place editing you have a single piece of text/content that you want to edit. You click on it and it brings up the form. For editing links I need to edit the URL and the anchor text. I needed to have two separate fields for this and Ajax.InPlaceEditor from controls.js wasn't entirely up to the task.

I ended up inheriting from Ajax.InPlaceEditor and altering some of the behavior to handle link editing. I feel like it's pretty much a hack job but it does what I need it to do, so it seems good enough. I felt that it would be somewhat beneficial to upload the code for in-place link editing.

I only uploaded the javascript and a html demo. The server-side stuff isn't included since this will work across different web languages. I use Pylons and Mako, for example.

You can grab the demo here: http://alwaysmovefast.com/public_svn/in_place_link_editor.

To give a short example of what I did for the server-side stuff, this could be my controller/action:

class LinksController(BaseController):
    def create(self):
        c.link = {
            'url': request.params['url'],
            'anchor': request.params['anchor'],
            'id': 'some_id'
        }
        return render('/links/create.mako')

And this is what '/links/create.mako' could look like to update the page:

<a href="${c.link['url']}">${c.link['anchor']}</a>

This is a very basic example. My create.mako actually looks something like this:

<script src="/javascripts/prototype.js"></script>
<script src="/javascripts/scriptaculous.js"></script>
<script src="/javascripts/application.js"></script>

<script>
    li = $('newlink');
    li.id = '${c.link['id']}';

    li.innerHTML = '<a href="${c.link['href']}">${c.link['anchor']}</a>';

    new_link = document.createElement('li');
    new_link.id = 'newlink';
    new_link.innerHTML = 'Insert Link';

    li.parentNode.appendChild(new_link);

    new InPlaceLinkEditor('newlink', '/links/create');
</script>

Basically what it does is update 'newlink' by changing its id to the id of the newly-created link on the server side and update the innerHTML of that object. It then creates a brand new link object and appends it to the old link's parent node. I then create a new InPlaceLinkEditor for the new 'newlink'. That's about it.

Hopefully you can find this stuff useful.

FormEncode Usage

Comment

There's a lack of documentation on how to use FormEncode in real projects so I've been meaning to write this article for some time now. I use FormEncode in my Pylons apps and I created a small app just for this article. You can grab the source here: http://alwaysmovefast.com/public_svn/formencode_tutorial. To make using FormEncode easier, I also created a few form helper methods that can be found in formencode_tutorial/lib/form_helpers.py. I also used a little CSS to make the form somewhat pretty.

FormEncode makes it pretty easy to do form validations and to then display any errors to your users. The way it works is that you pass a string of HTML to htmlfill.render(), along with some other options, and it returns parsed HTML for use in your pages. htmlfill.render() uses an errors option where you pass in a dict consisting of field names and error values. A typical error dict might look like: {'firstname': Invalid(u'Please enter your firstname',), 'lastname': Invalid(u'Please enter your lastname',)}.

htmlfill.render() will parse those errors and inject them into your <form:error> tag for that particular element. As well as accepting an error dict, htmlfill.render() can accept a default value dict that will inject default values into your form elements.

When injecting errors into your HTML template, it will also use an error formatter. The default error formatter looks like this:

def default_formatter(error):
    return '<span class="error-message">%s</span><br />' % html_quote(error)

You can also use custom error formatters for a little more control over the look and feel of your displayed errors. I like to use this as my formatter:

def p_error_formatter(error):
    return '<p>%s</p>' % htmlfill.html_quote(error)

The reason I use this as my formatter is because I display label elements below my form elements and above the errors. It's mostly personal preference.

I created a helper to abstract away some of the finer details of using formencode. One of them helps me create text fields with the label and form:error tags prepackaged and ready to pass to htmlfill.render():

# return HTML string that can be passed to htmlfill.render()
def tfield(name, label=None, **options):
    if label is None: label = name

    s = text_field(name, **options)

    id = ''
    if options.has_key('id'): id = options['id']
    else: options['id'] = name

    # append a label to this field for good measure
    s += '<label for="%s">%s</label>' % (id, label)

    # use a custom error formatter. when htmlfill.render() parses this html
    # it looks for the key 'p_error_formatter' and uses that formatter.
    # remember that 'p_error_formatter' is looked for in the error_formatters dict
    # defined above.
    s += '<form:error name="%s" format="p_error_formatter"></form:error>' % name
    return 

Notice the form:error tag? That's where htmlfill.render() inserts error text (if there is any) and formats it according to the 'p_error_formatter'. So how does htmlfill.render() know how to handle 'p_error_formatter'? Easy. You create a dict with your error formatter(s) and pass it to htmlfill.render(). When htmlfill.render() parses your HTML, it will take 'p_error_formatter' and do a lookup in your error formatter dict and call that function with the error text.

Here's my error_formatter dict:

error_formatter = {
    'default': htmlfill.default_formatter,
    'p_error_formatter': p_error_formatter
}

error_formatter['p_error_formatter'] is the p_error_formatter() function I defined above.

I also created my own render() function that just calls htmlfill.render() with some predetermined args:

def render(html, defaults=None, errors=None):
    return htmlfill.render(
        html,
        defaults=defaults,
        errors=errors,
        error_formatters=error_formatters,
        auto_insert_errors=False
    )

That's about it for the core htmlfill stuff. I have a couple other helper methods in form_helpers.py and some more CSS that makes the background of form elements red when there's an error.

If you're running Pylons, just go into the root formencode_tutorial directory and run 'paster serve ––reload development.ini' and check out how it's all tied together.

Feel free to leave comments if you have any questions or if I missed something. Happy hacking!

Pylons

Comment

I've been working on some Pylons projects lately because I've been wanting to move into using Python for web apps instead of Ruby. It took awhile to find the right Python web framework for me. I checked out Django, TurboGears, web.py, and a couple others. I finally landed on Pylons and it was an instant hit with me.

There's still a lot to be done on the Pylons core but it's very exciting to use and I never feel like the framework is holding me back.

This is only going to be a short post until I come up with something interesting to write an article about. Just thought I'd share that Pylons is pretty amazing.

Tagged with: python, pylons, web development
Twitter

davidreynolds: I just became the mayor of Uncle Jack's Billiards on @foursquare! http://4sq.com/a6yEqh