← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:stormify-potemplate into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:stormify-potemplate into launchpad:master.

Commit message:
Convert POTemplate to Storm

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/447290
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:stormify-potemplate into launchpad:master.
diff --git a/lib/lp/registry/model/distroseries.py b/lib/lp/registry/model/distroseries.py
index e801a05..bcb4a27 100644
--- a/lib/lp/registry/model/distroseries.py
+++ b/lib/lp/registry/model/distroseries.py
@@ -17,7 +17,7 @@ from typing import List
 
 import apt_pkg
 from lazr.delegates import delegate_to
-from storm.expr import SQL, And, Column, Desc, Is, Join, Or, Select, Table
+from storm.expr import SQL, And, Column, Desc, Is, Join, Not, Or, Select, Table
 from storm.locals import JSON, Int, Reference, ReferenceSet
 from storm.store import Store
 from zope.component import getUtility
@@ -1066,14 +1066,16 @@ class DistroSeries(
 
     def getTranslatableSourcePackages(self):
         """See `IDistroSeries`."""
-        query = """
-            POTemplate.sourcepackagename = SourcePackageName.id AND
-            POTemplate.iscurrent = TRUE AND
-            POTemplate.distroseries = %s""" % sqlvalues(
-            self.id
-        )
-        result = SourcePackageName.select(
-            query, clauseTables=["POTemplate"], orderBy=["name"], distinct=True
+        result = (
+            IStore(SourcePackageName)
+            .find(
+                SourcePackageName,
+                POTemplate.sourcepackagename == SourcePackageName.id,
+                Is(POTemplate.iscurrent, True),
+                POTemplate.distroseries == self,
+            )
+            .order_by(SourcePackageName.name)
+            .config(distinct=True)
         )
         return [
             SourcePackage(sourcepackagename=spn, distroseries=self)
@@ -1085,25 +1087,34 @@ class DistroSeries(
         # Note that both unlinked packages and
         # linked-with-no-productseries packages are considered to be
         # "unlinked translatables".
-        query = """
-            SourcePackageName.id NOT IN (SELECT DISTINCT
-             sourcepackagename FROM Packaging WHERE distroseries = %s) AND
-            POTemplate.sourcepackagename = SourcePackageName.id AND
-            POTemplate.distroseries = %s""" % sqlvalues(
-            self.id, self.id
-        )
-        unlinked = SourcePackageName.select(
-            query, clauseTables=["POTemplate"], orderBy=["name"]
-        )
-        query = """
-            Packaging.sourcepackagename = SourcePackageName.id AND
-            Packaging.productseries = NULL AND
-            POTemplate.sourcepackagename = SourcePackageName.id AND
-            POTemplate.distroseries = %s""" % sqlvalues(
-            self.id
-        )
-        linked_but_no_productseries = SourcePackageName.select(
-            query, clauseTables=["POTemplate", "Packaging"], orderBy=["name"]
+        unlinked = (
+            IStore(SourcePackageName)
+            .find(
+                SourcePackageName,
+                Not(
+                    SourcePackageName.id.is_in(
+                        Select(
+                            Packaging.sourcepackagenameID,
+                            where=(Packaging.distroseries == self),
+                            distinct=True,
+                        )
+                    )
+                ),
+                POTemplate.sourcepackagename == SourcePackageName.id,
+                POTemplate.distroseries == self,
+            )
+            .order_by(SourcePackageName.name)
+        )
+        linked_but_no_productseries = (
+            IStore(SourcePackageName)
+            .find(
+                SourcePackageName,
+                Packaging.sourcepackagename == SourcePackageName.id,
+                Is(Packaging.productseriesID, None),
+                POTemplate.sourcepackagename == SourcePackageName.id,
+                POTemplate.distroseries == self,
+            )
+            .order_by(SourcePackageName.name)
         )
         result = unlinked.union(linked_but_no_productseries)
         return [
@@ -1759,7 +1770,7 @@ class DistroSeriesSet:
             .find(
                 DistroSeries,
                 DistroSeries.hide_all_translations == False,
-                DistroSeries.id == POTemplate.distroseriesID,
+                DistroSeries.id == POTemplate.distroseries_id,
             )
             .config(distinct=True)
         )
diff --git a/lib/lp/registry/model/product.py b/lib/lp/registry/model/product.py
index b879223..6ab546e 100644
--- a/lib/lp/registry/model/product.py
+++ b/lib/lp/registry/model/product.py
@@ -2206,7 +2206,7 @@ class ProductSet:
                 (Product, Person),
                 Product.active == True,
                 Product.id == ProductSeries.productID,
-                POTemplate.productseriesID == ProductSeries.id,
+                POTemplate.productseries_id == ProductSeries.id,
                 Product.translations_usage == ServiceUsage.LAUNCHPAD,
                 Person.id == Product._ownerID,
             )
diff --git a/lib/lp/registry/model/projectgroup.py b/lib/lp/registry/model/projectgroup.py
index 46cd5cb..428c718 100644
--- a/lib/lp/registry/model/projectgroup.py
+++ b/lib/lp/registry/model/projectgroup.py
@@ -222,7 +222,7 @@ class ProjectGroup(
         origin = [
             Product,
             Join(ProductSeries, Product.id == ProductSeries.productID),
-            Join(POTemplate, ProductSeries.id == POTemplate.productseriesID),
+            Join(POTemplate, ProductSeries.id == POTemplate.productseries_id),
         ]
         return (
             store.using(*origin)
diff --git a/lib/lp/services/statistics/model/statistics.py b/lib/lp/services/statistics/model/statistics.py
index 4b4ff60..5d1dcb1 100644
--- a/lib/lp/services/statistics/model/statistics.py
+++ b/lib/lp/services/statistics/model/statistics.py
@@ -199,7 +199,9 @@ class LaunchpadStatisticSet:
                 translations_usage=ServiceUsage.LAUNCHPAD
             ).count(),
         )
-        self.update("potemplate_count", POTemplate.select().count())
+        self.update(
+            "potemplate_count", IStore(POTemplate).find(POTemplate).count()
+        )
         ztm.commit()
         self.update("pofile_count", IStore(POFile).find(POFile).count())
         ztm.commit()
diff --git a/lib/lp/translations/browser/potemplate.py b/lib/lp/translations/browser/potemplate.py
index 92b7b50..92e3cec 100644
--- a/lib/lp/translations/browser/potemplate.py
+++ b/lib/lp/translations/browser/potemplate.py
@@ -1027,7 +1027,7 @@ class BaseSeriesTemplatesView(LaunchpadView):
                 And(
                     Packaging.distroseries == self.context.id,
                     Packaging.sourcepackagename
-                    == POTemplate.sourcepackagenameID,
+                    == POTemplate.sourcepackagename_id,
                 ),
             )
             .joinOuter(
@@ -1046,13 +1046,13 @@ class BaseSeriesTemplatesView(LaunchpadView):
             .joinOuter(
                 OtherTemplate,
                 And(
-                    OtherTemplate.productseriesID == ProductSeries.id,
+                    OtherTemplate.productseries_id == ProductSeries.id,
                     OtherTemplate.name == POTemplate.name,
                 ),
             )
             .joinInner(
                 SourcePackageName,
-                SourcePackageName.id == POTemplate.sourcepackagenameID,
+                SourcePackageName.id == POTemplate.sourcepackagename_id,
             )
         )
 
diff --git a/lib/lp/translations/browser/serieslanguage.py b/lib/lp/translations/browser/serieslanguage.py
index e515105..4f98411 100644
--- a/lib/lp/translations/browser/serieslanguage.py
+++ b/lib/lp/translations/browser/serieslanguage.py
@@ -55,7 +55,7 @@ class BaseSeriesLanguageView(LaunchpadView):
             load_related(
                 SourcePackageName,
                 self.batchnav.currentBatch(),
-                ["sourcepackagenameID"],
+                ["sourcepackagename_id"],
             )
         else:
             self.batchnav = BatchNavigator(self.context.pofiles, self.request)
diff --git a/lib/lp/translations/doc/poexport-queue.rst b/lib/lp/translations/doc/poexport-queue.rst
index ff5297d..3b35992 100644
--- a/lib/lp/translations/doc/poexport-queue.rst
+++ b/lib/lp/translations/doc/poexport-queue.rst
@@ -14,6 +14,7 @@ succeed and the ones that failed with the error associated.
     >>> import transaction
     >>> from zope.component import getUtility
     >>> from lp.registry.interfaces.person import IPersonSet
+    >>> from lp.services.database.interfaces import IStore
     >>> from lp.testing.faketransaction import FakeTransaction
     >>> from lp.testing.mail_helpers import pop_notifications, print_emails
     >>> from lp.translations.scripts.po_export_queue import ExportResult
@@ -27,7 +28,7 @@ To note error messages with the failure file, it should happen inside an
 exception handling so we can get the exception error:
 
     >>> from lp.translations.model.potemplate import POTemplate
-    >>> potemplate = POTemplate.get(1)
+    >>> potemplate = IStore(POTemplate).get(POTemplate, 1)
     >>> pofile = potemplate.getPOFileByLang("es")
     >>> personset = getUtility(IPersonSet)
     >>> carlos = personset.getByName("carlos")
diff --git a/lib/lp/translations/doc/poexport-request.rst b/lib/lp/translations/doc/poexport-request.rst
index cede80c..d7442c3 100644
--- a/lib/lp/translations/doc/poexport-request.rst
+++ b/lib/lp/translations/doc/poexport-request.rst
@@ -5,6 +5,7 @@ The queue of export requests is served by a cron script. Let's check if it can
 serve those requests properly.
 
     >>> from zope.component import getUtility
+    >>> from lp.services.database.interfaces import IStore
     >>> from lp.translations.interfaces.poexportrequest import (
     ...     IPOExportRequestSet,
     ... )
@@ -25,7 +26,7 @@ Requesting PO files
 Our user requests the Catalan and Czech translations of a template.
 
     >>> from lp.translations.model.potemplate import POTemplate
-    >>> potemplate = POTemplate.get(2)
+    >>> potemplate = IStore(POTemplate).get(POTemplate, 2)
     >>> ca = potemplate.getPOFileByLang("ca")
     >>> cs = potemplate.getPOFileByLang("cs")
 
@@ -104,7 +105,6 @@ Duplicate requests
 On another occasion, the user requests just Catalan translation.  The
 queue is initially empty.
 
-    >>> from lp.services.database.interfaces import IStore
     >>> def render_request(request):
     ...     if request.pofile is None:
     ...         return request.potemplate.name
@@ -239,7 +239,7 @@ just PO files.
     >>> cs.potemplate.distroseries is None
     False
     >>> request_set.addRequest(person, None, [ca, cs])
-    >>> product_template = potemplate.get(1)
+    >>> product_template = IStore(POTemplate).get(POTemplate, 1)
     >>> product_template.productseries is None
     False
     >>> request_set.addRequest(person, product_template)
diff --git a/lib/lp/translations/doc/potemplate.rst b/lib/lp/translations/doc/potemplate.rst
index f302f86..25b03a2 100644
--- a/lib/lp/translations/doc/potemplate.rst
+++ b/lib/lp/translations/doc/potemplate.rst
@@ -181,10 +181,11 @@ POTemplate
 POTemplate is an object with all strings that must be translated for a
 concrete context.
 
+    >>> from lp.services.database.interfaces import IStore
     >>> from lp.testing import verifyObject
     >>> from lp.translations.interfaces.potemplate import IPOTemplate
     >>> from lp.translations.model.potemplate import POTemplate
-    >>> potemplate = POTemplate.get(1)
+    >>> potemplate = IStore(POTemplate).get(POTemplate, 1)
 
 It implements the IPOTemplate interface.
 
@@ -260,7 +261,7 @@ First, we can see the relatives in a IProductSeries context.
 
 Let's get a new IPOTemplate that belongs to an IDistroSeries:
 
-    >>> potemplate = POTemplate.get(4)
+    >>> potemplate = IStore(POTemplate).get(POTemplate, 4)
     >>> print(potemplate.title)
     Template "evolution-2.2" in Ubuntu Hoary package "evolution"
 
@@ -275,7 +276,7 @@ context:
 
 But we can see that there is a third template in this context:
 
-    >>> not_current_template = POTemplate.get(9)
+    >>> not_current_template = IStore(POTemplate).get(POTemplate, 9)
     >>> not_current_template.productseries == potemplate.productseries
     True
 
diff --git a/lib/lp/translations/doc/rosetta-karma.rst b/lib/lp/translations/doc/rosetta-karma.rst
index 58c985b..9ae8415 100644
--- a/lib/lp/translations/doc/rosetta-karma.rst
+++ b/lib/lp/translations/doc/rosetta-karma.rst
@@ -10,11 +10,12 @@ like SQLObjects are not persistent between transactions.
     >>> from lp.app.interfaces.launchpad import ILaunchpadCelebrities
     >>> from lp.registry.interfaces.karma import IKarmaActionSet
     >>> from lp.registry.interfaces.person import IPersonSet
+    >>> from lp.services.database.interfaces import IStore
+    >>> from lp.services.database.sqlbase import flush_database_caches
     >>> from lp.translations.enums import RosettaImportStatus
     >>> from lp.translations.interfaces.translationimportqueue import (
     ...     ITranslationImportQueue,
     ... )
-    >>> from lp.services.database.sqlbase import flush_database_caches
     >>> from lp.translations.model.potemplate import POTemplate
 
     >>> translation_import_queue = getUtility(ITranslationImportQueue)
@@ -46,7 +47,7 @@ Let's say that we have this .pot file to import:
     ... msgid "foo"
     ... msgstr ""
     ... """
-    >>> potemplate = POTemplate.get(1)
+    >>> potemplate = IStore(POTemplate).get(POTemplate, 1)
 
 We are going to import it as the Rosetta expert team, like we do with
 automatic imports from Ubuntu. In this case, we shouldn't give any kind
@@ -76,7 +77,7 @@ We tell the PO template to import from the file data it has.  If any karma is
 assigned to the team, our karma_helper will print it out here.
 
     >>> entry = translation_import_queue[entry_id]
-    >>> potemplate = POTemplate.get(1)
+    >>> potemplate = IStore(POTemplate).get(POTemplate, 1)
     >>> (subject, message) = potemplate.importFromQueue(entry)
 
 (Nothing printed means no karma was assigned)
@@ -91,7 +92,7 @@ Let's do the same import as the Foo Bar user.
 
 Do the import.
 
-    >>> potemplate = POTemplate.get(1)
+    >>> potemplate = IStore(POTemplate).get(POTemplate, 1)
     >>> entry = translation_import_queue.addOrUpdateEntry(
     ...     potemplate.path,
     ...     potemplate_contents,
@@ -112,7 +113,7 @@ Tell the PO template to import from the file data it has, and see the karma
 being assigned.
 
     >>> entry = translation_import_queue[entry_id]
-    >>> potemplate = POTemplate.get(1)
+    >>> potemplate = IStore(POTemplate).get(POTemplate, 1)
     >>> (subject, message) = potemplate.importFromQueue(entry)
     Karma added: action=translationtemplateimport, product=evolution
     >>> transaction.commit()
@@ -140,7 +141,7 @@ Let's say that we have this .po file to import:
     ... """
     ...     % datetime.now(timezone.utc).isoformat()
     ... ).encode()
-    >>> potemplate = POTemplate.get(1)
+    >>> potemplate = IStore(POTemplate).get(POTemplate, 1)
     >>> pofile = potemplate.getPOFileByLang("es")
 
 As we can see, we don't have any information in that file about who
@@ -173,7 +174,7 @@ to make sure it's stored properly.
 Tell the PO template to import from the file data it has.  If any karma is
 assigned to the team, our karma_helper will print it out here.
 
-    >>> potemplate = POTemplate.get(1)
+    >>> potemplate = IStore(POTemplate).get(POTemplate, 1)
     >>> entry = translation_import_queue[entry_id]
     >>> pofile = potemplate.getPOFileByLang("es")
     >>> (subject, message) = pofile.importFromQueue(entry)
@@ -183,7 +184,7 @@ assigned to the team, our karma_helper will print it out here.
 We attach the new file as coming from upstream, that means that we
 will give karma only for the upload action.
 
-    >>> potemplate = POTemplate.get(1)
+    >>> potemplate = IStore(POTemplate).get(POTemplate, 1)
     >>> pofile = potemplate.getPOFileByLang("es")
     >>> entry = translation_import_queue.addOrUpdateEntry(
     ...     pofile.path,
@@ -204,7 +205,7 @@ to make sure it's stored properly.
 
 Tell the PO file to import from the file data it has.
 
-    >>> potemplate = POTemplate.get(1)
+    >>> potemplate = IStore(POTemplate).get(POTemplate, 1)
     >>> entry = translation_import_queue[entry_id]
     >>> pofile = potemplate.getPOFileByLang("es")
     >>> (subject, message) = pofile.importFromQueue(entry)
@@ -228,7 +229,7 @@ case, we will give karma *only* because the translation change.
 
 We attach the new file as not coming from upstream.
 
-    >>> potemplate = POTemplate.get(1)
+    >>> potemplate = IStore(POTemplate).get(POTemplate, 1)
     >>> pofile = potemplate.getPOFileByLang("es")
     >>> entry = translation_import_queue.addOrUpdateEntry(
     ...     pofile.path,
@@ -251,7 +252,7 @@ Tell the PO file to import from the file data it has.  The user has rights
 to edit translations directly, so their suggestion is approved directly.
 No karma is awarded for this action.
 
-    >>> potemplate = POTemplate.get(1)
+    >>> potemplate = IStore(POTemplate).get(POTemplate, 1)
     >>> entry = translation_import_queue[entry_id]
     >>> pofile = potemplate.getPOFileByLang("es")
     >>> (subject, message) = pofile.importFromQueue(entry)
@@ -262,7 +263,7 @@ To do this test, we are going to repeat previous import.
 
 We import it again without changes and see that we don't get karma changes.
 
-    >>> potemplate = POTemplate.get(1)
+    >>> potemplate = IStore(POTemplate).get(POTemplate, 1)
     >>> pofile = potemplate.getPOFileByLang("es")
     >>> entry = translation_import_queue.addOrUpdateEntry(
     ...     pofile.path,
@@ -284,7 +285,7 @@ to make sure it's stored properly.
 Tell the PO file to import from the file data it has and see that no karma is
 assigned.  If it was, it'd be printed after the call to importFromQueue().
 
-    >>> potemplate = POTemplate.get(1)
+    >>> potemplate = IStore(POTemplate).get(POTemplate, 1)
     >>> entry = translation_import_queue[entry_id]
     >>> pofile = potemplate.getPOFileByLang("es")
     >>> (subject, message) = pofile.importFromQueue(entry)
@@ -311,7 +312,7 @@ handles translations for a given pofile.
 
 No Privileges Person is a translator that fits this requirement.
 
-    >>> potemplate = POTemplate.get(1)
+    >>> potemplate = IStore(POTemplate).get(POTemplate, 1)
     >>> pofile = potemplate.getPOFileByLang("es")
     >>> no_priv = personset.getByEmail("no-priv@xxxxxxxxxxxxx")
     >>> pofile.canEditTranslations(no_priv)
@@ -344,7 +345,7 @@ But now, they will provide a new suggestion.
 At this moment, karma is assigned and thus is printed here.
 
     >>> no_priv = personset.getByEmail("no-priv@xxxxxxxxxxxxx")
-    >>> potemplate = POTemplate.get(1)
+    >>> potemplate = IStore(POTemplate).get(POTemplate, 1)
     >>> pofile = potemplate.getPOFileByLang("es")
     >>> potmsgset = potemplate.getPOTMsgSetByMsgIDText("foo")
     >>> translationmessage = potmsgset.submitSuggestion(
@@ -362,7 +363,7 @@ Now, they will approve a suggestion.  This will give them karma for
 reviewing the suggestion and will also give karma to the user who made the
 suggestion for it being approved.
 
-    >>> potemplate = POTemplate.get(1)
+    >>> potemplate = IStore(POTemplate).get(POTemplate, 1)
     >>> pofile = potemplate.getPOFileByLang("es")
     >>> potmsgset = potemplate.getPOTMsgSetByMsgIDText("foo")
     >>> new_translations = {0: "somethingelse"}
@@ -378,7 +379,7 @@ Finally, this reviewer, is going to add a new translation directly. They
 should get karma for their translation, but not for a review.
 
     >>> kurem = personset.getByEmail("kurem@xxxxxxxxx")
-    >>> potemplate = POTemplate.get(1)
+    >>> potemplate = IStore(POTemplate).get(POTemplate, 1)
     >>> pofile = potemplate.getPOFileByLang("es")
     >>> potmsgset = potemplate.getPOTMsgSetByMsgIDText("foo")
     >>> new_translations = {0: "changed again"}
@@ -433,7 +434,6 @@ Now, let's ensure that we've covered every one of Rosetta's karma
 actions.
 
     >>> from lp.registry.model.karma import KarmaCategory
-    >>> from lp.services.database.interfaces import IStore
     >>> translation_category = (
     ...     IStore(KarmaCategory)
     ...     .find(KarmaCategory, name="translations")
diff --git a/lib/lp/translations/doc/rosetta-poimport-script.rst b/lib/lp/translations/doc/rosetta-poimport-script.rst
index 199edb8..3fc6252 100644
--- a/lib/lp/translations/doc/rosetta-poimport-script.rst
+++ b/lib/lp/translations/doc/rosetta-poimport-script.rst
@@ -1,14 +1,15 @@
 PO import script
 ================
 
-    >>> from lp.translations.model.potemplate import POTemplate
     >>> from lp.app.interfaces.launchpad import ILaunchpadCelebrities
     >>> from lp.registry.interfaces.person import IPersonSet
+    >>> from lp.services.config import config
+    >>> from lp.services.database.interfaces import IStore
     >>> from lp.translations.enums import RosettaImportStatus
     >>> from lp.translations.interfaces.translationimportqueue import (
     ...     ITranslationImportQueue,
     ... )
-    >>> from lp.services.config import config
+    >>> from lp.translations.model.potemplate import POTemplate
     >>> from datetime import datetime, timezone
     >>> rosetta_experts = getUtility(ILaunchpadCelebrities).rosetta_experts
 
@@ -21,7 +22,7 @@ Login as an admin to be able to do changes to the import queue.
 
 We don't care about a POTemplate we are working with, so just pick any.
 
-    >>> potemplate = POTemplate.get(1)
+    >>> potemplate = IStore(POTemplate).get(POTemplate, 1)
 
 Provide a POFile with Last-Translator set to a user not existing in
 the sampledata.
diff --git a/lib/lp/translations/doc/rosetta-translation.rst b/lib/lp/translations/doc/rosetta-translation.rst
index 90968fc..866b444 100644
--- a/lib/lp/translations/doc/rosetta-translation.rst
+++ b/lib/lp/translations/doc/rosetta-translation.rst
@@ -1,4 +1,3 @@
-
 Rosetta Translation Objects
 ===========================
 
@@ -7,8 +6,9 @@ from POTemplate down to POTranslation.
 
 Get a PO template.
 
+    >>> from lp.services.database.interfaces import IStore
     >>> from lp.translations.model.potemplate import POTemplate
-    >>> template = POTemplate.get(1)
+    >>> template = IStore(POTemplate).get(POTemplate, 1)
     >>> template.name == "evolution-2.2"
     True
 
diff --git a/lib/lp/translations/doc/vpotexport.rst b/lib/lp/translations/doc/vpotexport.rst
index 4d68783..854c0f7 100644
--- a/lib/lp/translations/doc/vpotexport.rst
+++ b/lib/lp/translations/doc/vpotexport.rst
@@ -3,11 +3,12 @@ Template export sets
 
 POTemplate.getTranslationRows serialises a template's rows for export.
 
+    >>> from lp.services.database.interfaces import IStore
     >>> from lp.translations.model.potemplate import POTemplate
 
 We need a template to export, for instance, the one with id = 1.
 
-    >>> potemplate = POTemplate.get(1)
+    >>> potemplate = IStore(POTemplate).get(POTemplate, 1)
     >>> vpot_rows = list(potemplate.getTranslationRows())
 
 There are rows.
diff --git a/lib/lp/translations/interfaces/potemplate.py b/lib/lp/translations/interfaces/potemplate.py
index 92fcc0d..33e2787 100644
--- a/lib/lp/translations/interfaces/potemplate.py
+++ b/lib/lp/translations/interfaces/potemplate.py
@@ -132,7 +132,7 @@ class IPOTemplate(IRosettaStats):
         vocabulary="SourcePackageName",
     )
 
-    sourcepackagenameID = Int(
+    sourcepackagename_id = Int(
         title=_("Source Package Name ID"),
         description=_("The ID of the source package that uses this template."),
         required=False,
diff --git a/lib/lp/translations/model/pofile.py b/lib/lp/translations/model/pofile.py
index 1f76e09..60e9571 100644
--- a/lib/lp/translations/model/pofile.py
+++ b/lib/lp/translations/model/pofile.py
@@ -20,6 +20,7 @@ from storm.expr import (
     Coalesce,
     Desc,
     Exists,
+    IsNot,
     Join,
     LeftJoin,
     Not,
@@ -866,7 +867,7 @@ class POFile(StormBase, POFileMixIn):
             self._getCompletePluralFormsConditions(table_name="Other")
         )
         params = {
-            "potemplate": quote(self.potemplate),
+            "potemplate": quote(self.potemplate.id),
             "language": quote(self.language),
             "flag": side_traits.flag_name,
             "other_flag": side_traits.other_side_traits.flag_name,
@@ -948,7 +949,7 @@ class POFile(StormBase, POFileMixIn):
         )
         params = {
             "language": quote(self.language),
-            "potemplate": quote(self.potemplate),
+            "potemplate": quote(self.potemplate.id),
             "flag": flag_name,
             "suggestion_nonempty": suggestion_nonempty,
         }
@@ -1307,7 +1308,7 @@ class POFile(StormBase, POFileMixIn):
             .getForTemplate(self.potemplate)
             .flag_name
         )
-        template_id = quote(self.potemplate)
+        template_id = quote(self.potemplate.id)
         params = {
             "flag": flag_name,
             "language": quote(self.language),
@@ -1682,12 +1683,12 @@ class POFileSet:
         MatchingProductSeries = ClassAlias(ProductSeries)
         MatchingProductSeriesJoin = LeftJoin(
             MatchingProductSeries,
-            MatchingPOT.productseriesID == MatchingProductSeries.id,
+            MatchingPOT.productseries_id == MatchingProductSeries.id,
         )
         MatchingDistroSeries = ClassAlias(DistroSeries)
         MatchingDistroSeriesJoin = LeftJoin(
             MatchingDistroSeries,
-            MatchingPOT.distroseriesID == MatchingDistroSeries.id,
+            MatchingPOT.distroseries_id == MatchingDistroSeries.id,
         )
 
         # Find any sharing POTemplate corresponding to MatchingPOT
@@ -1697,11 +1698,11 @@ class POFileSet:
         OtherProductSeries = ClassAlias(ProductSeries)
         OtherProductSeriesJoin = LeftJoin(
             OtherProductSeries,
-            OtherPOT.productseriesID == OtherProductSeries.id,
+            OtherPOT.productseries_id == OtherProductSeries.id,
         )
         OtherDistroSeries = ClassAlias(DistroSeries)
         OtherDistroSeriesJoin = LeftJoin(
-            OtherDistroSeries, OtherPOT.distroseriesID == OtherDistroSeries.id
+            OtherDistroSeries, OtherPOT.distroseries_id == OtherDistroSeries.id
         )
 
         # And find a sharing POFile corresponding to a sharing POTemplate,
@@ -1734,22 +1735,22 @@ class POFileSet:
                     # OtherPOT is a sharing template with MatchingPOT
                     # in the same distribution and sourcepackagename.
                     And(
-                        MatchingPOT.distroseriesID is not None,
-                        OtherPOT.distroseriesID is not None,
+                        IsNot(MatchingPOT.distroseries_id, None),
+                        IsNot(OtherPOT.distroseries_id, None),
                         (
                             MatchingDistroSeries.distributionID
                             == OtherDistroSeries.distributionID
                         ),
                         (
-                            MatchingPOT.sourcepackagenameID
-                            == OtherPOT.sourcepackagenameID
+                            MatchingPOT.sourcepackagename_id
+                            == OtherPOT.sourcepackagename_id
                         ),
                     ),
                     # OtherPOT is a sharing template with MatchingPOT
                     # in the same product.
                     And(
-                        MatchingPOT.productseriesID is not None,
-                        OtherPOT.productseriesID is not None,
+                        IsNot(MatchingPOT.productseries_id, None),
+                        IsNot(OtherPOT.productseries_id, None),
                         (
                             MatchingProductSeries.productID
                             == OtherProductSeries.productID
diff --git a/lib/lp/translations/model/potemplate.py b/lib/lp/translations/model/potemplate.py
index a1a52ef..58d73cb 100644
--- a/lib/lp/translations/model/potemplate.py
+++ b/lib/lp/translations/model/potemplate.py
@@ -12,16 +12,16 @@ __all__ = [
     "TranslationTemplatesCollection",
 ]
 
-import datetime
 import logging
 import operator
 import os
+from datetime import datetime, timezone
 
 from psycopg2.extensions import TransactionRollbackError
 from storm.expr import SQL, And, Desc, Join, LeftJoin, Or
 from storm.info import ClassAlias
-from storm.properties import Int
-from storm.references import ReferenceSet
+from storm.properties import Bool, DateTime, Int, Unicode
+from storm.references import Reference, ReferenceSet
 from storm.store import Store
 from zope.component import getAdapter, getUtility
 from zope.interface import implementer
@@ -35,17 +35,12 @@ from lp.registry.model.sourcepackagename import SourcePackageName
 from lp.services.database.bulk import load_related
 from lp.services.database.collection import Collection
 from lp.services.database.constants import DEFAULT
-from lp.services.database.datetimecol import UtcDateTimeCol
 from lp.services.database.decoratedresultset import DecoratedResultSet
 from lp.services.database.enumcol import DBEnum
 from lp.services.database.interfaces import IPrimaryStore, IStore
-from lp.services.database.sqlbase import SQLBase, flush_database_updates
-from lp.services.database.sqlobject import (
-    BoolCol,
-    ForeignKey,
-    IntCol,
-    StringCol,
-)
+from lp.services.database.sqlbase import flush_database_updates
+from lp.services.database.stormbase import StormBase
+from lp.services.database.stormexpr import RegexpMatch
 from lp.services.helpers import shortlist
 from lp.services.mail.helpers import get_email_template
 from lp.services.propertycache import cachedproperty
@@ -187,71 +182,58 @@ def get_pofiles_for(potemplates, language):
 
 
 @implementer(IPOTemplate)
-class POTemplate(SQLBase, RosettaStats):
-    _table = "POTemplate"
-
-    productseries = ForeignKey(
-        foreignKey="ProductSeries",
-        dbName="productseries",
-        notNull=False,
-        default=None,
-    )
-    priority = IntCol(dbName="priority", notNull=True, default=DEFAULT)
-    name = StringCol(dbName="name", notNull=True)
-    translation_domain = StringCol(dbName="translation_domain", notNull=True)
-    description = StringCol(dbName="description", notNull=False, default=None)
-    copyright = StringCol(dbName="copyright", notNull=False, default=None)
-    datecreated = UtcDateTimeCol(dbName="datecreated", default=DEFAULT)
-    path = StringCol(dbName="path", notNull=True)
-    source_file = ForeignKey(
-        foreignKey="LibraryFileAlias",
-        dbName="source_file",
-        notNull=False,
-        default=None,
+class POTemplate(StormBase, RosettaStats):
+    __storm_table__ = "POTemplate"
+
+    id = Int(primary=True)
+    productseries_id = Int(name="productseries", allow_none=True, default=None)
+    productseries = Reference(productseries_id, "ProductSeries.id")
+    priority = Int(name="priority", allow_none=False, default=DEFAULT)
+    name = Unicode(name="name", allow_none=False)
+    translation_domain = Unicode(name="translation_domain", allow_none=False)
+    description = Unicode(name="description", allow_none=True, default=None)
+    copyright = Unicode(name="copyright", allow_none=True, default=None)
+    datecreated = DateTime(
+        name="datecreated", default=DEFAULT, tzinfo=timezone.utc
     )
+    path = Unicode(name="path", allow_none=False)
+    source_file_id = Int(name="source_file", allow_none=True, default=None)
+    source_file = Reference(source_file_id, "LibraryFileAlias.id")
     source_file_format = DBEnum(
         name="source_file_format",
         enum=TranslationFileFormat,
         default=TranslationFileFormat.PO,
         allow_none=False,
     )
-    iscurrent = BoolCol(dbName="iscurrent", notNull=True, default=True)
-    messagecount = IntCol(dbName="messagecount", notNull=True, default=0)
-    owner = ForeignKey(
-        dbName="owner",
-        foreignKey="Person",
-        storm_validator=validate_public_person,
-        notNull=True,
+    iscurrent = Bool(name="iscurrent", allow_none=False, default=True)
+    messagecount = Int(name="messagecount", allow_none=False, default=0)
+    owner_id = Int(
+        name="owner", validator=validate_public_person, allow_none=False
     )
-    sourcepackagename = ForeignKey(
-        foreignKey="SourcePackageName",
-        dbName="sourcepackagename",
-        notNull=False,
-        default=None,
+    owner = Reference(owner_id, "Person.id")
+    sourcepackagename_id = Int(
+        name="sourcepackagename", allow_none=True, default=None
     )
-    from_sourcepackagename = ForeignKey(
-        foreignKey="SourcePackageName",
-        dbName="from_sourcepackagename",
-        notNull=False,
-        default=None,
+    sourcepackagename = Reference(sourcepackagename_id, "SourcePackageName.id")
+    from_sourcepackagename_id = Int(
+        name="from_sourcepackagename", allow_none=True, default=None
     )
-    sourcepackageversion = StringCol(
-        dbName="sourcepackageversion", notNull=False, default=None
+    from_sourcepackagename = Reference(
+        from_sourcepackagename_id, "SourcePackageName.id"
     )
-    distroseries = ForeignKey(
-        foreignKey="DistroSeries",
-        dbName="distroseries",
-        notNull=False,
-        default=None,
+    sourcepackageversion = Unicode(
+        name="sourcepackageversion", allow_none=True, default=None
     )
-    header = StringCol(dbName="header", notNull=True)
-    languagepack = BoolCol(dbName="languagepack", notNull=True, default=False)
-    date_last_updated = UtcDateTimeCol(
-        dbName="date_last_updated", default=DEFAULT
+    distroseries_id = Int(name="distroseries", allow_none=True, default=None)
+    distroseries = Reference(distroseries_id, "DistroSeries.id")
+    header = Unicode(name="header", allow_none=False)
+    languagepack = Bool(name="languagepack", allow_none=False, default=False)
+    date_last_updated = DateTime(
+        name="date_last_updated", default=DEFAULT, tzinfo=timezone.utc
     )
 
     # joins
-    pofiles = ReferenceSet("<primary key>", "POFile.potemplate_id")
+    pofiles = ReferenceSet("id", "POFile.potemplate_id")
 
     # In-memory cache: maps language_code to list of POFiles
     # translating this template to that language.
@@ -259,6 +241,29 @@ class POTemplate(SQLBase, RosettaStats):
 
     _uses_english_msgids = None
 
+    def __init__(
+        self,
+        name,
+        translation_domain,
+        path,
+        owner,
+        header,
+        productseries=None,
+        distroseries=None,
+        sourcepackagename=None,
+        languagepack=False,
+    ):
+        super().__init__()
+        self.name = name
+        self.translation_domain = translation_domain
+        self.path = path
+        self.owner = owner
+        self.header = header
+        self.productseries = productseries
+        self.distroseries = distroseries
+        self.sourcepackagename = sourcepackagename
+        self.languagepack = languagepack
+
     @cachedproperty
     def _sharing_ids(self):
         """Return the IDs of all sharing templates including this one."""
@@ -415,7 +420,7 @@ class POTemplate(SQLBase, RosettaStats):
                 .find(
                     POTemplate,
                     POTemplate.id != self.id,
-                    POTemplate.productseriesID == self.productseries.id,
+                    POTemplate.productseries == self.productseries,
                     POTemplate.iscurrent,
                 )
                 .order_by(POTemplate.name)
@@ -429,9 +434,8 @@ class POTemplate(SQLBase, RosettaStats):
                 .find(
                     POTemplate,
                     POTemplate.id != self.id,
-                    POTemplate.distroseriesID == self.distroseries.id,
-                    POTemplate.sourcepackagenameID
-                    == self.sourcepackagename.id,
+                    POTemplate.distroseries == self.distroseries,
+                    POTemplate.sourcepackagename == self.sourcepackagename,
                     POTemplate.iscurrent,
                 )
                 .order_by(POTemplate.name)
@@ -852,7 +856,7 @@ class POTemplate(SQLBase, RosettaStats):
         # Since we have no PO file for this language yet, create one.
         language = self._lookupLanguage(language_code)
 
-        now = datetime.datetime.now()
+        now = datetime.now()
         data = {
             "year": now.year,
             "languagename": language.englishname,
@@ -1142,7 +1146,7 @@ class POTemplate(SQLBase, RosettaStats):
     @property
     def translation_side(self):
         """See `IPOTemplate`."""
-        if self.productseriesID is not None:
+        if self.productseries_id is not None:
             return TranslationSide.UPSTREAM
         else:
             return TranslationSide.UBUNTU
@@ -1192,22 +1196,21 @@ class POTemplateSubset:
 
         # Construct the base clauses.
         if productseries is not None:
-            self.clauses.append(POTemplate.productseriesID == productseries.id)
+            self.clauses.append(POTemplate.productseries == productseries)
             if ordered_by_names:
                 self.orderby = [POTemplate.name]
         else:
-            self.clauses.append(POTemplate.distroseriesID == distroseries.id)
+            self.clauses.append(POTemplate.distroseries == distroseries)
             if ordered_by_names:
                 self.orderby = [SourcePackageName.name, POTemplate.name]
             if from_sourcepackagename is not None:
                 self.clauses.append(
-                    POTemplate.from_sourcepackagenameID
-                    == from_sourcepackagename.id
+                    POTemplate.from_sourcepackagename == from_sourcepackagename
                 )
                 self.sourcepackagename = from_sourcepackagename
             elif sourcepackagename is not None:
                 self.clauses.append(
-                    POTemplate.sourcepackagename == sourcepackagename.id
+                    POTemplate.sourcepackagename == sourcepackagename
                 )
             else:
                 # Select all POTemplates in a Distroseries.
@@ -1235,7 +1238,7 @@ class POTemplateSubset:
                     store.find(
                         (POTemplate, SourcePackageName),
                         (
-                            POTemplate.sourcepackagenameID
+                            POTemplate.sourcepackagename_id
                             == SourcePackageName.id
                         ),
                         condition,
@@ -1319,7 +1322,7 @@ class POTemplateSubset:
             )
         header_params = {
             "origin": "PACKAGE VERSION",
-            "templatedate": datetime.datetime.now(),
+            "templatedate": datetime.now(),
             "languagename": "LANGUAGE",
             "languagecode": "LL",
         }
@@ -1527,9 +1530,9 @@ class POTemplateSet:
         from lp.registry.model.product import Product
         from lp.registry.model.productseries import ProductSeries
 
-        pses = load_related(ProductSeries, templates, ["productseriesID"])
+        pses = load_related(ProductSeries, templates, ["productseries_id"])
         load_related(Product, pses, ["productID"])
-        load_related(SourcePackageName, templates, ["sourcepackagenameID"])
+        load_related(SourcePackageName, templates, ["sourcepackagename_id"])
 
     def wipeSuggestivePOTemplatesCache(self):
         """See `IPOTemplateSet`."""
@@ -1544,7 +1547,7 @@ class POTemplateSet:
         rowcount = (
             IPrimaryStore(POTemplate)
             .execute(
-                "DELETE FROM SuggestivePOTemplate " "WHERE potemplate = ?",
+                "DELETE FROM SuggestivePOTemplate WHERE potemplate = ?",
                 params=(potemplate.id,),
             )
             .rowcount
@@ -1697,10 +1700,10 @@ class POTemplateSharingSubset:
                 POTemplate,
                 And(
                     Or(
-                        POTemplate.productseriesID == ProductSeries.id,
+                        POTemplate.productseries_id == ProductSeries.id,
                         And(
-                            POTemplate.distroseriesID == DistroSeries.id,
-                            POTemplate.sourcepackagenameID
+                            POTemplate.distroseries_id == DistroSeries.id,
+                            POTemplate.sourcepackagename_id
                             == Location.sourcepackagename_id,
                         ),
                     ),
@@ -1722,9 +1725,7 @@ class POTemplateSharingSubset:
         if name_pattern is None:
             templatename_clause = True
         else:
-            templatename_clause = SQL(
-                "POTemplate.name ~ ?", params=(name_pattern,)
-            )
+            templatename_clause = RegexpMatch(POTemplate.name, name_pattern)
 
         return self._queryPOTemplates(templatename_clause)
 
@@ -1837,15 +1838,13 @@ class TranslationTemplatesCollection(Collection):
     starting_table = POTemplate
 
     def restrictProductSeries(self, productseries):
-        return self.refine(POTemplate.productseriesID == productseries.id)
+        return self.refine(POTemplate.productseries == productseries)
 
     def restrictDistroSeries(self, distroseries):
-        return self.refine(POTemplate.distroseriesID == distroseries.id)
+        return self.refine(POTemplate.distroseries == distroseries)
 
     def restrictSourcePackageName(self, sourcepackagename):
-        return self.refine(
-            POTemplate.sourcepackagenameID == sourcepackagename.id
-        )
+        return self.refine(POTemplate.sourcepackagename == sourcepackagename)
 
     def restrictCurrent(self, current_value=True):
         """Select based on `POTemplate.iscurrent`.
diff --git a/lib/lp/translations/model/translationsperson.py b/lib/lp/translations/model/translationsperson.py
index 7107ded..99cd46d 100644
--- a/lib/lp/translations/model/translationsperson.py
+++ b/lib/lp/translations/model/translationsperson.py
@@ -390,7 +390,7 @@ class TranslationsPerson:
         # ProductSeries it may lead to up to two: one for the Product
         # and one for the ProjectGroup.
         DistroSeriesJoin = LeftJoin(
-            DistroSeries, DistroSeries.id == POTemplate.distroseriesID
+            DistroSeries, DistroSeries.id == POTemplate.distroseries_id
         )
 
         # If there's a DistroSeries, it should be the distro's
@@ -404,7 +404,7 @@ class TranslationsPerson:
         DistroJoin = LeftJoin(Distribution, distrojoin_conditions)
 
         ProductSeriesJoin = LeftJoin(
-            ProductSeries, ProductSeries.id == POTemplate.productseriesID
+            ProductSeries, ProductSeries.id == POTemplate.productseries_id
         )
         ProductJoin = LeftJoin(
             Product,
diff --git a/lib/lp/translations/scripts/migrate_current_flag.py b/lib/lp/translations/scripts/migrate_current_flag.py
index 26046bf..ced0c6a 100644
--- a/lib/lp/translations/scripts/migrate_current_flag.py
+++ b/lib/lp/translations/scripts/migrate_current_flag.py
@@ -133,7 +133,7 @@ class MigrateCurrentFlagProcess:
         return (
             self.store.find(
                 Product,
-                POTemplate.productseriesID == ProductSeries.id,
+                POTemplate.productseries_id == ProductSeries.id,
                 ProductSeries.productID == Product.id,
             )
             .group_by(Product)
@@ -151,7 +151,7 @@ class MigrateCurrentFlagProcess:
                 == TranslationTemplateItem.potmsgset_id
             ),
             TranslationTemplateItem.potemplate_id == POTemplate.id,
-            POTemplate.productseriesID == ProductSeries.id,
+            POTemplate.productseries_id == ProductSeries.id,
             ProductSeries.productID == product.id,
         ).config(distinct=True)
 
diff --git a/lib/lp/translations/scripts/remove_translations.py b/lib/lp/translations/scripts/remove_translations.py
index c042b8a..5864987 100644
--- a/lib/lp/translations/scripts/remove_translations.py
+++ b/lib/lp/translations/scripts/remove_translations.py
@@ -471,7 +471,8 @@ def remove_translations(
             " = TranslationMessage.potmsgset"
         )
         conditions.add(
-            "TranslationTemplateItem.potemplate = %s" % sqlvalues(potemplate)
+            "TranslationTemplateItem.potemplate = %s"
+            % sqlvalues(potemplate.id)
         )
 
     if language_code is not None:
diff --git a/lib/lp/translations/scripts/scrub_pofiletranslator.py b/lib/lp/translations/scripts/scrub_pofiletranslator.py
index 686275f..5536710 100644
--- a/lib/lp/translations/scripts/scrub_pofiletranslator.py
+++ b/lib/lp/translations/scripts/scrub_pofiletranslator.py
@@ -251,9 +251,11 @@ def preload_work_items(work_items):
     pofiles = load(POFile, [work_item.pofile_id for work_item in work_items])
     load_related(Language, pofiles, ["language_id"])
     templates = load_related(POTemplate, pofiles, ["potemplate_id"])
-    distroseries = load_related(DistroSeries, templates, ["distroseriesID"])
+    distroseries = load_related(DistroSeries, templates, ["distroseries_id"])
     load_related(Distribution, distroseries, ["distributionID"])
-    productseries = load_related(ProductSeries, templates, ["productseriesID"])
+    productseries = load_related(
+        ProductSeries, templates, ["productseries_id"]
+    )
     load_related(Product, productseries, ["productID"])
     return {pofile.id: pofile for pofile in pofiles}
 
diff --git a/lib/lp/translations/tests/test_translationmerger.py b/lib/lp/translations/tests/test_translationmerger.py
index f1c6e54..d44d69e 100644
--- a/lib/lp/translations/tests/test_translationmerger.py
+++ b/lib/lp/translations/tests/test_translationmerger.py
@@ -9,6 +9,7 @@ from storm.store import Store
 from zope.component import getUtility
 from zope.security.proxy import removeSecurityProxy
 
+from lp.services.database.interfaces import IStore
 from lp.services.log.logger import FakeLogger
 from lp.services.worlddata.interfaces.language import ILanguageSet
 from lp.testing import (
@@ -798,7 +799,9 @@ class TestSharingMigrationPerformance(
         self.stable_pofile = None
         self._flushDbObjects()
 
-        self.templates = [POTemplate.get(id) for id in template_ids]
+        self.templates = [
+            IStore(POTemplate).get(POTemplate, id) for id in template_ids
+        ]
 
     def assertNoStatementsInvolvingTable(self, table_name, statements):
         """The specified table name is not in any of the statements."""
diff --git a/lib/lp/translations/utilities/translation_import.py b/lib/lp/translations/utilities/translation_import.py
index 901bbc1..a27c53c 100644
--- a/lib/lp/translations/utilities/translation_import.py
+++ b/lib/lp/translations/utilities/translation_import.py
@@ -144,7 +144,7 @@ class ExistingPOFileInDatabase:
             "translation_columns": ", ".join(translations),
             "translation_joins": "\n".join(msgstr_joins),
             "language": quote(self.pofile.language),
-            "potemplate": quote(self.pofile.potemplate),
+            "potemplate": quote(self.pofile.potemplate.id),
             "flag": self._getFlagName(),
         }
 
diff --git a/lib/lp/translations/utilities/translationmerger.py b/lib/lp/translations/utilities/translationmerger.py
index e1a8ab1..76c4dd0 100644
--- a/lib/lp/translations/utilities/translationmerger.py
+++ b/lib/lp/translations/utilities/translationmerger.py
@@ -411,11 +411,12 @@ class TranslationMerger:
         ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
         result = store.find(
             Packaging,
-            Packaging.productseries == POTemplate.productseriesID,
-            Packaging.distroseries == PackageTemplate.distroseriesID,
+            Packaging.productseries == POTemplate.productseries_id,
+            Packaging.distroseries == PackageTemplate.distroseries_id,
             Packaging.distroseries == DistroSeries.id,
             DistroSeries.distribution == ubuntu.id,
-            Packaging.sourcepackagename == PackageTemplate.sourcepackagenameID,
+            Packaging.sourcepackagename
+            == PackageTemplate.sourcepackagename_id,
         )
         result.config(distinct=True)
         return result
diff --git a/lib/lp/translations/utilities/translationsplitter.py b/lib/lp/translations/utilities/translationsplitter.py
index d4503b9..26db2ad 100644
--- a/lib/lp/translations/utilities/translationsplitter.py
+++ b/lib/lp/translations/utilities/translationsplitter.py
@@ -105,12 +105,12 @@ class TranslationSplitter(TranslationSplitterBase):
             (UpstreamItem, UbuntuItem),
             UpstreamItem.potmsgset_id == UbuntuItem.potmsgset_id,
             UbuntuItem.potemplate_id == UbuntuTemplate.id,
-            UbuntuTemplate.sourcepackagenameID
+            UbuntuTemplate.sourcepackagename_id
             == self.sourcepackage.sourcepackagename.id,
-            UbuntuTemplate.distroseriesID
+            UbuntuTemplate.distroseries_id
             == self.sourcepackage.distroseries.id,
             UpstreamItem.potemplate_id == UpstreamTemplate.id,
-            UpstreamTemplate.productseriesID == self.productseries.id,
+            UpstreamTemplate.productseries_id == self.productseries.id,
         )
 
 
diff --git a/lib/lp/translations/vocabularies.py b/lib/lp/translations/vocabularies.py
index b856f80..fcf3aec 100644
--- a/lib/lp/translations/vocabularies.py
+++ b/lib/lp/translations/vocabularies.py
@@ -13,15 +13,14 @@ __all__ = [
     "TranslationTemplateVocabulary",
 ]
 
+from storm.expr import Is
 from storm.locals import Desc, Not, Or
 from zope.schema.vocabulary import SimpleTerm
 
 from lp.registry.interfaces.distroseries import IDistroSeries
 from lp.services.compat import tzname
-from lp.services.database.sqlobject import AND
 from lp.services.webapp.vocabulary import (
     NamedStormVocabulary,
-    SQLObjectVocabularyBase,
     StormVocabularyBase,
 )
 from lp.services.worlddata.interfaces.language import ILanguage
@@ -95,24 +94,24 @@ class TranslationMessageVocabulary(StormVocabularyBase):
             yield self.toTerm(message)
 
 
-class TranslationTemplateVocabulary(SQLObjectVocabularyBase):
+class TranslationTemplateVocabulary(StormVocabularyBase):
     """The set of all POTemplates for a given product or package."""
 
     _table = POTemplate
-    _orderBy = "name"
+    _order_by = "name"
 
     def __init__(self, context):
         if context.productseries != None:
-            self._filter = AND(
-                POTemplate.iscurrent == True,
+            self._clauses = [
+                Is(POTemplate.iscurrent, True),
                 POTemplate.productseries == context.productseries,
-            )
+            ]
         else:
-            self._filter = AND(
-                POTemplate.iscurrent == True,
+            self._clauses = [
+                Is(POTemplate.iscurrent, True),
                 POTemplate.distroseries == context.distroseries,
                 POTemplate.sourcepackagename == context.sourcepackagename,
-            )
+            ]
         super().__init__(context)
 
     def toTerm(self, obj):