Changeset 47

Show
Ignore:
Timestamp:
02/12/07 15:27:25 (6 years ago)
Author:
ged
Message:

- Changed tests so that when one test fails, other unrelated ones don't.

- now options are initialized to their "global defaults" values instead
of hard coded values, so we can set a bunch of options on all entities.

- added a way to set custom column names for belongs_to relations.

- implemented a way to delay setup (to have a behaviour somewhat similar
to something non dynamic). This is to be used in conjunction with the
new "setup_all" function.

- made autoload work for belongs_to/has_one/has_many relations. The user
must provide column names in that case. It would be possible (and quite
easy, I think) to guess things based on foreign keys when there is only
one relation of the same type between two entities, but I haven't done
it yet, and don't plan to do it before the release. It doesn't work yet
for HasAndBelongsToMany relationships.

Location:
elixir/trunk
Files:
1 added
12 modified

Legend:

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

    r45 r47  
    3030           'belongs_to', 'has_one', 'has_many', 'has_and_belongs_to_many',  
    3131           'using_options', 'using_table_options', 'using_mapper_options', 
    32            'create_all', 'drop_all', 'metadata', 'objectstore'] + \ 
     32           'options_defaults', 'metadata', 'objectstore', 
     33           'create_all', 'drop_all', 'setup_all', 'cleanup_all',  
     34           'delay_setup'] + \ 
    3335          sqlalchemy.types.__all__ 
    3436 
    35 __pudge_all__ = ['create_all', 'drop_all', 'metadata', 'objectstore'] 
     37__pudge_all__ = ['create_all', 'drop_all', 'metadata', 'objectstore',  
     38                 'delay_setup', 'setup_all'] 
    3639 
    3740# connect 
     
    6265    for md in metadatas: 
    6366        md.drop_all() 
     67 
     68delayed_entities = set() 
     69delay_setup = False 
     70 
     71def setup_all(): 
     72    '''Setup the table and mapper for all entities which have been delayed. 
     73     
     74    This should be used in conjunction with setting ``delay_setup`` to ``True`` 
     75    before defining your entities. 
     76    ''' 
     77    global delay_setup 
     78    delay_setup = False 
     79    for entity in delayed_entities: 
     80        entity.setup() 
     81    delayed_entities.clear() 
     82    create_all() 
     83 
     84def cleanup_all(): 
     85    drop_all() 
     86    for md in metadatas: 
     87        md.clear() 
     88    metadatas.clear() 
     89    EntityDescriptor.uninitialized_rels.clear() 
     90 
     91    objectstore.clear() 
     92    sqlalchemy.clear_mappers() 
     93 
  • elixir/trunk/elixir/entity.py

    r46 r47  
    77from elixir.statements              import Statement 
    88from elixir.fields                  import Field 
     9from elixir.options                 import options_defaults 
    910 
    1011import sys 
     
    1819DEFAULT_AUTO_PRIMARYKEY_NAME = "id" 
    1920DEFAULT_AUTO_PRIMARYKEY_TYPE = Integer 
    20  
    21 class Entity(object): 
    22     ''' 
    23     The base class for all entities 
    24      
    25     All Elixir model objects should inherit from this class. Statements can 
    26     appear within the body of the definition of an entity to define its 
    27     fields, relationships, and other options. 
    28      
    29     Here is an example: 
    30  
    31     :: 
    32      
    33         class Person(Entity): 
    34             has_field('name', Unicode(128)) 
    35             has_field('birthdate', DateTime, default=datetime.now) 
    36      
    37     Please note, that if you don't specify any primary keys, Elixir will 
    38     automatically create one called ``id``. 
    39      
    40     For further information, please refer to the provided examples or 
    41     tutorial. 
    42     ''' 
    43      
    44     class __metaclass__(type): 
    45         def __init__(cls, name, bases, dict_): 
    46             # only process subclasses of Entity, not Entity itself 
    47             if bases[0] is object: 
    48                 return 
    49              
    50             # create the entity descriptor 
    51             desc = cls._descriptor = EntityDescriptor(cls) 
    52             EntityDescriptor.current = desc 
    53              
    54             # process statements 
    55             Statement.process(cls) 
    56              
    57             # setup misc options here (like tablename etc.) 
    58             desc.setup_options() 
    59              
    60             # create table & assign (empty) mapper 
    61             desc.setup() 
    62              
    63             # try to setup all uninitialized relationships 
    64             EntityDescriptor.setup_relationships() 
    6521 
    6622 
     
    9046 
    9147        # set default value for options 
     48        self.order_by = None 
     49        self.tablename = None 
    9250        self.metadata = getattr(self.module, 'metadata', elixir.metadata) 
    93         self.autoload = None 
    94         self.tablename = None 
    95         self.shortnames = False 
    96         self.auto_primarykey = True 
    97         self.order_by = None 
    98         self.mapper_options = dict() 
    99         self.table_options = dict() 
     51 
     52        for option in ('autoload', 'shortnames', 'auto_primarykey'): 
     53            setattr(self, option, options_defaults[option]) 
     54 
     55        for option_dict in ('mapper_options', 'table_options'): 
     56            setattr(self, option_dict, options_defaults[option_dict].copy()) 
    10057     
    10158    def setup_options(self): 
     
    12279        ''' 
    12380         
     81        if elixir.delay_setup: 
     82            elixir.delayed_entities.add(self) 
     83            return 
     84 
     85        self.setup_table() 
    12486        self.setup_mapper() 
    12587        
     
    12890        EntityDescriptor.uninitialized_rels.update( 
    12991            self.relationships.values()) 
     92 
     93        # try to setup all uninitialized relationships 
     94        EntityDescriptor.setup_relationships() 
    13095     
    13196    def setup_mapper(self): 
     
    138103         
    139104        session = getattr(self.module, 'session', elixir.objectstore) 
    140         table = self.setup_table() 
    141105         
    142106        kwargs = self.mapper_options 
     
    144108            kwargs['order_by'] = self.translate_order_by(self.order_by) 
    145109         
    146         assign_mapper(session.context, self.entity, table, **kwargs) 
     110        assign_mapper(session.context, self.entity, self.entity.table,  
     111                      **kwargs) 
    147112        elixir.metadatas.add(self.metadata) 
    148113     
     
    157122                col = desc(col) 
    158123            order.append(col) 
    159              
    160124        return order 
    161125 
     
    183147            kwargs['autoload'] = True 
    184148         
    185         table = Table(self.tablename, self.metadata, *args, **kwargs) 
    186         self.entity.table = table 
    187         return table 
     149        self.entity.table = Table(self.tablename, self.metadata,  
     150                                  *args, **kwargs) 
    188151     
    189152    def create_auto_primary_key(self): 
     
    232195                    raise Exception( 
    233196                            "Several relations match as inverse of the '%s' " 
    234                             "relation in class '%s'. You should specify " 
     197                            "relation in entity '%s'. You should specify " 
    235198                            "inverse relations manually by using the inverse " 
    236199                            "keyword." 
     
    246209        return matching_rel 
    247210 
    248  
    249211    @classmethod 
    250212    def setup_relationships(cls): 
     
    253215                EntityDescriptor.uninitialized_rels.remove(relationship) 
    254216 
     217 
     218class Entity(object): 
     219    ''' 
     220    The base class for all entities 
     221     
     222    All Elixir model objects should inherit from this class. Statements can 
     223    appear within the body of the definition of an entity to define its 
     224    fields, relationships, and other options. 
     225     
     226    Here is an example: 
     227 
     228    :: 
     229     
     230        class Person(Entity): 
     231            has_field('name', Unicode(128)) 
     232            has_field('birthdate', DateTime, default=datetime.now) 
     233     
     234    Please note, that if you don't specify any primary keys, Elixir will 
     235    automatically create one called ``id``. 
     236     
     237    For further information, please refer to the provided examples or 
     238    tutorial. 
     239    ''' 
     240     
     241    class __metaclass__(type): 
     242        def __init__(cls, name, bases, dict_): 
     243            # only process subclasses of Entity, not Entity itself 
     244            if bases[0] is object: 
     245                return 
     246             
     247            # create the entity descriptor 
     248            desc = cls._descriptor = EntityDescriptor(cls) 
     249            EntityDescriptor.current = desc 
     250             
     251            # process statements 
     252            Statement.process(cls) 
     253             
     254            # setup misc options here (like tablename etc.) 
     255            desc.setup_options() 
     256             
     257            # create table & assign (empty) mapper 
     258            desc.setup() 
     259 
  • elixir/trunk/elixir/fields.py

    r45 r47  
    11''' 
     2Field statements for Elixir entities 
     3 
    24====== 
    35Fields 
  • elixir/trunk/elixir/options.py

    r46 r47  
    11''' 
     2Option statements for Elixir entities 
     3 
    24======= 
    35Options 
     
    68This module provides DSL statements for defining options on your Elixir 
    79entities.  There are three different kinds of options that can be set  
    8 up, and for this there are three different statements: 
    9  
     10up, and for this there are three different statements: using_options_, 
     11using_table_options_ and using_mapper_options_. Alternatively, options can be 
     12set on all Elixir entities by modifying the `options_defaults` dictionary 
     13before defining any entity. 
    1014 
    1115`using_options` 
     
    2529The list of supported arguments are as follows: 
    2630 
    27 +---------------------+---------------------------------------------------+ 
    28 | Option Name         | Description                                       | 
    29 +=====================+===================================================+ 
    30 | ``metadata``        | Specify a custom MetaData                         | 
    31 +---------------------+---------------------------------------------------+ 
    32 | ``autoload``        | Automatically load column definitions from the    | 
    33 |                     | existing database table                           | 
    34 +---------------------+---------------------------------------------------+ 
    35 | ``tablename``       | Specify a custom tablename                        | 
    36 +---------------------+---------------------------------------------------+ 
    37 | ``shortnames``      | Usually tablenames include the full module-path   | 
    38 |                     | to the entity, but lower-cased and separated by   | 
    39 |                     | underscores ("_"), eg.: "project1_model_myentity" | 
    40 |                     | for an entity named "MyEntity" in the module      | 
    41 |                     | "project1.model".  If shortnames is True, the     | 
    42 |                     | tablename will just be the entity's classname     | 
    43 |                     | lower-cased, ie. "myentity".                      | 
    44 +---------------------+---------------------------------------------------+ 
    45 | ``auto_primarykey`` | If given as string, it will represent the         | 
    46 |                     | auto-primary-key's column name.  If this option   | 
    47 |                     | is True, it will allow auto-creation of a primary | 
    48 |                     | key if there's no primary key defined for the     | 
    49 |                     | corresponding entity.  If this option is False,   | 
    50 |                     | it will disallow auto-creation of a primary key.  | 
    51 +---------------------+---------------------------------------------------+ 
    52 | ``order_by``        | How to order select results. Either a string or a | 
    53 |                     | list of strings, composed of the field name,      | 
    54 |                     | optionally lead by a minus (descending order).    | 
    55 +---------------------+---------------------------------------------------+ 
     31+---------------------+-------------------------------------------------------+ 
     32| Option Name         | Description                                           | 
     33+=====================+=======================================================+ 
     34| ``metadata``        | Specify a custom MetaData                             | 
     35+---------------------+-------------------------------------------------------+ 
     36| ``autoload``        | Automatically load column definitions from the        | 
     37|                     | existing database table.  For now, it does **not**    | 
     38|                     | work for ``has_and_belongs_to_many`` relationships.   | 
     39|                     | Using autoloaded tables implies setting               | 
     40|                     | ``delay_setup`` to ``True`` before defining your      | 
     41|                     | entities.                                             | 
     42+---------------------+-------------------------------------------------------+ 
     43| ``tablename``       | Specify a custom tablename                            | 
     44+---------------------+-------------------------------------------------------+ 
     45| ``shortnames``      | Usually tablenames include the full module-path       | 
     46|                     | to the entity, but lower-cased and separated by       | 
     47|                     | underscores ("_"), eg.: "project1_model_myentity"     | 
     48|                     | for an entity named "MyEntity" in the module          | 
     49|                     | "project1.model".  If shortnames is True, the         | 
     50|                     | tablename will just be the entity's classname         | 
     51|                     | lower-cased, ie. "myentity".                          | 
     52+---------------------+-------------------------------------------------------+ 
     53| ``auto_primarykey`` | If given as string, it will represent the             | 
     54|                     | auto-primary-key's column name.  If this option       | 
     55|                     | is True, it will allow auto-creation of a primary     | 
     56|                     | key if there's no primary key defined for the         | 
     57|                     | corresponding entity.  If this option is False,       | 
     58|                     | it will disallow auto-creation of a primary key.      | 
     59+---------------------+-------------------------------------------------------+ 
     60| ``order_by``        | How to order select results. Either a string or a     | 
     61|                     | list of strings, composed of the field name,          | 
     62|                     | optionally lead by a minus (descending order).        | 
     63+---------------------+-------------------------------------------------------+ 
    5664 
    5765For examples, please refer to the examples and unit tests. 
     
    6775without any processing. 
    6876 
    69 For further information, please refer to the SQLAlchemy documentation. 
    70  
     77For further information, please refer to the `SQLAlchemy table's documentation <http://www.sqlalchemy.org/docs/docstrings.myt 
     78#docstrings_sqlalchemy.schema_Table>`_. 
    7179 
    7280`using_mapper_options` 
     
    7987without any processing. 
    8088 
    81 For further information, please refer to the SQLAlchemy documentation. 
     89For further information, please refer to the `SQLAlchemy mapper  
     90function's documentation <http://www.sqlalchemy.org/docs/adv_datamapping.myt 
     91#advdatamapping_mapperoptions>`_. 
    8292''' 
    8393 
    8494from elixir.statements import Statement 
    8595 
    86 __all__ = ['using_options', 'using_table_options', 'using_mapper_options'] 
     96__all__ = ['using_options', 'using_table_options', 'using_mapper_options', 
     97           'options_defaults'] 
    8798 
    88 __pudge_all__ = [] 
     99__pudge_all__ = ['options_defaults'] 
     100 
     101options_defaults = dict( 
     102    autoload=None, 
     103    shortnames=False, 
     104    auto_primarykey=True, 
     105    mapper_options=dict(), 
     106    table_options=dict(), 
     107) 
    89108 
    90109class UsingOptions(object):     
     
    108127class UsingTableOptions(object):     
    109128    def __init__(self, entity, *args, **kwargs): 
    110         entity._descriptor.table_options = kwargs 
     129        entity._descriptor.table_options.update(kwargs) 
    111130 
    112131 
    113132class UsingMapperOptions(object): 
    114133    def __init__(self, entity, *args, **kwargs): 
    115         entity._descriptor.mapper_options = kwargs 
     134        entity._descriptor.mapper_options.update(kwargs) 
    116135 
    117136 
  • elixir/trunk/elixir/relationships.py

    r45 r47  
    11''' 
     2Relationship statements for Elixir entities 
     3 
    24============= 
    35Relationships 
     
    2830keyword arguments are supported, but you should keep in mind, the following  
    2931keyword arguments are taken care of by Elixir and should not be used:  
    30 ``uselist``, ``remote_side``, ``primaryjoin`` and ``secondaryjoin``. 
     32``uselist``, ``remote_side``, ``secondary``, ``primaryjoin`` and  
     33``secondaryjoin``. 
    3134 
    3235.. _order_by: 
     
    5861foreign key referencing the `id` column of the `Person` entity. 
    5962 
     63In addition to the keyword arguments inherited from SQLAlchemy, ``belongs_to`` 
     64relationships accept an optional ``colname`` keyword argument, used to specify 
     65a custom name for the column which will be created. 
     66 
    6067`has_one` 
    6168--------- 
     
    9299Note that an ``has_many`` relationship **cannot exist** without a  
    93100corresponding ``belongs_to`` relationship in the other way. This is because the 
    94 ``has_one`` relationship needs the foreign_key created by the ``belongs_to``  
     101``has_one`` relationship needs the foreign key created by the ``belongs_to``  
    95102relationship. 
    96103 
     
    164171        self.kwargs = kwargs 
    165172         
    166         #CHECKME: is this useful? 
    167173        self.entity._descriptor.relationships[self.name] = self 
    168      
    169     def create_keys(self, autoload=False): 
     174        self.setup_done = False 
     175     
     176    def create_keys(self): 
    170177        ''' 
    171178        Subclasses (ie. concrete relationships) may override this method to  
     
    193200            return False 
    194201         
     202        if self.setup_done: 
     203            return True 
     204 
     205#        FIXME: this should only happen if the relation was not setup already 
    195206        self.create_keys() 
    196207        self.create_tables() 
    197208        self.create_properties() 
     209        self.setup_done = True 
    198210         
    199211        return True 
     
    274286    ''' 
    275287     
    276      
    277     def create_keys(self, autoload=False): 
     288    def __init__(self, entity, name, *args, **kwargs): 
     289        self.colname = kwargs.pop('colname', None) 
     290        if self.colname and not isinstance(self.colname, list): 
     291            self.colname = [self.colname] 
     292        super(BelongsTo, self).__init__(entity, name, *args, **kwargs) 
     293     
     294    def create_keys(self): 
    278295        ''' 
    279296        Find all primary keys on the target and create foreign keys on the  
     
    284301        target_desc = self.target._descriptor 
    285302         
     303        #FIXME: this is buggy, because it seems  
     304        # like the field is used for two different purpose.  
     305        # FK is what? 
     306 
     307        # convert strings to Field instances 
    286308        if self.foreign_key: 
    287309            self.foreign_key = [source_desc.fields[k] 
     
    289311                                       if isinstance(k, basestring)] 
    290312            return 
    291          
    292         fk_refcols = list() 
    293         fk_colnames = list() 
    294313 
    295314        self.foreign_key = list() 
    296315        self.primaryjoin_clauses = list() 
    297316 
    298         for key in target_desc.primary_keys: 
    299             pk_col = key.column 
    300  
    301             colname = '%s_%s' % (self.name, pk_col.name) 
    302             # we use a Field here instead of using a Column directly  
    303             # because of add_field  
    304             field = Field(pk_col.type, colname=colname, index=True) 
    305             source_desc.add_field(field) 
    306  
    307             self.foreign_key.append(field) 
    308  
    309             # build the list of local columns which will be part of 
    310             # the foreign key 
    311             fk_colnames.append(colname) 
    312  
    313             # build the list of columns the foreign key will point to 
    314             fk_refcols.append(target_desc.tablename + '.' + pk_col.name) 
    315  
    316             # build up the primary join. This is needed when you have several 
    317             # belongs_to relations between two objects 
    318             self.primaryjoin_clauses.append(field.column == pk_col) 
    319          
    320         # TODO: better constraint-naming? 
    321         #CHECKME: do we really need use_alter systematically? 
    322         source_desc.add_constraint(ForeignKeyConstraint( 
    323                                         fk_colnames, fk_refcols, 
    324                                         name=self.name +'_fk', 
    325                                         use_alter=True)) 
     317        if source_desc.autoload: 
     318            if not self.colname: 
     319                raise Exception( 
     320                        "Entity '%s' is autoloaded but relation '%s' has no " 
     321                        "column name specified. You should specify it by " 
     322                        "using the colname keyword." 
     323                        % (self.entity.__name__, self.name) 
     324                      ) 
     325 
     326            #TODO: test if this works when colname is a list 
     327            for colname in self.colname: 
     328                for col in self.entity.table.columns: 
     329                    if col.name == colname: 
     330                        # We need to take the first foreign key, but  
     331                        # foreign_keys is an util.OrderedSet which doesn't  
     332                        # support indexation. 
     333                        fk_iter = iter(col.foreign_keys) 
     334                        fk = fk_iter.next() 
     335                        self.primaryjoin_clauses.append(col == fk.column) 
     336 
     337            if not self.primaryjoin_clauses: 
     338                raise Exception("Column '%s' not found in table '%s'"  
     339                                % (self.colname, self.entity.table.name)) 
     340        else: 
     341            fk_refcols = list() 
     342            fk_colnames = list() 
     343 
     344            if self.colname and \ 
     345               len(self.colname) != len(target_desc.primary_keys): 
     346                raise Exception( 
     347                        "The number of column names provided in the colname " 
     348                        "keyword argument of the '%s' relationship of the " 
     349                        "'%s' entity is not the same as the number of columns " 
     350                        "of the primary key of '%s'." 
     351                        % (self.name, self.entity.__name__,  
     352                           self.target.__name__) 
     353                      ) 
     354 
     355            for key_num, key in enumerate(target_desc.primary_keys): 
     356                pk_col = key.column 
     357 
     358                if self.colname: 
     359                    colname = self.colname[key_num] 
     360                else: 
     361                    colname = '%s_%s' % (self.name, pk_col.name) 
     362 
     363                # we use a Field here instead of using a Column directly  
     364                # because of add_field  
     365                field = Field(pk_col.type, colname=colname, index=True) 
     366                source_desc.add_field(field) 
     367 
     368                self.foreign_key.append(field) 
     369 
     370                # build the list of local columns which will be part of 
     371                # the foreign key 
     372                fk_colnames.append(colname) 
     373 
     374                # build the list of columns the foreign key will point to 
     375                fk_refcols.append(target_desc.tablename + '.' + pk_col.name) 
     376 
     377                # build up the primary join. This is needed when you have  
     378                # several belongs_to relations between two objects 
     379                self.primaryjoin_clauses.append(field.column == pk_col) 
     380             
     381            # TODO: better constraint-naming? 
     382            #CHECKME: do we really need use_alter systematically? 
     383            source_desc.add_constraint(ForeignKeyConstraint( 
     384                                            fk_colnames, fk_refcols, 
     385                                            name=self.name +'_fk', 
     386                                            use_alter=True)) 
    326387     
    327388    def create_properties(self): 
     
    342403    uselist = False 
    343404 
    344     def create_keys(self, autoload=False): 
     405    def create_keys(self): 
    345406        # make sure the inverse is set up because it creates the 
    346407        # foreign key we'll need 
     
    350411        kwargs = self.kwargs 
    351412         
     413        #TODO: for now, we don't break any test if we remove those 3 lines. 
     414        # So, we should either complete the selfref test to prove that they 
     415        # are indeed useful, or remove them. It might be they are indeed 
     416        # useless because of the primaryjoin, and that the remote_side is 
     417        # already setup in the other way (belongs_to). 
    352418        if self.entity is self.target: 
     419            #FIXME: this won't work for autoloaded relations 
     420            # so I need to change the type of foreign_key  
    353421            kwargs['remote_side'] = [field.column 
    354422                                        for field in self.inverse.foreign_key] 
     
    376444    def __init__(self, entity, name, *args, **kwargs): 
    377445        self.user_tablename = kwargs.pop('tablename', None) 
    378         self.secondary = None 
     446        self.secondary_table = None 
    379447        super(HasAndBelongsToMany, self).__init__(entity, name,  
    380448                                                  *args, **kwargs) 
     
    382450    def create_tables(self): 
    383451        if self.inverse: 
    384             if self.inverse.secondary: 
    385                 self.secondary = self.inverse.secondary 
     452            if self.inverse.secondary_table: 
     453                self.secondary_table = self.inverse.secondary_table 
    386454                self.primaryjoin_clauses = self.inverse.secondaryjoin_clauses 
    387455                self.secondaryjoin_clauses = self.inverse.primaryjoin_clauses 
    388456 
    389         if not self.secondary: 
     457        if not self.secondary_table: 
    390458            e1_desc = self.entity._descriptor 
    391459            e2_desc = self.target._descriptor 
     
    461529 
    462530            args = columns + constraints 
    463             self.secondary = Table(tablename, e1_desc.metadata, *args) 
     531            self.secondary_table = Table(tablename, e1_desc.metadata, *args) 
    464532     
    465533    def create_properties(self): 
     
    474542                self.target._descriptor.translate_order_by(kwargs['order_by']) 
    475543 
    476         self.property = relation(self.target, secondary=self.secondary, 
     544        self.property = relation(self.target, secondary=self.secondary_table, 
    477545                                 uselist=True, **kwargs) 
    478546        self.entity.mapper.add_property(self.name, self.property) 
  • elixir/trunk/tests/test_hasfield.py

    r17 r47  
    2424     
    2525    def teardown(self): 
    26         drop_all() 
     26        cleanup_all() 
    2727     
    2828    def test_hasfield(self): 
  • elixir/trunk/tests/test_movies.py

    r17 r47  
    77from sqlalchemy import create_engine 
    88from elixir     import * 
    9  
    109 
    1110class Director(Entity): 
     
    6362     
    6463    def teardown(self): 
    65         drop_all() 
     64        cleanup_all() 
    6665     
    6766    def test_bidirectional(self): 
  • elixir/trunk/tests/test_multi.py

    r17 r47  
    88from elixir import * 
    99 
    10 #TODO: test multi-col-pk VS many2many selfref 
    11  
    12 # multi belongs-to 
    13 class Person(Entity): 
    14     has_field('name', Unicode(32)) 
    15      
    16     has_many('pets', of_kind='Animal', inverse='owner') 
    17     has_many('animals', of_kind='Animal', inverse='feeder') 
    18      
    19     def __str__(self): 
    20         s = '%s\n' % self.name.encode('utf-8')   
    21         for pet in self.pets: 
    22             s += '  * pet: %s\n' % pet.name 
    23         return s 
    24  
    25 class Animal(Entity): 
    26     has_field('name', String(15)) 
    27     has_field('color', String(15)) 
    28      
    29     belongs_to('owner', of_kind='Person') 
    30     belongs_to('feeder', of_kind='Person') 
    31  
    3210#----------- 
    3311 
    34 class Article(Entity): 
    35     has_field('title', String(100)) 
     12class TestMultiBelongsTo(object): 
     13    def setup(self): 
     14        global Person, Animal 
     15         
     16        #--------------------------------------- 
     17        # classes for the multi belongs_to test 
    3618 
    37     has_and_belongs_to_many('editor_tags', of_kind='Tag') 
    38     has_and_belongs_to_many('user_tags', of_kind='Tag') 
    39      
    40 class Tag(Entity): 
    41     has_field('name', String(20), primary_key=True) 
     19        class Person(Entity): 
     20            has_field('name', Unicode(32)) 
     21             
     22            has_many('pets', of_kind='Animal', inverse='owner') 
     23            has_many('animals', of_kind='Animal', inverse='feeder') 
     24             
     25            def __str__(self): 
     26                s = '%s\n' % self.name.encode('utf-8')   
     27                for pet in self.pets: 
     28                    s += '  * pet: %s\n' % pet.name 
     29                return s 
    4230 
    43 #----------- 
     31        class Animal(Entity): 
     32            has_field('name', String(15)) 
     33            has_field('color', String(15)) 
     34             
     35            belongs_to('owner', of_kind='Person') 
     36            belongs_to('feeder', of_kind='Person') 
    4437 
    45 class TestMulti(object): 
    46     def setup(self): 
    4738        engine = sqlalchemy.create_engine('sqlite:///') 
    48 #        engine.echo = True 
    4939        metadata.connect(engine) 
    5040        create_all() 
    5141     
    5242    def teardown(self): 
    53         drop_all() 
     43        cleanup_all() 
    5444     
    5545    def test_belongs_to_multi_ref(self): 
     
    7060        assert homer == lisa.pets[0].feeder 
    7161        assert homer == slh.owner 
     62 
     63class TestMultiHasAndBelongsToMany(object): 
     64    def setup(self): 
     65        global Article, Tag 
     66 
     67        class Article(Entity): 
     68            has_field('title', String(100)) 
     69 
     70            has_and_belongs_to_many('editor_tags', of_kind='Tag') 
     71            has_and_belongs_to_many('user_tags', of_kind='Tag') 
     72             
     73        class Tag(Entity): 
     74            has_field('name', String(20), primary_key=True) 
     75 
     76        engine = sqlalchemy.create_engine('sqlite:///') 
     77        metadata.connect(engine) 
     78        create_all() 
     79 
     80    def teardown(self): 
     81        cleanup_all() 
    7282 
    7383    def test_has_and_belongs_to_many_multi_ref(self): 
     
    96106    test.test_belongs_to_multi_ref() 
    97107    test.teardown() 
     108 
     109    test = TestMultiHasAndBelongsToMany() 
    98110    test.setup() 
    99111    test.test_has_and_belongs_to_many_multi_ref() 
  • elixir/trunk/tests/test_oneway.py

    r17 r47  
    88from elixir     import * 
    99 
     10#FIXME: this shouldn't be necessary. cleanup_all should handle it. The problem 
     11# is that with this damn dynamic behavior, we can't easily re-setup the  
     12# entities once they've been setup once 
     13metadata.clear() 
    1014 
    1115class Person(Entity): 
     
    1317        name = Field(Unicode(30)) 
    1418    ) 
    15      
    16     using_options(shortnames=True, order_by="name") 
    17  
    1819 
    1920class Animal(Entity): 
     
    2425     
    2526    belongs_to('owner', of_kind='Person') 
    26      
    27     using_options(shortnames=True, order_by="name") 
    2827 
    2928 
     
    3534     
    3635    def teardown(self): 
    37         drop_all() 
     36        cleanup_all() 
    3837     
    3938    def test_oneway(self): 
  • elixir/trunk/tests/test_options.py

    r22 r47  
    2828     
    2929    def teardown(self): 
    30         drop_all() 
     30        cleanup_all() 
    3131     
    3232        
  • elixir/trunk/tests/test_order_by.py

    r22 r47  
    33""" 
    44 
    5 import nose 
    65from sqlalchemy import create_engine 
    76from elixir     import * 
     
    3231    def setup(self): 
    3332        engine = create_engine('sqlite:///') 
     33#        engine.echo = True 
    3434        metadata.connect(engine) 
    3535        create_all() 
     
    5757 
    5858    def teardown(self): 
     59        # we don't use cleanup_all because setup and teardown are called for  
     60        # each test, and since the class is not redefined, it will not be 
     61        # reinitialized so we can't kill it 
    5962        drop_all() 
    6063     
     
    6265        records = Record.select() 
    6366 
    64         print "-year, title" 
     67        print "-year, +title" 
    6568        for record in records: 
    6669            print record 
     
    7477        records = Artist.get_by(name="Dream Theater").records 
    7578 
    76         print "+year, title" 
     79        print "+year, -title" 
    7780        for record in records: 
    7881            print record 
     
    8184        assert records[2].year <= records[5].year 
    8285        assert records[3].year <= records[4].year 
     86        assert records[-1].title == 'Octavarium' 
    8387        assert records[-1].year == 2005 
    8488 
  • elixir/trunk/tests/test_selfref.py

    r17 r47  
    77 
    88 
    9 class Person(Entity): 
    10     with_fields( 
    11         name = Field(Unicode(30)) 
    12     ) 
    13      
    14     belongs_to('father', of_kind='Person', inverse='children') 
    15     has_many('children', of_kind='Person', inverse='father') 
    16     has_and_belongs_to_many('friends', of_kind='Person') 
    17  
    18 # define a self-referential table with several relations 
    19 class TreeNode(Entity): 
    20     has_field('name', String(50), nullable=False) 
    21  
    22     belongs_to('parent', of_kind='TreeNode') 
    23     has_many('children', of_kind='TreeNode', inverse='parent') 
    24     belongs_to('root', of_kind='TreeNode') 
    25  
    26     def __str__(self): 
    27         return self._getstring(0) 
    28  
    29     def _getstring(self, level): 
    30         s = ('  ' * level) + "%s (%s,%s,%s, %d)" % (self.name, self.id, self.parent_id, self.root_id, id(self)) + '\n' 
    31         s += ''.join([n._getstring(level+1) for n in self.children]) 
    32         return s 
    33  
    349class TestSelfRef(object): 
    3510    def setup(self): 
    3611        engine = create_engine('sqlite:///') 
    3712        metadata.connect(engine) 
    38 #        engine.echo = True 
    39         create_all() 
    4013     
    4114    def teardown(self): 
    42         drop_all() 
     15        cleanup_all() 
    4316     
    4417    def test_belongs_to_selfref(self): 
     18        class Person(Entity): 
     19            with_fields( 
     20                name = Field(Unicode(30)) 
     21            ) 
     22             
     23            belongs_to('father', of_kind='Person', inverse='children') 
     24            has_many('children', of_kind='Person', inverse='father') 
     25 
     26        create_all() 
     27 
    4528        grampa = Person(name="Abe") 
    4629        homer = Person(name="Homer") 
     
    6649 
    6750    def test_has_and_belongs_to_many_selfref(self): 
     51        class Person(Entity): 
     52            with_fields( 
     53                name = Field(Unicode(30)) 
     54            ) 
     55             
     56            has_and_belongs_to_many('friends', of_kind='Person') 
     57 
     58        create_all() 
     59 
    6860        barney = Person(name="Barney") 
    6961        homer = Person(name="Homer", friends=[barney]) 
     
    7971        assert barney in homer.friends 
    8072 
     73class TestMultiSelfRef(object): 
     74    def setup(self): 
     75        engine = create_engine('sqlite:///') 
     76        metadata.connect(engine) 
     77     
     78    def teardown(self): 
     79        cleanup_all() 
     80 
    8181    def test_belongs_to_multiple_selfref(self): 
     82        # define a self-referential table with several relations 
     83        class TreeNode(Entity): 
     84            has_field('name', String(50), nullable=False) 
     85 
     86            belongs_to('parent', of_kind='TreeNode') 
     87            has_many('children', of_kind='TreeNode', inverse='parent') 
     88            belongs_to('root', of_kind='TreeNode') 
     89 
     90            def __str__(self): 
     91                return self._getstring(0) 
     92 
     93            def _getstring(self, level): 
     94                s = '  ' * level + \ 
     95                    "%s (%s,%s,%s, %d)" % (self.name, self.id, self.parent_id, 
     96                                           self.root_id, id(self)) + \ 
     97                    '\n' 
     98                s += ''.join([n._getstring(level+1) for n in self.children]) 
     99                return s 
     100 
     101        create_all() 
     102 
    82103        node2 = TreeNode(name='node2') 
    83104        node2.children.append(TreeNode(name='subnode1')) 
     
    102123    test.test_has_and_belongs_to_many_selfref() 
    103124    test.teardown()         
     125 
     126    test = TestMultiSelfRef() 
    104127    test.setup() 
    105128    test.test_belongs_to_multiple_selfref()