Add Content from Python
This How-to applies to:
Any version.
This How-to is intended for:
Developers
When you want to add content, there are three things to consider: where, which type of content, and the properties of the object we create.
Where
The most common place to add content is "here"; that is, the folderish object which is our current context. This is how we add content in the UI:
we visit a Folder (or similar) and select the type to add. In Python, this is named either context in a Python Script or Page Template, or
as self in product code.
You can use self or context directly of course, but we'll bind it to the name container (you'll see why shortly):
container = context
Or (in product code):
container = self
(From here on, we'll use context like in a Python Script; make the substitution if you're in product code.)
But what if you want the content somewhere else? Well, you just get a handle on the folderish object you want. There are lots of ways to do this. Often we want to put it in a subobject of something we already have a handle on. We can use attribute notation:
container = context.dogs
Of course, you must be careful in doing this that what you ask for does exist, or else you'll get an AttributeError. We can use getattr to get
around this:
container = getattr(context, 'dogs', None)
This will just result in None, Python's null value, if dogs is not present in the container. You can use an if or boolean statement
to decide what to do then. This is much better than just failing with a exception. (This one is also good if the name has dots or dashes or other
characters that would confuse Python.) Remember that acquisition is in effect here!
There is also mapping access (sometimes called "subscript") notation:
container = context['dogs']
This one is a little different, in that it checks only direct containment. In other words, no acquisition.
You'll notice that in the last two cases, we use a string. It doesn't have to be hardcoded:
subobj = context.REQUEST.pickObj # or any other way we can get a string!
container = context[subobj]
You are not, of course, limited to one level:
container = context.dogs.retrievers
And you can even go to your parent:
container = context.aq_inner.aq_parent
Here's a sibling:
parent = context.aq_inner.aq_parent
container = parent.plants
Sometimes you want to do an "absolute path". We can get a handle on the Plone Site object and traverse from it:
from Products.CMFCore.utils import getToolByName
urltool = getToolByName(context, 'portal_url')
portal = urltool.getPortalObject()
container = portal.animals.dogs.retrievers
There are other ways, too. Other methods you can call will return an object (like portal_membership.getHomeFolder).
There is also restrictedTraverse, the getObject method on catalog results, and other ways you might find.
So by now you've decided where to put your content (which, for the purpose of the next section, we'll just assume is container).
Now we need to put something in it!
Creating Content
It is easy to create content in a place we have a handle to: we are provided, through acquisition, a factory method. A factory, just like it sounds, is a method that is responsible for creating objects. We give this factory the name of the type of content we want to create (and some other info) and it does the rest. The method is 'invokeFactory':
id = "nowHearThis"
container.invokeFactory(type_name="News Item", id=id)
The type_name is the name of the content Type you want to create. Note that this is the id of the object you see in the portal_types
tool, which may not be the same as the name you see in the Plone UI (which uses the type's title, if available.) The id is the
"short name" of the new object, which you will see in the URL.
Note that invokeFactory will obey restrictions on creation, as set in the types tool ("implicitly addable" and filters.) You can go a
little lower to bypass this restriction with the Types Tool's constructContent method, which invokeFactory uses:
typestool = getToolByName(context, 'portal_types')
typestool.constructContent(type_name="News Item", container=container, id=id)
These methods will also accept other parameters, which will be passed on to the object's constructor method. You can use these
if you know the constructor; title is fairly common.
If you want to create a non-content object (like something Zope-specific), you have to do it another way. Look for manage_addProduct.
Working with Content
So we've created some content, but it's basically empty. To work with it, we must get a handle on it:
obj = container[id]
will work. You can use its methods and attributes. Commonly we will do:
obj.setTitle("Now Hear This!")
Most other things you can do with the content depend on its API. Read the code, or check out DocFinderTab
An example of this approach
Tie-in
Is there some way this can be done from a Python script in a shell?
Tie-in
One can get a Python console hooked up to a live Zope instance through "zopectl debug". The same technique can be used to run Python scripts: "zopectl run".
Creating an event
Frank
May also want to reindex()
To remedy this, include:
obj.reindexObject()
... after setting attributes.