[Zope3-Users] browser:form

Jeff Shell eucci.group at gmail.com
Tue Feb 21 23:41:07 EST 2006


On 2/21/06, David Johnson <djohnson at jsatech.com> wrote:
> Thanks. I will look into formlib.  My goals is to be able to provide forms
> that allow editing of SQL based database entries.  I am exploring SQL and
> Zope integration, and I liked the simplicity of the browser:form concept.

You'll (most likely) want zope.formlib then. It will give you the
control you'll need while still providing a lot of useful base classes
and helpful functions to keep form generation and validation simple.

I had started writing my thoughts and past experiences with Zope - SQL
integration here, but am moving that to a separate topic.

> Here is what I am generally trying to do.  Imagine a database of contacts.
> My thought is to architect it as follows.
> 1. Create a view which simply lists all the contacts. Each contact displayed
> would be an href link to the individual contact.
> 2. Clicking on a contact which generate a form to edit that contact.
>
> The href link for the contact would contain the contact id.
> http://localhost/mycontacts/contact.html?contactid=1234
>
> The __init__ of the browser:form I was planning to use to get the
> "contactid" from the request.

With formlib, you'd be able to get/set this in the update() method, or
if you're clever, you can do it in publishTraverse so you can have url
like '.../mycontacts/contact/1234'.

If you're doing really custom data handling - ie, the data's not an
object that a field can bind to but a dictionary - you'll probably
want to use formlib.FormBase and override setUpWidgets and update()
both. I'll show the publishTraverse trick that I've started to use
too. This *should* work.

Note: ``IContactGateway`` is a complete fabrication of my imagination.
In this scenario, it loads and stores contact information from a SQL
Database, returning a dictionary (load) and accepting a primary key
and dictionary of validated / restricted data (only elements from the
IContact interface in this case) to save.

from zope.app import zapi
from zope.formlib import form
from myapp.interfaces import IContact, IContactGateway

class ContactEditForm(form.FormBase):

    form_fields = form.FormFields(IContact)

    contactid = ''

    def publishTraverse(self, request, name):
        """ Used by zope.publisher to traverse to 'name' """
        # We expect 'name' to be a contactid. Set it as the attribute. Return
        # self to the publisher, which will call __call__ (which then calls
        # self.update() and returns self.render()).
        self.contactid = name
        return self

    def setUpWidgets(self, ignore_request=False):
        """
        Overrides FormBase.setUpWidget to send ``self.contact_data`` to the
        ``data`` argument of form.setUpWidgets.
        """
        self.adapters = {}
        self.widgets = form.setUpWidgets(
            self.form_fields, self.prefix, request=self.request, form=self,
            adapters=self.adapters, ignore_request=ignore_request,
            data=self.contact_data
            )

    def update(self):
        # How you load the contact record is up to you...
        contacts = self.contacts = zapi.getUtility(IContactGateway)
        self.contact_data = contacts.get(self.contactid)

        # FormBase.update calls self.setUpWidgets and responds to any
        # submitted action.
        super(ContactEditForm, self).update()

    # Here's where you can respond to 'save'. This creates a 'Save Contact'
    # button and calls the following method if form validation succeeds.
    @form.action(u'Save Contact')
    def handleSaveContact(self, action, data):
        # 'data' is the validated dictionary. How you save it is up to you
        # :-)
        self.contacts.save(self.contactid, data)
        self.status = "Changes Saved"

In *theory*, that should set you up. The generated form's action will
be a full URL which should include the contactid, so 'publishTraverse'
will get called again with that contact id. (ie - it will render
``form action=".../contacts/1234"``, with 'contacts' being the name of
this view).

Alternately, in 'update', you could do::

    self.contactid = self.request.get('contactid')

But you would have to ensure that contactid is included in the rendered form.

> I would use sqlos, but my limited exposure makes me think it is not as
> flexible as I would like.

I haven't used sqlos. A benefit of sqlos is that, in theory, it would
load your Contact object and make it appear pretty much like any other
Zope object. At that point you could use more regular forms. That
would be a good thing. However, I personally feel there are other
options that could be explored.. They may not exist yet :), but I've
got thoughts brewing.

--
Jeff Shell


More information about the Zope3-users mailing list