summaryrefslogtreecommitdiff
path: root/Mailman/database
diff options
context:
space:
mode:
Diffstat (limited to 'Mailman/database')
-rw-r--r--Mailman/database/__init__.py14
-rw-r--r--Mailman/database/listmanager.py4
-rw-r--r--Mailman/database/messagestore.py16
-rw-r--r--Mailman/database/model/__init__.py23
-rw-r--r--Mailman/database/model/address.py18
-rw-r--r--Mailman/database/model/language.py10
-rw-r--r--Mailman/database/model/mailinglist.py226
-rw-r--r--Mailman/database/model/member.py10
-rw-r--r--Mailman/database/model/message.py10
-rw-r--r--Mailman/database/model/pending.py57
-rw-r--r--Mailman/database/model/preferences.py16
-rw-r--r--Mailman/database/model/requests.py24
-rw-r--r--Mailman/database/model/roster.py38
-rw-r--r--Mailman/database/model/user.py14
-rw-r--r--Mailman/database/model/version.py6
-rw-r--r--Mailman/database/usermanager.py31
16 files changed, 271 insertions, 246 deletions
diff --git a/Mailman/database/__init__.py b/Mailman/database/__init__.py
index e9c338952..acc74642f 100644
--- a/Mailman/database/__init__.py
+++ b/Mailman/database/__init__.py
@@ -25,10 +25,11 @@ __all__ = [
import os
+from locknix.lockfile import Lock
from elixir import objectstore
from zope.interface import implements
-from Mailman.interfaces import IDatabase, IPending
+from Mailman.interfaces import IDatabase
from Mailman.database.listmanager import ListManager
from Mailman.database.usermanager import UserManager
from Mailman.database.messagestore import MessageStore
@@ -55,14 +56,13 @@ class StockDatabase:
self.pendings = None
self.requests = None
- def initialize(self):
+ def initialize(self, debug=None):
from Mailman.configuration import config
from Mailman.database import model
- from Mailman.lockfile import LockFile
# Serialize this so we don't get multiple processes trying to create
# the database at the same time.
- with LockFile(os.path.join(config.LOCK_DIR, 'dbcreate.lck')):
- model.initialize()
+ with Lock(os.path.join(config.LOCK_DIR, 'dbcreate.lck')):
+ model.initialize(debug)
self.list_manager = ListManager()
self.user_manager = UserManager()
self.message_store = MessageStore()
@@ -72,3 +72,7 @@ class StockDatabase:
def flush(self):
objectstore.flush()
+
+ def _reset(self):
+ model._reset()
+
diff --git a/Mailman/database/listmanager.py b/Mailman/database/listmanager.py
index 0f6d7a9aa..46f0aa859 100644
--- a/Mailman/database/listmanager.py
+++ b/Mailman/database/listmanager.py
@@ -26,7 +26,7 @@ from Mailman import Errors
from Mailman.Utils import split_listname, fqdn_listname
from Mailman.configuration import config
from Mailman.database.model import MailingList, Pendings
-from Mailman.interfaces import IListManager, IPending
+from Mailman.interfaces import IListManager
@@ -63,5 +63,5 @@ class ListManager(object):
@property
def names(self):
- for mlist in MailingList.select():
+ for mlist in MailingList.query.filter_by().all():
yield fqdn_listname(mlist.list_name, mlist.host_name)
diff --git a/Mailman/database/messagestore.py b/Mailman/database/messagestore.py
index bbaa6976b..e0e6cd9f1 100644
--- a/Mailman/database/messagestore.py
+++ b/Mailman/database/messagestore.py
@@ -97,11 +97,11 @@ class MessageStore:
return pickle.load(fp)
def get_messages_by_message_id(self, message_id):
- for msgrow in Message.select_by(message_id=message_id):
+ for msgrow in Message.query.filter_by(message_id=message_id):
yield self._msgobj(msgrow)
def get_messages_by_hash(self, hash):
- for msgrow in Message.select_by(hash=hash):
+ for msgrow in Message.query.filter_by(hash=hash):
yield self._msgobj(msgrow)
def _getmsg(self, global_id):
@@ -110,15 +110,15 @@ class MessageStore:
seqno = int(seqno)
except ValueError:
return None
- msgrows = Message.select_by(id=seqno)
- if not msgrows:
+ messages = Message.query.filter_by(id=seqno)
+ if messages.count() == 0:
return None
- assert len(msgrows) == 1, 'Multiple id matches'
- if msgrows[0].hash <> hash:
+ assert messages.count() == 1, 'Multiple id matches'
+ if messages[0].hash <> hash:
# The client lied about which message they wanted. They gave a
# valid sequence number, but the hash did not match.
return None
- return msgrows[0]
+ return messages[0]
def get_message(self, global_id):
msgrow = self._getmsg(global_id)
@@ -126,7 +126,7 @@ class MessageStore:
@property
def messages(self):
- for msgrow in Message.select():
+ for msgrow in Message.query.filter_by().all():
yield self._msgobj(msgrow)
def delete_message(self, global_id):
diff --git a/Mailman/database/model/__init__.py b/Mailman/database/model/__init__.py
index ed91fe018..86f79a84b 100644
--- a/Mailman/database/model/__init__.py
+++ b/Mailman/database/model/__init__.py
@@ -36,11 +36,15 @@ from urlparse import urlparse
import Mailman.Version
-elixir.delay_setup = True
-
from Mailman import constants
from Mailman.Errors import SchemaVersionMismatchError
from Mailman.configuration import config
+
+# This /must/ be set before any Elixir classes are defined (i.e. imported).
+# This tells Elixir to use the short table names (i.e. the class name) instead
+# of a mangled full class path.
+elixir.options_defaults['shortnames'] = True
+
from Mailman.database.model.address import Address
from Mailman.database.model.language import Language
from Mailman.database.model.mailinglist import MailingList
@@ -54,7 +58,7 @@ from Mailman.database.model.version import Version
-def initialize():
+def initialize(debug):
# Calculate the engine url
url = Template(config.SQLALCHEMY_ENGINE_URL).safe_substitute(config.paths)
# XXX By design of SQLite, database file creation does not honor
@@ -72,16 +76,17 @@ def initialize():
# could have chmod'd the file after the fact, but half dozen and all...
touch(url)
engine = create_engine(url)
- engine.echo = config.SQLALCHEMY_ECHO
- elixir.metadata.connect(engine)
+ engine.echo = (config.SQLALCHEMY_ECHO if debug is None else debug)
+ elixir.metadata.bind = engine
elixir.setup_all()
+ elixir.create_all()
# Validate schema version.
v = Version.get_by(component='schema')
if not v:
# Database has not yet been initialized
v = Version(component='schema',
version=Mailman.Version.DATABASE_SCHEMA_VERSION)
- elixir.objectstore.flush()
+ elixir.session.flush()
elif v.version <> Mailman.Version.DATABASE_SCHEMA_VERSION:
# XXX Update schema
raise SchemaVersionMismatchError(v.version)
@@ -96,3 +101,9 @@ def touch(url):
# Ignore errors
if fd > 0:
os.close(fd)
+
+
+def _reset():
+ for entity in elixir.entities:
+ for row in entity.query.filter_by().all():
+ row.delete()
diff --git a/Mailman/database/model/address.py b/Mailman/database/model/address.py
index 391004413..3ba3c3dbf 100644
--- a/Mailman/database/model/address.py
+++ b/Mailman/database/model/address.py
@@ -32,16 +32,14 @@ USER_KIND = 'Mailman.database.model.user.User'
class Address(Entity):
implements(IAddress)
- has_field('address', Unicode)
- has_field('_original', Unicode)
- has_field('real_name', Unicode)
- has_field('verified_on', DateTime)
- has_field('registered_on', DateTime)
- # Relationships
- belongs_to('user', of_kind=USER_KIND)
- belongs_to('preferences', of_kind=PREFERENCE_KIND)
- # Options
- using_options(shortnames=True)
+ address = Field(Unicode)
+ _original = Field(Unicode)
+ real_name = Field(Unicode)
+ verified_on = Field(DateTime)
+ registered_on = Field(DateTime)
+
+ user = ManyToOne(USER_KIND)
+ preferences = ManyToOne(PREFERENCE_KIND)
def __init__(self, address, real_name):
super(Address, self).__init__()
diff --git a/Mailman/database/model/language.py b/Mailman/database/model/language.py
index e065d5bad..ffdbd2cba 100644
--- a/Mailman/database/model/language.py
+++ b/Mailman/database/model/language.py
@@ -16,9 +16,13 @@
# USA.
from elixir import *
+from zope.interface import implements
+from Mailman.interfaces import ILanguage
+
+
class Language(Entity):
- has_field('code', Unicode)
- # Options
- using_options(shortnames=True)
+ implements(ILanguage)
+
+ code = Field(Unicode)
diff --git a/Mailman/database/model/mailinglist.py b/Mailman/database/model/mailinglist.py
index fff5cbb9c..4057c2161 100644
--- a/Mailman/database/model/mailinglist.py
+++ b/Mailman/database/model/mailinglist.py
@@ -35,131 +35,129 @@ class MailingList(Entity):
implements(IMailingList)
# List identity
- has_field('list_name', Unicode),
- has_field('host_name', Unicode),
+ list_name = Field(Unicode)
+ host_name = Field(Unicode)
# Attributes not directly modifiable via the web u/i
- has_field('created_at', DateTime),
- has_field('web_page_url', Unicode),
- has_field('admin_member_chunksize', Integer),
- has_field('hold_and_cmd_autoresponses', PickleType),
+ created_at = Field(DateTime)
+ web_page_url = Field(Unicode)
+ admin_member_chunksize = Field(Integer)
+ hold_and_cmd_autoresponses = Field(PickleType)
# Attributes which are directly modifiable via the web u/i. The more
# complicated attributes are currently stored as pickles, though that
# will change as the schema and implementation is developed.
- has_field('next_request_id', Integer),
- has_field('next_digest_number', Integer),
- has_field('admin_responses', PickleType),
- has_field('postings_responses', PickleType),
- has_field('request_responses', PickleType),
- has_field('digest_last_sent_at', Float),
- has_field('one_last_digest', PickleType),
- has_field('volume', Integer),
- has_field('last_post_time', DateTime),
+ next_request_id = Field(Integer)
+ next_digest_number = Field(Integer)
+ admin_responses = Field(PickleType)
+ postings_responses = Field(PickleType)
+ request_responses = Field(PickleType)
+ digest_last_sent_at = Field(Float)
+ one_last_digest = Field(PickleType)
+ volume = Field(Integer)
+ last_post_time = Field(DateTime)
# Attributes which are directly modifiable via the web u/i. The more
# complicated attributes are currently stored as pickles, though that
# will change as the schema and implementation is developed.
- has_field('accept_these_nonmembers', PickleType),
- has_field('acceptable_aliases', PickleType),
- has_field('admin_immed_notify', Boolean),
- has_field('admin_notify_mchanges', Boolean),
- has_field('administrivia', Boolean),
- has_field('advertised', Boolean),
- has_field('anonymous_list', Boolean),
- has_field('archive', Boolean),
- has_field('archive_private', Boolean),
- has_field('archive_volume_frequency', Integer),
- has_field('autorespond_admin', Boolean),
- has_field('autorespond_postings', Boolean),
- has_field('autorespond_requests', Integer),
- has_field('autoresponse_admin_text', Unicode),
- has_field('autoresponse_graceperiod', TimeDeltaType),
- has_field('autoresponse_postings_text', Unicode),
- has_field('autoresponse_request_text', Unicode),
- has_field('ban_list', PickleType),
- has_field('bounce_info_stale_after', TimeDeltaType),
- has_field('bounce_matching_headers', Unicode),
- has_field('bounce_notify_owner_on_disable', Boolean),
- has_field('bounce_notify_owner_on_removal', Boolean),
- has_field('bounce_processing', Boolean),
- has_field('bounce_score_threshold', Integer),
- has_field('bounce_unrecognized_goes_to_list_owner', Boolean),
- has_field('bounce_you_are_disabled_warnings', Integer),
- has_field('bounce_you_are_disabled_warnings_interval', TimeDeltaType),
- has_field('collapse_alternatives', Boolean),
- has_field('convert_html_to_plaintext', Boolean),
- has_field('default_member_moderation', Boolean),
- has_field('description', Unicode),
- has_field('digest_footer', Unicode),
- has_field('digest_header', Unicode),
- has_field('digest_is_default', Boolean),
- has_field('digest_send_periodic', Boolean),
- has_field('digest_size_threshold', Integer),
- has_field('digest_volume_frequency', Integer),
- has_field('digestable', Boolean),
- has_field('discard_these_nonmembers', PickleType),
- has_field('emergency', Boolean),
- has_field('encode_ascii_prefixes', Boolean),
- has_field('filter_action', Integer),
- has_field('filter_content', Boolean),
- has_field('filter_filename_extensions', PickleType),
- has_field('filter_mime_types', PickleType),
- has_field('first_strip_reply_to', Boolean),
- has_field('forward_auto_discards', Boolean),
- has_field('gateway_to_mail', Boolean),
- has_field('gateway_to_news', Boolean),
- has_field('generic_nonmember_action', Integer),
- has_field('goodbye_msg', Unicode),
- has_field('header_filter_rules', PickleType),
- has_field('hold_these_nonmembers', PickleType),
- has_field('include_list_post_header', Boolean),
- has_field('include_rfc2369_headers', Boolean),
- has_field('info', Unicode),
- has_field('linked_newsgroup', Unicode),
- has_field('max_days_to_hold', Integer),
- has_field('max_message_size', Integer),
- has_field('max_num_recipients', Integer),
- has_field('member_moderation_action', Boolean),
- has_field('member_moderation_notice', Unicode),
- has_field('mime_is_default_digest', Boolean),
- has_field('moderator_password', Unicode),
- has_field('msg_footer', Unicode),
- has_field('msg_header', Unicode),
- has_field('new_member_options', Integer),
- has_field('news_moderation', EnumType),
- has_field('news_prefix_subject_too', Boolean),
- has_field('nntp_host', Unicode),
- has_field('nondigestable', Boolean),
- has_field('nonmember_rejection_notice', Unicode),
- has_field('obscure_addresses', Boolean),
- has_field('pass_filename_extensions', PickleType),
- has_field('pass_mime_types', PickleType),
- has_field('personalize', EnumType),
- has_field('post_id', Integer),
- has_field('preferred_language', Unicode),
- has_field('private_roster', Boolean),
- has_field('real_name', Unicode),
- has_field('reject_these_nonmembers', PickleType),
- has_field('reply_goes_to_list', EnumType),
- has_field('reply_to_address', Unicode),
- has_field('require_explicit_destination', Boolean),
- has_field('respond_to_post_requests', Boolean),
- has_field('scrub_nondigest', Boolean),
- has_field('send_goodbye_msg', Boolean),
- has_field('send_reminders', Boolean),
- has_field('send_welcome_msg', Boolean),
- has_field('subject_prefix', Unicode),
- has_field('subscribe_auto_approval', PickleType),
- has_field('subscribe_policy', Integer),
- has_field('topics', PickleType),
- has_field('topics_bodylines_limit', Integer),
- has_field('topics_enabled', Boolean),
- has_field('unsubscribe_policy', Integer),
- has_field('welcome_msg', Unicode),
+ accept_these_nonmembers = Field(PickleType)
+ acceptable_aliases = Field(PickleType)
+ admin_immed_notify = Field(Boolean)
+ admin_notify_mchanges = Field(Boolean)
+ administrivia = Field(Boolean)
+ advertised = Field(Boolean)
+ anonymous_list = Field(Boolean)
+ archive = Field(Boolean)
+ archive_private = Field(Boolean)
+ archive_volume_frequency = Field(Integer)
+ autorespond_admin = Field(Boolean)
+ autorespond_postings = Field(Boolean)
+ autorespond_requests = Field(Integer)
+ autoresponse_admin_text = Field(Unicode)
+ autoresponse_graceperiod = Field(TimeDeltaType)
+ autoresponse_postings_text = Field(Unicode)
+ autoresponse_request_text = Field(Unicode)
+ ban_list = Field(PickleType)
+ bounce_info_stale_after = Field(TimeDeltaType)
+ bounce_matching_headers = Field(Unicode)
+ bounce_notify_owner_on_disable = Field(Boolean)
+ bounce_notify_owner_on_removal = Field(Boolean)
+ bounce_processing = Field(Boolean)
+ bounce_score_threshold = Field(Integer)
+ bounce_unrecognized_goes_to_list_owner = Field(Boolean)
+ bounce_you_are_disabled_warnings = Field(Integer)
+ bounce_you_are_disabled_warnings_interval = Field(TimeDeltaType)
+ collapse_alternatives = Field(Boolean)
+ convert_html_to_plaintext = Field(Boolean)
+ default_member_moderation = Field(Boolean)
+ description = Field(Unicode)
+ digest_footer = Field(Unicode)
+ digest_header = Field(Unicode)
+ digest_is_default = Field(Boolean)
+ digest_send_periodic = Field(Boolean)
+ digest_size_threshold = Field(Integer)
+ digest_volume_frequency = Field(Integer)
+ digestable = Field(Boolean)
+ discard_these_nonmembers = Field(PickleType)
+ emergency = Field(Boolean)
+ encode_ascii_prefixes = Field(Boolean)
+ filter_action = Field(Integer)
+ filter_content = Field(Boolean)
+ filter_filename_extensions = Field(PickleType)
+ filter_mime_types = Field(PickleType)
+ first_strip_reply_to = Field(Boolean)
+ forward_auto_discards = Field(Boolean)
+ gateway_to_mail = Field(Boolean)
+ gateway_to_news = Field(Boolean)
+ generic_nonmember_action = Field(Integer)
+ goodbye_msg = Field(Unicode)
+ header_filter_rules = Field(PickleType)
+ hold_these_nonmembers = Field(PickleType)
+ include_list_post_header = Field(Boolean)
+ include_rfc2369_headers = Field(Boolean)
+ info = Field(Unicode)
+ linked_newsgroup = Field(Unicode)
+ max_days_to_hold = Field(Integer)
+ max_message_size = Field(Integer)
+ max_num_recipients = Field(Integer)
+ member_moderation_action = Field(Boolean)
+ member_moderation_notice = Field(Unicode)
+ mime_is_default_digest = Field(Boolean)
+ moderator_password = Field(Unicode)
+ msg_footer = Field(Unicode)
+ msg_header = Field(Unicode)
+ new_member_options = Field(Integer)
+ news_moderation = Field(EnumType)
+ news_prefix_subject_too = Field(Boolean)
+ nntp_host = Field(Unicode)
+ nondigestable = Field(Boolean)
+ nonmember_rejection_notice = Field(Unicode)
+ obscure_addresses = Field(Boolean)
+ pass_filename_extensions = Field(PickleType)
+ pass_mime_types = Field(PickleType)
+ personalize = Field(EnumType)
+ post_id = Field(Integer)
+ preferred_language = Field(Unicode)
+ private_roster = Field(Boolean)
+ real_name = Field(Unicode)
+ reject_these_nonmembers = Field(PickleType)
+ reply_goes_to_list = Field(EnumType)
+ reply_to_address = Field(Unicode)
+ require_explicit_destination = Field(Boolean)
+ respond_to_post_requests = Field(Boolean)
+ scrub_nondigest = Field(Boolean)
+ send_goodbye_msg = Field(Boolean)
+ send_reminders = Field(Boolean)
+ send_welcome_msg = Field(Boolean)
+ subject_prefix = Field(Unicode)
+ subscribe_auto_approval = Field(PickleType)
+ subscribe_policy = Field(Integer)
+ topics = Field(PickleType)
+ topics_bodylines_limit = Field(Integer)
+ topics_enabled = Field(Boolean)
+ unsubscribe_policy = Field(Integer)
+ welcome_msg = Field(Unicode)
# Relationships
## has_and_belongs_to_many(
## 'available_languages',
## of_kind='Mailman.database.model.languages.Language')
- # Options
- using_options(shortnames=True)
def __init__(self, fqdn_listname):
super(MailingList, self).__init__()
diff --git a/Mailman/database/model/member.py b/Mailman/database/model/member.py
index 1dc942323..4f353a06c 100644
--- a/Mailman/database/model/member.py
+++ b/Mailman/database/model/member.py
@@ -32,13 +32,11 @@ PREFERENCE_KIND = 'Mailman.database.model.preferences.Preferences'
class Member(Entity):
implements(IMember)
- has_field('role', EnumType)
- has_field('mailing_list', Unicode)
+ role = Field(EnumType)
+ mailing_list = Field(Unicode)
# Relationships
- belongs_to('address', of_kind=ADDRESS_KIND)
- belongs_to('preferences', of_kind=PREFERENCE_KIND)
- # Options
- using_options(shortnames=True)
+ address = ManyToOne(ADDRESS_KIND)
+ preferences = ManyToOne(PREFERENCE_KIND)
def __repr__(self):
return '<Member: %s on %s as %s>' % (
diff --git a/Mailman/database/model/message.py b/Mailman/database/model/message.py
index df8371c6a..eb4b4616d 100644
--- a/Mailman/database/model/message.py
+++ b/Mailman/database/model/message.py
@@ -18,13 +18,15 @@
from elixir import *
from zope.interface import implements
+from Mailman.interfaces import IMessage
+
class Message(Entity):
"""A message in the message store."""
- has_field('hash', Unicode)
- has_field('path', Unicode)
- has_field('message_id', Unicode)
+ implements(IMessage)
- using_options(shortnames=True)
+ hash = Field(Unicode)
+ path = Field(Unicode)
+ message_id = Field(Unicode)
diff --git a/Mailman/database/model/pending.py b/Mailman/database/model/pending.py
index ae2ad3d60..75bb59d3c 100644
--- a/Mailman/database/model/pending.py
+++ b/Mailman/database/model/pending.py
@@ -27,30 +27,30 @@ from zope.interface import implements
from zope.interface.verify import verifyObject
from Mailman.configuration import config
-from Mailman.interfaces import IPending, IPendable
+from Mailman.interfaces import (
+ IPendings, IPendable, IPendedKeyValue, IPended)
-PEND_KIND = 'Mailman.database.model.pending.Pending'
+PEND_KIND = 'Mailman.database.model.pending.Pended'
class PendedKeyValue(Entity):
"""A pended key/value pair, tied to a token."""
- has_field('key', Unicode)
- has_field('value', Unicode)
- # Relationships
- belongs_to('pended', of_kind=PEND_KIND)
- # Options
- using_options(shortnames=True)
+ implements(IPendedKeyValue)
+ key = Field(Unicode)
+ value = Field(Unicode)
+ pended = ManyToOne(PEND_KIND)
-class Pending(Entity):
+
+class Pended(Entity):
"""A pended event, tied to a token."""
- has_field('token', Unicode)
- has_field('expiration_date', DateTime)
- # Options
- using_options(shortnames=True)
+ implements(IPended)
+
+ token = Field(Unicode)
+ expiration_date = Field(DateTime)
@@ -62,7 +62,7 @@ class UnpendedPendable(dict):
class Pendings(object):
"""Implementation of the IPending interface."""
- implements(IPending)
+ implements(IPendings)
def add(self, pendable, lifetime=None):
verifyObject(IPendable, pendable)
@@ -75,17 +75,19 @@ class Pendings(object):
# clock values basically help obscure the random number generator, as
# does the hash calculation. The integral parts of the time values
# are discarded because they're the most predictable bits.
- while True:
+ for attempts in range(3):
now = time.time()
x = random.random() + now % 1.0 + time.clock() % 1.0
# Use sha1 because it produces shorter strings.
token = hashlib.sha1(repr(x)).hexdigest()
# In practice, we'll never get a duplicate, but we'll be anal
# about checking anyway.
- if not Pending.select_by(token=token):
+ if Pended.query.filter_by(token=token).count() == 0:
break
+ else:
+ raise AssertionError('Could not find a valid pendings token')
# Create the record, and then the individual key/value pairs.
- pending = Pending(
+ pending = Pended(
token=token,
expiration_date=datetime.datetime.now() + lifetime)
for key, value in pendable.items():
@@ -93,17 +95,18 @@ class Pendings(object):
return token
def confirm(self, token, expunge=True):
- pendings = Pending.select_by(token=token)
- assert 0 <= len(pendings) <= 1, 'Unexpected token search results'
- if len(pendings) == 0:
+ pendings = Pended.query.filter_by(token=token)
+ if pendings.count() == 0:
return None
+ assert pendings.count() == 1, (
+ 'Unexpected token count: %d' % pendings.count())
pending = pendings[0]
pendable = UnpendedPendable()
# Find all PendedKeyValue entries that are associated with the pending
# object's ID.
- q = PendedKeyValue.filter(
- PendedKeyValue.c.pended_id == Pending.c.id).filter(
- Pending.c.id == pending.id)
+ q = PendedKeyValue.query.filter(
+ PendedKeyValue.c.pended_id == Pended.c.id).filter(
+ Pended.c.id == pending.id)
for keyvalue in q.all():
pendable[keyvalue.key] = keyvalue.value
if expunge:
@@ -114,13 +117,13 @@ class Pendings(object):
def evict(self):
now = datetime.datetime.now()
- for pending in Pending.select():
+ for pending in Pended.query.filter_by().all():
if pending.expiration_date < now:
# Find all PendedKeyValue entries that are associated with the
# pending object's ID.
- q = PendedKeyValue.filter(
- PendedKeyValue.c.pended_id == Pending.c.id).filter(
- Pending.c.id == pending.id)
+ q = PendedKeyValue.query.filter(
+ PendedKeyValue.c.pended_id == Pended.c.id).filter(
+ Pended.c.id == pending.id)
for keyvalue in q:
keyvalue.delete()
pending.delete()
diff --git a/Mailman/database/model/preferences.py b/Mailman/database/model/preferences.py
index 07d4d84e2..8cbb77e6a 100644
--- a/Mailman/database/model/preferences.py
+++ b/Mailman/database/model/preferences.py
@@ -31,15 +31,13 @@ USER_KIND = 'Mailman.database.model.user.User'
class Preferences(Entity):
implements(IPreferences)
- has_field('acknowledge_posts', Boolean)
- has_field('hide_address', Boolean)
- has_field('preferred_language', Unicode)
- has_field('receive_list_copy', Boolean)
- has_field('receive_own_postings', Boolean)
- has_field('delivery_mode', EnumType)
- has_field('delivery_status', EnumType)
- # Options
- using_options(shortnames=True)
+ acknowledge_posts = Field(Boolean)
+ hide_address = Field(Boolean)
+ preferred_language = Field(Unicode)
+ receive_list_copy = Field(Boolean)
+ receive_own_postings = Field(Boolean)
+ delivery_mode = Field(EnumType)
+ delivery_status = Field(EnumType)
def __repr__(self):
return '<Preferences object at %#x>' % id(self)
diff --git a/Mailman/database/model/requests.py b/Mailman/database/model/requests.py
index ea917c2b9..037483c1a 100644
--- a/Mailman/database/model/requests.py
+++ b/Mailman/database/model/requests.py
@@ -49,23 +49,21 @@ class ListRequests:
@property
def count(self):
- results = _Request.select_by(mailing_list=self.mailing_list)
- return len(results)
+ return _Request.query.filter_by(mailing_list=self.mailing_list).count()
def count_of(self, request_type):
- results = _Request.select_by(mailing_list=self.mailing_list,
- type=request_type)
- return len(results)
+ return _Request.query.filter_by(mailing_list=self.mailing_list,
+ type=request_type).count()
@property
def held_requests(self):
- results = _Request.select_by(mailing_list=self.mailing_list)
+ results = _Request.query.filter_by(mailing_list=self.mailing_list)
for request in results:
yield request
def of_type(self, request_type):
- results = _Request.select_by(mailing_list=self.mailing_list,
- type=request_type)
+ results = _Request.query.filter_by(mailing_list=self.mailing_list,
+ type=request_type)
for request in results:
yield request
@@ -132,10 +130,8 @@ class Requests:
class _Request(Entity):
"""Table for mailing list hold requests."""
- has_field('key', Unicode)
- has_field('type', EnumType)
- has_field('data_hash', Unicode)
+ key = Field(Unicode)
+ type = Field(EnumType)
+ data_hash = Field(Unicode)
# Relationships
- belongs_to('mailing_list', of_kind=MAILINGLIST_KIND)
- # Options
- using_options(shortnames=True)
+ mailing_list = ManyToOne(MAILINGLIST_KIND)
diff --git a/Mailman/database/model/roster.py b/Mailman/database/model/roster.py
index e59bc8b17..c8fa86d58 100644
--- a/Mailman/database/model/roster.py
+++ b/Mailman/database/model/roster.py
@@ -49,8 +49,9 @@ class AbstractRoster(object):
@property
def members(self):
- for member in Member.select_by(mailing_list=self._mlist.fqdn_listname,
- role=self.role):
+ for member in Member.query.filter_by(
+ mailing_list=self._mlist.fqdn_listname,
+ role=self.role):
yield member
@property
@@ -72,18 +73,18 @@ class AbstractRoster(object):
yield member.address
def get_member(self, address):
- results = Member.select(
+ results = Member.query.filter(
and_(Member.c.mailing_list == self._mlist.fqdn_listname,
Member.c.role == self.role,
Address.c.address == address,
Member.c.address_id == Address.c.id))
- if len(results) == 0:
+ if results.count() == 0:
return None
- elif len(results) == 1:
+ elif results.count() == 1:
return results[0]
else:
- assert len(results) <= 1, (
- 'Too many matching member results: %s' % results)
+ raise AssertionError('Too many matching member results: %s' %
+ results.count())
@@ -120,7 +121,7 @@ class AdministratorRoster(AbstractRoster):
def members(self):
# Administrators are defined as the union of the owners and the
# moderators.
- members = Member.select(
+ members = Member.query.filter(
and_(Member.c.mailing_list == self._mlist.fqdn_listname,
or_(Member.c.role == MemberRole.owner,
Member.c.role == MemberRole.moderator)))
@@ -128,18 +129,18 @@ class AdministratorRoster(AbstractRoster):
yield member
def get_member(self, address):
- results = Member.select(
+ results = Member.query.filter(
and_(Member.c.mailing_list == self._mlist.fqdn_listname,
or_(Member.c.role == MemberRole.moderator,
Member.c.role == MemberRole.owner),
Address.c.address == address,
Member.c.address_id == Address.c.id))
- if len(results) == 0:
+ if results.count() == 0:
return None
- elif len(results) == 1:
+ elif results.count() == 1:
return results[0]
else:
- assert len(results) <= 1, (
+ raise AssertionError(
'Too many matching member results: %s' % results)
@@ -154,8 +155,9 @@ class RegularMemberRoster(AbstractRoster):
# Query for all the Members which have a role of MemberRole.member and
# are subscribed to this mailing list. Then return only those members
# that have a regular delivery mode.
- for member in Member.select_by(mailing_list=self._mlist.fqdn_listname,
- role=MemberRole.member):
+ for member in Member.query.filter_by(
+ mailing_list=self._mlist.fqdn_listname,
+ role=MemberRole.member):
if member.delivery_mode == DeliveryMode.regular:
yield member
@@ -179,8 +181,9 @@ class DigestMemberRoster(AbstractRoster):
# Query for all the Members which have a role of MemberRole.member and
# are subscribed to this mailing list. Then return only those members
# that have one of the digest delivery modes.
- for member in Member.select_by(mailing_list=self._mlist.fqdn_listname,
- role=MemberRole.member):
+ for member in Member.query.filter_by(
+ mailing_list=self._mlist.fqdn_listname,
+ role=MemberRole.member):
if member.delivery_mode in _digest_modes:
yield member
@@ -193,5 +196,6 @@ class Subscribers(AbstractRoster):
@property
def members(self):
- for member in Member.select_by(mailing_list=self._mlist.fqdn_listname):
+ for member in Member.query.filter_by(
+ mailing_list=self._mlist.fqdn_listname):
yield member
diff --git a/Mailman/database/model/user.py b/Mailman/database/model/user.py
index 683ec0f90..895beef9f 100644
--- a/Mailman/database/model/user.py
+++ b/Mailman/database/model/user.py
@@ -32,13 +32,11 @@ PREFERENCE_KIND = 'Mailman.database.model.preferences.Preferences'
class User(Entity):
implements(IUser)
- has_field('real_name', Unicode)
- has_field('password', Unicode)
- # Relationships
- has_many('addresses', of_kind=ADDRESS_KIND)
- belongs_to('preferences', of_kind=PREFERENCE_KIND)
- # Options
- using_options(shortnames=True)
+ real_name = Field(Unicode)
+ password = Field(Unicode)
+
+ addresses = OneToMany(ADDRESS_KIND)
+ preferences = ManyToOne(PREFERENCE_KIND)
def __repr__(self):
return '<User "%s" at %#x>' % (self.real_name, id(self))
@@ -47,13 +45,11 @@ class User(Entity):
if address.user is not None:
raise Errors.AddressAlreadyLinkedError(address)
address.user = self
- self.addresses.append(address)
def unlink(self, address):
if address.user is None:
raise Errors.AddressNotLinkedError(address)
address.user = None
- self.addresses.remove(address)
def controls(self, address):
found = Address.get_by(address=address)
diff --git a/Mailman/database/model/version.py b/Mailman/database/model/version.py
index 7b12778ce..dbbf5b8c1 100644
--- a/Mailman/database/model/version.py
+++ b/Mailman/database/model/version.py
@@ -19,7 +19,5 @@ from elixir import *
class Version(Entity):
- has_field('component', Unicode)
- has_field('version', Integer)
- # Options
- using_options(shortnames=True)
+ component = Field(Unicode)
+ version = Field(Integer)
diff --git a/Mailman/database/usermanager.py b/Mailman/database/usermanager.py
index 1958080fd..6bc2ed53d 100644
--- a/Mailman/database/usermanager.py
+++ b/Mailman/database/usermanager.py
@@ -36,7 +36,7 @@ class UserManager(object):
def create_user(self, address=None, real_name=None):
user = User()
- user.real_name = (real_name if real_name is not None else '')
+ user.real_name = ('' if real_name is None else real_name)
if address:
addrobj = Address(address, user.real_name)
addrobj.preferences = Preferences()
@@ -49,19 +49,28 @@ class UserManager(object):
@property
def users(self):
- for user in User.select():
+ for user in User.query.filter_by().all():
yield user
def get_user(self, address):
- found = Address.get_by(address=address.lower())
- return found and found.user
+ addresses = Address.query.filter_by(address=address.lower())
+ if addresses.count() == 0:
+ return None
+ elif addresses.count() == 1:
+ return addresses[0].user
+ else:
+ raise AssertionError('Unexpected query count')
def create_address(self, address, real_name=None):
- found = Address.get_by(address=address.lower())
- if found:
+ addresses = Address.query.filter_by(address=address.lower())
+ if addresses.count() == 1:
+ found = addresses[0]
raise Errors.ExistingAddressError(found.original_address)
+ assert addresses.count() == 0, 'Unexpected results'
if real_name is None:
real_name = ''
+ # It's okay not to lower case the 'address' argument because the
+ # constructor will do the right thing.
address = Address(address, real_name)
address.preferences = Preferences()
return address
@@ -74,9 +83,15 @@ class UserManager(object):
address.delete()
def get_address(self, address):
- return Address.get_by(address=address.lower())
+ addresses = Address.query.filter_by(address=address.lower())
+ if addresses.count() == 0:
+ return None
+ elif addresses.count() == 1:
+ return addresses[0]
+ else:
+ raise AssertionError('Unexpected query count')
@property
def addresses(self):
- for address in Address.select():
+ for address in Address.query.filter_by().all():
yield address