Changeset 548

Show
Ignore:
Timestamp:
08/16/07 21:49:57 (1 year ago)
Author:
brian
Message:

Miracle of Models, Node and Content are now separable, RevisionMapper? now using polymorphism

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • pagoda/trunk/Pagoda/pagoda/models/revision_mapper.py

    r547 r548  
    99from pagoda.models.template import Template 
    1010 
    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 
     13class 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 
    2818        self.revision_table = revision_table 
    2919        self.content_table = content_table 
     
    8171        if id_label is None: 
    8272            id_label = '%s_revision_id' % table.name 
     73         
     74         
    8375 
    8476        if isinstance(locale_column, basestring): 
     
    9183        self.subselects[table] = subselect 
    9284     
    93     def get_selectable(self, alias): 
     85    def get_selectable(self): 
    9486        # XXX: Danger! Alert! Caution! Hey! 
    9587        # 
     
    149141            ] 
    150142        ) 
    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        ) 
    152161 
    153162class RevisionMapperExtension(MapperExtension): 
     
    156165    mapped against a `Select` generated by `revisioned_table`. 
    157166     
    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    """     
    172168    def before_insert(self, mapper, connection, instance): 
    173169        """ 
    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. 
    185191        insert_tables = util.OrderedSet() 
    186          
    187192        for col in mapper.primary_key: 
    188193            if getattr(instance, col.name) is None: 
     
    190195                insert_tables.add(insert_table) 
    191196         
     197        # Insert into tables that need new records, and update `instance` 
     198        # with the inserted values. 
    192199        for insert_table in insert_tables: 
     200            # Get `instance` values for columns in `insert_table`. 
    193201            values = get_instance_values(instance, columns=insert_table.c) 
     202            # Insert! 
    194203            insert_result = insert_table.insert(values).execute() 
     204            # Get inserted values except for the primary key. 
    195205            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. 
    197208            primary_keys = list(insert_table.primary_key) 
    198209            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(): 
    202214                if mapper.c.has_key(column_name): 
    203215                    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) 
    208216         
    209217        # Set the labeled `revision_id` foreign keys on `instance`. 
     
    220228        # so that calling `flush` on it will do nothing. 
    221229        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 a 
    226     `RevisionableMapperExtension` instance to the mapper's `extension` list if 
    227     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 explicitly 
    233     # added by the caller. 
    234     for extension in extensions: 
    235         if isinstance(ext, RevisionMapperExtension): 
    236             break 
    237     else: 
    238         extension = RevisionMapperExtension() 
    239         extensions.append(extension) 
    240     kwargs['extension'] = extensions 
    241      
    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'] = properties 
    249      
    250     return assign_mapper(ctx, class_, *args, **kwargs) 
  • pagoda/trunk/Pagoda/pagoda/plugins/page/models.py

    r547 r548  
    55from pagoda.models.revision_mapper import * 
    66 
    7 __all__ = ['page_generic_table', 'page_localized_table', 'page_table', 'Page'] 
     7__all__ = ['page_generic_table', 'page_localized_table', 'Page'] 
    88 
    99page_generic_table = Table('page_generic', metadata, 
     
    2222) 
    2323 
    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( 
     24class Page(Node): 
     25    pagoda_content_type = 'page' 
     26 
     27revision_builder = RevisionMapperBuilder(Page) 
     28revision_builder.add_table(node_table) 
     29revision_builder.add_table(page_generic_table, 'generic_revision_id') 
     30revision_builder.add_table( 
    2831    page_localized_table, 'localized_revision_id', 'content_locale' 
    2932) 
     33mapper_options = revision_builder.get_options() 
    3034 
    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 
     35assign_mapper(session.context, Page, **mapper_options) 

Log in as guest/pagoda to create tickets