Colin Watson has proposed merging ~cjwatson/launchpad:stormify-bug into launchpad:master.
Commit message: Convert Bug to Storm Requested reviews: Launchpad code reviewers (launchpad-reviewers) For more details, see: https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/447358 -- Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:stormify-bug into launchpad:master.
diff --git a/lib/lp/answers/doc/karma.rst b/lib/lp/answers/doc/karma.rst index 5bc61ef..488a2a3 100644 --- a/lib/lp/answers/doc/karma.rst +++ b/lib/lp/answers/doc/karma.rst @@ -216,7 +216,7 @@ Linking to a bug ................ >>> from lp.bugs.model.bug import Bug - >>> questionbug = firefox_question.linkBug(Bug.get(5)) + >>> questionbug = firefox_question.linkBug(IStore(Bug).get(Bug, 5)) Karma added: action=questionlinkedtobug, product=firefox, person=name12 diff --git a/lib/lp/bugs/browser/bug.py b/lib/lp/bugs/browser/bug.py index 3a34c23..190ba78 100644 --- a/lib/lp/bugs/browser/bug.py +++ b/lib/lp/bugs/browser/bug.py @@ -1202,11 +1202,8 @@ class BugTextView(LaunchpadView): else: text.append("duplicate-of: ") - if bug.duplicates: - dupes = " ".join(str(dupe.id) for dupe in bug.duplicates) - text.append("duplicates: %s" % dupes) - else: - text.append("duplicates: ") + dupes = " ".join(str(dupe.id) for dupe in bug.duplicates) + text.append("duplicates: %s" % dupes) if bug.private: # XXX kiko 2007-10-31: this could include date_made_private and diff --git a/lib/lp/bugs/browser/buglisting.py b/lib/lp/bugs/browser/buglisting.py index cef7d6e..3b579c7 100644 --- a/lib/lp/bugs/browser/buglisting.py +++ b/lib/lp/bugs/browser/buglisting.py @@ -682,7 +682,7 @@ class BugTaskListingItem: assignee = None if self.assignee_id is not None: assignee = self.people[self.assignee_id].displayname - reporter = self.people[self.bug.ownerID] + reporter = self.people[self.bug.owner_id] # the case that there is no target context (e.g. viewing bug that # are related to a user account) is intercepted diff --git a/lib/lp/bugs/browser/bugtask.py b/lib/lp/bugs/browser/bugtask.py index ad3a7a2..adedd6b 100644 --- a/lib/lp/bugs/browser/bugtask.py +++ b/lib/lp/bugs/browser/bugtask.py @@ -435,7 +435,7 @@ class BugTaskView(LaunchpadView, BugViewMixin, FeedsMixin): self.context = context list( getUtility(IPersonSet).getPrecachedPersonsFromIDs( - [self.context.bug.ownerID], need_validity=True + [self.context.bug.owner_id], need_validity=True ) ) diff --git a/lib/lp/bugs/doc/bug.rst b/lib/lp/bugs/doc/bug.rst index e1c54e5..7893e52 100644 --- a/lib/lp/bugs/doc/bug.rst +++ b/lib/lp/bugs/doc/bug.rst @@ -263,7 +263,7 @@ private: >>> from lp.bugs.model.bug import Bug >>> from lp.services.database.interfaces import IStore - >>> all_bugs = set(IStore(Bug).find(Bug).values(Bug.id)) + >>> all_bugs = set(IStore(Bug).find(Bug.id)) >>> taskset = getUtility(IBugTaskSet) >>> def hidden_bugs(): diff --git a/lib/lp/bugs/doc/bugnotificationrecipients.rst b/lib/lp/bugs/doc/bugnotificationrecipients.rst index c633ea9..c3688c9 100644 --- a/lib/lp/bugs/doc/bugnotificationrecipients.rst +++ b/lib/lp/bugs/doc/bugnotificationrecipients.rst @@ -14,7 +14,8 @@ action: >>> from lp.bugs.model.bug import Bug >>> from lp.registry.model.distribution import Distribution >>> from lp.registry.model.product import Product - >>> bug_one = Bug.get(1) + >>> from lp.services.database.interfaces import IStore + >>> bug_one = IStore(Bug).get(Bug, 1) >>> recipients = bug_one.getBugNotificationRecipients() The instance of BugNotificationRecipients we get back correctly @@ -124,7 +125,7 @@ additional step is involved. A BugNotificationRecipients instance is created, annotating that it represents a master bug (of which we are a duplicate of). - >>> bug_two = Bug.get(2) + >>> bug_two = IStore(Bug).get(Bug, 2) >>> recipients = BugNotificationRecipients(duplicateof=bug_two) >>> foo_bar = personset.getByEmail("foo....@canonical.com") diff --git a/lib/lp/bugs/doc/bugnotifications.rst b/lib/lp/bugs/doc/bugnotifications.rst index c7aaeb5..4b4d755 100644 --- a/lib/lp/bugs/doc/bugnotifications.rst +++ b/lib/lp/bugs/doc/bugnotifications.rst @@ -354,7 +354,7 @@ this document: ... status=CveStatus.ENTRY, ... ) >>> from lp.bugs.model.bug import Bug - >>> bug = Bug.get(1) + >>> bug = IStore(Bug).get(Bug, 1) >>> bugcve = cve.linkBug(bug) # note this creates the event and notifies >>> latest_notification = ( diff --git a/lib/lp/bugs/doc/cve.rst b/lib/lp/bugs/doc/cve.rst index e06fe5f..86b2a3f 100644 --- a/lib/lp/bugs/doc/cve.rst +++ b/lib/lp/bugs/doc/cve.rst @@ -75,7 +75,8 @@ You can link a CVE to a bug. You can also see which CVEs are currently linked to a bug: >>> from lp.bugs.model.bug import Bug - >>> b = Bug.get(1) + >>> from lp.services.database.interfaces import IStore + >>> b = IStore(Bug).get(Bug, 1) >>> for c in b.cves: ... print(c.displayname) ... diff --git a/lib/lp/bugs/interfaces/bug.py b/lib/lp/bugs/interfaces/bug.py index e02f2ff..acf5c6e 100644 --- a/lib/lp/bugs/interfaces/bug.py +++ b/lib/lp/bugs/interfaces/bug.py @@ -240,7 +240,7 @@ class IBugView(Interface): max_length=50000, ) ) - ownerID = Int(title=_("Owner"), required=True, readonly=True) + owner_id = Int(title=_("Owner"), required=True, readonly=True) owner = exported( Reference(IPerson, title=_("The owner's IPerson"), readonly=True) ) diff --git a/lib/lp/bugs/model/bug.py b/lib/lp/bugs/model/bug.py index dc2d66b..2db7afe 100644 --- a/lib/lp/bugs/model/bug.py +++ b/lib/lp/bugs/model/bug.py @@ -33,6 +33,7 @@ from lazr.lifecycle.snapshot import Snapshot from lazr.restful.declarations import error_status from storm.expr import ( SQL, + Add, And, Coalesce, Desc, @@ -170,19 +171,10 @@ from lp.registry.model.pillar import pillar_sort_key from lp.registry.model.teammembership import TeamParticipation from lp.services.config import config from lp.services.database import bulk -from lp.services.database.constants import UTC_NOW -from lp.services.database.datetimecol import UtcDateTimeCol +from lp.services.database.constants import DEFAULT, UTC_NOW from lp.services.database.decoratedresultset import DecoratedResultSet from lp.services.database.enumcol import DBEnum from lp.services.database.interfaces import IStore -from lp.services.database.sqlbase import SQLBase, sqlvalues -from lp.services.database.sqlobject import ( - ForeignKey, - IntCol, - SQLMultipleJoin, - SQLObjectNotFound, - StringCol, -) from lp.services.database.stormbase import StormBase from lp.services.database.stormexpr import WithMaterialized from lp.services.fields import DuplicateBug @@ -261,7 +253,6 @@ class BugTag(StormBase): __storm_table__ = "BugTag" id = Int(primary=True) - bug = ForeignKey(dbName="bug", foreignKey="Bug", notNull=True) bug_id = Int(name="bug", allow_none=False) bug = Reference(bug_id, "Bug.id") @@ -356,33 +347,36 @@ def update_bug_heat(bug_ids): @implementer(IBug, IInformationType) -class Bug(SQLBase, InformationTypeMixin): +class Bug(StormBase, InformationTypeMixin): """A bug.""" - _defaultOrder = "-id" + __storm_table__ = "Bug" + __storm_order__ = "-id" # db field names - name = StringCol(unique=True, default=None) - title = StringCol(notNull=True) - description = StringCol(notNull=False, default=None) - owner = ForeignKey( - dbName="owner", - foreignKey="Person", - storm_validator=validate_public_person, - notNull=True, + id = Int(primary=True) + name = Unicode(default=None) + title = Unicode(allow_none=False) + description = Unicode(allow_none=True, default=None) + owner_id = Int( + name="owner", validator=validate_public_person, allow_none=False + ) + owner = Reference(owner_id, "Person.id") + duplicateof_id = Int(name="duplicateof", default=None) + duplicateof = Reference(duplicateof_id, "Bug.id") + datecreated = DateTime( + allow_none=False, default=UTC_NOW, tzinfo=timezone.utc + ) + date_last_updated = DateTime( + allow_none=False, default=UTC_NOW, tzinfo=timezone.utc ) - duplicateof = ForeignKey( - dbName="duplicateof", foreignKey="Bug", default=None + date_made_private = DateTime( + allow_none=True, default=None, tzinfo=timezone.utc ) - datecreated = UtcDateTimeCol(notNull=True, default=UTC_NOW) - date_last_updated = UtcDateTimeCol(notNull=True, default=UTC_NOW) - date_made_private = UtcDateTimeCol(notNull=False, default=None) - who_made_private = ForeignKey( - dbName="who_made_private", - foreignKey="Person", - storm_validator=validate_public_person, - default=None, + who_made_private_id = Int( + name="who_made_private", validator=validate_public_person, default=None ) + who_made_private = Reference(who_made_private_id, "Person.id") information_type = DBEnum( enum=InformationType, allow_none=False, default=InformationType.PUBLIC ) @@ -397,25 +391,44 @@ class Bug(SQLBase, InformationTypeMixin): BugWatch.bug_id, order_by=(BugWatch.bugtracker_id, BugWatch.remotebug), ) - duplicates = SQLMultipleJoin("Bug", joinColumn="duplicateof", orderBy="id") + duplicates = ReferenceSet("id", "Bug.duplicateof_id", order_by="Bug.id") linked_bugbranches = ReferenceSet( "id", BugBranch.bug_id, order_by=BugBranch.id ) - date_last_message = UtcDateTimeCol(default=None) - number_of_duplicates = IntCol(notNull=True, default=0) - message_count = IntCol(notNull=True, default=0) - users_affected_count = IntCol(notNull=True, default=0) - users_unaffected_count = IntCol(notNull=True, default=0) - heat = IntCol(notNull=True, default=0) - heat_last_updated = UtcDateTimeCol(default=None) - latest_patch_uploaded = UtcDateTimeCol(default=None) + date_last_message = DateTime(default=None, tzinfo=timezone.utc) + number_of_duplicates = Int(allow_none=False, default=0) + message_count = Int(allow_none=False, default=0) + users_affected_count = Int(allow_none=False, default=0) + users_unaffected_count = Int(allow_none=False, default=0) + heat = Int(allow_none=False, default=0) + heat_last_updated = DateTime(default=None, tzinfo=timezone.utc) + latest_patch_uploaded = DateTime(default=None, tzinfo=timezone.utc) lock_status = DBEnum( name="lock_status", enum=BugLockStatus, allow_none=False, default=BugLockStatus.UNLOCKED, ) - lock_reason = StringCol(notNull=False, default=None) + lock_reason = Unicode(allow_none=True, default=None) + + def __init__( + self, + title, + owner, + description=None, + datecreated=DEFAULT, + date_made_private=None, + who_made_private=None, + information_type=InformationType.PUBLIC, + ): + super().__init__() + self.title = title + self.owner = owner + self.description = description + self.datecreated = datecreated + self.date_made_private = date_made_private + self.who_made_private = who_made_private + self.information_type = information_type @property def locked(self): @@ -1905,7 +1918,7 @@ class Bug(SQLBase, InformationTypeMixin): def _question_from_bug(self): for question in self.questions: if ( - question.owner_id == self.ownerID + question.owner_id == self.owner_id and question.datecreated == self.datecreated ): return question @@ -1934,10 +1947,12 @@ class Bug(SQLBase, InformationTypeMixin): # range. slices.append( BugMessage.index - >= SQL( - "(select max(index) from " - "bugmessage where bug=%s) + 1 - %s" - % (sqlvalues(self.id, -slice.start)) + >= Add( + Select( + Max(BugMessage.index), + where=(BugMessage.bug == self), + ), + slice.start + 1, ) ) else: @@ -2397,7 +2412,7 @@ class Bug(SQLBase, InformationTypeMixin): try: if duplicate_of is not None: field._validate(duplicate_of) - if self.duplicates: + if not self.duplicates.is_empty(): user = getUtility(ILaunchBag).user for duplicate in self.duplicates: old_value = duplicate.duplicateof @@ -3204,17 +3219,17 @@ class BugSet: def get(self, bugid): """See `IBugSet`.""" - try: - return Bug.get(bugid) - except SQLObjectNotFound: + bug = IStore(Bug).get(Bug, int(bugid)) + if bug is None: raise NotFoundError( "Unable to locate bug with ID %s." % str(bugid) ) + return bug def getByNameOrID(self, bugid): """See `IBugSet`.""" if self.valid_bug_name_re.match(bugid): - bug = Bug.selectOneBy(name=bugid) + bug = IStore(Bug).find(Bug, name=bugid).one() if bug is None: raise NotFoundError("Unable to locate bug with ID %s." % bugid) else: @@ -3334,6 +3349,8 @@ class BugSet: if params.tags: bug.tags = params.tags + Store.of(bug).flush() + # Link the bug to the message. BugMessage(bug=bug, message=params.msg, index=0) @@ -3497,7 +3514,7 @@ def generate_subscription_with(bug, person): BugSubscription, Join(Bug, Bug.id == BugSubscription.bug_id), ], - where=Or(Bug.id == bug.id, Bug.duplicateofID == bug.id), + where=Or(Bug.id == bug.id, Bug.duplicateof_id == bug.id), ), ), WithMaterialized( diff --git a/lib/lp/bugs/model/bugtask.py b/lib/lp/bugs/model/bugtask.py index 2a0dce7..2606bed 100644 --- a/lib/lp/bugs/model/bugtask.py +++ b/lib/lp/bugs/model/bugtask.py @@ -1019,7 +1019,7 @@ class BugTask(StormBase): return True elif ( self.status == BugTaskStatus.FIXRELEASED - and user.id != self.bug.ownerID + and user.id != self.bug.owner_id and not user.inTeam(self.bug.owner) ): # The bug reporter can reopen a Fix Released bug. @@ -1606,7 +1606,7 @@ class BugTaskSet: people_ids = set( [bugtask.assignee_id for bugtask in bugtasks] - + [bugtask.bug.ownerID for bugtask in bugtasks] + + [bugtask.bug.owner_id for bugtask in bugtasks] ) people = getUtility(IPersonSet).getPrecachedPersonsFromIDs(people_ids) return {person.id: person for person in people} diff --git a/lib/lp/bugs/model/bugtasksearch.py b/lib/lp/bugs/model/bugtasksearch.py index 9ec4c25..7f7dd20 100644 --- a/lib/lp/bugs/model/bugtasksearch.py +++ b/lib/lp/bugs/model/bugtasksearch.py @@ -1105,7 +1105,7 @@ def _build_exclude_conjoined_clause(milestone): """ # XXX: EdwinGrubbs 2010-12-15 bug=682989 # (ConjoinedPrimary.bug == X) produces the wrong sql, but - # (ConjoinedPrimary.bugID == X) works right. This bug applies to + # (ConjoinedPrimary.bug_id == X) works right. This bug applies to # all foreign keys on the ClassAlias. # Perform a LEFT JOIN to the conjoined primary bugtask. If the diff --git a/lib/lp/bugs/model/personsubscriptioninfo.py b/lib/lp/bugs/model/personsubscriptioninfo.py index 8e28966..aaff8d4 100644 --- a/lib/lp/bugs/model/personsubscriptioninfo.py +++ b/lib/lp/bugs/model/personsubscriptioninfo.py @@ -184,7 +184,7 @@ class PersonSubscriptions: # Preload bug owners, then all pillars. list( getUtility(IPersonSet).getPrecachedPersonsFromIDs( - [bug.ownerID for bug in bugs] + [bug.owner_id for bug in bugs] ) ) all_tasks = load_referencing(BugTask, bugs, ["bug_id"]) diff --git a/lib/lp/bugs/stories/bugs/xx-bug-text-pages.rst b/lib/lp/bugs/stories/bugs/xx-bug-text-pages.rst index bfa0b6e..116baaa 100644 --- a/lib/lp/bugs/stories/bugs/xx-bug-text-pages.rst +++ b/lib/lp/bugs/stories/bugs/xx-bug-text-pages.rst @@ -9,14 +9,15 @@ To demonstrate this feature, we'll use bug 1. We'll start by adding some attachments to the bug: >>> from io import BytesIO - >>> from lp.services.database.sqlbase import flush_database_updates - >>> from lp.testing import login, logout >>> from lp.bugs.model.bug import Bug >>> from lp.registry.model.person import Person + >>> from lp.services.database.interfaces import IStore + >>> from lp.services.database.sqlbase import flush_database_updates + >>> from lp.testing import login, logout >>> login("foo....@canonical.com") >>> mark = Person.selectOneBy(name="mark") >>> mark.display_name = "M\xe1rk Sh\xfattlew\xf2rth" - >>> bug = Bug.get(1) + >>> bug = IStore(Bug).get(Bug, 1) >>> content = BytesIO(b"<html><body>bogus</body></html>") >>> a1 = bug.addAttachment( ... mark, diff --git a/lib/lp/bugs/templates/bug-portlet-duplicates.pt b/lib/lp/bugs/templates/bug-portlet-duplicates.pt index 0a11c96..a2f0afa 100644 --- a/lib/lp/bugs/templates/bug-portlet-duplicates.pt +++ b/lib/lp/bugs/templates/bug-portlet-duplicates.pt @@ -3,7 +3,7 @@ xmlns:metal="http://xml.zope.org/namespaces/metal" xmlns:i18n="http://xml.zope.org/namespaces/i18n" class="portlet" id="portlet-duplicates" - tal:condition="context/duplicates"> + tal:condition="not: context/duplicates/is_empty"> <h2>Duplicates of this bug</h2> <ul> <li tal:repeat="dupe view/duplicates"> diff --git a/lib/lp/bugs/tests/bugs-emailinterface.rst b/lib/lp/bugs/tests/bugs-emailinterface.rst index 1a8a900..6555137 100644 --- a/lib/lp/bugs/tests/bugs-emailinterface.rst +++ b/lib/lp/bugs/tests/bugs-emailinterface.rst @@ -2085,7 +2085,7 @@ An empty unsigned mail to new@malone: If we submit an email with no affects command, it is rejected. >>> from lp.bugs.model.bug import Bug - >>> before_count = Bug.select().count() + >>> before_count = IStore(Bug).find(Bug).count() >>> submit_mail = b"""From: Foo Bar <foo....@canonical.com> ... To: n...@bugs.launchpad.ubuntu.com ... Date: Fri Jun 17 10:20:23 BST 2005 @@ -2099,7 +2099,7 @@ If we submit an email with no affects command, it is rejected. ... """ >>> process_email(submit_mail) - >>> before_count == Bug.select().count() + >>> before_count == IStore(Bug).find(Bug).count() True >>> print_latest_email() @@ -2118,7 +2118,7 @@ required. If it is missing, the message is also rejected. XXX: Gavin Panella 2009-07-24 bug=404010: The need for this test arises from the implementation of MaloneHandler. - >>> before_count = Bug.select().count() + >>> before_count = IStore(Bug).find(Bug).count() >>> submit_mail = b"""From: Foo Bar <foo....@canonical.com> ... To: n...@bugs.launchpad.ubuntu.com ... Date: Fri Jun 17 10:20:23 BST 2005 @@ -2130,7 +2130,7 @@ arises from the implementation of MaloneHandler. ... """ >>> process_email(submit_mail) - >>> before_count == Bug.select().count() + >>> before_count == IStore(Bug).find(Bug).count() True >>> print_latest_email() @@ -2151,7 +2151,7 @@ edit@bugs). XXX: Gavin Panella 2009-07-24 bug=404010: The need for this test arises from the implementation of MaloneHandler. - >>> before_count = Bug.select().count() + >>> before_count = IStore(Bug).find(Bug).count() >>> submit_mail = b"""\ ... From: Foo Bar <foo....@canonical.com> ... To: n...@bugs.launchpad.ubuntu.com @@ -2162,7 +2162,7 @@ arises from the implementation of MaloneHandler. ... """ >>> process_email(submit_mail) - >>> before_count == Bug.select().count() + >>> before_count == IStore(Bug).find(Bug).count() True >>> print_latest_email() @@ -2184,7 +2184,7 @@ bug-related commands do blow up before the check for a bugtask is reached. For example, unsubscribing oneself from a private bug then linking a CVE. - >>> before_count = Bug.select().count() + >>> before_count = IStore(Bug).find(Bug).count() >>> submit_mail = b"""\ ... From: Foo Bar <foo....@canonical.com> ... To: n...@bugs.launchpad.ubuntu.com @@ -2197,7 +2197,7 @@ linking a CVE. ... """ >>> process_email(submit_mail) - >>> before_count == Bug.select().count() + >>> before_count == IStore(Bug).find(Bug).count() True >>> print_latest_email() diff --git a/lib/lp/bugs/vocabularies.py b/lib/lp/bugs/vocabularies.py index 9b337e0..5586ef3 100644 --- a/lib/lp/bugs/vocabularies.py +++ b/lib/lp/bugs/vocabularies.py @@ -56,7 +56,6 @@ from lp.services.webapp.vocabulary import ( CountableIterator, IHugeVocabulary, NamedSQLObjectVocabulary, - SQLObjectVocabularyBase, StormVocabularyBase, ) @@ -85,9 +84,9 @@ class UsesBugsDistributionVocabulary(DistributionVocabulary): ) -class BugVocabulary(SQLObjectVocabularyBase): +class BugVocabulary(StormVocabularyBase): _table = Bug - _orderBy = "id" + _order_by = "id" @implementer(IHugeVocabulary) diff --git a/lib/lp/registry/tests/test_person.py b/lib/lp/registry/tests/test_person.py index e0f005c..3646ff7 100644 --- a/lib/lp/registry/tests/test_person.py +++ b/lib/lp/registry/tests/test_person.py @@ -1016,7 +1016,7 @@ class TestPersonStates(TestCaseWithFactory): ) def test_Bug_person_validator(self): - bug = Bug.select(limit=1)[0] + bug = IStore(Bug).find(Bug).first() for attr_name in ["owner", "who_made_private"]: self.assertRaises( PrivatePersonLinkageError, setattr, bug, attr_name, self.myteam diff --git a/lib/lp/scripts/harness.py b/lib/lp/scripts/harness.py index 7be9884..a5a03ec 100644 --- a/lib/lp/scripts/harness.py +++ b/lib/lp/scripts/harness.py @@ -77,8 +77,8 @@ def _get_locals(): ds = DistroSeries.get(1) prod = Product.get(1) proj = ProjectGroup.get(1) - b2 = Bug.get(2) - b1 = Bug.get(1) + b2 = store.get(Bug, 2) + b1 = store.get(Bug, 1) s = store.get(Specification, 1) q = store.get(Question, 1) # Silence unused name warnings diff --git a/lib/lp/services/database/tests/test_bulk.py b/lib/lp/services/database/tests/test_bulk.py index 6cdf569..b9f64c8 100644 --- a/lib/lp/services/database/tests/test_bulk.py +++ b/lib/lp/services/database/tests/test_bulk.py @@ -252,7 +252,7 @@ class TestLoaders(TestCaseWithFactory): expected = {bug.owner for bug in owning_objects} self.assertEqual( expected, - set(bulk.load_related(Person, owning_objects, ["ownerID"])), + set(bulk.load_related(Person, owning_objects, ["owner_id"])), ) def test_load_referencing(self): diff --git a/lib/lp/services/statistics/model/statistics.py b/lib/lp/services/statistics/model/statistics.py index 4b4ff60..f0a182e 100644 --- a/lib/lp/services/statistics/model/statistics.py +++ b/lib/lp/services/statistics/model/statistics.py @@ -106,10 +106,10 @@ class LaunchpadStatisticSet: getUtility(IPersonSet).updateStatistics() def _updateMaloneStatistics(self, ztm): - self.update("bug_count", Bug.select().count()) + store = IStore(Bug) + self.update("bug_count", store.find(Bug).count()) ztm.commit() - store = IStore(BugTask) self.update("bugtask_count", store.find(BugTask).count()) ztm.commit()
_______________________________________________ Mailing list: https://launchpad.net/~launchpad-reviewers Post to : launchpad-reviewers@lists.launchpad.net Unsubscribe : https://launchpad.net/~launchpad-reviewers More help : https://help.launchpad.net/ListHelp