- Timestamp:
- 11/09/09 16:21:19 (3 years ago)
- Location:
- elixir/trunk
- Files:
-
- 2 modified
-
elixir/entity.py (modified) (8 diffs)
-
tests/test_abstract.py (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
elixir/trunk/elixir/entity.py
r511 r512 47 47 48 48 def __init__(self, entity): 49 50 49 self.entity = entity 51 50 # entity.__module__ is not always reliable (eg in mod_python) 52 51 self.module = sys.modules.get(entity.__module__) 53 52 54 # used for multi-table inheritance55 self.join_condition = None56 self.has_pk = False57 self._pk_col_done = False58 59 53 self.builders = [] 60 54 … … 63 57 self.children = [] 64 58 59 bases = [] 65 60 for base in entity.__bases__: 66 61 if isinstance(base, EntityMeta): 67 if ( 68 is_entity(base) and 69 (not is_abstract_entity(base)) 70 ): 62 if is_entity(base) and not is_abstract_entity(base): 71 63 if self.parent: 72 64 raise Exception( … … 76 68 else: 77 69 self.parent = base 78 self.base = base._descriptor.base70 bases.extend(base._descriptor.bases) 79 71 self.parent._descriptor.children.append(entity) 80 72 else: 81 self.base = base 73 bases.append(base) 74 self.bases = bases 75 76 if not is_entity(entity): 77 return 78 79 # used for multi-table inheritance 80 self.join_condition = None 81 self.has_pk = False 82 self._pk_col_done = False 82 83 83 84 # columns and constraints waiting for a table to exist … … 96 97 self.table_args = [] 97 98 98 # base class options_defaults 99 base_defaults = getattr(self.base, 'options_defaults', {}) 99 # base class(es) options_defaults 100 base_defaults = {} 101 for base in self.bases: 102 base_defaults.update(getattr(base, 'options_defaults', {})) 103 100 104 complete_defaults = options.options_defaults.copy() 101 105 complete_defaults.update({ … … 697 701 698 702 def is_abstract_entity(dict_or_cls): 699 if isinstance(dict_or_cls, dict): 700 mutators = dict_or_cls.get(MUTATORS, []) 701 else: 702 mutators = getattr(dict_or_cls, MUTATORS, []) 703 704 for m in mutators: 705 if 'abstract' in m[2]: 706 return m[2]['abstract'] 703 if not isinstance(dict_or_cls, dict): 704 dict_or_cls = dict_or_cls.__dict__ 705 for mutator, args, kwargs in dict_or_cls.get(MUTATORS, []): 706 if 'abstract' in kwargs: 707 return kwargs['abstract'] 707 708 708 709 return False … … 713 714 the EntityMeta metaclass. 714 715 """ 716 # Create the entity descriptor 717 #XXX: we might want to use a simplified descriptor for bases and abstract 718 # classes which only need "bases", "options_defaults" and "abstract" 719 desc = cls._descriptor = EntityDescriptor(cls) 720 721 # Process mutators 722 # We *do* want mutators to be processed for base/abstract classes 723 # (so that statements like using_options_defaults work). 724 process_mutators(cls) 725 726 # We do not want to do any more processing for base/abstract classes 727 # (Entity et al.). 728 if not is_entity(cls) or is_abstract_entity(cls): 729 return 730 715 731 cls.table = None 716 732 cls.mapper = None 717 733 718 # create the entity descriptor 719 desc = cls._descriptor = EntityDescriptor(cls) 720 721 # Determine whether this entity is a *direct* subclass of its base entity 722 entity_bases = [] 734 # Copy the properties ('Property' instances) of the entity base class(es). 735 # We use getmembers (instead of __dict__) so that we also get the 736 # properties from the parents of the base class if any. 737 base_props = [] 723 738 for base in cls.__bases__: 724 if isinstance(base, EntityMeta): 725 if not is_entity(base) or is_abstract_entity(base): 726 entity_bases.append(base) 727 728 base_props = [] 729 if entity_bases: 730 # If so, copy the base entity properties ('Property' instances). 731 # We use inspect.getmembers (instead of __dict__) so that we also 732 # get the properties from the parents of the base_class if any. 733 for base in entity_bases: 734 base_props += [(name, deepcopy(attr)) for name, attr in 739 if isinstance(base, EntityMeta) and \ 740 (not is_entity(base) or is_abstract_entity(base)): 741 base_props += [(name, deepcopy(attr)) for name, attr in 735 742 getmembers(base, lambda a: isinstance(a, Property))] 736 743 … … 744 751 prop.attach(cls, name) 745 752 746 # Process mutators747 process_mutators(cls)748 749 753 # setup misc options here (like tablename etc.) 750 754 desc.setup_options() … … 759 763 760 764 def __init__(cls, name, bases, dict_): 761 # Only process further subclasses of the base classes (Entity et al.),762 # not the base classes themselves. We don't want the base entities to763 # be registered in an entity collection, nor to have a table name and764 # so on.765 766 if is_abstract_entity(dict_):767 return768 769 if not is_entity(cls):770 if isinstance(cls, EntityMeta):771 process_mutators(cls)772 return773 774 765 instrument_class(cls) 775 766 -
elixir/trunk/tests/test_abstract.py
r511 r512 3 3 """ 4 4 5 import re 6 5 7 from elixir import * 6 8 import elixir 9 10 def camel_to_underscore(entity): 11 return re.sub(r'(.+?)([A-Z])+?', r'\1_\2', entity.__name__).lower() 7 12 8 13 def setup(): … … 114 119 class AbstractDated(Entity): 115 120 using_options(abstract=True) 121 using_options_defaults(tablename=camel_to_underscore) 116 122 117 123 #TODO: add defaults … … 121 127 class AbstractContact(Entity): 122 128 using_options(abstract=True) 129 using_options_defaults(identity=camel_to_underscore) 123 130 124 131 first_name = Field(Unicode(50)) 125 132 last_name = Field(Unicode(50)) 126 133 127 class Contact(AbstractContact, AbstractDated):134 class DatedContact(AbstractContact, AbstractDated): 128 135 pass 129 136 130 137 setup_all(True) 131 138 132 for f in ('created_date', 'modified_date', 133 'first_name', 'last_name'): 134 assert f in Contact.table.columns, \ 135 "column '%s' does not exist in table " % f 139 assert 'created_date' in DatedContact.table.columns 140 assert 'modified_date' in DatedContact.table.columns 141 assert 'first_name' in DatedContact.table.columns 142 assert 'last_name' in DatedContact.table.columns 143 assert DatedContact._descriptor.identity == 'dated_contact' 144 assert DatedContact.table.name == 'dated_contact' 136 145 137 contact1 = Contact(first_name=u"Guido", last_name=u"van Rossum")146 contact1 = DatedContact(first_name=u"Guido", last_name=u"van Rossum") 138 147 session.commit() 139 148 149 def test_mixed_inheritance(self): 150 class AbstractDated(Entity): 151 using_options(abstract=True) 152 using_options_defaults(tablename=camel_to_underscore) 140 153 154 #TODO: add defaults 155 created_date = Field(DateTime) 156 modified_date = Field(DateTime) 157 158 class AbstractContact(Entity): 159 using_options(abstract=True) 160 using_options_defaults(identity=camel_to_underscore) 161 162 first_name = Field(Unicode(50)) 163 last_name = Field(Unicode(50)) 164 165 class Contact(AbstractContact): 166 using_options(inheritance='multi') 167 168 class DatedContact(AbstractDated, Contact): 169 using_options(inheritance='multi') 170 171 setup_all(True) 172 173 assert 'created_date' in DatedContact.table.columns 174 assert 'modified_date' in DatedContact.table.columns 175 assert DatedContact._descriptor.identity == 'dated_contact' 176 assert DatedContact.table.name == 'dated_contact' 177 178 contact1 = DatedContact(first_name=u"Guido", last_name=u"van Rossum") 179 session.commit() 180
