| 98 | | def setup(self): |
| 99 | | ''' |
| 100 | | Create tables, keys, columns that have been specified so far and |
| 101 | | assign a mapper. Will be called when an instance of the entity is |
| 102 | | created or a mapper is needed to access one or many instances of the |
| 103 | | entity. It will try to initialize the entity's relationships (along |
| 104 | | with any delayed relationship) but some of them might be delayed. |
| 105 | | ''' |
| 106 | | if elixir.delay_setup: |
| 107 | | elixir.delayed_entities.add(self) |
| 108 | | return |
| 109 | | |
| 110 | | self.setup_events() |
| 111 | | self.setup_table() |
| 112 | | self.setup_mapper() |
| 113 | | |
| 114 | | # This marks all relations of the entity (or, at least those which |
| 115 | | # have been added so far by statements) as being uninitialized |
| 116 | | EntityDescriptor.uninitialized_rels.update( |
| 117 | | self.relationships.values()) |
| 118 | | |
| 119 | | # try to setup all uninitialized relationships |
| 120 | | EntityDescriptor.setup_relationships() |
| 121 | | |
| 122 | | # finally, allow the statement to do any "finalization" |
| 123 | | Statement.finalize(self.entity) |
| 124 | | |
| | 171 | |
| | 172 | if self.polymorphic: |
| | 173 | if self.children and not self.parent: |
| | 174 | kwargs['polymorphic_on'] = \ |
| | 175 | self.fields[self.polymorphic].column |
| | 176 | if self.inheritance == 'multi': |
| | 177 | children = self._get_children() |
| | 178 | join = self.entity.table |
| | 179 | for child in children: |
| | 180 | join = join.outerjoin(child.table) |
| | 181 | kwargs['select_table'] = join |
| | 182 | |
| | 183 | if self.children or self.parent: |
| | 184 | #TODO: make this customizable (both callable and string) |
| | 185 | #TODO: include module name |
| | 186 | kwargs['polymorphic_identity'] = \ |
| | 187 | self.entity.__name__.lower() |
| | 188 | |
| | 189 | if self.inheritance == 'concrete': |
| | 190 | kwargs['concrete'] = True |
| 209 | | assign_mapper(session.context, self.entity, self.entity.table, |
| 210 | | properties=properties, **kwargs) |
| | 210 | if self.parent and self.inheritance == 'single': |
| | 211 | args = [] |
| | 212 | else: |
| | 213 | args = [self.entity.table] |
| | 214 | |
| | 215 | assign_mapper(session.context, self.entity, properties=properties, |
| | 216 | *args, **kwargs) |
| | 217 | |
| | 218 | def _get_children(self): |
| | 219 | children = self.children[:] |
| | 220 | for child in self.children: |
| | 221 | children.extend(child._descriptor._get_children()) |
| | 222 | return children |
| 255 | | # elif self.inheritance == 'concrete': |
| 256 | | # do not reuse parent table, but copy all fields |
| 257 | | # the problem is that, at this point, all "plain" fields |
| 258 | | # are known, but not those generated by relations |
| 259 | | # for field in self.fields.itervalues(): |
| 260 | | # self.add_field(field) |
| | 264 | elif self.inheritance == 'concrete': |
| | 265 | # copy all fields from parent table |
| | 266 | for field in self.parent._descriptor.fields.itervalues(): |
| | 267 | self.add_field(field.copy()) |
| | 268 | |
| | 269 | if self.polymorphic and self.inheritance in ('single', 'multi') and \ |
| | 270 | self.children and not self.parent: |
| | 271 | if not isinstance(self.polymorphic, basestring): |
| | 272 | self.polymorphic = DEFAULT_POLYMORPHIC_COL_NAME |
| | 273 | |
| | 274 | self.add_field(Field(DEFAULT_POLYMORPHIC_COL_TYPE, |
| | 275 | colname=self.polymorphic)) |
| | 296 | def create_pk_cols(self): |
| | 297 | """ |
| | 298 | Create primary_key columns. That is, add columns from belongs_to |
| | 299 | relationships marked as being a primary_key and then adds a primary |
| | 300 | key to the table if it hasn't already got one and needs one. |
| | 301 | |
| | 302 | This method is "semi-recursive" in that it calls the create_keys |
| | 303 | method on BelongsTo relationships and those in turn call create_pk_cols |
| | 304 | on their target. It shouldn't be possible to have an infinite loop |
| | 305 | since a loop of primary_keys is not a valid situation. |
| | 306 | """ |
| | 307 | for rel in self.relationships.itervalues(): |
| | 308 | rel.create_keys(True) |
| | 309 | |
| | 310 | if not self.autoload: |
| | 311 | if self.parent and self.inheritance == 'multi': |
| | 312 | # add foreign keys to the parent's primary key columns |
| | 313 | parent_desc = self.parent._descriptor |
| | 314 | for pk_col in parent_desc.primary_keys: |
| | 315 | colname = "%s_%s" % (self.parent.__name__.lower(), |
| | 316 | pk_col.name) |
| | 317 | field = Field(pk_col.type, ForeignKey(pk_col), |
| | 318 | colname=colname, primary_key=True) |
| | 319 | self.add_field(field) |
| | 320 | if not self.has_pk and self.auto_primarykey: |
| | 321 | self.create_auto_primary_key() |
| | 322 | |
| | 323 | |
| 356 | | def setup_relationships(cls): |
| 357 | | for relationship in list(EntityDescriptor.uninitialized_rels): |
| 358 | | if relationship.setup(): |
| 359 | | EntityDescriptor.uninitialized_rels.remove(relationship) |
| 360 | | setup_relationships = classmethod(setup_relationships) |
| | 398 | |
| | 399 | class TriggerProxy(object): |
| | 400 | def __init__(self, class_, attrname, setupfunc): |
| | 401 | self.class_ = class_ |
| | 402 | self.attrname = attrname |
| | 403 | self.setupfunc = setupfunc |
| | 404 | |
| | 405 | def __getattr__(self, name): |
| | 406 | self.setupfunc() |
| | 407 | proxied_attr = getattr(self.class_, self.attrname) |
| | 408 | return getattr(proxied_attr, name) |
| | 409 | |
| | 410 | def __repr__(self): |
| | 411 | proxied_attr = getattr(self.class_, self.attrname) |
| | 412 | return "<TriggerProxy (%s)>" % (self.class_.__name__) |
| 384 | | # create table & assign (empty) mapper |
| 385 | | desc.setup() |
| | 441 | # create trigger proxies |
| | 442 | # TODO: support entity_name... or maybe not. I'm not sure it makes |
| | 443 | # sense in Elixir. |
| | 444 | cls.setup_proxy() |
| | 445 | |
| | 446 | def setup_proxy(cls, entity_name=None): |
| | 447 | #TODO: move as much as possible of those "_private" values to the |
| | 448 | # descriptor, so that we don't mess the initial class. |
| | 449 | cls._class_key = sqlalchemy.orm.mapperlib.ClassKey(cls, entity_name) |
| | 450 | |
| | 451 | tablename = cls._descriptor.tablename |
| | 452 | schema = cls._descriptor.table_options.get('schema', None) |
| | 453 | cls._table_key = sqlalchemy.schema._get_table_key(tablename, schema) |
| | 454 | |
| | 455 | elixir._delayed_descriptors.append(cls._descriptor) |
| | 456 | |
| | 457 | mapper_proxy = TriggerProxy(cls, 'mapper', elixir.setup_all) |
| | 458 | table_proxy = TriggerProxy(cls, 'table', elixir.setup_all) |
| | 459 | |
| | 460 | sqlalchemy.orm.mapper_registry[cls._class_key] = mapper_proxy |
| | 461 | md = cls._descriptor.metadata |
| | 462 | md.tables[cls._table_key] = table_proxy |
| | 463 | |
| | 464 | # We need to monkeypatch the metadata's table iterator method because |
| | 465 | # otherwise it doesn't work if the setup is triggered by the |
| | 466 | # metadata.create_all(). |
| | 467 | # This is because ManyToMany relationships add tables AFTER the list |
| | 468 | # of tables that are going to be created is "computed" |
| | 469 | # (metadata.tables.values()). |
| | 470 | # see: |
| | 471 | # - table_iterator method in MetaData class in sqlalchemy/schema.py |
| | 472 | # - visit_metadata method in sqlalchemy/ansisql.py |
| | 473 | original_table_iterator = md.table_iterator |
| | 474 | if not hasattr(original_table_iterator, |
| | 475 | '_non_elixir_patched_iterator'): |
| | 476 | def table_iterator(*args, **kwargs): |
| | 477 | elixir.setup_all() |
| | 478 | return original_table_iterator(*args, **kwargs) |
| | 479 | table_iterator.__doc__ = original_table_iterator.__doc__ |
| | 480 | table_iterator._non_elixir_patched_iterator = \ |
| | 481 | original_table_iterator |
| | 482 | md.table_iterator = table_iterator |
| | 483 | |
| | 484 | cls._ready = True |
| | 485 | |
| | 486 | def __getattribute__(cls, name): |
| | 487 | if type.__getattribute__(cls, "_ready"): |
| | 488 | #TODO: we need to add all assign_mapper methods |
| | 489 | if name in ('c', 'table', 'mapper'): |
| | 490 | elixir.setup_all() |
| | 491 | return type.__getattribute__(cls, name) |
| | 492 | |
| | 493 | def __call__(cls, *args, **kwargs): |
| | 494 | elixir.setup_all() |
| | 495 | return type.__call__(cls, *args, **kwargs) |
| | 531 | def get_by(cls, *args, **kwargs): |
| | 532 | # warnings.warn("The get_by method on the class is deprecated." |
| | 533 | # "You should use cls.query.get_by", DeprecationWarning, |
| | 534 | # stacklevel=2) |
| | 535 | return cls.q.get_by(*args, **kwargs) |
| | 536 | get_by = classmethod(get_by) |
| | 537 | |
| | 538 | def select(cls, *args, **kwargs): |
| | 539 | # warnings.warn("The select method on the class is deprecated." |
| | 540 | # "You should use cls.query.select", DeprecationWarning, |
| | 541 | # stacklevel=2) |
| | 542 | return cls.q.select(*args, **kwargs) |
| | 543 | select = classmethod(select) |
| | 544 | |
| | 545 | |