← Back to team overview

launchpad-reviewers team mailing list archive

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

 

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

Commit message:
Convert BugNotification to Storm

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/385562
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:stormify-bugnotification into launchpad:master.
diff --git a/lib/lp/bugs/browser/tests/bug-views.txt b/lib/lp/bugs/browser/tests/bug-views.txt
index 2e89e54..2dccbf9 100644
--- a/lib/lp/bugs/browser/tests/bug-views.txt
+++ b/lib/lp/bugs/browser/tests/bug-views.txt
@@ -648,7 +648,8 @@ Emails are sent out by adding entries to the bugnotification table. We
 need to know how many messages are currently in that table.
 
     >>> from lp.bugs.model.bugnotification import BugNotification
-    >>> bn_set = BugNotification.select(BugNotification.q.bugID == bug_one.id)
+    >>> from lp.services.database.interfaces import IStore
+    >>> bn_set = IStore(BugNotification).find(BugNotification, bug=bug_one)
     >>> start_bugnotification_count = bn_set.count()
 
 Add 'new-tag' multiple times so that we can verify that it will only be added
@@ -666,11 +667,11 @@ once.
 Since the 'new-tag' was added, a new entry in the bugnotification table
 should exist.
 
-    >>> bn_set = BugNotification.select(BugNotification.q.bugID == bug_one.id,
-    ...                                 orderBy = BugNotification.q.id)
+    >>> bn_set = IStore(BugNotification).find(
+    ...     BugNotification, bug=bug_one).order_by(BugNotification.id)
     >>> start_bugnotification_count == bn_set.count() - 1
     True
-    >>> bn_set[-1].message.text_contents
+    >>> bn_set.last().message.text_contents
     u'** Tags added: new-tag'
 
 
diff --git a/lib/lp/bugs/doc/bug-change.txt b/lib/lp/bugs/doc/bug-change.txt
index 1d44ea1..881c389 100644
--- a/lib/lp/bugs/doc/bug-change.txt
+++ b/lib/lp/bugs/doc/bug-change.txt
@@ -107,7 +107,9 @@ BugActivity entries are added when addChange() is called.
 As are BugNotifications.
 
     >>> from lp.bugs.model.bugnotification import BugNotification
-    >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
+    >>> from lp.services.database.interfaces import IStore
+    >>> latest_notification = IStore(BugNotification).find(
+    ...     BugNotification).order_by(BugNotification.id).last()
     >>> print(latest_notification.message.text_contents)
     Some message text
 
@@ -131,7 +133,8 @@ But if getBugActivity() returns None, no activity entries will be added.
 
 And if getBugNotification() returns None, no notification will be added.
 
-    >>> new_latest_notification = BugNotification.selectFirst(orderBy='-id')
+    >>> new_latest_notification = IStore(BugNotification).find(
+    ...     BugNotification).order_by(BugNotification.id).last()
     >>> new_latest_notification.id == latest_notification.id
     True
 
@@ -158,7 +161,8 @@ bug's target for Meta data changes, but not for lifecycle changes.
     ...     filter.bug_notification_level = BugNotificationLevel.METADATA
     >>> example_bug.addChange(
     ...     TestBugChange(when=nowish, person=example_person))
-    >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
+    >>> latest_notification = IStore(BugNotification).find(
+    ...     BugNotification).order_by(BugNotification.id).last()
     >>> print(latest_notification.message.text_contents)
     Some message text
 
diff --git a/lib/lp/bugs/doc/bug-set-status.txt b/lib/lp/bugs/doc/bug-set-status.txt
index cc9ccfe..0626c48 100644
--- a/lib/lp/bugs/doc/bug-set-status.txt
+++ b/lib/lp/bugs/doc/bug-set-status.txt
@@ -41,7 +41,9 @@ It also emits an ObjectModifiedEvent so that BugNotification and
 BugActivity records are created.
 
     >>> from lp.bugs.model.bugnotification import BugNotification
-    >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
+    >>> from lp.services.database.interfaces import IStore
+    >>> latest_notification = IStore(BugNotification).find(
+    ...     BugNotification).order_by(BugNotification.id).last()
     >>> print(latest_notification.message.owner.displayname)
     No Privileges Person
     >>> print(latest_notification.message.text_contents)
diff --git a/lib/lp/bugs/doc/bugattachments.txt b/lib/lp/bugs/doc/bugattachments.txt
index 977c944..ac57c8f 100644
--- a/lib/lp/bugs/doc/bugattachments.txt
+++ b/lib/lp/bugs/doc/bugattachments.txt
@@ -237,7 +237,9 @@ Since the ObjectCreatedEvent was generated, a notification about the
 attachment was added.
 
     >>> from lp.bugs.model.bugnotification import BugNotification
-    >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
+    >>> from lp.services.database.interfaces import IStore
+    >>> latest_notification = IStore(BugNotification).find(
+    ...     BugNotification).order_by(BugNotification.id).last()
     >>> print(latest_notification.message.text_contents)
     ** Attachment added: "RA.txt"
        http://.../RA.txt
@@ -475,9 +477,8 @@ The libraryfile of this bug attachment is marked as "deleted".
 Deleting an attachment causes a notification to be sent. It's worth
 noting that the notification still includes the URL to the attachment.
 
-    >>> from lp.bugs.model.bugnotification import BugNotification
-    >>> latest_notification = BugNotification
-    >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
+    >>> latest_notification = IStore(BugNotification).find(
+    ...     BugNotification).order_by(BugNotification.id).last()
     >>> latest_notification.is_comment
     False
     >>> print(latest_notification.message.text_contents)
diff --git a/lib/lp/bugs/doc/bugnotification-sending.txt b/lib/lp/bugs/doc/bugnotification-sending.txt
index 19cd3a8..92c265f 100644
--- a/lib/lp/bugs/doc/bugnotification-sending.txt
+++ b/lib/lp/bugs/doc/bugnotification-sending.txt
@@ -619,7 +619,9 @@ more carefully at the notifications just to see that the status has
 been set properly.
 
     >>> from lp.bugs.model.bugnotification import BugNotification
-    >>> for notification in BugNotification.select(orderBy='id')[-8:]:
+    >>> from lp.services.database.interfaces import IStore
+    >>> for notification in list(IStore(BugNotification).find(
+    ...         BugNotification).order_by(BugNotification.id))[-8:]:
     ...     if notification.is_comment:
     ...         identifier = 'comment'
     ...     else:
diff --git a/lib/lp/bugs/doc/bugnotification-threading.txt b/lib/lp/bugs/doc/bugnotification-threading.txt
index 1864f0b..e73b4e3 100644
--- a/lib/lp/bugs/doc/bugnotification-threading.txt
+++ b/lib/lp/bugs/doc/bugnotification-threading.txt
@@ -139,8 +139,7 @@ References header.
     ...     owner=sample_person, title="New bug", comment="New bug.",
     ...     target=bug_one.default_bugtask.target)
     >>> bug = getUtility(IBugSet).createBug(params)
