Changeset 548
- Timestamp:
- 08/16/07 21:49:57 (1 year ago)
- Files:
-
- pagoda/trunk/Pagoda/pagoda/models/revision_mapper.py (modified) (7 diffs)
- pagoda/trunk/Pagoda/pagoda/plugins/page/models.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
pagoda/trunk/Pagoda/pagoda/models/revision_mapper.py
r547 r548 9 9 from pagoda.models.template import Template 10 10 11 __all__ = ['RevisionSelectable', 'RevisionMapperExtension', 12 'revision_mapper'] 13 14 class ContentModifiedWarning(UserWarning): 15 """ 16 Warn about content updates when a new revision may have been intended. 17 18 """ 19 pass 20 21 class KeyModifiedError(ValueError): 22 """Error indicating that a key column was changed in an update.""" 23 pass 24 25 class RevisionSelectable(object): 26 def __init__(self, revision_table=revision_table, 27 content_table=content_table): 11 __all__ = ['RevisionMapperBuilder', 'RevisionMapperExtension'] 12 13 class RevisionMapperBuilder(object): 14 def __init__(self, class_, revision_table=revision_table, 15 content_table=content_table, select_alias=None): 16 self.class_ = class_ 17 self.select_alias = select_alias or class_.pagoda_content_type 28 18 self.revision_table = revision_table 29 19 self.content_table = content_table … … 81 71 if id_label is None: 82 72 id_label = '%s_revision_id' % table.name 73 74 83 75 84 76 if isinstance(locale_column, basestring): … … 91 83 self.subselects[table] = subselect 92 84 93 def get_selectable(self , alias):85 def get_selectable(self): 94 86 # XXX: Danger! Alert! Caution! Hey! 95 87 # … … 149 141 ] 150 142 ) 151 return selectable.alias(alias) 143 return selectable.alias(self.select_alias) 144 145 def get_options(self): 146 selectable = self.get_selectable() 147 inherits = self.class_.__base__.mapper 148 for table, id_label in self.id_labels.iteritems(): 149 if table is inherits.local_table: 150 inherits_id_column = selectable.c[id_label] 151 return dict( 152 table=selectable, 153 select_table=selectable, 154 inherits=inherits, 155 inherit_condition=inherits.c.revision_id == inherits_id_column, 156 extension=RevisionMapperExtension(), 157 primary_key=selectable.primary_key, 158 polymorphic_on=selectable.c.content_type, 159 polymorphic_identity=self.class_.pagoda_content_type 160 ) 152 161 153 162 class RevisionMapperExtension(MapperExtension): … … 156 165 mapped against a `Select` generated by `revisioned_table`. 157 166 158 """ 159 def get(self, query, *args, **kwargs): 160 """ 161 Retrieve the revision uniquely identified by the sequence 162 (revision_id, content_id, content_locale). 163 164 """ 165 revision_id, content_id, content_locale = args[0] 166 return query.get_by( 167 revision_id=revision_id, 168 content_id=content_id, 169 content_locale=content_locale 170 ) 171 167 """ 172 168 def before_insert(self, mapper, connection, instance): 173 169 """ 174 Create new revision records from `instance`. If `instance.content_id` 175 is None, also create a new record in `content_table`. 176 177 Raise a `ValueError` if a `revision_id` on `instance` is given for any 178 table, since each revisioned table will share a new record in 179 `revision_table`. 180 181 """ 182 if instance.content_type is None: 183 instance.content_type = mapper.local_table.name 184 170 Create new revision records from `instance`, overriding 171 SQLAlchemy's default behavior. 172 173 This differs slightly from SQLAlchemy's normal operation as 174 follows: 175 176 Normally when you create an instance of a mapped class 177 while specifying a value for a primary key, SQLAlchemy will try to 178 create a new record in the appropriate table with the specified 179 primary key. 180 181 This extension doesn't do that. Instead, if a primary key is 182 specified, no record will be inserted into the applicable table. 183 Using this extension, any primary keys on `instance` that are set to 184 None will cause inserts to be done on the corresponding tables. 185 186 """ 187 188 # Determine tables that will get new records, according to their 189 # primary key. If they have an explicit primary key, do not insert 190 # because we want to use the existing record with that key. 185 191 insert_tables = util.OrderedSet() 186 187 192 for col in mapper.primary_key: 188 193 if getattr(instance, col.name) is None: … … 190 195 insert_tables.add(insert_table) 191 196 197 # Insert into tables that need new records, and update `instance` 198 # with the inserted values. 192 199 for insert_table in insert_tables: 200 # Get `instance` values for columns in `insert_table`. 193 201 values = get_instance_values(instance, columns=insert_table.c) 202 # Insert! 194 203 insert_result = insert_table.insert(values).execute() 204 # Get inserted values except for the primary key. 195 205 inserted_params = insert_result.last_inserted_params() 196 param_values = inserted_params.get_original_dict() 206 inserted_values = inserted_params.get_original_dict() 207 # Update with primary key values. 197 208 primary_keys = list(insert_table.primary_key) 198 209 inserted_ids = insert_result.last_inserted_ids() 199 id_values = dict(zip(primary_keys, inserted_ids)) 200 201 for column_name, value in param_values.iteritems(): 210 inserted_values.update(dict(zip(primary_keys, inserted_ids))) 211 212 # Update `instance` with inserted values. 213 for column_name, value in inserted_values.iteritems(): 202 214 if mapper.c.has_key(column_name): 203 215 setattr(instance, column_name, value) 204 205 for column, value in id_values.iteritems():206 if mapper.c.has_key(column.name):207 setattr(instance, column.name, value)208 216 209 217 # Set the labeled `revision_id` foreign keys on `instance`. … … 220 228 # so that calling `flush` on it will do nothing. 221 229 session._register_persistent(instance) 222 223 def revision_mapper(ctx, class_, *args, **kwargs):224 """225 A helper that passes all arguments to `assign_mapper`, but adds a226 `RevisionableMapperExtension` instance to the mapper's `extension` list if227 one does not exist, and copies docstrings from the mapper extension.228 229 """230 # Get the extension(s) specified for this mapper.231 extensions = util.to_list(kwargs.get('extension', []))232 # Add the `RevisionableMapperExtension` unless it was explicitly233 # added by the caller.234 for extension in extensions:235 if isinstance(ext, RevisionMapperExtension):236 break237 else:238 extension = RevisionMapperExtension()239 extensions.append(extension)240 kwargs['extension'] = extensions241 242 # Get the properties specified for this mapper.243 properties = dict(kwargs.get('properties', {}))244 if 'template' not in properties:245 properties['template'] = relation(Template)246 if 'author' not in properties:247 properties['author'] = relation(User)248 kwargs['properties'] = properties249 250 return assign_mapper(ctx, class_, *args, **kwargs)pagoda/trunk/Pagoda/pagoda/plugins/page/models.py
r547 r548 5 5 from pagoda.models.revision_mapper import * 6 6 7 __all__ = ['page_generic_table', 'page_localized_table', ' page_table', 'Page']7 __all__ = ['page_generic_table', 'page_localized_table', 'Page'] 8 8 9 9 page_generic_table = Table('page_generic', metadata, … … 22 22 ) 23 23 24 page_selectable = RevisionSelectable() 25 page_selectable.add_table(node_table) 26 page_selectable.add_table(page_generic_table, 'generic_revision_id') 27 page_selectable.add_table( 24 class Page(Node): 25 pagoda_content_type = 'page' 26 27 revision_builder = RevisionMapperBuilder(Page) 28 revision_builder.add_table(node_table) 29 revision_builder.add_table(page_generic_table, 'generic_revision_id') 30 revision_builder.add_table( 28 31 page_localized_table, 'localized_revision_id', 'content_locale' 29 32 ) 33 mapper_options = revision_builder.get_options() 30 34 31 page_table = page_selectable.get_selectable('page') 32 33 class Page(Node): 34 pass 35 36 assign_mapper(session.context, Page, page_table, 37 inherits=Node.mapper, 38 select_table=page_table, 39 polymorphic_on=page_table.c.content_type, 40 polymorphic_identity='page', 41 inherit_condition=page_table.c.node_revision_id == Node.c.revision_id, 42 ) 35 assign_mapper(session.context, Page, **mapper_options)
