Changeset 14

Show
Ignore:
Timestamp:
02/01/07 20:56:24 (6 years ago)
Author:
ged
Message:

- updated/added comments.
- removed some unused code
- when an inverse of a relation is found, also check that it has not several

inverses. Since the inverse of a relation is not processed, it was possible
to have a relation with several inverse not raise an exception.

- first step in making autoload tables work: do not create a pk for

autoloaded tables

- save the genereated SQLAlchemy relation in the "property" variable for

many-to-many relationships (as for the other types of relations).
By the way: I wonder if this is useful.

- made m-to-m table names consistent (independant of

whether the relation or its inverse is setup first)

- m-to-m relations do not match as inverse of each other if the name of their

table is different

Location:
supermodel/trunk/supermodel
Files:
3 modified

Legend:

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

    r9 r14  
    4040            desc.setup_options() 
    4141             
    42             # create table & assign mapper 
     42            # create table & assign (empty) mapper 
    4343            desc.setup() 
    4444             
     
    7171#        setattr(self.module, entity.__name__, entity) 
    7272        self.metadata = getattr(self.module, 'metadata', supermodel.metadata) 
    73         self.initialized = False 
    7473        self.autoload = None 
    7574        self.auto_primarykey = True 
     
    101100            and assign a mapper. Will be called when an instance of the 
    102101            entity is created or a mapper is needed to access one or many 
    103             instances of the entity. 
    104         """ 
    105          
    106         if not self.primary_keys and self.auto_primarykey: 
    107             self.create_auto_primary_key() 
    108          
    109         if not self.entity.mapper: 
    110             self.setup_mapper() 
    111          
     102            instances of the entity. This *doesn't* initialize relations. 
     103        """ 
     104         
     105        self.setup_mapper() 
     106        
     107        # This marks all relations of the entity (or, at least those which  
     108        # have been added so far by statements) as being uninitialized 
    112109        EntityDescriptor.uninitialized_rels.update( 
    113110            self.relationships.values()) 
     
    115112    def setup_mapper(self): 
    116113        """ 
    117             Initializes and assign a mapper to the given entity, 
     114            Initializes and assign an (empty!) mapper to the given entity, 
    118115            which needs a table defined, so it calls setup_table. 
    119116        """ 
     
    152149            return 
    153150         
     151        if not self.autoload: 
     152            if not self.primary_keys and self.auto_primarykey: 
     153                self.create_auto_primary_key() 
     154 
    154155        # create list of columns and constraints 
    155156        args = [field.column for field in self.fields.values()] \ 
     
    198199            table.append_constraint(constraint) 
    199200         
    200     def get_inverse_relation(self, rel): 
     201    def get_inverse_relation(self, rel, reverse=False): 
    201202        """Return the inverse relation of rel, if any, None otherwise.""" 
    202203 
     
    214215                            % (rel.name, rel.entity.__name__)  
    215216                          ) 
     217        # When a matching inverse is found, we check that it has only 
     218        # one relation matching as its own inverse. We don't need the result 
     219        # of the method though. But we do need to be careful not to start an 
     220        # infinite recursive loop. 
     221        if matching_rel and not reverse: 
     222            rel.entity._descriptor.get_inverse_relation(matching_rel, True) 
     223             
    216224        return matching_rel 
     225 
    217226 
    218227    @classmethod 
  • supermodel/trunk/supermodel/options.py

    r4 r14  
    2525            tablename:          Specify a custom tablename 
    2626            shortnames:         Usually tablenames include the full 
    27                                 module-path to the entity, but separated 
    28                                 by underscores ("_"), eg.: 
    29                                 "project1_model_MyEntity", for an entity named 
     27                                module-path to the entity, but lower-cased and 
     28                                separated by underscores ("_"), eg.: 
     29                                "project1_model_myentity", for an entity named 
    3030                                "MyEntity" in the module "project1.model". 
    31                                 If shortnames is true, the tablename will just 
    32                                 be the entity's classname, ie. "MyEntity". 
     31                                If shortnames is True, the tablename will just 
     32                                be the entity's classname lower-cased,  
     33                                ie. "myentity". 
    3334            auto_primarykey:    If given as string, it will represent the 
    3435                                auto-primary-key's column name 
  • supermodel/trunk/supermodel/relationships.py

    r9 r14  
    2121        self._target = None 
    2222         
    23         self.initialized = False 
    24         self.secondary = None 
    2523        self._inverse = None 
    26         self.foreign_key = None 
    27          
    2824        self.foreign_key = kwargs.pop('foreign_key', None) 
    2925        if self.foreign_key and not isinstance(self.foreign_key, list): 
    3026            self.foreign_key = [self.foreign_key] 
    3127         
     28        #CHECKME: is it of any use to store it somewhere? 
    3229        self.property = None # sqlalchemy property 
    3330         
     
    236233 
    237234class HasAndBelongsToMany(Relationship): 
    238      
    239235    def __init__(self, entity, name, *args, **kwargs): 
    240         self.tablename = kwargs.pop('tablename', None) 
     236        self.user_tablename = kwargs.pop('tablename', None) 
     237        self.secondary = None 
    241238        super(HasAndBelongsToMany, self).__init__(entity, name, *args, **kwargs) 
    242239     
     
    294291                                         name=desc.tablename + '_fk',  
    295292                                         use_alter=True)) 
    296          
    297             # In the table name code below, we use the name of the relation 
    298             # for the first entity (instead of the name of its primary key),  
    299             # so that we can have two many-to-many relations between the same 
    300             # objects without having a table name collision. On the other hand, 
    301             # we use the name of the primary key for the second entity  
    302             # (instead of the inverse relation's name) so that a many-to-many 
    303             # relation can be defined without inverse. 
    304             if not self.tablename: 
    305                 e2_pk_name = '_'.join([key.column.name for key in 
    306                                        e2_desc.primary_keys]) 
    307                 tablename = "%s_%s__%s_%s" % (e1_desc.tablename, self.name, 
    308                                               e2_desc.tablename, e2_pk_name) 
     293 
     294            if self.user_tablename: 
     295                tablename = self.user_tablename 
    309296            else: 
    310                 tablename = self.tablename 
     297                # We use the name of the relation for the first entity  
     298                # (instead of the name of its primary key), so that we can  
     299                # have two many-to-many relations between the same objects  
     300                # without having a table name collision.  
     301                source_part = "%s_%s" % (e1_desc.tablename, self.name) 
     302 
     303                # And we use the name of the primary key for the second entity 
     304                # when there is no inverse, so that a many-to-many relation  
     305                # can be defined without an inverse. 
     306                if self.inverse: 
     307                    e2_name = self.inverse.name 
     308                else: 
     309                    e2_name = '_'.join([key.column.name for key in 
     310                                        e2_desc.primary_keys]) 
     311                target_part = "%s_%s" % (e2_desc.tablename, e2_name) 
     312 
     313                # we need to keep the table name consistent (independant of  
     314                # whether this relation or its inverse is setup first) 
     315                if self.inverse and e1_desc.tablename < e2_desc.tablename: 
     316                    tablename = "%s__%s" % (target_part, source_part) 
     317                else: 
     318                    tablename = "%s__%s" % (source_part, target_part) 
    311319 
    312320            args = columns + constraints 
     
    320328            kwargs['secondaryjoin'] = and_(*self.secondaryjoin_clauses) 
    321329 
    322         m = self.entity.mapper 
    323         #FIXME: using post_update systematically is *really* not good 
    324         m.add_property(self.name, 
    325                        relation(self.target, secondary=self.secondary, 
    326                                 uselist=True, **kwargs)) 
    327  
     330        self.property = relation(self.target, secondary=self.secondary, 
     331                                 uselist=True, **kwargs) 
     332        self.entity.mapper.add_property(self.name, self.property) 
     333 
     334    def is_inverse(self, other): 
     335        return super(HasAndBelongsToMany, self).is_inverse(other) and \ 
     336               (self.user_tablename == other.user_tablename or  
     337                (not self.user_tablename and not other.user_tablename)) 
    328338 
    329339belongs_to = Statement(BelongsTo)