summaryrefslogtreecommitdiff
path: root/Mailman/database/model
diff options
context:
space:
mode:
authorBarry Warsaw2007-10-31 17:38:51 -0400
committerBarry Warsaw2007-10-31 17:38:51 -0400
commitf321d85d91a370294e771dbaa22493008d78dfdd (patch)
tree8cf4c3e7cab70ccc9059f147ff1bf4b3bf150115 /Mailman/database/model
parent1ad73a52bb9d82ef3af1e34ad9ef66ac2eda2909 (diff)
downloadmailman-f321d85d91a370294e771dbaa22493008d78dfdd.tar.gz
mailman-f321d85d91a370294e771dbaa22493008d78dfdd.tar.zst
mailman-f321d85d91a370294e771dbaa22493008d78dfdd.zip
Much progress, though not perfect, on migrating to SQLAlchemy 0.4 and Elixir
0.4. Lots of things changes, which broke lots of our code. There are still a couple of failures in the test suite that I don't understand. It seems that for pending.txt and requests.txt, sometimes strings come back from the database as 8-bit strings and other times as unicodes. It's impossible to make these tests work both separately and together. users.txt is also failing intermittently. Lots of different behavior between running the full test suite all together and running individual tests. Sigh. Note also that actually, Elixir 0.4.0 doesn't work for us. There's a bug in that version that prevented zope.interfaces and Elixir working together. Get the latest 0.4.0 from source to fix this. Other changes include: - Remove Mailman/lockfile.py. While I haven't totally eliminated locking, I have released the lockfile as a separate Python package called locknix, which Mailman 3.0 now depends on. - Renamed Mailman/interfaces/messagestore.py and added an IMessage interface. - bin/testall raises turns on SQLALCHEMY_ECHO when the verbosity is above 3 (that's three -v's because the default verbosity is 1). - add_domain() in config files now allows url_host to be optional. If not given, it defaults to email_host. - Added a non-public interface IDatabase._reset() used by the test suite to zap the database between doctests. Added an implementation in the model which just runs through all rows in all entities, deleting them. - [I]Pending renamed to [I]Pended - Don't allow Pendings.add() to infloop. - In the model's User impelementations, we don't need to append or remove the address when linking and unlinking. By setting the address.user attribute, SQLAlchemy appears to do the right thing, though I'm not 100% sure of that (see the above mentioned failures).
Diffstat (limited to 'Mailman/database/model')
-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
12 files changed, 229 insertions, 223 deletions
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)