How-To Extend A Basic Archetype Content Type
Ok, so you've got the Archetype examples to work, and now you'd like to know how to flesh out the basic example into something useful. (That's how I started; I knew I needed to make new Content Types, but didn't know how they worked. I got the examples going, then tried to figure out how to modify them to do what I wanted.) I found learning Plone/Zope very frustrating until I got to this point. Then, once I figured out how to make Content Types do what I wanted, it all made sense.
Adding FunctionsThis is probably one of the first things on your mind. Where do I add functions to my Content Type? How do I call them? What syntax do I use? Well, these were the things that I wondered about. I figured out how to do them after some trial and error. I never made a real website before, where I had to write scripts (a blog doesn't count). So even though I had a lot of python experience, I was confused at first. [First, know that the Zope server converts a URL path into an object path, to find the object that will render your page. This is covered in the Zope Developer's guide. The key point you need to know is that there's a parallel between the URL and your object hierarchy, but it's not exactly the same.]
To cut to the chase, Zope figures out which object/function the URL is pointing to, and it takes the query string (?arg=val,arg2=val2, etc.) and uses it to figure out all the right arguments for the function call. So, you define your function in the ususal way, i.e.,
class MyExample (BaseContent):
""" My example Archetype Content Type. """
# define the schema
# override the default actions
def my_function (self, foo1, foo2):
""" You need a doc string here!! I lost a lot of time finding this out.
Archetypes needs this when registering the function in the framework.
You'll get a 404 error if you forget the doc string. """
temp1 = "foo" # this is not persisted in the ZODB
self.this_is_a_persisted_member_in_the_ZODB = "I'm here to stay %s" % (foo1,)
# if you don't return anything, then the Zope server will not re-render a page
# anything you return will be rendered
# return "got here" will show up as text
# return context.index_html() will return the default page (should be reasonable in any content; people won't get lost)
# return context.base_edit() has the effect of "jumping" to the edit page
Adding Actions
I was confused when I saw the description of actions in the Plone manual. Here's how I think of them: they're just the hyper-linked tabs along the top of the Content Type (content actions) or horizontal site navagation (site actions). The links are typically to a Content Type function (that returns a page), or to a page template (I only know how to make .pt and .cpt types so far). The actions for the Content Type are defined (overriden) using the Factory Type Information format, and the process well described in the Archetype tutorials. I'll just add that you can make most of the tabs (actions) visible=True or visible=False. You can append your own actions that show up as tabs for your content type.
Changing Page ViewsYou can change the various views of your content type by defining new page templates to display your data. Typically, these page templates (.pt) are placed within the skins/ directory of your product. I can't fill you in on exactly how Zope maps the URL (http://.../myArchExample/my_view) to the my_view.pt, but the details are taken care of (by the Install.py script?) and you should put your page templates in the skins/ directory. [This section needs updating, as soon as I learn how it's done.]
Validating the Edit FormYou probably have a need to validate the data that users enter on the edit page. This process is called validation, and the scripts that implement the rules are called validators. There's a clean way to do this in Archetypes using built-in field validators and your own post_validation() function for the Content Type. You don't have to write any (.cpt,.cpy,.vpy) form templates, or controller scripts. Of course, validation is optional, so you can skip either step.
- Use field validators on individual entries (see the validator = (,) field attribute). This is the first-line of validation.
- Define a post_validation() function. This allows you to validate fields in the context of the entire class, and set error (re-do) flags for individual fields.
After the user hits the 'submit' button on the edit page, the field validators are run first. If any validators fail, the input field is highlighted, and the user is sent back to the edit page to fix the errors.
If all the field validators pass, then your post_validate(self, REQUEST, errors) is called. The form keys and data are passed to you in the REQUEST dictionary. Your code will validate the edit form values in the REQUEST dictionary. If you see errors that require fixing, you'll set them in the errors dictionary (using the corresponding key in the REQUEST). For example, here's a
class MyExample (BaseContent):
""" My example Archetype Content Type. """
# define the schema
# override the default actions
def post_validate (self, REQUEST, errors):
""" This function checks the edit form values in context.
It's called after the field validation passes. """
if REQUEST['type'] == 'buy' and REQUEST['quantity'] == 0:
error['quantity'] = "Quantity must be non-zero."
Adding Child Members
If your content type is a folder-like object, you can write functions that will add child objects. This may be useful, for example, if your Content Type is a ledger, and you need to add new transactions when the user hits an action. The following example code shows how to do this.
class MyExample (BaseFolder):
""" My example Archetype Content Type. """
# define the schema
# override the default actions
def addTransaction (self, type, quantity):
""" This function creates a new MyTransaction object in the folder. """
# create a unique id for this transaction
newId = self.generateUniqueId('MyTransaction')
# create a new MyTransaction object
self.invokeFactory(id=newId, type_name='MyTransaction')
myTransaction = getattr(self, newId)
return myTransaction.base_edit() # send the user to the edit page

