Changeset 321

Show
Ignore:
Timestamp:
04/22/08 13:33:34 (5 years ago)
Author:
ged
Message:

Fixed several problems with concrete inheritance and relationships.
Non-polymorphic concrete inheritance should be operational now.

Location:
elixir/trunk
Files:
4 modified

Legend:

Unmodified
Added
Removed
  • elixir/trunk/elixir/entity.py

    r318 r321  
    1111import sqlalchemy 
    1212from sqlalchemy                    import Table, Column, Integer, \ 
    13                                           desc, ForeignKey, and_ 
     13                                          desc, ForeignKey, and_, \ 
     14                                          ForeignKeyConstraint 
    1415from sqlalchemy.orm                import Query, MapperExtension,\ 
    1516                                          mapper, object_session, EXT_PASS 
     
    194195            if self.parent: 
    195196                if self.inheritance == 'multi': 
    196                     # add columns with foreign keys to the parent's primary  
     197                    # Add columns with foreign keys to the parent's primary  
    197198                    # key columns  
    198199                    parent_desc = self.parent._descriptor 
     
    206207                                   'key': pk_col.key} 
    207208 
    208                         # it seems like SA ForeignKey is not happy being given 
     209                        # It seems like SA ForeignKey is not happy being given 
    209210                        # a real column object when said column is not yet  
    210211                        # attached to a table 
     
    214215                                     primary_key=True) 
    215216                        self.add_column(col) 
     217                elif self.inheritance == 'concrete': 
     218                    # Copy primary key columns from the parent. 
     219                    for col in self.parent._descriptor.columns: 
     220                        if col.primary_key: 
     221                            self.add_column(col.copy()) 
    216222            elif not self.has_pk and self.auto_primarykey: 
    217223                if isinstance(self.auto_primarykey, basestring): 
     
    219225                else: 
    220226                    colname = options.DEFAULT_AUTO_PRIMARYKEY_NAME 
    221                  
     227 
    222228                self.add_column( 
    223229                    Column(colname, options.DEFAULT_AUTO_PRIMARYKEY_TYPE,  
     
    259265                # parent is a base entity (whatever the inheritance type -> elif 
    260266                # will need to be changed) 
    261                 # copy all columns from parent table 
     267 
     268                # Copy all non-primary key columns from parent table (primary  
     269                # key columns have already been copied earlier). 
    262270                for col in self.parent._descriptor.columns: 
    263                     self.add_column(col.copy()) 
    264                 #FIXME: copy constraints. But those are not as simple to copy 
    265                 #since the source column must be changed 
     271                    if not col.primary_key: 
     272                        self.add_column(col.copy()) 
     273 
     274                #FIXME: use the public equivalent of _get_colspec when available  
     275                for con in self.parent._descriptor.constraints: 
     276                    self.add_constraint( 
     277                        ForeignKeyConstraint( 
     278                            [c.key for c in con.columns], 
     279                            [e._get_colspec() for e in con.elements], 
     280                            name=con.name, #TODO: modify it 
     281                            onupdate=con.onupdate, ondelete=con.ondelete, 
     282                            use_alter=con.use_alter)) 
    266283 
    267284        if self.polymorphic and self.inheritance in ('single', 'multi') and \ 
     
    455472        # Autosetup triggers shouldn't be active anymore at this point, so we 
    456473        # can theoretically access the entity's table safely. But the problem  
    457         # is that if, for some reason, the "trigger" removal phase didn't  
     474        # is that if, for some reason, the trigger removal phase didn't  
    458475        # happen, we'll get an infinite loop. So we just make sure we don't  
    459476        # get one in any case. 
     
    477494                            (name, self.entity.__name__)) 
    478495        self.properties[name] = property 
     496 
    479497        mapper = self.entity.mapper 
    480498        if mapper: 
     
    548566 
    549567    def primary_keys(self): 
     568        """ 
     569        Returns the list of primary key columns of the entity. 
     570 
     571        This property isn't valid before the "create_pk_cols" phase. 
     572        """ 
    550573        if self.autoload: 
    551574            return [col for col in self.entity.table.primary_key.columns] 
     
    559582 
    560583class TriggerProxy(object): 
    561     """A class that serves as a "trigger" ; accessing its attributes runs 
     584    """ 
     585    A class that serves as a "trigger" ; accessing its attributes runs 
    562586    the setup_all function. 
    563587 
  • elixir/trunk/elixir/options.py

    r299 r321  
    2828+=====================+=======================================================+ 
    2929| ``inheritance``     | Specify the type of inheritance this entity must use. | 
    30 |                     | It can be one of ``single`` or ``multi``. Defaults to | 
    31 |                     | ``single``. Concrete inheritance is currently not in  | 
    32 |                     | a usable state.                                       | 
     30|                     | It can be one of ``single``, ``concrete`` or          | 
     31|                     | ``multi``. Defaults to ``single``.                    | 
     32|                     | Note that polymorphic concrete inheritance is         | 
     33|                     | currently not implemented.                            | 
    3334+---------------------+-------------------------------------------------------+ 
    3435| ``polymorphic``     | Whether the inheritance should be polymorphic or not. | 
  • elixir/trunk/elixir/relationships.py

    r319 r321  
    554554                           self.target.__name__)) 
    555555 
    556             for key_num, pk_col in enumerate(target_desc.primary_keys): 
     556            pks = target_desc.primary_keys 
     557            if not pks: 
     558                raise Exception("No primary key found in target table ('%s') " 
     559                                "for the '%s' relationship of the '%s' entity." 
     560                                % (self.target.tablename, self.name,  
     561                                   self.entity.__name__)) 
     562 
     563            for key_num, pk_col in enumerate(pks): 
    557564                if self.colname: 
    558565                    colname = self.colname[key_num] 
     
    562569                               'key': pk_col.key} 
    563570 
    564                 # we can't add the column to the table directly as the table 
     571                # We can't add the column to the table directly as the table 
    565572                # might not be created yet. 
    566573                col = Column(colname, pk_col.type, **self.column_kwargs) 
    567574                source_desc.add_column(col) 
    568575 
    569                 # build the list of local columns which will be part of 
     576                # Build the list of local columns which will be part of 
    570577                # the foreign key 
    571578                self.foreign_key.append(col) 
    572579 
    573                 # store the names of those columns 
     580                # Store the names of those columns 
    574581                fk_colnames.append(col.key) 
    575582 
    576                 # build the list of column "paths" the foreign key will  
     583                # Build the list of column "paths" the foreign key will  
    577584                # point to 
    578585                target_path = "%s.%s" % (target_desc.tablename, pk_col.key) 
     
    582589                fk_refcols.append(target_path) 
    583590 
    584                 # build up the primary join. This is needed when you have  
     591                # Build up the primary join. This is needed when you have  
    585592                # several belongs_to relationships between two objects 
    586593                self.primaryjoin_clauses.append(col == pk_col) 
  • elixir/trunk/tests/test_inherit.py

    r318 r321  
    1616    elixir.options_defaults['shortnames'] = False 
    1717 
    18 def do_tst(inheritance, polymorphic, with_rel, expected_res): 
     18def do_tst(inheritance, polymorphic, expected_res): 
    1919    class A(Entity): 
     20        using_options(inheritance=inheritance, polymorphic=polymorphic) 
    2021        data1 = Field(String(20)) 
    21         using_options(inheritance=inheritance, polymorphic=polymorphic) 
    2222 
    2323    class B(A): 
     24        using_options(inheritance=inheritance, polymorphic=polymorphic) 
    2425        data2 = Field(String(20)) 
    25         if with_rel: 
    26             many_c = OneToMany('C', inverse='some_b')         
    27         using_options(inheritance=inheritance, polymorphic=polymorphic) 
     26        some_e = ManyToOne('E') 
    2827 
    2928    class C(B): 
     29        using_options(inheritance=inheritance, polymorphic=polymorphic) 
    3030        data3 = Field(String(20)) 
    31         if with_rel: 
    32             some_b = ManyToOne('B', inverse='many_c') 
    33         using_options(inheritance=inheritance, polymorphic=polymorphic) 
    3431 
    3532    class D(A): 
     33        using_options(inheritance=inheritance, polymorphic=polymorphic) 
    3634        data4 = Field(String(20)) 
    37         using_options(inheritance=inheritance, polymorphic=polymorphic) 
    3835 
    3936    class E(A): 
    4037        using_options(inheritance=inheritance, polymorphic=polymorphic) 
     38        many_b = OneToMany('B') 
    4139 
    4240    setup_all(True) 
     
    148146 
    149147    def test_singletable_inheritance(self): 
    150         do_tst('single', False, True, { 
     148        do_tst('single', False, { 
    151149            'A': ('A', 'A', 'A', 'A', 'A'), 
    152150            'B': ('B', 'B', 'B', 'B', 'B'), 
     
    157155 
    158156    def test_polymorphic_singletable_inheritance(self): 
    159         do_tst('single', True, True, { 
     157        do_tst('single', True, { 
    160158            'A': ('A', 'B', 'C', 'D', 'E'), 
    161159            'B': ('B', 'C'), 
     
    166164 
    167165    def test_concrete_inheritance(self): 
    168         # concrete fails when there are relationships involved ! 
    169         do_tst('concrete', False, False, { 
     166        do_tst('concrete', False, { 
    170167            'A': ('A',), 
    171168            'B': ('B',), 
     
    176173 
    177174    def test_multitable_inheritance(self): 
    178         do_tst('multi', False, True, { 
     175        do_tst('multi', False, { 
    179176            'A': ('A', 'A', 'A', 'A', 'A'), 
    180177            'B': ('B', 'B'), 
     
    185182  
    186183    def test_polymorphic_multitable_inheritance(self): 
    187         do_tst('multi', True, True, { 
     184        do_tst('multi', True, { 
    188185            'A': ('A', 'B', 'C', 'D', 'E'), 
    189186            'B': ('B', 'C'), 
     
    193190        }) 
    194191 
    195