-    >>> notifications = IStore(BugNotification).find(
-    ...     BugNotification, BugNotification.bug == bug)
+    >>> notifications = IStore(BugNotification).find(BugNotification, bug=bug)
     >>> messages = [emails for notifications, omitted, emails in
     ...     get_email_notifications(notifications)]
     >>> len(messages)
diff --git a/lib/lp/bugs/doc/bugnotifications.txt b/lib/lp/bugs/doc/bugnotifications.txt
index b16b50a..1e364cc 100644
--- a/lib/lp/bugs/doc/bugnotifications.txt
+++ b/lib/lp/bugs/doc/bugnotifications.txt
@@ -41,7 +41,9 @@ those notifications look like.
     >>> firefox_crashes = firefox.createBug(params)
 
     >>> from lp.bugs.model.bugnotification import BugNotification
-    >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
+    >>> from lp.services.database.interfaces import IStore
+    >>> latest_notification = IStore(BugNotification).find(
+    ...     BugNotification).order_by(BugNotification.id).last()
     >>> print(latest_notification.message.owner.displayname)
     Sample Person
 
@@ -68,7 +70,8 @@ bugactivity.txt, but for now here is a small demo.
     >>> with notify_modified(firefox_crashes, ["description"]):
     ...     firefox_crashes.description = "a new description"
 
-    >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
+    >>> latest_notification = IStore(BugNotification).find(
+    ...     BugNotification).order_by(BugNotification.id).last()
     >>> print(latest_notification.message.owner.displayname)
     Sample Person
 
@@ -107,7 +110,8 @@ bug. Let's take a look at each type.
     >>> notify(ObjectCreatedEvent(
     ...     firefox_crashes_in_debian, firefox_crashes_in_debian.owner))
 
-    >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
+    >>> latest_notification = IStore(BugNotification).find(
+    ...     BugNotification).order_by(BugNotification.id).last()
     >>> print(latest_notification.message.owner.displayname)
     Sample Person
 
@@ -133,7 +137,8 @@ bug. Let's take a look at each type.
     >>> notify(ObjectCreatedEvent(
     ...     firefox_crashes_in_sid, firefox_crashes_in_sid.owner))
 
-    >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
+    >>> latest_notification = IStore(BugNotification).find(
+    ...     BugNotification).order_by(BugNotification.id).last()
     >>> print(latest_notification.message.owner.displayname)
     Sample Person
 
@@ -161,7 +166,8 @@ bug. Let's take a look at each type.
     >>> notify(ObjectCreatedEvent(
     ...     evolution_crashes_too, evolution_crashes_too.owner))
 
-    >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
+    >>> latest_notification = IStore(BugNotification).find(
+    ...     BugNotification).order_by(BugNotification.id).last()
     >>> print(latest_notification.message.owner.displayname)
     Sample Person
 
@@ -183,7 +189,8 @@ bug. Let's take a look at each type.
     >>> notify(ObjectCreatedEvent(
     ...     firefox_crashes_in_trunk, firefox_crashes_in_trunk.owner))
 
-    >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
+    >>> latest_notification = IStore(BugNotification).find(
+    ...     BugNotification).order_by(BugNotification.id).last()
     >>> print(latest_notification.message.owner.displayname)
     Sample Person
 
@@ -205,7 +212,8 @@ bug. Let's take a look at each type.
     ...     bug=firefox_crashes, owner=current_user)
     >>> notify(ObjectCreatedEvent(comment_on_firefox_crashes_in_debian))
 
-    >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
+    >>> latest_notification = IStore(BugNotification).find(
+    ...     BugNotification).order_by(BugNotification.id).last()
     >>> print(latest_notification.message.owner.displayname)
     Sample Person
 
@@ -228,7 +236,8 @@ task Fixed, and assigns themselves to it.
     ...         BugTaskStatus.FIXRELEASED, getUtility(ILaunchBag).user)
     ...     firefox_crashes_in_debian.transitionToAssignee(bug_submitter)
 
-    >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
+    >>> latest_notification = IStore(BugNotification).find(
+    ...     BugNotification).order_by(BugNotification.id).last()
     >>> print(latest_notification.message.owner.displayname)
     Sample Person
 
@@ -244,7 +253,8 @@ task Fixed, and assigns themselves to it.
     ...         BugTaskStatus.FIXRELEASED, getUtility(ILaunchBag).user)
     ...     firefox_crashes_in_trunk.transitionToAssignee(bug_submitter)
 
-    >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
+    >>> latest_notification = IStore(BugNotification).find(
+    ...     BugNotification).order_by(BugNotification.id).last()
     >>> print(latest_notification.message.owner.displayname)
     Sample Person
 
@@ -269,7 +279,8 @@ this document:
     >>> bug = Bug.get(1)
     >>> bugcve = cve.linkBug(bug) # note this creates the event and notifies
 
-    >>> latest_notification = BugNotification.selectFirst(orderBy='-id')
+    >>> latest_notification = IStore(BugNotification).find(
+    ...     BugNotification).order_by(BugNotification.id).last()
     >>> print(latest_notification.message.owner.displayname)
     Sample Person
 
@@ -285,7 +296,8 @@ During bulk imports or changes of bugs, we often want to suppress
 email notifications.  Due to the previous operation, there is a
 pending bug notification for bug 1:
 
-    >>> notifications = BugNotification.selectBy(bugID=1, date_emailed=None)
+    >>> notifications = IStore(BugNotification).find(
+    ...     BugNotification, bug_id=1, date_emailed=None)
     >>> notifications.count()
     1
 
@@ -293,6 +305,7 @@ This notification can be expired using the expireNotifications()
 method:
 
     >>> bug.expireNotifications()
-    >>> notifications = BugNotification.selectBy(bugID=1, date_emailed=None)
+    >>> notifications = IStore(BugNotification).find(
+    ...     BugNotification, bug_id=1, date_emailed=None)
     >>> notifications.count()
     0
diff --git a/lib/lp/bugs/doc/bugsubscription.txt b/lib/lp/bugs/doc/bugsubscription.txt
index fdae4e0..052dee6 100644
--- a/lib/lp/bugs/doc/bugsubscription.txt
+++ b/lib/lp/bugs/doc/bugsubscription.txt
@@ -365,11 +365,12 @@ INotificationRecipientSet instance for us:
 You can query for the addresses and reasons:
 
     >>> addresses = recipients.getEmails()
-    >>> [(address, recipients.getReason(address)[1]) for address in addresses]
-    [('foo.bar@xxxxxxxxxxxxx', 'Subscriber'),
-     ('mark@xxxxxxxxxxx', 'Subscriber'),
-     ('no-priv@xxxxxxxxxxxxx', u'Subscriber (linux-source-2.6.15 in Ubuntu)'),
-     ('test@xxxxxxxxxxxxx', 'Assignee')]
+    >>> for address in addresses:
+    ...     print('%s: %s' % (address, recipients.getReason(address)[1]))
+    foo.bar@xxxxxxxxxxxxx: Subscriber
+    mark@xxxxxxxxxxx: Subscriber
+    no-priv@xxxxxxxxxxxxx: Subscriber (linux-source-2.6.15 in Ubuntu)
+    test@xxxxxxxxxxxxx: Assignee
 
 If IBug.getBugNotificationRecipients() is passed a  BugNotificationLevel
 in its `level` parameter, only structural subscribers with that
