diff options
Diffstat (limited to 'Mailman/database')
| -rw-r--r-- | Mailman/database/__init__.py | 14 | ||||
| -rw-r--r-- | Mailman/database/listmanager.py | 4 | ||||
| -rw-r--r-- | Mailman/database/messagestore.py | 16 | ||||
| -rw-r--r-- | Mailman/database/model/__init__.py | 23 | ||||
| -rw-r--r-- | Mailman/database/model/address.py | 18 | ||||
| -rw-r--r-- | Mailman/database/model/language.py | 10 | ||||
| -rw-r--r-- | Mailman/database/model/mailinglist.py | 226 | ||||
| -rw-r--r-- | Mailman/database/model/member.py | 10 | ||||
| -rw-r--r-- | Mailman/database/model/message.py | 10 | ||||
| -rw-r--r-- | Mailman/database/model/pending.py | 57 | ||||
| -rw-r--r-- | Mailman/database/model/preferences.py | 16 | ||||
| -rw-r--r-- | Mailman/database/model/requests.py | 24 | ||||
| -rw-r--r-- | Mailman/database/model/roster.py | 38 | ||||
| -rw-r--r-- | Mailman/database/model/user.py | 14 | ||||
| -rw-r--r-- | Mailman/database/model/version.py | 6 | ||||
| -rw-r--r-- | Mailman/database/usermanager.py | 31 |
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 |
