Changeset 475
- Timestamp:
- 09/29/09 14:14:37 (4 years ago)
- Location:
- elixir/trunk
- Files:
-
- 3 modified
-
elixir/options.py (modified) (1 diff)
-
elixir/relationships.py (modified) (5 diffs)
-
tests/test_m2m.py (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
-
elixir/trunk/elixir/options.py
r466 r475 183 183 __doc_all__ = ['options_defaults'] 184 184 185 OLD_M2MCOL_NAMEFORMAT = "%(tablename)s_%(key)s%(numifself)s" 186 ALTERNATE_M2MCOL_NAMEFORMAT = "%(inversename)s_%(key)s" 187 188 def default_m2m_column_formatter(data): 189 if data['selfref']: 190 return ALTERNATE_M2MCOL_NAMEFORMAT % data 191 else: 192 return OLD_M2MCOL_NAMEFORMAT % data 193 194 NEW_M2MCOL_NAMEFORMAT = default_m2m_column_formatter 195 185 196 # format constants 186 197 FKCOL_NAMEFORMAT = "%(relname)s_%(key)s" 187 OLD_M2MCOL_NAMEFORMAT = "%(tablename)s_%(key)s%(numifself)s" 188 NEW_M2MCOL_NAMEFORMAT = "%(relname)s_%(key)s" 189 M2MCOL_NAMEFORMAT = OLD_M2MCOL_NAMEFORMAT 198 M2MCOL_NAMEFORMAT = NEW_M2MCOL_NAMEFORMAT 190 199 CONSTRAINT_NAMEFORMAT = "%(tablename)s_%(colnames)s_fk" 191 200 MULTIINHERITANCECOL_NAMEFORMAT = "%(entity)s_%(key)s" -
elixir/trunk/elixir/relationships.py
r465 r475 870 870 if options.MIGRATION_TO_07_AID: 871 871 self.column_format = \ 872 migration_aid_m2m_column_formatter(self.column_format) 872 migration_aid_m2m_column_formatter( 873 lambda data: options.OLD_M2MCOL_NAMEFORMAT % data, 874 self.column_format) 873 875 874 876 self.filter = filter … … 995 997 996 998 joins = (self.primaryjoin_clauses, self.secondaryjoin_clauses) 997 for num, desc, fk_name, rel, colnames in (998 (0, e1_desc, source_fk_name, self, self. local_colname),999 (1, e2_desc, target_fk_name, self.inverse, self .remote_colname)):999 for num, desc, fk_name, rel, inverse, colnames in ( 1000 (0, e1_desc, source_fk_name, self, self.inverse, self.local_colname), 1001 (1, e2_desc, target_fk_name, self.inverse, self, self.remote_colname)): 1000 1002 1001 1003 fk_colnames = [] … … 1005 1007 assert len(colnames) == len(desc.primary_keys) 1006 1008 else: 1007 #FIXME: desc is not the target desc. Do I need to fix to 1008 # code or the doc? in fact the relname corresponds to the 1009 # relationship going from the entity to the M2M, so the new 1010 # naming scheme might not really make sense 1011 data = {# relationship info 1009 # The data generated here will be fed to the M2M column 1010 # formatter to generate the name of the columns of the 1011 # intermediate table for *one* side of the relationship, 1012 # that is, from the intermediate table to the current 1013 # entity, as stored in the "desc" variable. 1014 data = {# A) relationships info 1015 1016 # the name of the rel going *from* the entity 1017 # we are currently generating a column pointing 1018 # *to*. This is generally *not* what you want to 1019 # use. eg in a "Post" and "Tag" example, with 1020 # relationships named 'tags' and 'posts', when 1021 # creating the columns from the intermediate 1022 # table to the "Post" entity, 'relname' will 1023 # contain 'tags'. 1012 1024 'relname': rel and rel.name or 'inverse', 1025 1026 # the name of the inverse relationship. In the 1027 # above example, 'inversename' will contain 1028 # 'posts'. 1029 'inversename': inverse and inverse.name 1030 or 'inverse', 1031 # is A == B? 1013 1032 'selfref': e1_desc is e2_desc, 1033 # provided for backward compatibility, DO NOT USE! 1014 1034 'num': num, 1035 # provided for backward compatibility, DO NOT USE! 1015 1036 'numifself': e1_desc is e2_desc and str(num + 1) 1016 1037 or '', 1017 # source (not target!) info 1018 'source': desc.entity, 1038 # B) target information (from the perspective of 1039 # the intermediate table) 1040 'target': desc.entity, 1019 1041 'entity': desc.entity.__name__.lower(), 1020 'tablename': desc.tablename 1042 'tablename': desc.tablename, 1043 1044 # C) current (intermediate) table name 1045 'current_table': tablename 1021 1046 } 1022 1047 colnames = [] … … 1045 1070 ondelete = rel and rel.ondelete 1046 1071 1072 #FIXME: fk_name is misleading 1047 1073 constraints.append( 1048 1074 ForeignKeyConstraint(fk_colnames, fk_refcols, … … 1104 1130 1105 1131 1106 def alternate_m2m_column_formatter(data): 1107 if data['selfref']: 1108 return options.NEW_M2MCOL_NAMEFORMAT % data 1109 else: 1110 return options.OLD_M2MCOL_NAMEFORMAT % data 1111 1112 1113 def migration_aid_m2m_column_formatter(formatter): 1132 def migration_aid_m2m_column_formatter(oldformatter, newformatter): 1114 1133 def debug_formatter(data): 1115 new_name =formatter(data)1116 old_name = options.OLD_M2MCOL_NAMEFORMAT % data1134 old_name = oldformatter(data) 1135 new_name = newformatter(data) 1117 1136 if new_name != old_name: 1118 1137 complete_data = data.copy() 1119 1138 complete_data.update(old_name=old_name, 1120 1139 new_name=new_name, 1121 dir=data['num'] is 0 and 'local' or 'remote')1140 targetname=data['target'].__name__) 1122 1141 # Specifying a stacklevel is useless in this case as the name 1123 1142 # generation is triggered by setup_all(), not by the declaration 1124 1143 # of the offending relationship. 1125 #FIXME: entity is probably wrong here since it refers to the target1126 #entity.1127 warnings.warn("The generated column name for the '%(relname)s' "1128 "relationship on the '%( entity)s' entity changed"1129 " from '%(old_name)s' to '%(new_name)s'."1144 warnings.warn("The '%(old_name)s' column in the " 1145 "'%(current_table)s' table, used as the " 1146 "intermediate table for the '%(relname)s' " 1147 "relationship on the '%(targetname)s' entity " 1148 "was renamed to '%(new_name)s'." 1130 1149 % complete_data) 1131 1150 return new_name -
elixir/trunk/tests/test_m2m.py
r464 r475 17 17 def test_simple(self): 18 18 class A(Entity): 19 name = Field(String(60)) 20 bs_ = ManyToMany('B') 21 22 class B(Entity): 19 using_options(shortnames=True) 20 name = Field(String(60)) 21 as_ = ManyToMany('A') 22 bs_ = ManyToMany('B') 23 24 class B(Entity): 25 using_options(shortnames=True) 23 26 name = Field(String(60)) 24 27 as_ = ManyToMany('A') … … 33 36 # check column names 34 37 m2m_cols = m2m_table.columns 35 assert 'bs__id' in m2m_cols 38 assert 'a_id' in m2m_cols 39 assert 'b_id' in m2m_cols 40 41 # check selfref m2m table column names were generated correctly 42 m2m_cols = A.as_.property.secondary.columns 36 43 assert 'as__id' in m2m_cols 44 assert 'inverse_id' in m2m_cols 37 45 38 46 # check the relationships work as expected … … 76 84 # this needs to be done before declaring the classes 77 85 elixir.options.M2MCOL_NAMEFORMAT = \ 78 elixir. relationships.alternate_m2m_column_formatter86 elixir.options.ALTERNATE_M2MCOL_NAMEFORMAT 79 87 80 88 class A(Entity): … … 93 101 # check m2m table column names were generated correctly 94 102 m2m_cols = A.bs_.property.secondary.columns 95 assert ' %s_id' % A.table.namein m2m_cols96 assert ' %s_id' % B.table.namein m2m_cols103 assert 'as__id' in m2m_cols 104 assert 'bs__id' in m2m_cols 97 105 98 106 # check selfref m2m table column names were generated correctly … … 101 109 assert 'inverse_id' in m2m_cols 102 110 103 #TODO: add an upgrade test 104 #def test_upgrade(self): 105 # elixir.options.M2MCOL_NAMEFORMAT = elixir.options.OLD_M2MCOL_NAMEFORMAT 111 def test_upgrade(self): 112 elixir.options.M2MCOL_NAMEFORMAT = elixir.options.OLD_M2MCOL_NAMEFORMAT 113 114 class A(Entity): 115 using_options(shortnames=True) 116 name = Field(String(20)) 117 links_to = ManyToMany('A') 118 is_linked_from = ManyToMany('A') 119 bs_ = ManyToMany('B') 120 121 class B(Entity): 122 using_options(shortnames=True) 123 name = Field(String(20)) 124 as_ = ManyToMany('A') 125 126 setup_all(True) 127 128 a = A(name='a1', links_to=[A(name='a2')]) 129 130 session.commit() 131 session.clear() 132 133 del A 134 del B 135 136 # do not drop the tables, that's the whole point! 137 cleanup_all() 138 139 # simulate a renaming of columns (as given by the migration aid) 140 # 'a_id1' to 'is_linked_from_id'. 141 # 'a_id2' to 'links_to_id'. 142 conn = metadata.bind.connect() 143 conn.execute("ALTER TABLE a_links_to__a_is_linked_from RENAME TO temp") 144 conn.execute("CREATE TABLE a_links_to__a_is_linked_from (" 145 "is_linked_from_id INTEGER NOT NULL, " 146 "links_to_id INTEGER NOT NULL, " 147 "PRIMARY KEY (is_linked_from_id, links_to_id), " 148 "CONSTRAINT a_fk1 FOREIGN KEY(is_linked_from_id) " 149 "REFERENCES a (id), " 150 "CONSTRAINT a_fk2 FOREIGN KEY(links_to_id) " 151 "REFERENCES a (id))") 152 conn.execute("INSERT INTO a_links_to__a_is_linked_from " 153 "(is_linked_from_id, links_to_id) " 154 "SELECT a_id1, a_id2 FROM temp") 155 conn.close() 156 157 # ... 158 elixir.options.M2MCOL_NAMEFORMAT = elixir.options.NEW_M2MCOL_NAMEFORMAT 106 159 # elixir.options.MIGRATION_TO_07_AID = True 107 # metadata.bind = 'sqlite://' 108 # elixir.options.M2MCOL_NAMEFORMAT = elixir.options.NEW_M2MCOL_NAMEFORMAT # assert False 160 161 class A(Entity): 162 using_options(shortnames=True) 163 name = Field(String(20)) 164 links_to = ManyToMany('A') 165 is_linked_from = ManyToMany('A') 166 bs_ = ManyToMany('B') 167 168 class B(Entity): 169 using_options(shortnames=True) 170 name = Field(String(20)) 171 as_ = ManyToMany('A') 172 173 setup_all() 174 175 a1 = A.get_by(name='a1') 176 assert len(a1.links_to) == 1 177 assert not a1.is_linked_from 178 179 a2 = a1.links_to[0] 180 assert a2.name == 'a2' 181 assert not a2.links_to 182 assert a2.is_linked_from == [a1] 109 183 110 184 def test_manual_column_format(self):