@@ -378,10 +379,11 @@ notification level or higher will be returned.
     >>> recipients = linux_source_bug.getBugNotificationRecipients(
     ...     level=BugNotificationLevel.COMMENTS)
     >>> addresses = recipients.getEmails()
-    >>> [(address, recipients.getReason(address)[1]) for address in addresses]
-    [('foo.bar@xxxxxxxxxxxxx', 'Subscriber'),
-     ('mark@xxxxxxxxxxx', 'Subscriber'),
-     ('test@xxxxxxxxxxxxx', 'Assignee')]
+    >>> for address in addresses:
+    ...     print('%s: %s' % (address, recipients.getReason(address)[1]))
+    foo.bar@xxxxxxxxxxxxx: Subscriber
+    mark@xxxxxxxxxxx: Subscriber
+    test@xxxxxxxxxxxxx: Assignee
 
 When Sample Person is unsubscribed from linux_source_bug, they are no
 longer included in the result of getBugNotificationRecipients() for
@@ -391,10 +393,11 @@ the COMMENTS level...
     >>> recipients = linux_source_bug.getBugNotificationRecipients(
     ...     level=BugNotificationLevel.COMMENTS)
     >>> addresses = recipients.getEmails()
-    >>> [(address, recipients.getReason(address)[1]) for address in addresses]
-    [('foo.bar@xxxxxxxxxxxxx', 'Subscriber'),
-     ('mark@xxxxxxxxxxx', 'Subscriber'),
-     ('test@xxxxxxxxxxxxx', 'Assignee')]
+    >>> for address in addresses:
+    ...     print('%s: %s' % (address, recipients.getReason(address)[1]))
+    foo.bar@xxxxxxxxxxxxx: Subscriber
+    mark@xxxxxxxxxxx: Subscriber
+    test@xxxxxxxxxxxxx: Assignee
 
 ...but remains included for the level LIFECYCLE.
 
@@ -402,11 +405,12 @@ the COMMENTS level...
     >>> recipients = linux_source_bug.getBugNotificationRecipients(
     ...     level=BugNotificationLevel.LIFECYCLE)
     >>> addresses = recipients.getEmails()
-    >>> [(address, recipients.getReason(address)[1]) for address in addresses]
-    [('foo.bar@xxxxxxxxxxxxx', 'Subscriber'),
-     ('mark@xxxxxxxxxxx', 'Subscriber'),
-     ('no-priv@xxxxxxxxxxxxx', u'Subscriber (linux-source-2.6.15 in Ubuntu)'),
-     ('test@xxxxxxxxxxxxx', 'Assignee')]
+    >>> for address in addresses:
+    ...     print('%s: %s' % (address, recipients.getReason(address)[1]))
+    foo.bar@xxxxxxxxxxxxx: Subscriber
+    mark@xxxxxxxxxxx: Subscriber
+    no-priv@xxxxxxxxxxxxx: Subscriber (linux-source-2.6.15 in Ubuntu)
+    test@xxxxxxxxxxxxx: Assignee
 
 To find out if someone is already directly subscribed to a bug, call
 IBug.isSubscribed, passing in an IPerson:
diff --git a/lib/lp/bugs/doc/bugwatch.txt b/lib/lp/bugs/doc/bugwatch.txt
index c280375..0431c68 100644
--- a/lib/lp/bugs/doc/bugwatch.txt
+++ b/lib/lp/bugs/doc/bugwatch.txt
@@ -317,14 +317,14 @@ tasks, because it's not a valid person and only valid persons can get karma.
 
 Finally, let's make sure that bug notifications were added:
 
-    >>> from lp.bugs.model.bugnotification import (
-    ...     BugNotification)
-    >>> unsent_notifications = (
-    ...     BugNotification.selectBy(date_emailed=None, orderBy='id'))
+    >>> from lp.bugs.model.bugnotification import BugNotification
+    >>> from lp.services.database.interfaces import IStore
+    >>> unsent_notifications = IStore(BugNotification).find(
+    ...     BugNotification, date_emailed=None).order_by(BugNotification.id)
 
     >>> for bug_notification in unsent_notifications:
     ...     print("Bug %s changed by %s:" % (
-    ...         bug_notification.bug.id,
+    ...         bug_notification.bug_id,
     ...         bug_notification.message.owner.displayname))
     ...     print(bug_notification.message.text_contents)
     Bug 1 changed by Bug Watch Updater:
diff --git a/lib/lp/bugs/doc/externalbugtracker-comment-imports.txt b/lib/lp/bugs/doc/externalbugtracker-comment-imports.txt
index 4f0aa00..7c7a5b0 100644
--- a/lib/lp/bugs/doc/externalbugtracker-comment-imports.txt
+++ b/lib/lp/bugs/doc/externalbugtracker-comment-imports.txt
@@ -431,13 +431,13 @@ watch, there can be a lot of comments. To avoid causing a lot of email
 notifications to be sent, only one notification is sent for all the
 comments.
 
-    >>> from lp.bugs.model.bugnotification import (
-    ...     BugNotification)
+    >>> from lp.bugs.model.bugnotification import BugNotification
+    >>> from lp.services.database.interfaces import IStore
     >>> old_notifications = set()
     >>> def get_new_notifications(bug):
     ...     new_notifications = [
-    ...         notification for notification in (
-    ...             BugNotification.selectBy(bug=bug, orderBy='id'))
+    ...         notification for notification in IStore(BugNotification).find(
+    ...             BugNotification, bug=bug).order_by(BugNotification.id)
     ...         if notification not in old_notifications]
     ...     old_notifications.update(new_notifications)
     ...     return new_notifications
diff --git a/lib/lp/bugs/mail/bugnotificationrecipients.py b/lib/lp/bugs/mail/bugnotificationrecipients.py
index bcec38d..ff1cf8b 100644
--- a/lib/lp/bugs/mail/bugnotificationrecipients.py
+++ b/lib/lp/bugs/mail/bugnotificationrecipients.py
@@ -3,6 +3,8 @@
 
 """Code for handling bug notification recipients in bug mail."""
 
