← Back to team overview

launchpad-reviewers team mailing list archive

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

 

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

Commit message:
Convert FeaturedProject to Storm

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/394874

This also removes the last user of NamedSQLObjectHugeVocabulary.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:stormify-featuredproject into launchpad:master.
diff --git a/lib/lp/registry/model/featuredproject.py b/lib/lp/registry/model/featuredproject.py
index 6440064..35d3579 100644
--- a/lib/lp/registry/model/featuredproject.py
+++ b/lib/lp/registry/model/featuredproject.py
@@ -1,4 +1,4 @@
-# Copyright 2009 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2020 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Database class for Featured Projects."""
@@ -8,15 +8,19 @@ __all__ = [
     'FeaturedProject',
     ]
 
-from sqlobject import IntCol
+from storm.locals import (
+    Int,
+    Reference,
+    )
 from zope.interface import implementer
 
 from lp.registry.interfaces.featuredproject import IFeaturedProject
-from lp.services.database.sqlbase import SQLBase
+from lp.services.database.interfaces import IStore
+from lp.services.database.stormbase import StormBase
 
 
 @implementer(IFeaturedProject)
-class FeaturedProject(SQLBase):
+class FeaturedProject(StormBase):
     """A featured project reference.
 
     This is a reference to the name of a project, product or distribution
@@ -24,6 +28,16 @@ class FeaturedProject(SQLBase):
     page.
     """
 
-    _defaultOrder = ['id']
+    __storm_table__ = 'FeaturedProject'
+    __storm_order__ = ['id']
 
-    pillar_name = IntCol(notNull=True)
+    id = Int(primary=True)
+    pillar_name_id = Int(name='pillar_name', allow_none=False)
+    pillar_name = Reference(pillar_name_id, 'PillarName.id')
+
+    def __init__(self, pillar_name):
+        super(FeaturedProject, self).__init__()
+        self.pillar_name = pillar_name
+
+    def destroySelf(self):
+        IStore(self).remove(self)
diff --git a/lib/lp/registry/model/pillar.py b/lib/lp/registry/model/pillar.py
index 8bfc356..3482abb 100644
--- a/lib/lp/registry/model/pillar.py
+++ b/lib/lp/registry/model/pillar.py
@@ -1,4 +1,4 @@
-# Copyright 2009-2017 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2020 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Launchpad Pillars share a namespace.
@@ -249,24 +249,23 @@ class PillarNameSet:
 
     def add_featured_project(self, project):
         """See `IPillarSet`."""
-        query = """
-            PillarName.name = %s
-            AND PillarName.id = FeaturedProject.pillar_name
-            """ % sqlvalues(project.name)
-        existing = FeaturedProject.selectOne(
-            query, clauseTables=['PillarName'])
+        existing = IStore(FeaturedProject).find(
+            FeaturedProject,
+            PillarName.name == project.name,
+            PillarName.id == FeaturedProject.pillar_name_id).one()
         if existing is None:
-            pillar_name = PillarName.selectOneBy(name=project.name)
-            return FeaturedProject(pillar_name=pillar_name.id)
+            pillar_name = IStore(PillarName).find(
+                PillarName, name=project.name).one()
+            featured_project = FeaturedProject(pillar_name=pillar_name)
+            IStore(FeaturedProject).add(featured_project)
+            return featured_project
 
     def remove_featured_project(self, project):
         """See `IPillarSet`."""
-        query = """
-            PillarName.name = %s
-            AND PillarName.id = FeaturedProject.pillar_name
-            """ % sqlvalues(project.name)
-        existing = FeaturedProject.selectOne(
-            query, clauseTables=['PillarName'])
+        existing = IStore(FeaturedProject).find(
+            FeaturedProject,
+            PillarName.name == project.name,
+            PillarName.id == FeaturedProject.pillar_name_id).one()
         if existing is not None:
             existing.destroySelf()
 
