24. Upgrade steps#
In this part you will:
Write code to update, create and move content
Enable features with upgrade steps
Tools and techniques covered:
upgrade steps
Check out mastering-plone-project at tag schema:
git checkout schema
The code at the end of the chapter:
git checkout upgrade_steps
More info in The code for the training
You recently changed the site configuration, when you added the behavior ploneconf.featured or when you turned talks into events in the chapter Add event dates to talks.
When projects evolve you sometimes want to modify various things while the site is already up and brimming with content and users. Upgrade steps are pieces of code that run when upgrading from one version of an add-on to a newer one. They can do just about anything. We will use an upgrade step to enable the new behavior instead of reinstalling the add-on.
Upgrade steps help make sure that changes are applied to multiple instances of the site in a consistent way. For example, you might have multiple environments (development, staging, production) or multiple developers working on their own local copies of the site. Once an upgrade step is defined, it can be applied in the same way to all of these, instead of making changes manually through the web.
24.1. Add upgrade steps#
We will create an upgrade step that:
runs the typeinfo step, i.e. loads the GenericSetup configuration stored in
profiles/default/types.xmlandprofiles/default/types/...so we don't have to reinstall the add-on to have our changes from above take effect.cleans up existing talks that might be scattered around the site in the early stages of creating it. We will move all talks to a (folderish) page
talks(unless they already are there).
Upgrade steps can be registered in their own ZCML file to prevent cluttering the main configure.zcml.
Update the file backend/src/ploneconf/site/upgrades/configure.zcml:
1<configure
2 xmlns="http://namespaces.zope.org/zope"
3 xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
4 >
5
6 <genericsetup:upgradeSteps
7 profile="ploneconf.site:default"
8 source="1000"
9 destination="1001"
10 >
11 <genericsetup:upgradeDepends
12 title="Update types"
13 description="Run the typeinfo import step"
14 import_steps="typeinfo"
15 />
16 <genericsetup:upgradeStep
17 title="Clean up site structure"
18 description="Move talks to to their page"
19 handler="ploneconf.site.upgrades.v1001.cleanup_site_structure"
20 />
21 </genericsetup:upgradeSteps>
22
23</configure>
The upgradeDepends directive runs the normal typeinfo import step, which is the one that processes the files in the profile's types folder.
The upgradeStep directive runs a custom handler that we will add below.
Tip
Have a look at Generic Setup import steps in the ZMI at http://localhost:8080/Plone/portal_setup/manage_importSteps to find the import step id.
Import step ids for upgradeDepends#
The upgrade step is registered to run when the profile version increased from 1000 to 1001.
The current version is stored in profiles/default/metadata.xml.
Change it to:
<version>1001</version>
Now let's add a file backend/src/ploneconf/site/upgrades/v1001.py with our custom cleanup_site_structure handler code.
1from plone import api
2
3import logging
4
5
6logger = logging.getLogger(__name__)
7
8
9def cleanup_site_structure(setup_tool):
10 portal = api.portal.get()
11
12 # Create the expected site structure
13 if "training" not in portal:
14 api.content.create(
15 container=portal, type="Document", id="training", title="Training"
16 )
17
18 if "schedule" not in portal:
19 schedule_folder = api.content.create(
20 container=portal, type="Document", id="schedule", title="Schedule"
21 )
22 else:
23 schedule_folder = portal["schedule"]
24 schedule_folder_url = schedule_folder.absolute_url()
25
26 if "location" not in portal:
27 api.content.create(
28 container=portal, type="Document", id="location", title="Location"
29 )
30
31 if "sponsors" not in portal:
32 api.content.create(
33 container=portal, type="Document", id="sponsors", title="Sponsors"
34 )
35
36 if "sprint" not in portal:
37 api.content.create(
38 container=portal, type="Document", id="sprint", title="Sprint"
39 )
40
41 # Find all talks
42 brains = api.content.find(portal_type="talk")
43 for brain in brains:
44 if schedule_folder_url in brain.getURL():
45 # Skip if the talk is already somewhere inside the target folder
46 continue
47 obj = brain.getObject()
48 # Move talk to the folder '/schedule'
49 api.content.move(source=obj, target=schedule_folder, safe_id=True)
50 logger.info(f"{obj.absolute_url()} moved to {schedule_folder_url}")
We create the required site structure if it does not exist yet.
The code makes extensive use of plone.api as discussed in the chapter Programming Plone.
24.2. Run upgrade steps#
After restarting the site we can run the upgrade step:
Go to the Add-ons control panel http://localhost:3000/controlpanel/addons. The add-on
ploneconf.siteshould now be marked with an Update label and have a button to upgrade from 1000 to 1001.Run the upgrade step by clicking on it.
On the console you should see logging messages like:
2024-09-15 11:26:14,114 INFO [ploneconf.site.upgrades.v1001:83][waitress-0] http://localhost:3000/talks/test-talk moved to http://localhost:3000/schedule
Alternatively you can also select which upgrade steps to run like this:
In the ZMI go to portal_setup
Go to the tab Upgrades
Select ploneconf.site from the dropdown and click Choose profile
Run the upgrade step.
24.3. Summary#
You wrote your first upgrade step.
You ran the upgrade step to apply changes to the existing site.