+from __future__ import absolute_import, print_function, unicode_literals
+
 __metaclass__ = type
 __all__ = [
     'BugNotificationRecipients',
diff --git a/lib/lp/bugs/mail/tests/test_bug_duplicate_notifications.py b/lib/lp/bugs/mail/tests/test_bug_duplicate_notifications.py
index 61ad6ca..431ad45 100644
--- a/lib/lp/bugs/mail/tests/test_bug_duplicate_notifications.py
+++ b/lib/lp/bugs/mail/tests/test_bug_duplicate_notifications.py
@@ -9,6 +9,7 @@ from zope.component import getUtility
 from lp.bugs.interfaces.bugtask import BugTaskStatus
 from lp.bugs.model.bugnotification import BugNotification
 from lp.bugs.scripts.bugnotification import construct_email_notifications
+from lp.services.database.interfaces import IStore
 from lp.services.webapp.interfaces import ILaunchBag
 from lp.services.webapp.snapshot import notify_modified
 from lp.testing import TestCaseWithFactory
@@ -46,7 +47,8 @@ class TestAssignmentNotification(TestCaseWithFactory):
             self.master_bug_task.transitionToStatus(
                 BugTaskStatus.CONFIRMED, self.user)
         transaction.commit()
-        latest_notification = BugNotification.selectFirst(orderBy='-id')
+        latest_notification = IStore(BugNotification).find(
+            BugNotification).order_by(BugNotification.id).last()
         notifications, omitted, messages = construct_email_notifications(
             [latest_notification])
         self.assertEqual(
diff --git a/lib/lp/bugs/mail/tests/test_bug_task_assignment.py b/lib/lp/bugs/mail/tests/test_bug_task_assignment.py
index 031bb14..3e34cdd 100644
--- a/lib/lp/bugs/mail/tests/test_bug_task_assignment.py
+++ b/lib/lp/bugs/mail/tests/test_bug_task_assignment.py
@@ -8,6 +8,7 @@ from zope.component import getUtility
 
 from lp.bugs.model.bugnotification import BugNotification
 from lp.bugs.scripts.bugnotification import construct_email_notifications
+from lp.services.database.interfaces import IStore
 from lp.services.mail import stub
 from lp.services.webapp.interfaces import ILaunchBag
 from lp.services.webapp.snapshot import notify_modified
@@ -91,7 +92,8 @@ class TestAssignmentNotification(TestCaseWithFactory):
         task changes and ensure the assignee is not one."""
         with notify_modified(self.bug_task, ['assignee'], user=self.user):
             self.bug_task.transitionToAssignee(self.person_assigned)
-        latest_notification = BugNotification.selectFirst(orderBy='-id')
+        latest_notification = IStore(BugNotification).find(
+            BugNotification).order_by(BugNotification.id).last()
         notifications, omitted, messages = construct_email_notifications(
             [latest_notification])
         self.assertEqual(len(notifications), 1,
@@ -106,7 +108,8 @@ class TestAssignmentNotification(TestCaseWithFactory):
         task changes."""
         with notify_modified(self.bug_task, ['assignee'], user=self.user):
             self.bug_task.transitionToAssignee(self.team_assigned)
-        latest_notification = BugNotification.selectFirst(orderBy='-id')
+        latest_notification = IStore(BugNotification).find(
+            BugNotification).order_by(BugNotification.id).last()
         notifications, omitted, messages = construct_email_notifications(
             [latest_notification])
         self.assertEqual(len(notifications), 1,
diff --git a/lib/lp/bugs/mail/tests/test_bug_task_deletion.py b/lib/lp/bugs/mail/tests/test_bug_task_deletion.py
index 4811568..0cbb63f 100644
--- a/lib/lp/bugs/mail/tests/test_bug_task_deletion.py
+++ b/lib/lp/bugs/mail/tests/test_bug_task_deletion.py
@@ -8,6 +8,7 @@ from zope.component import getUtility
 
 from lp.bugs.model.bugnotification import BugNotification
 from lp.bugs.scripts.bugnotification import construct_email_notifications
+from lp.services.database.interfaces import IStore
 from lp.services.webapp.interfaces import ILaunchBag
 from lp.testing import TestCaseWithFactory
 from lp.testing.layers import DatabaseFunctionalLayer
@@ -30,7 +31,8 @@ class TestDeletionNotification(TestCaseWithFactory):
         """Test X-Launchpad-Bug-Modifier appears when a bugtask is deleted."""
         self.bug_task.delete(self.user)
         transaction.commit()
-        latest_notification = BugNotification.selectFirst(orderBy='-id')
+        latest_notification = IStore(BugNotification).find(
+            BugNotification).order_by(BugNotification.id).last()
         notifications, omitted, messages = construct_email_notifications(
             [latest_notification])
         self.assertEqual(len(notifications), 1,
diff --git a/lib/lp/bugs/mail/tests/test_bug_task_modification.py b/lib/lp/bugs/mail/tests/test_bug_task_modification.py
index 6587370..c181a1b 100644
--- a/lib/lp/bugs/mail/tests/test_bug_task_modification.py
+++ b/lib/lp/bugs/mail/tests/test_bug_task_modification.py
@@ -9,6 +9,7 @@ from zope.component import getUtility
 from lp.bugs.interfaces.bugtask import BugTaskStatus
 from lp.bugs.model.bugnotification import BugNotification
 from lp.bugs.scripts.bugnotification import construct_email_notifications
+from lp.services.database.interfaces import IStore
 from lp.services.webapp.interfaces import ILaunchBag
 from lp.services.webapp.snapshot import notify_modified
 from lp.testing import TestCaseWithFactory
@@ -35,7 +36,8 @@ class TestModificationNotification(TestCaseWithFactory):
             self.bug_task.transitionToStatus(
                 BugTaskStatus.CONFIRMED, self.user)
         transaction.commit()
-        latest_notification = BugNotification.selectFirst(orderBy='-id')
+        latest_notification = IStore(BugNotification).find(
+            BugNotification).order_by(BugNotification.id).last()
         notifications, omitted, messages = construct_email_notifications(
             [latest_notification])
         self.assertEqual(len(notifications), 1,
diff --git a/lib/lp/bugs/mail/tests/test_handler.py b/lib/lp/bugs/mail/tests/test_handler.py
index e89c8fb..c5af232 100644
--- a/lib/lp/bugs/mail/tests/test_handler.py
+++ b/lib/lp/bugs/mail/tests/test_handler.py
@@ -30,6 +30,7 @@ from lp.bugs.mail.handler import (
 from lp.bugs.model.bugnotification import BugNotification
 from lp.registry.enums import BugSharingPolicy
 from lp.services.config import config
+from lp.services.database.interfaces import IStore
 from lp.services.identity.interfaces.emailaddress import EmailAddressStatus
 from lp.services.webapp.authorization import LaunchpadSecurityPolicy
 from lp.testing import (
@@ -220,7 +221,8 @@ class MaloneHandlerProcessTestCase(TestCaseWithFactory):
 
     @staticmethod
     def getLatestBugNotification():
-        return BugNotification.selectFirst(orderBy='-id')
+        return IStore(BugNotification).find(
+            BugNotification).order_by(BugNotification.id).last()
 
     def test_new_bug(self):
         project = self.factory.makeProduct(name='fnord')
@@ -322,7 +324,7 @@ class MaloneHandlerProcessTestCase(TestCaseWithFactory):
         self.assertEqual(1, len(bug.bugtasks))
         self.assertEqual(project, bug.bugtasks[0].target)
         recipients = set()
-        for notification in BugNotification.select():
+        for notification in IStore(BugNotification).find(BugNotification):
             for recipient in notification.recipients:
                 recipients.add(recipient.person)
         self.assertContentEqual([maintainer], recipients)
diff --git a/lib/lp/bugs/model/bug.py b/lib/lp/bugs/model/bug.py
index 7ea2576..98169c6 100644
--- a/lib/lp/bugs/model/bug.py
+++ b/lib/lp/bugs/model/bug.py
@@ -1216,10 +1216,11 @@ class Bug(SQLBase, InformationTypeMixin):
 
     def expireNotifications(self):
         """See `IBug`."""
-        for notification in BugNotification.selectBy(
-                bug=self, date_emailed=None):
-            notification.date_emailed = UTC_NOW
-            notification.syncUpdate()
+        store = IStore(BugNotification)
+        notifications = store.find(
+            BugNotification, bug=self, date_emailed=None)
+        notifications.set(date_emailed=UTC_NOW)
+        store.flush()
 
     def newMessage(self, owner=None, subject=None,
                    content=None, parent=None, bugwatch=None,
diff --git a/lib/lp/bugs/model/bugnotification.py b/lib/lp/bugs/model/bugnotification.py
index 77786c9..dc7e089 100644
--- a/lib/lp/bugs/model/bugnotification.py
+++ b/lib/lp/bugs/model/bugnotification.py
@@ -17,19 +17,17 @@ from datetime import (
     )
 
 import pytz
-from sqlobject import (
-    BoolCol,
-    ForeignKey,
-    StringCol,
-    )
 from storm.expr import (
     In,
     Join,
     LeftJoin,
     )
 from storm.locals import (
+    Bool,
+    DateTime,
     Int,
     Reference,
+    Unicode,
     )
 from storm.store import Store
 from zope.component import getUtility
@@ -51,34 +49,52 @@ from lp.bugs.model.structuralsubscription import StructuralSubscription
 from lp.registry.interfaces.person import IPersonSet
 from lp.services.config import config
 from lp.services.database import bulk
-from lp.services.database.datetimecol import UtcDateTimeCol
-from lp.services.database.enumcol import EnumCol
+from lp.services.database.enumcol import DBEnum
 from lp.services.database.interfaces import IStore
-from lp.services.database.sqlbase import SQLBase
 from lp.services.database.stormbase import StormBase
 from lp.services.messages.model.message import Message
 
 
 @implementer(IBugNotification)
-class BugNotification(SQLBase):
+class BugNotification(StormBase):
     """A textual representation about a bug change."""
 
-    message = ForeignKey(dbName='message', notNull=True, foreignKey='Message')
-    activity = ForeignKey(
-        dbName='activity', notNull=False, foreignKey='BugActivity')
-    bug = ForeignKey(dbName='bug', notNull=True, foreignKey='Bug')
-    is_comment = BoolCol(notNull=True)
-    date_emailed = UtcDateTimeCol(notNull=False)
-    status = EnumCol(
-        dbName='status',
-        schema=BugNotificationStatus, default=BugNotificationStatus.PENDING,
-        notNull=True)
+    __storm_table__ = 'BugNotification'
+
+    id = Int(primary=True)
+
+    message_id = Int(name='message', allow_none=False)
+    message = Reference(message_id, 'Message.id')
+
+    activity_id = Int('activity', allow_none=True)
+    activity = Reference(activity_id, 'BugActivity.id')
+
+    bug_id = Int(name='bug', allow_none=False)
+    bug = Reference(bug_id, 'Bug.id')
+
+    is_comment = Bool(allow_none=False)
+    date_emailed = DateTime(tzinfo=pytz.UTC, allow_none=True)
+    status = DBEnum(
+        name='status',
+        enum=BugNotificationStatus, default=BugNotificationStatus.PENDING,
+        allow_none=False)
+
+    def __init__(self, bug, is_comment, message, date_emailed=None,
+                 activity=None, status=BugNotificationStatus.PENDING):
+        self.bug = bug
+        self.is_comment = is_comment
+        self.message = message
+        self.date_emailed = date_emailed
+        self.activity = activity
+        self.status = status
 
     @property
     def recipients(self):
         """See `IBugNotification`."""
-        return BugNotificationRecipient.selectBy(
-            bug_notification=self, orderBy='id')
+        return IStore(BugNotificationRecipient).find(
+            BugNotificationRecipient,
+            BugNotificationRecipient.bug_notification == self).order_by(
+                BugNotificationRecipient.id)
 
     @property
     def bug_filters(self):
@@ -89,6 +105,9 @@ class BugNotification(SQLBase):
              BugNotificationFilter.bug_subscription_filter_id),
             BugNotificationFilter.bug_notification == self)
 
+    def destroySelf(self):
+        Store.of(self).remove(self)
+
 
 @implementer(IBugNotificationSet)
 class BugNotificationSet:
@@ -129,7 +148,7 @@ class BugNotificationSet:
             elif (last_omitted_notification is not None and
                 notification.message.ownerID ==
                    last_omitted_notification.message.ownerID and
-                notification.bugID == last_omitted_notification.bugID and
+                notification.bug_id == last_omitted_notification.bug_id and
                 last_omitted_notification.message.datecreated -
                 notification.message.datecreated < interval):
                 last_omitted_notification = notification
@@ -137,7 +156,7 @@ class BugNotificationSet:
                 last_omitted_notification = None
                 pending_notifications.append(notification)
                 people_ids.add(notification.message.ownerID)
-                bug_ids.add(notification.bugID)
+                bug_ids.add(notification.bug_id)
         # Now we do some calls that are purely for caching.
         # Converting these into lists forces the queries to execute.
         if pending_notifications:
@@ -308,15 +327,27 @@ class BugNotificationSet:
 
 
 @implementer(IBugNotificationRecipient)
-class BugNotificationRecipient(SQLBase):
+class BugNotificationRecipient(StormBase):
     """A recipient of a bug notification."""
 
-    bug_notification = ForeignKey(
-        dbName='bug_notification', notNull=True, foreignKey='BugNotification')
-    person = ForeignKey(
-        dbName='person', notNull=True, foreignKey='Person')
-    reason_header = StringCol(dbName='reason_header', notNull=True)
-    reason_body = StringCol(dbName='reason_body', notNull=True)
+    __storm_table__ = 'BugNotificationRecipient'
+
+    id = Int(primary=True)
+
+    bug_notification_id = Int(name='bug_notification', allow_none=False)
+    bug_notification = Reference(bug_notification_id, 'BugNotification.id')
+
+    person_id = Int(name='person', allow_none=False)
+    person = Reference(person_id, 'Person.id')
+
+    reason_header = Unicode(name='reason_header', allow_none=False)
+    reason_body = Unicode(name='reason_body', allow_none=False)
+
+    def __init__(self, bug_notification, person, reason_header, reason_body):
+        self.bug_notification = bug_notification
+        self.person = person
+        self.reason_header = reason_header
+        self.reason_body = reason_body
 
 
 @implementer(IBugNotificationFilter)
diff --git a/lib/lp/bugs/scripts/tests/test_bugimport.py b/lib/lp/bugs/scripts/tests/test_bugimport.py
index e706052..2e5d7e5 100644
--- a/lib/lp/bugs/scripts/tests/test_bugimport.py
+++ b/lib/lp/bugs/scripts/tests/test_bugimport.py
@@ -40,6 +40,7 @@ from lp.registry.interfaces.person import (
     )
 from lp.registry.interfaces.product import IProductSet
 from lp.services.config import config
+from lp.services.database.interfaces import IStore
 from lp.services.database.sqlbase import cursor
 from lp.services.identity.interfaces.account import AccountStatus
 from lp.services.identity.interfaces.emailaddress import IEmailAddressSet
@@ -453,7 +454,8 @@ class ImportBugTestCase(TestCase):
         logout()
 
     def assertNoPendingNotifications(self, bug):
-        notifications = BugNotification.selectBy(bug=bug, date_emailed=None)
+        notifications = IStore(BugNotification).find(
+            BugNotification, bug=bug, date_emailed=None)
         count = notifications.count()
         self.assertEqual(count, 0,
                          'Found %d pending notifications for bug %d'
diff --git a/lib/lp/bugs/scripts/tests/test_bugnotification.py b/lib/lp/bugs/scripts/tests/test_bugnotification.py
index 734b68f..99b59c6 100644
--- a/lib/lp/bugs/scripts/tests/test_bugnotification.py
+++ b/lib/lp/bugs/scripts/tests/test_bugnotification.py
@@ -1,6 +1,8 @@
 # Copyright 2009-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
-"""Tests for construction bug notification emails for sending."""
+"""Tests for constructing bug notification emails for sending."""
+
+from __future__ import absolute_import, print_function, unicode_literals
 
 __metaclass__ = type
 
@@ -15,7 +17,6 @@ import StringIO
 import unittest
 
 import pytz
-from storm.store import Store
 from testtools.matchers import (
     MatchesRegex,
     Not,
@@ -75,10 +76,7 @@ from lp.registry.interfaces.person import IPersonSet
 from lp.registry.interfaces.product import IProductSet
 from lp.services.config import config
 from lp.services.database.interfaces import IStore
-from lp.services.database.sqlbase import (
-    flush_database_updates,
-    sqlvalues,
-    )
+from lp.services.database.sqlbase import flush_database_updates
 from lp.services.mail.helpers import (
     get_contact_email_addresses,
     get_email_template,
@@ -1008,14 +1006,9 @@ class TestEmailNotificationsWithFilters(TestCaseWithFactory):
     def addNotificationRecipient(self, notification, person):
         # Manually insert BugNotificationRecipient for
         # construct_email_notifications to work.
-        # Not sure why using SQLObject constructor doesn't work (it
-        # tries to insert a row with only the ID which fails).
-        Store.of(notification).execute("""
-            INSERT INTO BugNotificationRecipient
-              (bug_notification, person, reason_header, reason_body)
-              VALUES (%s, %s, %s, %s)""" % sqlvalues(
-                          notification, person,
-                          u'reason header', u'reason body'))
+        BugNotificationRecipient(
+            bug_notification=notification, person=person,
+            reason_header='reason header', reason_body='reason body')
 
     def addNotification(self, person):
         # Add a notification along with recipient data.
@@ -1176,8 +1169,8 @@ class TestEmailNotificationsWithFilters(TestCaseWithFactory):
 def fetch_notifications(subscriber, bug):
     return IStore(BugNotification).find(
         BugNotification,
-        BugNotification.id == BugNotificationRecipient.bug_notificationID,
-        BugNotificationRecipient.personID == subscriber.id,
+        BugNotification.id == BugNotificationRecipient.bug_notification_id,
+        BugNotificationRecipient.person == subscriber,
         BugNotification.bug == bug)
 
 
diff --git a/lib/lp/bugs/stories/bugs/xx-bugs.txt b/lib/lp/bugs/stories/bugs/xx-bugs.txt
index 05f2622..0dd58c7 100644
--- a/lib/lp/bugs/stories/bugs/xx-bugs.txt
+++ b/lib/lp/bugs/stories/bugs/xx-bugs.txt
@@ -40,5 +40,5 @@ Bar. First, let's clear out the notification table:
 
 After the comment has been submitted, a notification is added:
 
-  >>> BugNotification.select().count()
+  >>> IStore(BugNotification).find(BugNotification).count()
   1
diff --git a/lib/lp/bugs/stories/webservice/xx-bug.txt b/lib/lp/bugs/stories/webservice/xx-bug.txt
index 119fb43..e36d172 100644
--- a/lib/lp/bugs/stories/webservice/xx-bug.txt
+++ b/lib/lp/bugs/stories/webservice/xx-bug.txt
@@ -113,6 +113,7 @@ bugs.
 
     >>> from lp.bugs.interfaces.bug import IBugSet
     >>> from lp.bugs.model.bugnotification import BugNotification
+    >>> from lp.services.database.interfaces import IStore
     >>> from lp.testing import ANONYMOUS, login, logout
     >>> from zope.component import getUtility
 
@@ -125,7 +126,8 @@ bugs.
     ...         activity.person.name))
     bug, added bug, salgado
 
-    >>> for notification in BugNotification.selectBy(bug=bug, orderBy='id'):
+    >>> for notification in IStore(BugNotification).find(
+    ...         BugNotification, bug=bug).order_by(BugNotification.id):
     ...     print('%s, %s, %s' % (
     ...         notification.message.owner.name, notification.is_comment,
     ...         notification.message.text_contents))
diff --git a/lib/lp/bugs/subscribers/tests/test_bug.py b/lib/lp/bugs/subscribers/tests/test_bug.py
index 28130c1..19658d1 100644
--- a/lib/lp/bugs/subscribers/tests/test_bug.py
+++ b/lib/lp/bugs/subscribers/tests/test_bug.py
@@ -61,9 +61,9 @@ class BugSubscriberTestCase(TestCaseWithFactory):
     def getNotifiedPersons(self, include_all=False):
         notified_persons = Store.of(self.bug).find(
             Person,
-            BugNotification.id == BugNotificationRecipient.bug_notificationID,
-            BugNotificationRecipient.personID == Person.id,
-            BugNotification.bugID == self.bug.id)
+            BugNotification.id == BugNotificationRecipient.bug_notification_id,
+            BugNotificationRecipient.person_id == Person.id,
+            BugNotification.bug == self.bug)
         if include_all:
             return list(notified_persons)
         else:
diff --git a/lib/lp/bugs/tests/bugs-emailinterface.txt b/lib/lp/bugs/tests/bugs-emailinterface.txt
index 2bc5460..ccb90bc 100644
--- a/lib/lp/bugs/tests/bugs-emailinterface.txt
+++ b/lib/lp/bugs/tests/bugs-emailinterface.txt
@@ -90,8 +90,10 @@ bug got submitted correctly:
     >>> from lp.services.mail import stub
     >>> bugset = getUtility(IBugSet)
     >>> from lp.bugs.model.bugnotification import BugNotification
+    >>> from lp.services.database.interfaces import IStore
     >>> def get_latest_added_bug():
-    ...     latest_notification = BugNotification.selectFirst(orderBy='-id')
+    ...     latest_notification = IStore(BugNotification).find(
+    ...         BugNotification).order_by(BugNotification.id).last()
     ...     return latest_notification.bug
     >>> bug = get_latest_added_bug()
 
@@ -129,7 +131,8 @@ The owner of the bug was set to the submitter:
 
 A notification was added:
 
-    >>> bug_notification = BugNotification.selectFirst(orderBy='-id')
+    >>> bug_notification = IStore(BugNotification).find(
+    ...     BugNotification).order_by(BugNotification.id).last()
     >>> print(bug_notification.message.owner.displayname)
     Foo Bar
 
@@ -174,7 +177,8 @@ this:
 
 A notification was added:
 
-    >>> bug_notification = BugNotification.selectFirst(orderBy='-id')
+    >>> bug_notification = IStore(BugNotification).find(
+    ...     BugNotification).order_by(BugNotification.id).last()
     >>> print(bug_notification.message.owner.displayname)
     Sample Person
 
@@ -1084,7 +1088,8 @@ created:
 
 A notification was added:
 
-    >>> bug_notification = BugNotification.selectFirst(orderBy='-id')
+    >>> bug_notification = IStore(BugNotification).find(
+    ...     BugNotification).order_by(BugNotification.id).last()
     >>> print(bug_notification.message.text_contents)
     ** Also affects: debian
     ...
@@ -1660,7 +1665,8 @@ edited:
     >>> print(linux_task.status.name)
     CONFIRMED
 
-    >>> bug_notification = BugNotification.selectFirst(orderBy='-id')
+    >>> bug_notification = IStore(BugNotification).find(
+    ...     BugNotification).order_by(BugNotification.id).last()
     >>> print(bug_notification.bug.id)
     10
     >>> print(bug_notification.message.text_contents)
@@ -1686,8 +1692,9 @@ The user is a bug supervisors of the upstream product
     mozilla-firefox (Ubuntu): New, assigned to no one
     mozilla-firefox (Debian): Confirmed, assigned to no one
 
-    >>> pending_notifications = BugNotification.select(
-    ...     orderBy='-id', limit=2)
+    >>> from storm.locals import Desc
+    >>> pending_notifications = IStore(BugNotification).find(
+    ...     BugNotification).order_by(Desc(BugNotification.id))[:2]
     >>> for bug_notification in pending_notifications:
     ...     print(bug_notification.bug.id)
     ...     print(bug_notification.message.text_contents)
@@ -1727,7 +1734,8 @@ The user is a package bug supervisor
     mozilla-firefox (Ubuntu): Confirmed, assigned to Helge Kreutzmann
     mozilla-firefox (Debian): Confirmed, assigned to no one
 
-    >>> pending_notifications = BugNotification.select(orderBy='-id', limit=2)
+    >>> pending_notifications = IStore(BugNotification).find(
+    ...     BugNotification).order_by(Desc(BugNotification.id))[:2]
     >>> for bug_notification in pending_notifications:
     ...     print(bug_notification.bug.id)
     ...     print(bug_notification.message.text_contents)
@@ -1758,7 +1766,8 @@ The user is a distribution member
     mozilla-firefox (Ubuntu): New, assigned to Sample Person
     mozilla-firefox (Debian): Confirmed, assigned to no one
 
-    >>> pending_notifications = BugNotification.select(orderBy='-id', limit=2)
+    >>> pending_notifications = IStore(BugNotification).find(
+    ...     BugNotification).order_by(Desc(BugNotification.id))[:2]
     >>> for bug_notification in pending_notifications:
     ...     print(bug_notification.bug.id)
     ...     print(bug_notification.message.text_contents)
@@ -2452,7 +2461,8 @@ An email may contain more than one attachment; all of them are stored.
 
 A bugnotification is sent for each attached file.
 
-    >>> bug_notifications = BugNotification.select(orderBy='-id', limit=3)
+    >>> bug_notifications = IStore(BugNotification).find(
+    ...     BugNotification).order_by(Desc(BugNotification.id))[:3]
     >>> for bug_notification in bug_notifications:
     ...     print('-------------------')
     ...     print(bug_notification.message.chunks[0].content)
diff --git a/lib/lp/bugs/tests/bugtarget-questiontarget.txt b/lib/lp/bugs/tests/bugtarget-questiontarget.txt
index cd2c7e0..2bf90ca 100644
--- a/lib/lp/bugs/tests/bugtarget-questiontarget.txt
+++ b/lib/lp/bugs/tests/bugtarget-questiontarget.txt
@@ -128,8 +128,11 @@ question and that the bugtasks are Invalid.
     >>> 'test@xxxxxxxxxxxxx' in recipients.getEmails()
     True
 
+    >>> from storm.locals import Desc
     >>> from lp.bugs.model.bugnotification import BugNotification
-    >>> bug_notifications = BugNotification.select(orderBy='-id')
+    >>> from lp.services.database.interfaces import IStore
+    >>> bug_notifications = IStore(BugNotification).find(
+    ...     BugNotification).order_by(Desc(BugNotification.id))
     >>> for notification in bug_notifications:
     ...     print(notification.message.text_contents)
     ** Converted to question:
diff --git a/lib/lp/bugs/tests/test_bugchanges.py b/lib/lp/bugs/tests/test_bugchanges.py
index 4f32051..48c74b0 100644
--- a/lib/lp/bugs/tests/test_bugchanges.py
+++ b/lib/lp/bugs/tests/test_bugchanges.py
@@ -20,6 +20,7 @@ from lp.bugs.interfaces.bugtask import (
 from lp.bugs.interfaces.cve import ICveSet
 from lp.bugs.model.bugnotification import BugNotification
 from lp.bugs.scripts.bugnotification import construct_email_notifications
+from lp.services.database.interfaces import IStore
 from lp.services.librarian.browser import ProxiedLibraryFileAlias
 from lp.services.webapp.publisher import canonical_url
 from lp.services.webapp.snapshot import notify_modified
@@ -80,8 +81,8 @@ class TestBugChanges(TestCaseWithFactory):
             bug = self.bug
         old_activities = set(bug.activity)
         old_notification_ids = set(
-            notification.id for notification in (
-                BugNotification.selectBy(bug=bug)))
+            notification.id for notification in IStore(BugNotification).find(
+                BugNotification, bug=bug))
 
         if append:
             self.old_activities.update(old_activities)
@@ -107,8 +108,8 @@ class TestBugChanges(TestCaseWithFactory):
     def getNewNotifications(self, bug=None):
         if bug is None:
             bug = self.bug
-        bug_notifications = BugNotification.selectBy(
-            bug=bug, orderBy='id')
+        bug_notifications = IStore(BugNotification).find(
+            BugNotification, bug=bug).order_by(BugNotification.id)
         new_notifications = [
             notification for notification in bug_notifications
             if notification.id not in self.old_notification_ids]
@@ -1506,8 +1507,8 @@ class TestBugChanges(TestCaseWithFactory):
 
         # Ensure that only the people subscribed to the bug that
         # gets marked as a duplicate are notified.
-        master_notifications = BugNotification.selectBy(
-            bug=self.bug, orderBy='id')
+        master_notifications = IStore(BugNotification).find(
+            BugNotification, bug=self.bug).order_by(BugNotification.id)
         new_notifications = [
             notification for notification in master_notifications
             if notification.id not in self.old_notification_ids]
diff --git a/lib/lp/bugs/tests/test_bugnotification.py b/lib/lp/bugs/tests/test_bugnotification.py
index b0ecfc3..6672082 100644
--- a/lib/lp/bugs/tests/test_bugnotification.py
+++ b/lib/lp/bugs/tests/test_bugnotification.py
@@ -23,6 +23,7 @@ from lp.bugs.model.bugnotification import (
     )
 from lp.bugs.model.bugsubscriptionfilter import BugSubscriptionFilterMute
 from lp.services.config import config
+from lp.services.database.interfaces import IStore
 from lp.services.messages.interfaces.message import IMessageSet
 from lp.services.messages.model.message import MessageSet
 from lp.services.webapp.snapshot import notify_modified
@@ -372,7 +373,8 @@ class TestNotificationsForDuplicates(TestCaseWithFactory):
         # bug, not to subscribers of the master bug.
         self.dupe_bug.newMessage(
             self.dupe_bug.owner, subject='subject', content='content')
-        latest_notification = BugNotification.selectFirst(orderBy='-id')
+        latest_notification = IStore(BugNotification).find(
+            BugNotification).order_by(BugNotification.id).last()
         recipients = set(
             recipient.person
             for recipient in latest_notification.recipients)
@@ -383,7 +385,8 @@ class TestNotificationsForDuplicates(TestCaseWithFactory):
         with notify_modified(
                 self.dupe_bug, ['description'], user=self.dupe_bug.owner):
             self.dupe_bug.description = 'A changed description'
-        latest_notification = BugNotification.selectFirst(orderBy='-id')
+        latest_notification = IStore(BugNotification).find(
+            BugNotification).order_by(BugNotification.id).last()
         recipients = set(
             recipient.person
             for recipient in latest_notification.recipients)
@@ -397,7 +400,8 @@ class TestNotificationsForDuplicates(TestCaseWithFactory):
         # provided by the Bug.addChange mechanism.
         branch = self.factory.makeBranch(owner=self.dupe_bug.owner)
         self.dupe_bug.linkBranch(branch, self.dupe_bug.owner)
-        latest_notification = BugNotification.selectFirst(orderBy='-id')
+        latest_notification = IStore(BugNotification).find(
+            BugNotification).order_by(BugNotification.id).last()
         recipients = set(
             recipient.person
             for recipient in latest_notification.recipients)
diff --git a/lib/lp/registry/doc/milestone.txt b/lib/lp/registry/doc/milestone.txt
index 2ad094c..5e4b086 100644
--- a/lib/lp/registry/doc/milestone.txt
+++ b/lib/lp/registry/doc/milestone.txt
@@ -317,8 +317,7 @@ Target change notifications
 When we change the milestone for a bug task, subscribers to both the old
 milestone and the new one are notified.
 
-    >>> from lp.bugs.model.bugnotification import (
-    ...     BugNotification)
+    >>> from lp.bugs.model.bugnotification import BugNotification
     >>> from lp.bugs.interfaces.bug import IBugSet
     >>> from lp.registry.interfaces.person import IPersonSet
     >>> from lp.registry.interfaces.product import IProductSet
@@ -353,7 +352,9 @@ change event.
 A new bug notification is created, and both Celso and David are in the
 list of recipients.
 
-    >>> notification = BugNotification.select("date_emailed IS NULL")[-1]
+    >>> from lp.services.database.interfaces import IStore
+    >>> notification = IStore(BugNotification).find(
+    ...     BugNotification, date_emailed=None).last()
     >>> print notification.message.chunks[0].content
     ** Changed in: firefox
         Milestone: 1.0 => 2.0
diff --git a/lib/lp/scripts/tests/test_garbo.py b/lib/lp/scripts/tests/test_garbo.py
index 14ba31f..1de59db 100644
--- a/lib/lp/scripts/tests/test_garbo.py
+++ b/lib/lp/scripts/tests/test_garbo.py
@@ -46,6 +46,7 @@ from zope.security.proxy import removeSecurityProxy
 
 from lp.answers.model.answercontact import AnswerContact
 from lp.app.enums import InformationType
+from lp.bugs.interfaces.bug import IBugSet
 from lp.bugs.model.bugnotification import (
     BugNotification,
     BugNotificationRecipient,
@@ -109,6 +110,7 @@ from lp.services.identity.interfaces.emailaddress import EmailAddressStatus
 from lp.services.job.interfaces.job import JobStatus
 from lp.services.job.model.job import Job
 from lp.services.librarian.model import TimeLimitedToken
+from lp.services.messages.interfaces.message import IMessageSet
 from lp.services.messages.model.message import Message
 from lp.services.openid.model.openidconsumer import OpenIDConsumerNonce
 from lp.services.scripts.tests import run_script
@@ -790,14 +792,18 @@ class TestGarbo(FakeAdapterMixin, TestCaseWithFactory):
     def test_BugNotificationPruner(self):
         # Create some sample data
         switch_dbuser('testadmin')
+        bug = getUtility(IBugSet).get(1)
+        message = getUtility(IMessageSet).get(
+            'foo@xxxxxxxxxxx-332342--1231')[0]
+        person = self.factory.makePerson()
         notification = BugNotification(
-            messageID=1,
-            bugID=1,
+            message=message,
+            bug=bug,
             is_comment=True,
             date_emailed=None)
         BugNotificationRecipient(
             bug_notification=notification,
-            personID=1,
+            person=person,
             reason_header='Whatever',
             reason_body='Whatever')
         # We don't create an entry exactly 30 days old to avoid
@@ -806,12 +812,12 @@ class TestGarbo(FakeAdapterMixin, TestCaseWithFactory):
             message = Message(rfc822msgid=str(delta))
             notification = BugNotification(
                 message=message,
-                bugID=1,
+                bug=bug,
                 is_comment=True,
                 date_emailed=UTC_NOW + SQL("interval '%d days'" % delta))
             BugNotificationRecipient(
                 bug_notification=notification,
-                personID=1,
+                person=person,
                 reason_header='Whatever',
                 reason_body='Whatever')
 
diff --git a/lib/lp/soyuz/doc/closing-bugs-from-changelogs.txt b/lib/lp/soyuz/doc/closing-bugs-from-changelogs.txt
index d4c42cc..75f99ff 100644
--- a/lib/lp/soyuz/doc/closing-bugs-from-changelogs.txt
+++ b/lib/lp/soyuz/doc/closing-bugs-from-changelogs.txt
@@ -146,8 +146,10 @@ comment addition. The both notifications will be batched together into a
 single email later.
 
     >>> from lp.bugs.model.bugnotification import BugNotification
-    >>> notifications = BugNotification.select(orderBy='id')
-    >>> for notification in notifications[-2:]:
+    >>> from lp.services.database.interfaces import IStore
+    >>> notifications = IStore(BugNotification).find(
+    ...     BugNotification).order_by(BugNotification.id)
+    >>> for notification in list(notifications)[-2:]:
     ...     print("From %s:\n%s\n" % (
     ...         notification.message.owner.displayname,
     ...         notification.message.text_contents))
@@ -178,7 +180,8 @@ packages are synced from Debian.
 
     >>> close_bugs_for_queue_item(queue_item)
 
-    >>> notifications = BugNotification.select(orderBy='id')
+    >>> notifications = IStore(BugNotification).find(
+    ...     BugNotification).order_by(BugNotification.id)
     >>> new_notifications = notifications[number_of_old_notifications:]
     >>> [notification.message.text_contents
     ...  for notification in new_notifications]