diff --git a/edbob/db/extensions/__init__.py b/edbob/db/extensions/__init__.py index e71e4e5..c99c7c7 100644 --- a/edbob/db/extensions/__init__.py +++ b/edbob/db/extensions/__init__.py @@ -178,7 +178,8 @@ class Extension(edbob.Object): for name in model.__all__: obj = getattr(model, name) if isinstance(obj, type) and issubclass(obj, model.Base): - obj.__table__.tometadata(metadata) + if obj.__tablename__ not in metadata.tables: + obj.__table__.tometadata(metadata) # def remove_class(self, name): # """ diff --git a/edbob/db/extensions/auth/__init__.py b/edbob/db/extensions/auth/__init__.py index f6e2674..24c975f 100644 --- a/edbob/db/extensions/auth/__init__.py +++ b/edbob/db/extensions/auth/__init__.py @@ -35,3 +35,4 @@ from edbob.db.extensions.auth.model import __all__ class AuthExtension(Extension): name = 'auth' + required_extensions = ['contact'] diff --git a/edbob/db/extensions/auth/model.py b/edbob/db/extensions/auth/model.py index 61514d2..44f486b 100644 --- a/edbob/db/extensions/auth/model.py +++ b/edbob/db/extensions/auth/model.py @@ -32,38 +32,11 @@ from sqlalchemy.ext.associationproxy import association_proxy import edbob from edbob.db.model import Base, uuid_column +from edbob.db.extensions.contact import Person from edbob.sqlalchemy import getset_factory -__all__ = ['Person', 'Role', 'User', 'UserRole', 'Permission'] - - -def get_person_display_name(context): - first_name = context.current_parameters['first_name'] - last_name = context.current_parameters['last_name'] - if not (first_name or last_name): - return None - return '%(first_name)s %(last_name)s' % locals() - - -class Person(Base): - """ - Represents a real, living and breathing person. (Or, at least was - previously living and breathing, in the case of the deceased.) - """ - - __tablename__ = 'people' - - uuid = uuid_column() - first_name = Column(String(50)) - last_name = Column(String(50)) - display_name = Column(String(100), default=get_person_display_name) - - def __repr__(self): - return "" % self.display_name - - def __str__(self): - return str(self.display_name or '') +__all__ = ['Role', 'User', 'UserRole', 'Permission'] class Permission(Base): @@ -165,12 +138,12 @@ class User(Base): return self.username -Person.user = relationship( - User, - back_populates='person', - uselist=False) - User.person = relationship( Person, back_populates='user', uselist=False) + +Person.user = relationship( + User, + back_populates='person', + uselist=False) diff --git a/edbob/db/extensions/contact/__init__.py b/edbob/db/extensions/contact/__init__.py new file mode 100644 index 0000000..99317d8 --- /dev/null +++ b/edbob/db/extensions/contact/__init__.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +################################################################################ +# +# edbob -- Pythonic Software Framework +# Copyright © 2010-2012 Lance Edgar +# +# This file is part of edbob. +# +# edbob is free software: you can redistribute it and/or modify it under the +# terms of the GNU Affero General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) +# any later version. +# +# edbob is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for +# more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with edbob. If not, see . +# +################################################################################ + +""" +``edbob.db.extensions.contact`` -- 'contact' Extension +""" + +from edbob.db.extensions import Extension + +from edbob.db.extensions.contact.model import * +from edbob.db.extensions.contact.model import __all__ + + +class ContactExtension(Extension): + + name = 'contact' diff --git a/edbob/db/extensions/contact/model.py b/edbob/db/extensions/contact/model.py new file mode 100644 index 0000000..18ae19e --- /dev/null +++ b/edbob/db/extensions/contact/model.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +################################################################################ +# +# edbob -- Pythonic Software Framework +# Copyright © 2010-2012 Lance Edgar +# +# This file is part of edbob. +# +# edbob is free software: you can redistribute it and/or modify it under the +# terms of the GNU Affero General Public License as published by the Free +# Software Foundation, either version 3 of the License, or (at your option) +# any later version. +# +# edbob is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for +# more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with edbob. If not, see . +# +################################################################################ + +""" +``edbob.db.extensions.contact.model`` -- Schema Definition +""" + +from sqlalchemy import Column, String, Integer +from sqlalchemy import and_ +from sqlalchemy.orm import relationship +from sqlalchemy.ext.orderinglist import ordering_list + +from edbob.db.model import Base, uuid_column + + +__all__ = ['Phone', 'Person', 'PersonPhone'] + + +def get_person_display_name(context): + """ + Provides a default value for :attr:`Person.display_name`, constructed from + :attr:`Person.first_name` and :attr:`Person.last_name`. + """ + + first_name = context.current_parameters['first_name'] + last_name = context.current_parameters['last_name'] + if first_name and last_name: + return first_name + ' ' + last_name + if first_name: + return first_name + if last_name: + return last_name + return None + + +class Phone(Base): + """ + Represents a phone (or fax) number associated with a contactable entity. + """ + + __tablename__ = 'phones' + + uuid = uuid_column() + parent_type = Column(String(20), nullable=False) + parent_uuid = Column(String(32), nullable=False) + preference = Column(Integer, nullable=False) + type = Column(String(15)) + number = Column(String(20), nullable=False) + + __mapper_args__ = {'polymorphic_on': parent_type} + + def __repr__(self): + return "<%s: %s>" % (self.__class__.__name__, self.number) + + def __unicode__(self): + return unicode(self.number) + + +class PersonPhone(Phone): + """ + Represents a phone (or fax) number associated with a :class:`Person`. + """ + + __mapper_args__ = {'polymorphic_identity': 'Person'} + + +class Person(Base): + """ + Represents a real, living and breathing person. (Or, at least was + previously living and breathing, in the case of the deceased.) + """ + + __tablename__ = 'people' + + uuid = uuid_column() + first_name = Column(String(50)) + last_name = Column(String(50)) + display_name = Column(String(100), default=get_person_display_name) + + def __repr__(self): + return "" % self.display_name + + def __unicode__(self): + return unicode(self.display_name or '') + +Person.phones = relationship( + PersonPhone, + backref='person', + primaryjoin=PersonPhone.parent_uuid == Person.uuid, + foreign_keys=[PersonPhone.parent_uuid], + collection_class=ordering_list('preference', count_from=1), + order_by=PersonPhone.preference) + +Person.phone = relationship( + PersonPhone, + primaryjoin=and_( + PersonPhone.parent_uuid == Person.uuid, + PersonPhone.preference == 1, + ), + foreign_keys=[PersonPhone.parent_uuid], + uselist=False, + viewonly=True) diff --git a/setup.py b/setup.py index be83e97..375ad88 100644 --- a/setup.py +++ b/setup.py @@ -217,6 +217,7 @@ uuid = edbob.commands:UuidCommand [edbob.db.extensions] auth = edbob.db.extensions.auth:AuthExtension +contact = edbob.db.extensions.contact:ContactExtension """, )