Changeset 350
- Timestamp:
- 07/02/08 15:17:29 (5 years ago)
- Location:
- elixir/trunk/elixir
- Files:
-
- 5 modified
-
entity.py (modified) (2 diffs)
-
ext/associable.py (modified) (11 diffs)
-
fields.py (modified) (2 diffs)
-
properties.py (modified) (2 diffs)
-
relationships.py (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
-
elixir/trunk/elixir/entity.py
r349 r350 157 157 # key columns 158 158 parent_desc = self.parent._descriptor 159 schema = parent_desc.table_options.get('schema', None) 160 tablename = parent_desc.tablename 161 if schema is not None: 162 tablename = "%s.%s" % (schema, tablename) 159 tablename = parent_desc.table_fullname 163 160 for pk_col in parent_desc.primary_keys: 164 161 colname = options.MULTIINHERITANCECOL_NAMEFORMAT % \ … … 534 531 else: 535 532 return None 533 534 def table_fullname(self): 535 ''' 536 Complete name of the table for the related entity. 537 Includes the schema name if there is one specified. 538 ''' 539 schema = self.table_options.get('schema', None) 540 if schema is not None: 541 return "%s.%s" % (schema, self.tablename) 542 else: 543 return self.tablename 544 table_fullname = property(table_fullname) 536 545 537 546 def columns(self): -
elixir/trunk/elixir/ext/associable.py
r310 r350 9 9 ------------------------------ 10 10 11 A frequent pattern in database schemas is the has_and_belongs_to_many, or a 11 A frequent pattern in database schemas is the has_and_belongs_to_many, or a 12 12 many-to-many table. Quite often multiple tables will refer to a single one 13 13 creating quite a few many-to-many intermediate tables. 14 14 15 15 Polymorphic associations lower the amount of many-to-many tables by setting up 16 a table that allows relations to any other table in the database, and relates 17 it to the associable table. In some implementations, this layout does not 18 enforce referential integrity with database foreign key constraints, this 19 implementation uses an additional many-to-many table with foreign key 16 a table that allows relations to any other table in the database, and relates 17 it to the associable table. In some implementations, this layout does not 18 enforce referential integrity with database foreign key constraints, this 19 implementation uses an additional many-to-many table with foreign key 20 20 constraints to avoid this problem. 21 21 … … 38 38 39 39 .. sourcecode:: python 40 40 41 41 class Tag(Entity): 42 42 name = Field(Unicode) 43 43 44 44 acts_as_taggable = associable(Tag) 45 45 46 46 class Entry(Entity): 47 47 title = Field(Unicode) 48 48 acts_as_taggable('tags') 49 49 50 50 class Article(Entity): 51 51 title = Field(Unicode) … … 56 56 57 57 .. sourcecode:: python 58 58 59 59 class Address(Entity): 60 60 street = Field(String(130)) 61 61 city = Field(String(100)) 62 62 63 63 is_addressable = associable(Address, 'addresses') 64 64 65 65 class Person(Entity): 66 66 name = Field(Unicode) 67 67 orders = OneToMany('Order') 68 68 is_addressable() 69 69 70 70 class Order(Entity): 71 71 order_num = Field(primary_key=True) … … 73 73 person = ManyToOne('Person') 74 74 is_addressable('address', uselist=False) 75 75 76 76 home = Address(street='123 Elm St.', city='Spooksville') 77 77 user = Person(name='Jane Doe') 78 78 user.addresses.append(home) 79 79 80 80 neworder = Order(item_count=4) 81 81 neworder.address = home 82 82 user.orders.append(neworder) 83 83 84 84 # Queries using the added helpers 85 85 Person.select_by_addresses(city='Cupertino') … … 131 131 132 132 class GenericAssoc(object): 133 133 134 134 def __init__(self, tablename): 135 135 self.type = tablename 136 136 137 137 #TODO: inherit from entity builder 138 138 class Associable(object): 139 139 """An associable Elixir Statement object""" 140 140 141 141 def __init__(self, entity, name=None, uselist=True, lazy=True): 142 142 self.entity = entity 143 143 self.lazy = lazy 144 144 self.uselist = uselist 145 145 146 146 if name is None: 147 147 self.name = plural_name … … 150 150 151 151 def after_table(self): 152 col = sa.Column('%s_assoc_id' % interface_name, sa.Integer, 152 col = sa.Column('%s_assoc_id' % interface_name, sa.Integer, 153 153 sa.ForeignKey('%s.id' % able_name)) 154 154 self.entity._descriptor.add_column(col) … … 162 162 tablename = "%s_to_%s" % (able_name, interface_name) 163 163 association_to_table = sa.Table(tablename, metadata, 164 sa.Column('assoc_id', sa.Integer, 165 sa.ForeignKey(association_table.c.id, 166 ondelete="CASCADE"), 164 sa.Column('assoc_id', sa.Integer, 165 sa.ForeignKey(association_table.c.id, 166 ondelete="CASCADE"), 167 167 primary_key=True), 168 168 #FIXME: this assumes a single id col 169 sa.Column('%s_id' % interface_name, sa.Integer, 170 sa.ForeignKey(assoc_entity.table.c.id, 171 ondelete="RESTRICT"), 169 sa.Column('%s_id' % interface_name, sa.Integer, 170 sa.ForeignKey(assoc_entity.table.c.id, 171 ondelete="RESTRICT"), 172 172 primary_key=True), 173 173 ) … … 186 186 order_by=assoc_entity.mapper.order_by) 187 187 }) 188 188 189 189 entity = self.entity 190 190 entity.mapper.add_property( 191 attr_name, 191 attr_name, 192 192 sa.orm.relation(GenericAssoc, lazy=self.lazy, 193 193 backref='_backref_%s' % entity.table.name) … … 197 197 def get(self): 198 198 if getattr(self, attr_name) is None: 199 setattr(self, attr_name, 199 setattr(self, attr_name, 200 200 GenericAssoc(entity.table.name)) 201 201 return getattr(self, attr_name).targets … … 211 211 def set(self, value): 212 212 if getattr(self, attr_name) is None: 213 setattr(self, attr_name, 213 setattr(self, attr_name, 214 214 GenericAssoc(entity.table.name)) 215 215 getattr(self, attr_name).targets = [value] 216 216 setattr(entity, self.name, property(get, set)) 217 217 218 # self.name is both set via mapper synonym and the python 218 # self.name is both set via mapper synonym and the python 219 219 # property, but that's how synonym properties work. 220 # adding synonym property after "real" property otherwise it 220 # adding synonym property after "real" property otherwise it 221 221 # breaks when using SQLAlchemy > 0.4.1 222 222 entity.mapper.add_property(self.name, sa.orm.synonym(attr_name)) … … 227 227 .filter_by(**kwargs).all() 228 228 setattr(entity, 'select_by_%s' % self.name, classmethod(select_by)) 229 229 230 230 def select(cls, *args, **kwargs): 231 231 return cls.query.join([attr_name, 'targets']) \ -
elixir/trunk/elixir/fields.py
r347 r350 187 187 self.column = Column(self.colname, self.type, 188 188 *self.args, **self.kwargs) 189 self. entity._descriptor.add_column(self.column)189 self.add_table_column(self.column) 190 190 191 191 def create_properties(self): … … 196 196 self.property = deferred(self.column, group=group) 197 197 elif self.name != self.colname: 198 # if the property name is different from the column name, we need to199 # add an explicit property (otherwise nothing is needed as it's done200 # automatically by SA)198 # if the property name is different from the column name, we need 199 # to add an explicit property (otherwise nothing is needed as it's 200 # done automatically by SA) 201 201 self.property = self.column 202 202 203 203 if self.property: 204 self. entity._descriptor.add_property(self.name, self.property)204 self.add_mapper_property(self.name, self.property) 205 205 206 206 if self.synonym: 207 self.entity._descriptor.add_property(self.synonym, 208 synonym(self.name)) 207 self.add_mapper_property(self.synonym, synonym(self.name)) 209 208 210 209 -
elixir/trunk/elixir/properties.py
r348 r350 77 77 pass 78 78 79 # helper methods 80 def add_mapper_property(self, name, prop): 81 self.entity._descriptor.add_property(name, prop) 82 83 def add_table_column(self, column): 84 self.entity._descriptor.add_column(column) 85 79 86 80 87 class CounterMeta(type): … … 146 153 prop_value = self.prop 147 154 prop_value = self.evaluate_property(prop_value) 148 self. entity._descriptor.add_property(self.name, prop_value)155 self.add_mapper_property(self.name, prop_value) 149 156 150 157 def evaluate_property(self, prop): -
elixir/trunk/elixir/relationships.py
r347 r350 414 414 kwargs.update(self.get_prop_kwargs()) 415 415 self.property = relation(self.target, **kwargs) 416 self. entity._descriptor.add_property(self.name, self.property)416 self.add_mapper_property(self.name, self.property) 417 417 418 418 def target(self): … … 571 571 # Build the list of column "paths" the foreign key will 572 572 # point to 573 target_path = "%s.%s" % (target_desc.tablename, pk_col.key) 574 schema = target_desc.table_options.get('schema', None) 575 if schema is not None: 576 target_path = "%s.%s" % (schema, target_path) 577 fk_refcols.append(target_path) 573 fk_refcols.append("%s.%s" % \ 574 (target_desc.table_fullname, pk_col.key)) 578 575 579 576 # Build up the primary join. This is needed when you have … … 757 754 758 755 joins = (self.primaryjoin_clauses, self.secondaryjoin_clauses) 759 for num, desc, fk_name, m2min (756 for num, desc, fk_name, rel in ( 760 757 (0, e1_desc, source_fk_name, self), 761 758 (1, e2_desc, target_fk_name, self.inverse)): … … 784 781 # Build the list of column "paths" the foreign key will 785 782 # point to 786 target_path = "%s.%s" % (desc.tablename, pk_col.key) 787 schema = desc.table_options.get('schema', None) 788 if schema is not None: 789 target_path = "%s.%s" % (schema, target_path) 783 target_path = "%s.%s" % (desc.table_fullname, pk_col.key) 790 784 fk_refcols.append(target_path) 791 785 … … 794 788 joins[num].append(col == pk_col) 795 789 796 onupdate = m2m and m2m.onupdate797 ondelete = m2m and m2m.ondelete790 onupdate = rel and rel.onupdate 791 ondelete = rel and rel.ondelete 798 792 799 793 constraints.append(