@@ -280,7 +279,7 @@ class PillarNameSet:
 
         store = IStore(PillarName)
         pillar_names = store.find(
-            PillarName, PillarName.id == FeaturedProject.pillar_name)
+            PillarName, PillarName.id == FeaturedProject.pillar_name_id)
 
         def preload_pillars(rows):
             pillar_names = (
diff --git a/lib/lp/registry/vocabularies.py b/lib/lp/registry/vocabularies.py
index 31d9f3d..008dcbc 100644
--- a/lib/lp/registry/vocabularies.py
+++ b/lib/lp/registry/vocabularies.py
@@ -211,7 +211,6 @@ from lp.services.webapp.vocabulary import (
     CountableIterator,
     FilteredVocabularyBase,
     IHugeVocabulary,
-    NamedSQLObjectHugeVocabulary,
     NamedSQLObjectVocabulary,
     NamedStormHugeVocabulary,
     SQLObjectVocabularyBase,
@@ -1827,7 +1826,7 @@ class VocabularyFilterDistribution(VocabularyFilter):
         return [PillarName.distribution != None]
 
 
-class PillarVocabularyBase(NamedSQLObjectHugeVocabulary):
+class PillarVocabularyBase(NamedStormHugeVocabulary):
     """Active `IPillar` objects vocabulary."""
     displayname = 'Needs to be overridden'
     _table = PillarName
@@ -1872,8 +1871,8 @@ class PillarVocabularyBase(NamedSQLObjectHugeVocabulary):
             ]
         base_clauses = [
             ProductSet.getProductPrivacyFilter(getUtility(ILaunchBag).user)]
-        if self._filter:
-            base_clauses.extend(self._filter)
+        if self._clauses:
+            base_clauses.extend(self._clauses)
         if vocab_filter:
             base_clauses.extend(vocab_filter.filter_terms)
         equal_clauses = base_clauses + [PillarName.name == query]
@@ -1898,7 +1897,7 @@ class PillarVocabularyBase(NamedSQLObjectHugeVocabulary):
 class DistributionOrProductVocabulary(PillarVocabularyBase):
     """Active `IDistribution` or `IProduct` objects vocabulary."""
     displayname = 'Select a project'
-    _filter = [PillarName.projectgroup == None, PillarName.active == True]
+    _clauses = [PillarName.projectgroup == None, PillarName.active == True]
 
     def __contains__(self, obj):
         if IProduct.providedBy(obj):
@@ -1917,7 +1916,7 @@ class DistributionOrProductVocabulary(PillarVocabularyBase):
 class DistributionOrProductOrProjectGroupVocabulary(PillarVocabularyBase):
     """Active `IProduct`, `IProjectGroup` or `IDistribution` vocabulary."""
     displayname = 'Select a project'
-    _filter = [PillarName.active == True]
+    _clauses = [PillarName.active == True]
 
     def __contains__(self, obj):
         if IProduct.providedBy(obj) or IProjectGroup.providedBy(obj):
@@ -1938,16 +1937,17 @@ class FeaturedProjectVocabulary(
                                DistributionOrProductOrProjectGroupVocabulary):
     """Vocabulary of projects that are featured on the LP Home Page."""
 
-    _filter = AND(PillarName.q.id == FeaturedProject.q.pillar_name,
-                  PillarName.q.active == True)
-    _clauseTables = ['FeaturedProject']
+    _clauses = [
+        PillarName.id == FeaturedProject.pillar_name_id,
+        PillarName.active == True,
+        ]
 
     def __contains__(self, obj):
         """See `IVocabulary`."""
-        query = """PillarName.id=FeaturedProject.pillar_name
-                   AND PillarName.name = %s""" % sqlvalues(obj.name)
-        return PillarName.selectOne(
-                   query, clauseTables=['FeaturedProject']) is not None
+        return IStore(PillarName).find(
+            PillarName,
+            PillarName.id == FeaturedProject.pillar_name_id,
+            PillarName.name == obj.name).one()
 
 
 class SourcePackageNameIterator(BatchedCountableIterator):
diff --git a/lib/lp/services/webapp/vocabulary.py b/lib/lp/services/webapp/vocabulary.py
index 8ccb7b2..b77961f 100644
--- a/lib/lp/services/webapp/vocabulary.py
+++ b/lib/lp/services/webapp/vocabulary.py
@@ -1,4 +1,4 @@
-# Copyright 2009-2016 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2020 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Vocabularies pulling stuff from the database.
@@ -15,7 +15,6 @@ __all__ = [
     'FilteredVocabularyBase',
     'ForgivingSimpleVocabulary',
     'IHugeVocabulary',
-    'NamedSQLObjectHugeVocabulary',
     'NamedSQLObjectVocabulary',
     'NamedStormHugeVocabulary',
     'NamedStormVocabulary',
@@ -214,10 +213,9 @@ class BatchedCountableIterator(CountableIterator):
     """A wrapping iterator with hook to create descriptions for its terms."""
     # XXX kiko 2007-01-18: note that this class doesn't use the item_wrapper
     # at all. I hate compatibility shims. We can't remove it from the __init__
-    # because it is always supplied by NamedSQLObjectHugeVocabulary, and
-    # we don't want child classes to have to reimplement it.  This
-    # probably indicates we need to reconsider how these classes are
-    # split.
+    # because it is always supplied by NamedStormVocabulary, and we don't
+    # want child classes to have to reimplement it.  This probably indicates
+    # we need to reconsider how these classes are split.
     def __iter__(self):
         """See CountableIterator"""
         return iter(self.getTermsWithDescriptions(self._iterator))
@@ -437,40 +435,6 @@ class NamedSQLObjectVocabulary(SQLObjectVocabularyBase):
         return self.emptySelectResults()
 
 
-@implementer(IHugeVocabulary)
-class NamedSQLObjectHugeVocabulary(NamedSQLObjectVocabulary):
-    """A NamedSQLObjectVocabulary that implements IHugeVocabulary."""
-    _orderBy = 'name'
-    displayname = None
-    step_title = 'Search'
-    # The iterator class will be used to wrap the results; its iteration
-    # methods should return SimpleTerms, as the reference implementation
-    # CountableIterator does.
-    iterator = CountableIterator
-
-    def __init__(self, context=None):
-        NamedSQLObjectVocabulary.__init__(self, context)
-        if self.displayname is None:
-            self.displayname = 'Select %s' % self.__class__.__name__
-
-    def search(self, query, vocab_filter=None):
-        # XXX kiko 2007-01-17: this is a transitional shim; we're going to
-        # get rid of search() altogether, but for now we've only done it for
-        # the NamedSQLObjectHugeVocabulary.
-        raise NotImplementedError
-
-    def searchForTerms(self, query=None, vocab_filter=None):
-        if not query:
-            return self.emptySelectResults()
-
-        query = ensure_unicode(query).lower()
-        clause = CONTAINSSTRING(self._table.q.name, query)
-        if self._filter:
-            clause = AND(clause, self._filter)
-        results = self._table.select(clause, orderBy=self._orderBy)
-        return self.iterator(results.count(), results, self.toTerm)
-
-
 @implementer(IVocabulary, IVocabularyTokenized)
 class StormVocabularyBase(FilteredVocabularyBase):
     """A base class for widgets that are rendered to collect values