Title-less Type

by Jens W. Klein last modified Dec 22, 2009 05:08 PM
In some cases, you don't want to ask the user for a title because the type naturally doesn't have a title. This document is for those cases.

The first you have to know is that you can't really have a type without title. Titles are needed for breadcrumbs, search results, folder listings, etc. So, instead, we'll hide the title in some different ways but there will still be one (but the user won't see it and won't be asked for it).

There are three ways to accomplish this:

  • Turn the title field into another field.
  • Save something else to the title field.
  • Dynamically generate the title field.

    My need to do this was while writing a type for Quotes ("To be or not to be, that is the question" by "Shakespeare") for which a title doesn't make sense.The scheme of my type is like this:

schema = BaseSchema.copy() + \
         Schema((StringField("Quote",
                             searchable=1,
                             required=1,
                             widget=TextAreaWidget(label="Quote")),
                 StringField("Author",
                             searchable=1,
                             required=1,
                             widget=StringWidget(label="Author"))))

Turn the title field into another field.

This is the easiest solution but you need another field that should match the title, it might not be your case.

In my case, it could be the Author field (or even the Quote field, but I'd have to change the widget), the truth is that none of them satisfy me, but, let's do the case.

I turn my Schema into:

schema = BaseSchema.copy() + \
         Schema((StringField("Quote",
                             searchable=1,
                             required=1,
                             widget=TextAreaWidget(label="Quote")
                 ),
         ))

And latter, all I have to do, is change the label of title's widget by adding this call:

schema["title"].widget.label = "Author"

in the class of the type (outside any function, after defining schema). And that's all, everytime it would ask in a web-form for the title field, it would instead ask for the 'Author' field.

Save something else to the title field.

This solution involves hiding the title field from the user where we don't want it (the edit view for example) and filling it with data from another field.

Doing more or less the same as in the previous case, we leave the scheme as the original (with Quote and Author fields) and with the following calls, after schema was defined, we'll hide title:

schema["title"].required = 0
schema["title"].widget.visible = {"edit": "invisible", "view": "invisible"}

The first line is needed to not make the title field mandatory since there is no way the user could fill it once we hide it and if it is mandatory, Plone will go on asking for it. The second line is to make it invisible.

Then what we need is a function that writes something to title. We'll write the Author to the title, similar to what we've done before by re-implementing the function that sets the Author (in the type class, in my case, Quote):

def setAuthor(self, value):
	self.Schema()['Author'].set(self, value)
	self.setTitle(value)

That function gets called to save the Author. What we do is, in the second line (the first is the definition), save the author and in the third line we set the same value in the title field.

Dynamically generate the title field

In this case, we will generate the title field dynamically:

To remove the title field, outside our class, at the begining of our script (in my case Quote.py) we define the scheme as follows (note: this is outside the class):

schema = BaseSchema + Schema((ComputedField('title',
                                          searchable=1,
                                          expression='context._computeTitle()',
                                          accessor='Title')
                              StringField('Quote',
                                          searchable=1,
                                          required=1,
                                          widget=TextAreaWidget(label="Quote")),
                              StringField('Author',
                                          searchable=1,
                                          required=1,
                                          widget=StringWidget(label="Author"))))

ComputedFields act like fields, but get their value from the "expression" parameter.

Normally, when you make a field with the id of "foo", you get "getFoo" as the automatically-generated accessor. This would give us "getTitle" as the accessor to get the title. Dublin Core metadata, however, has specific names it expects, so we want this to be "Title()", not "getTitle()". The accessor="Title" (note the capital T) makes our automatically generated accessor be "Title".

Here's an example of our generating the title.

def _computeTitle(self):
	"""Get object's title."""
	quote = self.getQuote
	if len(quote) > 20:
		quote = quote[:20] + " [...]"
	author = self.getAuthor()
	title = quote + " by " + author
	return title

And that's all.