You are not logged in Log in Join
You are here: Home » Members » scamps » Developing database-oriented web applications using zope and zetadb

Log in
Name

Password

 

Developing database-oriented web applications using zope and zetadb

Typically, development of web-based business applications has a great number of repetitive tasks, like check security issues, design bored html forms, data validation, translations, or make reports. You don't need to start from zero, using zope + zetadb most of these tasks will be made automatically. This document will try to introduce to you in a new and fast way to develop your web applications.

What is zope?

Zope is a open source web application server that provides a extraordinary development framework. Some of its features are a integrated security model, HTML templates methods, script programming, session management and a highly extensible architecture. Based on python language, there are hundreds of zope products to extend its base functionality, for instance to connect to relational databases, make professional content management or any other thing you can imagine. Zope really has successfully applied object oriented programming to web development, and nowadays is one of the best (commercial or non commercial) web development frameworks. For more information, visit http://www.zope.org.

What is zetadb?

Zetadb is a zope product that adds specific functionality to zope in order to obtain a rapid application development tool for relational databases oriented web applications. Some things zetadb does are add internationalization through GNU gettext tools, integrate OpenOffice in zope and, the most interesting, be able to generate zope objects from database structure. So, using zope + zetadb, you can obtain translatable objects to maintain your database over the web with no programming and, if you need to add some logic, you will be able to do it modifying generated zope objects or adding new ones. To obtain more information please visit http://zetadb.sourceforge.net

ZMI and zope products

