Title-less Type
This How-to applies to: Any version.
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 hidding 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.
copy BaseSchema
Added .copy()
always add .copy()
Hiding ComputedFields
widget = ComputedWidget(modes=('view'))
Please post if you except :)
Code Example
name="title",
searchable=1,
accessor="Title",
widget=StringWidget(
label="Nombre del proyecto",
i18n_domain = "ATProyectoCCV"
)
),
errata in the "Dynamically generate the title field" section
Single quotes are missing in the ComputedField schema example (should look like the following):
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"))))
Additionally, parentheses are missing from the third line of the _computeTitle function (should look like the following):
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
Last thing: the _computeTitle function only worked for me INSIDE the class. Fails with an AttributeError when placed outside the class.
Re: errata in the "Dynamically generate the title field" section
ComputedField('title',
searchable=1,
expression='context._computeTitle()',
accessor='Title'),
Hid more titles than I wanted to
I found that if I did: schema = BaseSchema + Schema( [my schema stuff] ) and schema["title"].required = 0 schema["title"].widget.visible = {"edit": "invisible", "view": "invisible"}
it seems to modify BaseSchema for all my other types, making the title always invisible. I fixed it by cloning the baseschema with: schema = BaseSchema.copy() + Schema( [my schema stuff] )