Changeset 144

Show
Ignore:
Timestamp:
07/05/07 10:48:39 (6 years ago)
Author:
ged
Message:

- Changed the order of relationship kwargs processing so that computed kwargs

can be overridden by kwargs manually passed to the statement. This should
only be used if you know what you are doing.

- Made inverse relationships use backrefs. This fixes the "bidirectional

coherency" problem some people had before doing a flush. (based on a patch
from Remi Jolin). Added corresponding test.

Location:
elixir/trunk
Files:
3 modified

Legend:

Unmodified
Added
Removed
  • elixir/trunk/CHANGES

    r143 r144  
    1515  the method level and module level) anymore. Uses nosetest's module level  
    1616  fixture. 
    17 - Fixed some buggy tests. 
     17- Changed the order of relationship kwargs processing so that computed kwargs 
     18  can be overridden by kwargs manually passed to the statement. This should 
     19  only be used if you know what you are doing. 
    1820- Applied patch from Ants Aasma to make Elixir compatible with the 0.4 branch 
    1921  of SQLAlchemy. 
     22- Fixed some buggy tests. 
    2023- Fixed relationships to tables using a schema (Patch by Neil Blakey-Milner) 
     24- Made inverse relationships use backrefs. This fixes the "bidirectional 
     25  coherency" problem some people had before doing a flush. (based on a patch  
     26  from Remi Jolin). 
    2127 
    22280.3.0 - 2007-03-27 
  • elixir/trunk/elixir/relationships.py

    r141 r144  
    192192from sqlalchemy         import ForeignKeyConstraint, Column, \ 
    193193                               Table, and_ 
    194 from sqlalchemy.orm     import relation 
     194from sqlalchemy.orm     import relation, backref 
    195195from elixir.statements  import Statement 
    196196from elixir.fields      import Field 
     
    216216         
    217217        self.property = None # sqlalchemy property 
    218          
     218        self.backref = None  # sqlalchemy backref 
     219 
    219220        #TODO: unused for now 
    220221        self.args = args 
     
    240241        properties to the involved entities. 
    241242        ''' 
     243        kwargs = {} 
     244        if self.inverse: 
     245            # check if the inverse was already processed (and this has already defined 
     246            # a backref) 
     247            if self.inverse.backref: 
     248                kwargs['backref'] = self.inverse.backref 
     249            else: 
     250                kwargs = self.get_prop_kwargs() 
     251 
     252                # SQLAlchemy doesn't like when 'secondary' is both defined on 
     253                # the relation and the backref 
     254                kwargs.pop('secondary', None) 
     255 
     256                # define backref for use by the inverse 
     257                self.backref = backref(self.name, **kwargs) 
     258                return 
     259 
     260        kwargs.update(self.get_prop_kwargs()) 
     261        self.property = relation(self.target, **kwargs) 
     262        self.entity.mapper.add_property(self.name, self.property) 
    242263     
    243264    def setup(self): 
     
    249270            return False 
    250271 
    251         if self.property: 
     272        if self.property or self.backref: 
    252273            return True 
    253274 
     
    362383        if source_desc.autoload: 
    363384            #TODO: test if this works when colname is a list 
     385 
    364386            if self.colname: 
    365387                self.primaryjoin_clauses = \ 
     
    429451                                            name=fk_name, 
    430452                                            **self.constraint_kwargs)) 
    431      
    432     def create_properties(self): 
    433         kwargs = self.kwargs 
     453 
     454    def get_prop_kwargs(self): 
     455        kwargs = {'uselist': False} 
    434456         
    435457        if self.entity.table is self.target.table: 
     
    443465            kwargs['primaryjoin'] = and_(*self.primaryjoin_clauses) 
    444466 
    445         kwargs['uselist'] = False 
    446  
    447         self.property = relation(self.target, **kwargs) 
    448         self.entity.mapper.add_property(self.name, self.property) 
     467        kwargs.update(self.kwargs) 
     468 
     469        return kwargs 
    449470 
    450471 
     
    470491        self.inverse.setup() 
    471492     
    472     def create_properties(self): 
    473         kwargs = self.kwargs 
     493    def get_prop_kwargs(self): 
     494        kwargs = {'uselist': self.uselist} 
    474495         
    475496        #TODO: for now, we don't break any test if we remove those 2 lines. 
     
    486507            kwargs['primaryjoin'] = and_(*self.inverse.primaryjoin_clauses) 
    487508 
    488         kwargs['uselist'] = self.uselist 
    489          
    490         self.property = relation(self.target, **kwargs) 
    491         self.entity.mapper.add_property(self.name, self.property) 
     509        kwargs.update(self.kwargs) 
     510 
     511        return kwargs 
    492512 
    493513 
    494514class HasMany(HasOne): 
    495515    uselist = True 
    496  
    497     def create_properties(self): 
    498         if 'order_by' in self.kwargs: 
    499             self.kwargs['order_by'] = \ 
     516     
     517    def get_prop_kwargs(self): 
     518        kwargs = super(HasMany, self).get_prop_kwargs() 
     519 
     520        if 'order_by' in kwargs: 
     521            kwargs['order_by'] = \ 
    500522                self.target._descriptor.translate_order_by( 
    501                     self.kwargs['order_by']) 
    502  
    503         super(HasMany, self).create_properties() 
     523                    kwargs['order_by']) 
     524 
     525        return kwargs 
    504526 
    505527 
     
    653675                                    self.entity.table) 
    654676 
    655     def create_properties(self): 
    656         kwargs = self.kwargs 
     677    def get_prop_kwargs(self): 
     678        kwargs = {'secondary': self.secondary_table,  
     679                  'uselist': self.uselist} 
    657680 
    658681        if self.target is self.entity: 
     
    660683            kwargs['secondaryjoin'] = and_(*self.secondaryjoin_clauses) 
    661684 
     685        kwargs.update(self.kwargs) 
     686 
    662687        if 'order_by' in kwargs: 
    663688            kwargs['order_by'] = \ 
    664689                self.target._descriptor.translate_order_by(kwargs['order_by']) 
    665690 
    666         self.property = relation(self.target, secondary=self.secondary_table, 
    667                                  uselist=self.uselist, **kwargs) 
    668         self.entity.mapper.add_property(self.name, self.property) 
     691        return kwargs 
    669692 
    670693    def is_inverse(self, other): 
     
    686709    else: 
    687710        cols2 = None 
     711 
     712    # Build a map of fk constraints pointing to the correct table. 
     713    # The map is indexed on the local col names. 
    688714    constraint_map = {} 
    689715    for constraint in local_table.constraints: 
    690716        if isinstance(constraint, ForeignKeyConstraint): 
    691             use_constraint = False 
     717 
     718            use_constraint = True 
    692719            fk_colnames = [] 
     720 
     721            # if all columns point to the correct table, we use the constraint 
    693722            for fk in constraint.elements: 
    694                 fk_colnames.append(fk.parent.name) 
    695723                if fk.references(target_table): 
    696                     use_constraint = True 
     724                    fk_colnames.append(fk.parent.name) 
     725                else: 
     726                    use_constraint = False 
    697727            if use_constraint: 
    698728                fk_colnames.sort() 
     
    704734    # know the other join is either not used (is None) or has an explicit  
    705735    # match. 
     736         
     737#TODO: rewrite this. Even with the comment, I don't even understand it myself. 
    706738    for cols, constraint in constraint_map.iteritems(): 
    707739        if cols == cols1 or (cols != cols2 and  
  • elixir/trunk/tests/test_movies.py

    r125 r144  
    6565        objectstore.clear() 
    6666     
     67    def test_backref(self): 
     68        swars = Movie(title="Star Wars", year=1977) 
     69        glucas = Director(name="George Lucas") 
     70        swars.director = glucas 
     71 
     72        # does it work before a flush? 
     73        assert swars in glucas.movies 
     74 
    6775    def test_bidirectional(self): 
    6876        brunner = Movie(title="Blade Runner", year=1982)