Generate custom unique ids for new objects
This How-to applies to:
Plone 2.0.x
This How-to is intended for:
Developers
The script portal_skins/plone_scripts/createObject.py is used to create objects when the user selects the relevant type in the add new drop-down. This in turn calls the script portal_skins/plone_scripts/generateUniqueId.py, which generates a unique default id (short name) for new objects based on the object's type, the current date and time, and a random number.
This generated id can be very long, upsetting the navtree (at least until Plone 2.1 comes along and fixes it) and making very unfriendly URLs. Also, in certain circumstances you may wish to have more control over the default id. For instance, in ArchPackage (in the Collective) we needed objects of the type Improvement Proposal, which live exclusively inside an Improvement Proposal container (called Roadmap), to have sequentially numbered ids, so that the first proposal had id 1, the second id 2 etc.
There are three general strategies you may employ.
First, if you are comfortable customising the site-wide generateUniqueId.py, you can do so in your custom folder or a product's skin folder. This gets passed the parameter type_name, which you can use to create type-specific naming schemes. However, you should be aware that if you are trying to create a re-usable product (and in general, you ought to do that anyway), this is a site-wide script used for all types in the site. Thus, another product may ovveride this script again, rendering your customisation ineffective. Another caveat is that you must ensure the id is unique, either by relying on random numbers like the current script does, or checking it explicitly against context.objectIds (), which will return the currently used ids in the folder where the object is being created.
Second, if, like in the case of ArchPackage above, you want to custom-name a type which is only addable inside a container you control (that is, Implicitly addable is turned off in portal_types for the object, and the object is set in the list of allowed content types for some folderish container also part of your product, like the Roadmap container above), you can let acquisition work its magic by defining a method like so on your container class:
security.declareProtected (ADD_CONTENTS_PERMISSION, 'generateUniqueId')
def generateUniqueId (self, type_name):
"""Generate a unique ID for sub objects according to the following rules ....
"""
newId = ""
...
# Set newId to something sensible - it must be unique in the container!
# You can use "if newId in self.objectIds ()" to test whether it is
return newId
ADD_CONTENTS_PERMISSION is assumed to be a constant containing the add contents permission for your product. Modify as necessary.
Third, it may be possible to use the manage_afterAdd () hook in your class to rename an object after it has been created. This may be necessary if you need custom ids generated, possibly with the actual id field hidden in your schema, and neither of the above two solutions will work (i.e. you can't customise the default script, and you don't control the parent container so you can't add a method to it). However, I have not seen any examples of this. Anyone with success stories or alternative suggestions, please leave a comment.
This worked for me in Plone 2.5
_at_rename_after_creation = True
def generateNewId(self):
"""Generate random 20 character alphanumeric id for this object.
This id is used when automatically renaming an object after creation.
"""
import string
from random import Random
newname = ''.join( Random().sample(string.letters+string.digits, 20) )
return newname
Update for Plone 2.1