ZMI (Zope management interface) is the web interface you see when you enter in zope manager (typically in http://localhost:8080/manage or http://localhost:9673/manage ). Here you can define users, groups, permissions, and also what objects will be contained in your zope.

Inside ZMI, you can add objects using a select box where there are all zope classes available. When you add a object of certain type, you are creating an instance of this class type. The list of classes you can add changes depending on what zope products are installed. When you install a zope product under $ZOPE_HOME/lib/python/Products and zope is restarted, new objects types will appear in the list. Each product can add one or more classes. For instance, installing zetadb results in a new set of "zetadb *" objects available in the select box.

Using zetadb wizard

First and most important step is to create a well designed relational database. It is very important to define accurate primary and foreign keys, as well as sequences for autonumeric primary keys. We will use a very simple example using postgresql to do this article more understandable:

shell:$ su postgres
shell:$ createdb zetadb_example
shell:$ psql zetadb_example
psql>
create user zetadb password 'zope';
\connect - zetadb
create table category ( id_category numeric(4,0) primary key, description varchar(64) not null);
create sequence seq_category;
alter table category alter column id_category set default nextval('seq_category');
create table product ( id_product varchar(8) primary key, description varchar(64) not null, long_description text, cost_price numeric(8,2), sell_price numeric(8,2) not null, id_category numeric(4,0) not null, foreign key (id_category) references category(id_category));


That's right. Now we have a simple postgresql database. Now you should ensure that postgresql will allow connections from zope, verifying this line in /etc/postgresql/pg_hba.conf:
host all all 127.0.0.1 255.255.255.255 md5

Let's go to ZMI and do the following:
  • Add a object of type zetadb application. If you don't have this type of object in your list, please revise your installation of zope and zetadb. To create the application object, you should provide a id, a title, a default language, and a physical directory where you want to put locale files of your application.
  • Inside the zetadb application object, create a zetadb postgresql. Fill the id and title. The database connection string for our example should be host=localhost dbname=zetadb_example user=zetadb password=zope
  • Add two zetadb table managers objects, one per table to manage. Start with the category table manager. Enter in it and go to the Properties tab. Add the fields you want to manage, checking in which forms should appear each field (insert, edit, search or list). Note that id_category is a autonumeric field (through a sequence), so don't check insert and edit options for this field. Also note that in the fields list will appear product, that is not a field, but a link with the products using this category.
  • Click Build objects button and go to the contents tab. The necessary objects to insert, edit, delete, search and list categories are created. You can test it visiting http://localhost:9673/zetadb_example/category
  • Now we will do the same with the product table. When you'll have inserted all the fields, click id_category field. As this field is a foreign key, you will have the option to select a Field to Show from category table. That's it, if you want to view a select box with category descriptions in it, you should select description as a Field to show. After that, click also the Build Objects button.
Now you can maintain data in category and product tables over the web, and all without programming any line of code !! If you want, you can add some other objects to add functionality, or a little menu using a zetadb zpt object, or new options in the toolbar, or whatever you want to do using zope.

Just note that you can execute the Build Objects action as may times as you need. If the object is not modified by you, it will be recreated with new criteria. Otherwise, it won't be changed. If you want that a object you have modified be created again, you should rename or delete it before to press the Build Objects button.

zetadb zpt objects

All objects generated with the name *_form are zetadb zpt objects. This class of objects inherits from Zope Pages Templates (ZPT) and have all their functionality, plus some other useful issues added.

ZPT's have two main advantages over the use of DTML. First of all, it allows the code be moved from the programmer to the designer and vice versa as many times as needed without loose code. This is possible due to the TAL language, that is represented as attributes inside HTML tags. This attributes aren't recognized by HTML editors, but they aren't erased from the code.

The other advantage is that TAL language is limited to representation issues, so it is impossible apply any logic in a ZPT. All the logic should be programmed in python scripts. This enforce to you to separate logical from representation.

Here you have some little examples of TAL:

<tr tal:repeat="record here/select_action">
<td>Description</td>
<td tal:content="record/description">
This text will be replaced by the record description
</td>
</tr>


This piece of code calls a object named select_action (probably a python script or sql method) and, for each result, it displays a row with the description field of the record. Note that in TAL syntax, all objects a referenced as URL's (here/select_action, record/description). This syntax could be too rigid in some cases. For example, if you need to pass parameters, or if you want to use some python function, you can use a python syntax:

<tr tal:repeat="record python:here.select_action(some_param)">
<td>Description</td>
<td tal:content="python:str(record.description).lower()">
This text will be replaced by the lower record description
</td>
</tr>


Note that in this case you should use python syntax to reference objects (here.select_action, record.description). Most used instructions of TAL language are: tal:repeat, tal:condition, tal:define, tal:attributes, tal:content and tal:replace. In Zope Book you will find all you need to learn TAL, in special chapters Using Zope Page Templates and Zope Page Templates Reference

Finally, the main improvement of zetadb zpt over ZPT is its translatable function. All strings in the form _('string') will be considerated translatable strings. For instance:

<tr tal:repeat="record python:here.select_action(some_param)">
<td>_('Description')</td>
<td tal:content="python:_(record.description)">
This text will be replaced by the translated record description
</td>
</tr>

zetadb script objects

All objects generated with the name *_action are zetadb script objects. This class of objects inherits from Python Scripts and have all their functionality, plus some other useful issues added.

Python Scripts are a subset of python language, allowing only some security-tested functions. You can obtain more information in Zope Book, chapters Advanced Zope Scripting and Appendix B: API Reference

Main improvements of zetadb script are the translation function (idem as in ZPT's) and some bindings with database information: db (a reference to the database object), table_name (the table name being managed), fields (a list with information of the table fields) and pk (a list with field names composing the primary key of the table).

zetadb sql objects

All objects generated with the name *_sql are zetadb sql objects. This class of objects inherits from Z SQL Methods and have all their functionality. By now, there are no extra functionality added, just internal issues.

Z SQL Methods have the main advantage of give database independent queries. This is accomplished merging SQL syntax with some <dtml-> instructions. Here you have an example:

select id_category, description
from category
<dtml-sqlgroup where>
<dtml-sqltest p_id_category column=id_category op=eq type=int optional>
<dtml-and>
<dtml-sqltest p_description column=description op=eq type=nb optional>
</dtml-sqlgroup>


More information in Zope Book: Relational Database Connetivity

Extending functionality

You can extend zetadb wizard functionality adding new zope objects or modifying generated ones. We recommend the use of zetadb zpt, zetadb scripts, zetadb sql and zetadb openoffice to do this. You will need a basic knowledge of ZPT's, Python Scripts and Z SQL Methods to obtain good results. Also some knowledge of Zope API will be needed. Now we will introduce to you in the very basics.

REQUEST. This is the object that manage the HTTP request. Through this objects you can obtain variables sended from a previous form or any other CGI information. Typically used in zetadb scripts:

request = context.REQUEST
description = request['description']


SESSION. It storages information across the user session. It could be accessed through the REQUEST object.

request = context.REQUEST
if not(request.SESSION.has_key('category')):
   request.SESSION['category'] = {}


RESPONSE. Used mainly to redirect to another page.

request = context.REQUEST
request.RESPONSE.redirect('list_form')


here / container. Are references to the container of current object. The first one is used in ZPT's, and the second one in Python Scripts. Now we should talk about another zope property named Acquisition, that allows child objects to acquire properties of their ancestors (understanding parent-child relation as a containment relation, not object inheritance). This is very useful to share objects. Here you have an example:

We will suppose this structure:
/Folder1/script1
/Folder1/Folder2/Folder3/zpt1
/Folder1/Folder2/Folder3/script2

And this code in zpt1;
<:tr tal:condition="here/script1">
<:td tal:content="here/script2">
Here will be script2 results if script1 is true
<:/td>
<:/tr>

The call to script2 is normal, as we have seen before. The call to script1 works due to Acquisition, Folder3 has no object or method named script1, also Folder2, but Folder1 has it, and the object in Folder1 is called. Understanding acquisition is very important in order not to duplicate code / objects

Security. Finally, we will see how to obtain user information or perform security checks in our programs. It is not used very often, but sometimes is needed:

To obtain user name in a ZPT:
user/getUserName

To do the same in a python script:
from AccessControl import getSecurityManager
return getSecurityManager().getUser().getUserName()

To check a permission over an object in a python script:
from AccessControl import getSecurityManager
return getSecurityManager().checkPermission(permission, object)

To do the same in ZPT, the best way is to have a python script to do it and call to it.

You can read this chapters of Zope Book for more information Appendix B: API Reference, Acquisition and Users and Security

Translations

Zetadb objects use GNU translation utilities. As we have seen before, any content in the form _('Content') is translatable content but, how we translate it? OK, here you have the steps to translate your application.
  • In your application object, go to Properties tab and fill the physical path where you want to put your locale files (note that user running zope should have write permission on it)
  • In the same screen, choose a default language (you can put any code here, es_ES, ca, en_ES ...)
  • Go to the Locale tab and put, separated by spaces, the codes of all the languages you want to support in your application.
  • Press (Re)generate Locales button. If you go to the path you have choose for your locales, you will find a .po file for each language code you have written
  • Each translator should edit his .po file with a text editor or any specialized tool (like kbabel) and translate all the strings needed. It is also important choose the charset to use.
  • When you will have translated .po files, then you can press Compile Locales button. A .mo file will be generated for each language code and your application will begin to speak many languages.
  • You can repeat the process as many times as needed. New strings will be added to .po files and obsoleted ones will be removed, but previous translation will be preserved.
  • To change the language you can pass an additional parameter language to any page, indicating which language code should be used, and the session of the user will change its language. If you pass no language parameter, application default language will be used. In our example this will show category forms in catalan language: http://localhost:9673/zetadb_example/category?language=ca

OpenOffice

Zetadb has joined the powerful of ZPT's with OpenOffice, so you will be able to easy generated data-filled OpenOffice documents in your applications

To do it, add a zetadb openoffice document (you will need a .sxw or .sxc file). This file will be converted into a folder with some zope objects. You can modify content.xml files adding TAL instructions in order to obtain dynamic content. Here you are using TAL over XML instead of HTML, but it works perfectly :-) You can even use translation issues on it.

When you call to your "zetadb openoffice" object URL, all its content is dynamized and the resultant OpenOffice document is send to the browser. In a near future, we hope you will be able to obtain the same document also in PDF or DOC formats.

Your own products

If you need something than nor zope neither any of its products can do, you can write your own zope product and create as many classes of objects you want. Create zope products requires some python knowledge and also to know some things about zope internal structure. An explanation of this will be to large and I think it should be out of a introduction to zope for a newbies (what this article want to be). If you are interested, I recommend to read the chapter Zope Products of Zope Development Guide and also download some zope products and try to understand its code.


NOTE:  This article is copyrighted by Santi Camps Taltavull ([email protected]) and is under the terms of GNU Free Documentation License.. Despited of this, I will appreciate a e-mail notifying any translation of it.

Comment

Discussion icon No ENUMs allowed in database tables

Posted by: daragh at 2004-07-11

When adding zetadb mysql, you get a weird "invalid literal for int()" error message. This is because ENUMs are not supported by zetadb

Also see: http://sourceforge.net/mailarchive/forum.php?thread_id=3656846&forum_id=34920

Comment

Discussion icon mySQL v4.1 + NEW PASSWORD

Posted by: daragh at 2004-07-11

You will find that from MySQL 4.1 on, the password use a new (md5?) longer format, which zetadb cannot handle - this is reflected in a "client needs to be upgraded" error message. To continue using old-style passwords, see

http://dev.mysql.com/doc/mysql/en/Old_client.html

Comment

Discussion icon Invalid connection string

Posted by: janez at 2004-08-20

I received this string: Invalid connection string with the same parmeters I succed to have a connection without zetadb postgresql using onli ZPsycopgDA! WHY?