← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~cjwatson/launchpad/testfix-git-target-inline-default-repo into lp:launchpad

 

Colin Watson has proposed merging lp:~cjwatson/launchpad/testfix-git-target-inline-default-repo into lp:launchpad.

Commit message:
Preload default Git repositories for projects so that ProductSet:+review-licenses can have a constant query count again.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/testfix-git-target-inline-default-repo/+merge/258528

Preload default Git repositories for projects so that ProductSet:+review-licenses can have a constant query count again.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/testfix-git-target-inline-default-repo into lp:launchpad.
=== modified file 'lib/lp/code/interfaces/gitrepository.py'
--- lib/lp/code/interfaces/gitrepository.py	2015-05-07 15:03:27 +0000
+++ lib/lp/code/interfaces/gitrepository.py	2015-05-07 16:52:35 +0000
@@ -724,6 +724,13 @@
         This only exists to keep lazr.restful happy.
         """
 
+    def preloadDefaultRepositoriesForProjects(projects):
+        """Get preloaded default repositories for a list of projects.
+
+        :return: A dict mapping project IDs to their default repositories.
+            Projects that do not have default repositories are omitted.
+        """
+
 
 class IGitRepositoryDelta(Interface):
     """The quantitative changes made to a Git repository that was edited or

=== modified file 'lib/lp/code/model/gitrepository.py'
--- lib/lp/code/model/gitrepository.py	2015-05-07 15:03:27 +0000
+++ lib/lp/code/model/gitrepository.py	2015-05-07 16:52:35 +0000
@@ -122,7 +122,10 @@
     )
 from lp.services.features import getFeatureFlag
 from lp.services.mail.notificationrecipientset import NotificationRecipientSet
-from lp.services.propertycache import cachedproperty
+from lp.services.propertycache import (
+    cachedproperty,
+    get_property_cache,
+    )
 from lp.services.webapp.authorization import available_with_permission
 
 
@@ -283,6 +286,9 @@
             if existing is not None:
                 raise GitDefaultConflict(existing, self.target)
         self.target_default = value
+        if IProduct.providedBy(self.target):
+            get_property_cache(self.target)._default_git_repository = (
+                self if value else None)
 
     @property
     def display_name(self):
@@ -894,6 +900,14 @@
         """See `IGitRepositorySet`."""
         return []
 
+    @staticmethod
+    def preloadDefaultRepositoriesForProjects(projects):
+        repositories = bulk.load_referencing(
+            GitRepository, projects, ["project_id"],
+            extra_conditions=[GitRepository.target_default == True])
+        return {
+            repository.project_id: repository for repository in repositories}
+
 
 def get_git_repository_privacy_filter(user, repository_class=GitRepository):
     public_filter = repository_class.information_type.is_in(

=== modified file 'lib/lp/registry/model/product.py'
--- lib/lp/registry/model/product.py	2015-05-07 11:38:18 +0000
+++ lib/lp/registry/model/product.py	2015-05-07 16:52:35 +0000
@@ -580,9 +580,13 @@
         """See `IPillar`."""
         return "Project"
 
+    @cachedproperty
+    def _default_git_repository(self):
+        return getUtility(IGitRepositorySet).getDefaultRepository(self)
+
     @property
     def official_codehosting(self):
-        repository = getUtility(IGitRepositorySet).getDefaultRepository(self)
+        repository = self._default_git_repository
         return (
             self.development_focus.branch is not None or
             repository is not None)
@@ -617,7 +621,7 @@
 
     @property
     def codehosting_usage(self):
-        repository = getUtility(IGitRepositorySet).getDefaultRepository(self)
+        repository = self._default_git_repository
         if self.development_focus.branch is None and repository is None:
             return ServiceUsage.UNKNOWN
         elif (repository is not None or
@@ -1582,7 +1586,8 @@
 def get_precached_products(products, need_licences=False,
                            need_projectgroups=False, need_series=False,
                            need_releases=False, role_names=None,
-                           need_role_validity=False):
+                           need_role_validity=False,
+                           need_codehosting_usage=False):
     """Load and cache product information.
 
     :param products: the products for which to pre-cache information
@@ -1592,10 +1597,13 @@
     :param need_releases: whether to cache release information
     :param role_names: the role names to cache eg bug_supervisor
     :param need_role_validity: whether to cache validity information
+    :param need_codehosting_usage: whether to cache codehosting usage
+        information
     :return: a list of products
     """
 
-    # Circular import.
+    # Circular imports.
+    from lp.code.interfaces.gitrepository import IGitRepositorySet
     from lp.registry.model.projectgroup import ProjectGroup
 
     product_ids = set(obj.id for obj in products)
@@ -1684,6 +1692,13 @@
         person_ids.discard(None)
         list(getUtility(IPersonSet).getPrecachedPersonsFromIDs(
             person_ids, need_validity=need_role_validity))
+    if need_codehosting_usage:
+        repository_set = getUtility(IGitRepositorySet)
+        repository_map = repository_set.preloadDefaultRepositoriesForProjects(
+            products)
+        for product_id in product_ids:
+            caches[product_id]._default_git_repository = repository_map.get(
+                product_id)
     return products
 
 
@@ -2001,7 +2016,8 @@
             return get_precached_products(
                 products, role_names=['_owner', 'registrant'],
                 need_role_validity=True, need_licences=True,
-                need_series=True, need_releases=True)
+                need_series=True, need_releases=True,
+                need_codehosting_usage=True)
 
         return DecoratedResultSet(result, pre_iter_hook=eager_load)
 


Follow ups