Changeset 428 for elixir/trunk/elixir

Show
Ignore:
Timestamp:
12/08/08 17:10:58 (3 years ago)
Author:
ged
Message:

implemented ManyToOne relationships to plain SA-mapped classes. OneToMany and ManyToMany relationships don't work yet

Location:
elixir/trunk/elixir
Files:
2 modified

Legend:

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

    r425 r428  
    99import inspect 
    1010import types 
     11import warnings 
    1112 
    1213from copy import copy 
    1314 
    1415import sqlalchemy 
    15 from sqlalchemy     import Table, Column, Integer, desc, ForeignKey, and_, \ 
    16                            ForeignKeyConstraint 
     16from sqlalchemy import Table, Column, Integer, desc, ForeignKey, and_, \ 
     17                       ForeignKeyConstraint 
    1718from sqlalchemy.orm import MapperExtension, mapper, object_session, \ 
    1819                           EXT_CONTINUE, polymorphic_union, ScopedSession, \ 
    1920                           ColumnProperty 
     21from sqlalchemy.sql import ColumnCollection 
    2022 
    2123import elixir 
     
    6769 
    6870        # columns and constraints waiting for a table to exist 
    69         self._columns = [] 
     71        self._columns = ColumnCollection() 
    7072        self.constraints = [] 
    7173 
     
    270272                self.add_column(Column(self.version_id_col, Integer)) 
    271273 
    272             args = self.columns + self.constraints + self.table_args 
     274            args = list(self.columns) + self.constraints + self.table_args 
    273275        self.entity.table = Table(self.tablename, self.metadata, 
    274276                                  *args, **kwargs) 
     
    473475            check_duplicate = not self.allowcoloverride 
    474476 
    475         if check_duplicate and self.get_column(col.key, False) is not None: 
    476             raise Exception("Column '%s' already exist in '%s' ! " % 
    477                             (col.key, self.entity.__name__)) 
    478         self._columns.append(col) 
     477        if col.key in self._columns: 
     478            if check_duplicate: 
     479                raise Exception("Column '%s' already exist in '%s' ! " % 
     480                                (col.key, self.entity.__name__)) 
     481            else: 
     482                del self._columns[col.key] 
     483        self._columns.add(col) 
    479484 
    480485        if col.primary_key: 
     
    510515#FIXME: something like this is needed to propagate the relationships from 
    511516# parent entities to their children in a concrete inheritance scenario. But 
    512 # this doesn't work because of the backref matching code. 
     517# this doesn't work because of the backref matching code. In most case 
     518# (test_concrete.py) it doesn't even happen at all. 
    513519#        if self.children and self.inheritance == 'concrete': 
    514520#            for child in self.children: 
     
    531537        #TODO: this needs to work whether the table is already setup or not 
    532538        #TODO: support SA table/autoloaded entity 
    533         for col in self.columns: 
    534             if col.key == key: 
    535                 return col 
    536         if check_missing: 
    537             raise Exception("No column named '%s' found in the table of the " 
    538                             "'%s' entity!" % (key, self.entity.__name__)) 
    539         return None 
     539        try: 
     540            return self.columns[key] 
     541        except KeyError: 
     542            if check_missing: 
     543                raise Exception("No column named '%s' found in the table of " 
     544                                "the '%s' entity!" 
     545                                % (key, self.entity.__name__)) 
    540546 
    541547    def get_inverse_relation(self, rel, check_reverse=True): 
     
    617623    primary_keys = property(primary_keys) 
    618624 
     625    def table(self): 
     626        if self.entity.table: 
     627            return self.entity.table 
     628        else: 
     629            return FakeTable(self) 
     630    table = property(table) 
     631 
    619632    def primary_key_properties(self): 
    620633        """ 
     
    637650        return self._pk_props 
    638651    primary_key_properties = property(primary_key_properties) 
     652 
     653class FakePK(object): 
     654    def __init__(self, descriptor): 
     655        self.descriptor = descriptor 
     656 
     657    def columns(self): 
     658        return self.descriptor.primary_keys 
     659    columns = property(columns) 
     660 
     661class FakeTable(object): 
     662    def __init__(self, descriptor): 
     663        self.descriptor = descriptor 
     664        self.primary_key = FakePK(descriptor) 
     665 
     666    def columns(self): 
     667        return self.descriptor.columns 
     668    columns = property(columns) 
     669 
     670    def fullname(self): 
     671        ''' 
     672        Complete name of the table for the related entity. 
     673        Includes the schema name if there is one specified. 
     674        ''' 
     675        schema = self.descriptor.table_options.get('schema', None) 
     676        if schema is not None: 
     677            return "%s.%s" % (schema, self.descriptor.tablename) 
     678        else: 
     679            return self.descriptor.tablename 
     680    fullname = property(fullname) 
     681 
    639682 
    640683class TriggerProxy(object): 
     
    900943        desc._pk_col_done = False 
    901944        desc.has_pk = False 
    902         desc._columns = [] 
     945        desc._columns = ColumnCollection() 
    903946        desc.constraints = [] 
    904947        desc.properties = {} 
  • elixir/trunk/elixir/relationships.py

    r427 r428  
    399399 
    400400from sqlalchemy import ForeignKeyConstraint, Column, Table, and_ 
    401 from sqlalchemy.orm import relation, backref 
     401from sqlalchemy.orm import relation, backref, class_mapper 
    402402from sqlalchemy.ext.associationproxy import association_proxy 
    403403 
     
    484484    def target(self): 
    485485        if not self._target: 
    486             if isinstance(self.of_kind, EntityMeta): 
    487                 self._target = self.of_kind 
    488             else: 
     486            if isinstance(self.of_kind, basestring): 
    489487                collection = self.entity._descriptor.collection 
    490488                self._target = collection.resolve(self.of_kind, self.entity) 
     489            else: 
     490                self._target = self.of_kind 
    491491        return self._target 
    492492    target = property(target) 
     
    510510            else: 
    511511                check_reverse = not self.kwargs.get('viewonly', False) 
    512                 inverse = self.target._descriptor.get_inverse_relation(self, 
    513                             check_reverse=check_reverse) 
    514  
     512                if isinstance(self.target, EntityMeta): 
     513                    inverse = self.target._descriptor.get_inverse_relation( 
     514                        self, check_reverse=check_reverse) 
     515                else: 
     516                    inverse = None 
    515517            self._inverse = inverse 
    516518            if inverse and not self.kwargs.get('viewonly', False): 
     
    599601        return isinstance(other, (OneToMany, OneToOne)) 
    600602 
     603    def target_table(self): 
     604        if isinstance(self.target, EntityMeta): 
     605            return self.target._descriptor.table 
     606        else: 
     607            return class_mapper(self.target).local_table 
     608    target_table = property(target_table) 
     609 
    601610    def create_keys(self, pk): 
    602611        ''' 
     
    615624        # for that, I need: 
    616625        # - the list of primary key columns of the target table (type and name) 
     626        # - a way to get to a column from its name 
    617627        # - the name of the target table 
    618         target_desc = self.target._descriptor 
    619         #make sure the target has all its pk set up 
    620         target_desc.create_pk_cols() 
     628        #XXX: use a fake table object on pure Elixir case and always introspect 
     629        #the table as if it was pure SA??? 
     630        if isinstance(self.target, EntityMeta): 
     631            # make sure the target has all its pk set up 
     632            self.target._descriptor.create_pk_cols() 
     633 
     634        target_table = self.target_table 
    621635 
    622636        if source_desc.autoload: 
     
    629643                        _get_join_clauses(self.entity.table, 
    630644                                          self.colname, None, 
    631                                           self.target.table)[0] 
     645                                          target_table)[0] 
    632646                    if not self.primaryjoin_clauses: 
    633647                        colnames = ', '.join(self.colname) 
     
    645659 
    646660            if self.target_column is None: 
    647                 target_columns = target_desc.primary_keys 
     661                target_columns = target_table.primary_key.columns 
    648662            else: 
    649                 target_columns = [target_desc.get_column(col) 
     663                target_columns = [target_table.columns[col] 
    650664                                  for col in self.target_column] 
    651665 
     
    653667                raise Exception("No primary key found in target table ('%s') " 
    654668                                "for the '%s' relationship of the '%s' entity." 
    655                                 % (self.target.tablename, self.name, 
     669                                % (target_table.name, self.name, 
    656670                                   self.entity.__name__)) 
    657671            if self.colname and \ 
     
    687701                    if col.key == self.name: 
    688702                        raise ValueError( 
    689                                  "ManyToOne named '%s' in '%s' conficts " \ 
    690                                  " with the column of the same name. " \ 
    691                                  "You should probably define the foreign key "\ 
    692                                  "field manually and use the 'field' "\ 
    693                                  "argument on the ManyToOne relationship" \ 
     703                                 "ManyToOne named '%s' in '%s' conficts " 
     704                                 " with the column of the same name. " 
     705                                 "You should probably define the foreign key " 
     706                                 "field manually and use the 'field' " 
     707                                 "argument on the ManyToOne relationship" 
    694708                                 % (self.name, self.entity.__name__)) 
    695709 
     
    704718                # point to 
    705719                fk_refcols.append("%s.%s" % \ 
    706                                   (target_desc.table_fullname, target_col.key)) 
     720                                  (target_table.fullname, target_col.key)) 
    707721 
    708722                # Build up the primary join. This is needed when you have 
    709                 # several belongs_to relationships between two objects 
     723                # several ManyToOne relationships between two objects 
    710724                self.primaryjoin_clauses.append(col == target_col) 
    711725 
     
    725739        kwargs = {'uselist': False} 
    726740 
    727         if self.entity.table is self.target.table: 
     741        if self.entity.table is self.target_table: 
    728742            # this is needed because otherwise SA has no way to know what is 
    729743            # the direction of the relationship since both columns present in 
     
    733747            # doesn't help in this case. 
    734748            kwargs['remote_side'] = \ 
    735                 [col for col in self.target.table.primary_key.columns] 
     749                [col for col in self.target_table.primary_key.columns] 
    736750 
    737751        if self.primaryjoin_clauses: 
     
    882896                return 
    883897 
     898        #needs: table_options['schema'], autoload, tablename, primary_keys, 
     899        #entity.__name__, table_fullname 
    884900        e1_desc = self.entity._descriptor 
    885901        e2_desc = self.target._descriptor