Changeset 323
- Timestamp:
- 04/28/08 18:26:51 (5 years ago)
- Location:
- elixir/trunk
- Files:
-
- 7 modified
-
CHANGES (modified) (2 diffs)
-
elixir/__init__.py (modified) (1 diff)
-
elixir/entity.py (modified) (9 diffs)
-
elixir/options.py (modified) (6 diffs)
-
setup.cfg (modified) (1 diff)
-
setup.py (modified) (1 diff)
-
tests/test_inherit.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
elixir/trunk/CHANGES
r322 r323 1 0. 5.31 0.6.0 2 2 3 3 New features: … … 6 6 simple JSON-like dictionary notation (patch from Paul Johnston, 7 7 closes ticket #40). 8 - Added experimental (!) support for concrete table inheritance (both 9 polymorphic or not). 10 - Added new "identity" option which can be used to set a custom polymorphic 11 identity for an entity. It also accepts a callable so that you can generate 12 the identity name automatically from the class itself. 8 13 9 14 Bug fixes: -
elixir/trunk/elixir/__init__.py
r314 r323 37 37 38 38 39 __version__ = '0. 5.2'39 __version__ = '0.6.0' 40 40 41 41 __all__ = ['Entity', 'EntityMeta', -
elixir/trunk/elixir/entity.py
r322 r323 13 13 desc, ForeignKey, and_, \ 14 14 ForeignKeyConstraint 15 from sqlalchemy.orm import Query, MapperExtension,\ 16 mapper, object_session, EXT_PASS 15 from sqlalchemy.orm import Query, MapperExtension, \ 16 mapper, object_session, EXT_PASS, \ 17 polymorphic_union 17 18 from sqlalchemy.ext.sessioncontext import SessionContext 18 19 … … 114 115 elixir.entities) 115 116 116 for option in ('autosetup', 'inheritance', 'polymorphic', 117 for option in ('autosetup', 'inheritance', 'polymorphic', 'identity', 117 118 'autoload', 'tablename', 'shortnames', 118 119 'auto_primarykey', 'version_id_col', … … 152 153 153 154 entity = self.entity 154 if self.inheritance == 'concrete' and self.polymorphic:155 raise NotImplementedError("Polymorphic concrete inheritance is "156 "not yet implemented.")157 158 155 if self.parent: 159 156 if self.inheritance == 'single': … … 169 166 elif callable(self.tablename): 170 167 self.tablename = self.tablename(entity) 168 169 if not self.identity: 170 if 'polymorphic_identity' in self.mapper_options: 171 self.identity = self.mapper_options['polymorphic_identity'] 172 else: 173 #TODO: include module name 174 self.identity = entity.__name__.lower() 175 elif 'polymorphic_identity' in kwargs: 176 raise Exception('You cannot use the "identity" option and the ' 177 'polymorphic_identity mapper option at the same ' 178 'time.') 179 elif callable(self.identity): 180 self.identity = self.identity(entity) 181 182 if self.polymorphic: 183 if not isinstance(self.polymorphic, basestring): 184 self.polymorphic = options.DEFAULT_POLYMORPHIC_COL_NAME 171 185 172 186 #--------------------- … … 282 296 use_alter=con.use_alter)) 283 297 284 if self.polymorphic and self.inheritance in ('single', 'multi') and \ 298 if self.polymorphic and \ 299 self.inheritance in ('single', 'multi') and \ 285 300 self.children and not self.parent: 286 if not isinstance(self.polymorphic, basestring):287 self.polymorphic = options.DEFAULT_POLYMORPHIC_COL_NAME288 289 301 self.add_column(Column(self.polymorphic, 290 302 options.POLYMORPHIC_COL_TYPE)) … … 379 391 if self.entity.mapper: 380 392 return 381 393 394 # for now we don't support the "abstract" parent class in a concrete 395 # inheritance scenario as demonstrated in 396 # sqlalchemy/test/orm/inheritance/concrete.py 397 # this should be added along other 382 398 kwargs = self.mapper_options 383 399 if self.order_by: … … 396 412 self.parent._descriptor.primary_keys) 397 413 kwargs['inherit_condition'] = \ 398 and_(*[pc == c for c, pc in col_pairs])414 and_(*[pc == c for c, pc in col_pairs]) 399 415 400 416 if self.polymorphic: 401 417 if self.children and not self.parent: 402 kwargs['polymorphic_on'] = \ 403 self.get_column(self.polymorphic) 418 if self.inheritance == 'concrete': 419 keys = [(self.identity, self.entity.table)] 420 keys.extend([(child._descriptor.identity, child.table) 421 for child in self._get_children()]) 422 pjoin = polymorphic_union( 423 dict(keys), self.polymorphic, 'pjoin') 424 kwargs['select_table'] = pjoin 425 kwargs['polymorphic_on'] = \ 426 getattr(pjoin.c, self.polymorphic) 427 else: 428 kwargs['polymorphic_on'] = \ 429 self.get_column(self.polymorphic) 404 430 #TODO: this is an optimization, and it breaks the multi 405 431 # table polymorphic inheritance test with a relation. … … 412 438 # join = join.outerjoin(child.table) 413 439 # kwargs['select_table'] = join 414 440 415 441 if self.children or self.parent: 416 #TODO: make this customizable (both callable and string) 417 #TODO: include module name 418 if 'polymorphic_identity' not in kwargs: 419 kwargs['polymorphic_identity'] = \ 420 self.entity.__name__.lower() 421 422 if self.inheritance == 'concrete': 442 kwargs['polymorphic_identity'] = self.identity 443 444 if self.parent and self.inheritance == 'concrete': 423 445 kwargs['concrete'] = True 424 446 … … 494 516 (name, self.entity.__name__)) 495 517 self.properties[name] = property 518 519 #FIXME: something like this is needed to propagate the relationships from 520 # parent entities to their children in a concrete inheritance scenario. But 521 # this doesn't work because of the backref matching code. 522 # if self.children and self.inheritance == 'concrete': 523 # for child in self.children: 524 # child._descriptor.add_property(name, property) 496 525 497 526 mapper = self.entity.mapper -
elixir/trunk/elixir/options.py
r321 r323 39 39 | | column to this argument. | 40 40 +---------------------+-------------------------------------------------------+ 41 | ``identity`` | Specify a custom polymorphic identity. When using | 42 | | polymorphic inheritance, this value (usually a | 43 | | string) will represent this particular entity (class) | 44 | | . It will be used to differentiate it from other | 45 | | entities (classes) in your inheritance hierarchy when | 46 | | loading from the database instances of different | 47 | | entities in that hierarchy at the same time. | 48 | | This value will be stored by default in the | 49 | | "row_type" column of the entity's table (see above). | 50 | | You can either provide a | 51 | | plain string or a callable. The callable will be | 52 | | given the entity (ie class) as argument and must | 53 | | return a value (usually a string) representing the | 54 | | polymorphic identity of that entity. | 55 | | By default, this value is automatically generated: it | 56 | | is the name of the entity lower-cased. | 57 +---------------------+-------------------------------------------------------+ 41 58 | ``metadata`` | Specify a custom MetaData for this entity. | 42 59 | | By default, entities uses the global | … … 53 70 | | given the entity (ie class) as argument and must | 54 71 | | return a string representing the name of the table | 55 | | for that entity. | 56 +---------------------+-------------------------------------------------------+ 57 | ``shortnames`` | Usually tablenames include the full module-path | 58 | | to the entity, but lower-cased and separated by | 59 | | underscores ("_"), eg.: "project1_model_myentity" | 60 | | for an entity named "MyEntity" in the module | 61 | | "project1.model". If shortnames is ``True``, the | 62 | | tablename will just be the entity's classname | 63 | | lower-cased, ie. "myentity". | 72 | | for that entity. By default, the tablename is | 73 | | automatically generated: it is a concatenation of the | 74 | | full module-path to the entity and the entity (class) | 75 | | name itself. The result is lower-cased and separated | 76 | | by underscores ("_"), eg.: for an entity named | 77 | | "MyEntity" in the module "project1.model", the | 78 | | generated table name will be | 79 | | "project1_model_myentity". | 80 +---------------------+-------------------------------------------------------+ 81 | ``shortnames`` | Specify whether or not the automatically generated | 82 | | table names include the full module-path | 83 | | to the entity. Defaults to ``True``. | 64 84 +---------------------+-------------------------------------------------------+ 65 85 | ``auto_primarykey`` | If given as string, it will represent the | … … 69 89 | | corresponding entity. If this option is False, | 70 90 | | it will disallow auto-creation of a primary key. | 91 | | Defaults to ``True``. | 71 92 +---------------------+-------------------------------------------------------+ 72 93 | ``version_id_col`` | If this option is True, it will create a version | … … 75 96 | | This can be used to prevent concurrent modifications | 76 97 | | to the entity's table rows (i.e. it will raise an | 77 | | exception if it happens). |98 | | exception if it happens). Defaults to ``False``. | 78 99 +---------------------+-------------------------------------------------------+ 79 100 | ``order_by`` | How to order select results. Either a string or a | … … 90 111 | | in which case your entity will be mapped using a | 91 112 | | non-contextual mapper. This option can also be set | 92 | | for all entities of a module via by setting the|113 | | for all entities of a module by setting the | 93 114 | | ``__session__`` attribute of that module. | 94 115 +---------------------+-------------------------------------------------------+ … … 168 189 inheritance='single', 169 190 polymorphic=True, 191 identity=None, 170 192 autoload=False, 171 193 tablename=None, -
elixir/trunk/setup.cfg
r314 r323 9 9 modules = elixir, elixir.ext.associable, elixir.ext.versioned, 10 10 elixir.ext.encrypted, elixir.ext.list 11 trac_browser_url = http://elixir.ematia.de/trac/browser/elixir/tags/0. 5.211 trac_browser_url = http://elixir.ematia.de/trac/browser/elixir/tags/0.6.0 12 12 -
elixir/trunk/setup.py
r314 r323 2 2 3 3 setup(name="Elixir", 4 version="0. 5.2",4 version="0.6.0", 5 5 description="Declarative Mapper for SQLAlchemy", 6 6 long_description=""" -
elixir/trunk/tests/test_inherit.py
r321 r323 55 55 56 56 for query_class in ('A', 'B', 'C', 'D', 'E'): 57 # print res[query_class], expected_res[query_class] 57 58 assert len(res[query_class]) == len(expected_res[query_class]) 58 59 for real, expected in zip(res[query_class], expected_res[query_class]): … … 172 173 }) 173 174 175 def test_polymorphic_concrete_inheritance(self): 176 # to get this test to work, I need to duplicate parent relationships in 177 # the children. The problem is that the properties are setup post 178 # mapper setup, so I'll need to add some logic into the add_property 179 # method which I'm reluctant to do. 180 do_tst('concrete', True, { 181 'A': ('A', 'B', 'C', 'D', 'E'), 182 'B': ('B', 'C'), 183 'C': ('C',), 184 'D': ('D',), 185 'E': ('E',), 186 }) 187 174 188 def test_multitable_inheritance(self): 175 189 do_tst('multi', False, {
