Modeling Content Types with Schemata
Object oriented analysis, design, and programming solves a wide array of problems. Relational databases make persistence of data a snap. How can you bring the best of these two worlds together? In this article, you'll look at using an object schema to define the attributes of content types. You'll explore Archetypes, a content schema system, and see how it fits in with content management systems. And, you'll create new content types to tackle simple but real-world challenge, managing your DVD library, and all with a pretty user interface.
Presenting Pages in Portals
In the last article, you got a whirlwind introduction to Plone, a content management system (CMS) that lets you create, edit, and publish content through the web to maintain a portal, intranet, public-facing company website, or other web presence. But, CMSs can do a lot more than lower the bar for non-technical people to take web publishing into their own hands. And that's where content types come into play.
A portal's job, regardless of the kind of application in which it's situated, is to publish content. Out of the box, CMSs like Plone and others include content types that reflect traditional web usage: web pages and images that appear on those pages. Plone adds a few additional content types as well, again reflecting the typical use of portals and similar web applications: news items, events, folders in which to make hierarchies of content, and a few others.
But, Plone goes one step further in letting web developers define content types of their own. By modeling application objects as content types, you can go on to define complete web applications in terms of content management. This has the immediate effect of simplifying web application development by hooking into all of the features that a CMS provides "for free": persistence into a database, authentication and authorization, form and page generation, data validation, and so forth.
How do you define content types? By using a schema language called Archetypes. Try it out!
Plone Goes to the Movies
Almost any domestic challenge, whether organizing recipes, scheduling sports meets, or publishing the family newsletter, can be made worse with computer technology. Yet occasionally, computers do help out. And, that's what you'll attempt to do with Plone: Develop a small web application to manage your DVD collection. Rather than futilely try to keep DVDs in order on a shelf by genre or title or age-appropriate rating, you'll put them in an arbitrary but numbered order, and let Plone index them to make locating the right film trivial. And, rather than simply create a free-text web page for each DVD, you'll define a new content type that captures all of the metadata (that is, data about data, in this case, data about a DVD) for each DVD. So, grab a Sharpie and number those DVDs—and then head back to the computer.
With Plone, you use the Archetypes schema language to define your own content types. Content types are defined in products. Products are add-ons to Plone (through its underlying Zope application server) that provide new features. To define a new product, you create a subdirectory in your Plone installation's Products directory. You also create a few standard subdirectories under this directory that holds the initialization code and user interface elements for the product. For the DVD Collection product, we'll make a directory called DVDCollection. Windows users can use Windows Explorer to make the directories, or you might enter the following commands at the Command Prompt:
C:>cd "c:Program FilesPlone 2DataProducts" C:Program FilesPlone 2DataProducts>mkdir DVDCollection C:Program FilesPlone 2DataProducts>mkdir DVDCollectionExtensions C:Program FilesPlone 2DataProducts>mkdir DVDCollectionskins C:Program FilesPlone 2DataProducts>mkdir DVDCollectionskinsDVDCollection
Mac OS X and other Unix users would do something similar to this:
% cd /Applications/Plone-2.5.2/Instance/Products % mkdir DVDCollection % mkdir DVDCollection/Extensions % mkdir DVDCollection/skins % mkdir DVDCollection/skins/DVDCollection
Now, fire up your favorite text editor. The first file you need to make in the DVDCollection directory is called __init__.py. Yes, the py extension means you'll be writing some Python code. Plone, Archetypes, and even the Zope application server on which Plone runs are written in Python, an object-oriented scripting language that focuses on readability and programmer productivity. By learning a little bit of Python, you'll be joining such ranks as NASA, Google, YouTube, and more, all of whom use Python prodigiously.
What do you put in this file? Mostly just boilerplate. Here it is:
from config import SKINS_DIR, GLOBALS, PROJECT_NAME from Products.Archetypes.public import process_types, listTypes from Products.CMFCore.DirectoryView import registerDirectory from Products.CMFCore.permissions import AddPortalContent from Products.CMFCore.utils import ContentInit registerDirectory(SKINS_DIR, GLOBALS) def initialize(context): import DVD contentTypes, constructors, facTypeInfo = process_types(listTypes(PROJECT_NAME), PROJECT_NAME) ContentInit(PROJECT_NAME + ' Content', content_types=contentTypes, permission=AddPortalContent, extra_constructors=constructors, fti=facTypeInfo).initialize(context)
Seasoned Python programmers know that having a file called __init__.py makes the directory in which it's located a Python package. However, because the directory DVDCollection is under your Plone installation's Products directory, it's also a product for the application server on which Plone is running. In other words, products are special kinds of Python packages.
The __init__.py file referenced a module called config, so next you need to create that in the DVDCollection directory by making another Python file, config.py. Its contents should be:
PROJECT_NAME = 'DVDCollection' GLOBALS = globals() SKINS_DIR = 'skins'
This file just provides definitions for the name of the project, the currently available global symbols, and also the location of the "skins" directory. The skins directory is where user-interface elements of your product go, such as images, CSS files, page templates, and so forth.
Lastly, you need one more file of boilerplate. This file should be called Install.py and it needs to go under the Extensions subdirectory. Here's what you put inside that file:
from Products.Archetypes.Extensions.utils import installTypes, install_subskin from Products.Archetypes.public import listTypes from Products.DVDCollection.config import PROJECT_NAME, GLOBALS from StringIO import StringIO def install(context): log = StringIO() installTypes(context, log, listTypes(PROJECT_NAME), PROJECT_NAME) print >>log, 'Installed %s content types' % PROJECT_NAME install_subskin(context, log, GLOBALS) print >>log, 'Installed skin layer' return log.getvalue()
Where's your DVD content type in all this? Nowhere, yet! You'll create one more Python file in the DVDCollection directory, this time called DVD.py. Inside this file, you'll use the Archetypes schema language to define the attributes of a DVD in your collection, as well as create a Python class definition for a new class called DVD.
Calling Archetypes a schema language is a stretch. It's really just a collection of Python class definitions and functions that you use in a specific way to define the data fields of a content type. The field definitions include the name and data type of each field, validation to apply to incoming data, on-page widgets to use to display the field and to provide for users to edit their values, and so forth. You can mix in other schemata and build re-usable libraries of schemata to make defining new content types a piece of cake.
After specifying the schema for your content type, you define a new Python class that refers to that schema. Plone uses that class definition to create objects of your content type, persist them into its object database, display them on a web page, and so forth.
For your DVD collection, you need to define one content type: a DVD. You'll do all this in a file called DVD.py in the DVDCollection directory. Because you're giving each DVD its own number, start out this file with the field definition for a field called dvdNumber:
from Products.Archetypes.public import Schema, IntegerField, IntegerWidget idSchema = Schema(( IntegerField('dvdNumber', required=True, index='FieldIndex:schema', widget=IntegerWidget(label='DVD Number', description='Numeric ID assigned to each DVD.')), ))
You now have a complete schema called idSchema with a single field called dvdNumber. Setting the required flag to True says that when users create a new DVD in your web application, they must provide a value for this field—leaving it blank will give an error message. The index attribute says that you want Plone to index this field for quick lookup and retrieval. Finally, you tell Archetypes what on-screen widget to use: in this case, an IntegerWidget. This widget automatically checks what users enter for values to make sure they're valid integers.
Now, create another schema that'll give you ways to classify DVDs. Add the following to the DVD.py file:
from Products.Archetypes.public import StringField, SelectionWidget, MultiSelectionWidget classifierSchema = Schema(( IntegerField('runningTime', required=False, widget=IntegerWidget(label='Running Time', description='Length of the movie in minutes.')), StringField('rating', required=False, index='FieldIndex', vocabulary=['G', 'PG', 'PG-13', 'R', 'NC-17'], widget=SelectionWidget(label='Rating', description='MPAA rating assigned to the movie.')), StringField('genre', required=True, index='FieldIndex', vocabulary=['Action/Adventure', 'Comedy', 'Drama', 'Family', 'Romance', 'Spaghetti Western'], multiValued=True, widget=MultiSelectionWidget(label='Genre', description='Artisitic style of the movie.', format='checkbox')), ))
Page 1 of 2