Changeset 347

Show
Ignore:
Timestamp:
06/19/08 15:35:21 (5 years ago)
Author:
ged
Message:

removed trailing spaces

Location:
elixir/trunk
Files:
37 modified

Legend:

Unmodified
Added
Removed
  • elixir/trunk/CHANGES

    r346 r347  
    66  simple JSON-like dictionary notation (patch from Paul Johnston, 
    77  closes ticket #40). 
    8 - Added experimental (!) support for concrete table inheritance (both  
     8- Added experimental (!) support for concrete table inheritance (both 
    99  polymorphic or not). Concrete polymorphic inheritance requires SQLalchemy 
    1010  0.4.5 or later. 
     
    2525- Fixed multi-table inheritance when using a non default schema (closes #38) 
    2626- Fixed ManyToOne relationships using 'key' kwarg in their column_kwargs 
    27   (patch by Jason R. Coombs)  
    28 - Fixed inheritance with autoloaded entities: when using autoload, we  
     27  (patch by Jason R. Coombs) 
     28- Fixed inheritance with autoloaded entities: when using autoload, we 
    2929  shouldn't try to add columns to the table (closes tickets #41 and #43). 
    3030- Fixed ColumnProperty to work with latest version of SQLAlchemy (O.4.5 and 
    3131  later) 
    32 - Fixed ManyToMany relationships when not using the default schema  
     32- Fixed ManyToMany relationships when not using the default schema 
    3333  (patch from Diez B. Roggisch, closes ticket #48) 
    3434 
     
    4040 
    4141New features: 
    42 - Added an optional `check_concurrency` keyword argument to the versioning  
    43   extension, supporting the usage of SQLAlchemy's built-in optimistic  
     42- Added an optional `check_concurrency` keyword argument to the versioning 
     43  extension, supporting the usage of SQLAlchemy's built-in optimistic 
    4444  concurrency check. 
    4545 
    4646Changes: 
    47 - Made Elixir python 2.3 compatible again (based on patches from  
     47- Made Elixir python 2.3 compatible again (based on patches from 
    4848  Jason R. Coombs) 
    4949 
     
    5858New features: 
    5959- Added a new elixir plugin for managing entities as (ordered) lists. 
    60 - Added a `column_format` keyword argument to `ManyToMany` which can be used  
     60- Added a `column_format` keyword argument to `ManyToMany` which can be used 
    6161  to specify an alternate format string for column names in the mapping table. 
    6262- Added support for custom base classes which inherit from another class (ie 
    6363  not directly from object). 
    6464- Added an alternate (nicer) syntax to define synonym properties.  This syntax 
    65   has a more limited scope, except that it can refer to properties defined in  
     65  has a more limited scope, except that it can refer to properties defined in 
    6666  a parent entity. This is based on a patch from Alexandre da Silva. 
    6767 
     
    7171- The polymorphic_identity kwarg in using_mapper_options is not overriden 
    7272  anymore by the one generated by Elixir (patch from Ben Bangert). 
    73 - Moved the format of the multi-table inheritance column to a constant in  
     73- Moved the format of the multi-table inheritance column to a constant in 
    7474  options (so that it can be changed globally). 
    7575- The foreign key constraint of the column in a multi-table inheritance is 
     
    8080  options) if it doesn't use any statement itself. 
    8181- Made inheritance work for custom base classes (closes #25). 
    82 - Fixed the inverse relationship matching when the inverse relationship is  
     82- Fixed the inverse relationship matching when the inverse relationship is 
    8383  defined in a parent Entity (thanks to Alexandre da Silva). 
    8484- Fixed bug in setup_entities (it always used the global entity list and not 
     
    90900.5.0 - 2007-12-08 
    9191 
    92 Please see http://elixir.ematia.de/trac/wiki/Migrate04to05 for detailed  
     92Please see http://elixir.ematia.de/trac/wiki/Migrate04to05 for detailed 
    9393upgrade notes. 
    9494 
     
    104104  the class), before the setup phase happens, it won't work. This was done 
    105105  because of a change in SQLAlchemy trunk (future SA 0.4.2) which broke that 
    106   piece of code (and prevented to use autosetup at all). Since that code  
    107   was a hack in the first place, instead of doing some even uglier hackery,  
     106  piece of code (and prevented to use autosetup at all). Since that code 
     107  was a hack in the first place, instead of doing some even uglier hackery, 
    108108  I got rid of it altogether. 
    109109- Moved some format strings to constants in options, so that one can change 
     
    111111- Allow overriding primary_key columns on autoloaded entities (closes tickets 
    112112  #20 and #22) 
    113 - Columns created by ManyToOne relationships can now optionally (through  
     113- Columns created by ManyToOne relationships can now optionally (through 
    114114  column_kwargs) *not* create an index (ie it's not harcoded anymore). 
    115115  Suggestion by Jason R. Coombs. 
    116116 
    117117Bug fixes: 
    118 - Fixed a nasty bug which prevented inheritance to work correctly when using  
     118- Fixed a nasty bug which prevented inheritance to work correctly when using 
    119119  the attribute syntax in many cases. 
    120120- Fixed associable extension to work with SQLAlchemy trunk (future 0.4.2). 
     
    123123  which prevented to reuse class properties of one class in other (subsequent) 
    124124  classes. 
    125 - Fixed our tests to work with SA trunk (future 0.4.2) (unicode data + use of  
     125- Fixed our tests to work with SA trunk (future 0.4.2) (unicode data + use of 
    126126  deprecated attributes) 
    127127 
    1281280.4.0 - 2007-10-29 
    129129 
    130 Please see http://elixir.ematia.de/trac/wiki/Migrate03to04 for detailed  
     130Please see http://elixir.ematia.de/trac/wiki/Migrate03to04 for detailed 
    131131upgrade notes. 
    132132 
     
    134134- Implemented a new syntax to declare fields and relationships, much closer to 
    135135  what is found in other Python ORM's. The with_fields syntax is now 
    136   deprecated in favor of a that new syntax. The old statement based (has_field  
     136  deprecated in favor of a that new syntax. The old statement based (has_field 
    137137  et al.) syntax is still available though (and will remain so for quite some 
    138138  time). This was done with help from a patch by Adam Gomaa. 
     
    140140  non-polymorphic multi-table (aka joined table) inheritance. 
    141141- Added ext sub-package for additional Elixir statements. 
    142 - Added associable extension for generating polymorphic associations with  
     142- Added associable extension for generating polymorphic associations with 
    143143  Elixir statements. 
    144144- Added versioning extension to keep track to all changes to your entities by 
     
    152152- Added support to add any SQLAlchemy property on your mapper, through the 
    153153  GenericProperty class (as well as the has_property statement). These can 
    154   work even if they rely on the entity columns (an thus need them to be  
     154  work even if they rely on the entity columns (an thus need them to be 
    155155  defined before the property can be declared). See tests/test_properties.py 
    156156  for examples. 
    157 - Added support for "manual session management" (ie you can now define an  
     157- Added support for "manual session management" (ie you can now define an 
    158158  entity with "using_options(session=None)" and it won't use any 
    159159  SessionContext extension, nor receive the "query" attribute. 
     
    172172  the Entity class. Now, if people don't like them, they have the option to 
    173173  simply provide another base class. 
    174 - Default objectstore is now a ScopedSession when working on SQLAlchemy 0.4.  
    175   It means that it's not wrapped in an Objectstore object at all. This means,  
    176   that depending on the version of SA you are using, you'll get a slightly  
     174- Default objectstore is now a ScopedSession when working on SQLAlchemy 0.4. 
     175  It means that it's not wrapped in an Objectstore object at all. This means, 
     176  that depending on the version of SA you are using, you'll get a slightly 
    177177  different behavior. 
    178178- Relationships to other classes can now also be defined using the classes 
     
    184184- Added "through" and "via" keyword arguments on relationships and has_field 
    185185  statement, to proxy values through relationships (uses association_proxy) 
    186 - Made EntityMeta public, so that people can actually define their own base  
     186- Made EntityMeta public, so that people can actually define their own base 
    187187  class. 
    188188- Changed the order of relationship kwargs processing so that computed kwargs 
     
    198198  enables one "normal" entity (fully defined in Elixir) to refer to an entity 
    199199  which is autoloaded. 
    200 - Added translation (from column name to column object) of the primary_key  
     200- Added translation (from column name to column object) of the primary_key 
    201201  mapper option so that it can actually be used. This allows to have entities 
    202202  without any primary key defined at the table level. 
     
    208208Bug fixes: 
    209209- Reworked/cleaned tests so that they don't leak stuff to other tests (both at 
    210   the method level and module level) anymore. Uses nosetest's module level  
     210  the method level and module level) anymore. Uses nosetest's module level 
    211211  fixture. 
    212212- Fixed relationships to entities whose primary_key field has been defined 
     
    215215- Fixed relationships to tables using a schema (patch by Neil Blakey-Milner) 
    216216- Made inverse relationships use backrefs. This fixes the "bidirectional 
    217   coherency" problem some people had before doing a flush. (based on a patch  
     217  coherency" problem some people had before doing a flush. (based on a patch 
    218218  from Remi Jolin). 
    219219 
     
    227227  name for your intermediary table than the one generated. You also _have to_ 
    228228  specify at least one of either local_side or remote_side argument. 
    229 - Added support for the "version_id_col" option on entities. This option adds  
     229- Added support for the "version_id_col" option on entities. This option adds 
    230230  a column to the table which will be used to prevent concurrent modifications 
    231231  on any row of the entity's table (i.e. it will raise an error if it happens). 
     
    238238  belongs_to relationships. The content of that argument is forwarded to the 
    239239  foreign key constraint. 
    240 - Foreign key names generated by belongs_to relationships use column names  
     240- Foreign key names generated by belongs_to relationships use column names 
    241241  instead of relation names in case we have a relation with the same name 
    242242  defined in several entities inheriting from the same entity using single- 
     
    265265  0.1.0 while I had introduced more decorators in the trunk in the mean time). 
    266266 
    267 - Made some PEP8 tweaks in many places. Used the pep8 script provided with  
     267- Made some PEP8 tweaks in many places. Used the pep8 script provided with 
    268268  Cheesecake. 
    269269- Some cleanup/useless code removal 
     
    274274  ensure that we apply statements to the proper class.  We now attach the 
    275275  statement list to the class itself, rather than attaching it to a global 
    276   list that is neither threadsafe, nor safe when doing nested class  
     276  list that is neither threadsafe, nor safe when doing nested class 
    277277  definition.  Also added a test to validate that this works. 
    278278- implemented singletable non-polymorphic inheritance 
    279279- added support to pass non-keyword arguments to tables. You just pass 
    280280  them to the using_table_options statement and they will be forwarded to the 
    281   table along with the keyword arguments. This can be used to set table  
     281  table along with the keyword arguments. This can be used to set table 
    282282  constraints. 
    283283- added support for deferred columns (use the "deferred" keyword argument on 
     
    285285- added a "required" keyword argument on fields and BelongsTo 
    286286  relationships. This is the opposite of the "nullable" SA argument. 
    287 - added a "column_kwargs" keyword argument to BelongsTo relationships  
     287- added a "column_kwargs" keyword argument to BelongsTo relationships 
    288288  to forward any keyword argument directly to the SA Column. 
    289289- added support for the use_alter and constraint_kwargs kwargs on BelongsTo 
     
    293293    -> removed it from HasAndBelongsToMany relations, since I think a 
    294294       circular foreign key dependency can't happen with those relations. 
    295 - fixed foreign key names on MySQL (and possibly other) databases by  
     295- fixed foreign key names on MySQL (and possibly other) databases by 
    296296  making sure the generated name is unique for the whole database, and not 
    297297  only for the table on which it applies. 
  • elixir/trunk/README

    r254 r347  
    44 
    55Elixir is a declarative layer on top of the `SQLAlchemy library 
    6 <http://www.sqlalchemy.org/>`_. It is a fairly thin wrapper, which provides  
    7 the ability to create simple Python classes that map directly to relational  
     6<http://www.sqlalchemy.org/>`_. It is a fairly thin wrapper, which provides 
     7the ability to create simple Python classes that map directly to relational 
    88database tables (this pattern is often referred to as the Active Record design 
    9 pattern), providing many of the benefits of traditional databases  
    10 without losing the convenience of Python objects.  
     9pattern), providing many of the benefits of traditional databases 
     10without losing the convenience of Python objects. 
    1111 
    12 Elixir is intended to replace the ActiveMapper SQLAlchemy extension, and the  
     12Elixir is intended to replace the ActiveMapper SQLAlchemy extension, and the 
    1313TurboEntity project but does not intend to replace SQLAlchemy's core features, 
    14 and instead focuses on providing a simpler syntax for defining model objects  
    15 when you do not need the full expressiveness of SQLAlchemy's manual mapper  
     14and instead focuses on providing a simpler syntax for defining model objects 
     15when you do not need the full expressiveness of SQLAlchemy's manual mapper 
    1616definitions. 
    1717 
  • elixir/trunk/TODO

    r271 r347  
    2727------------ 
    2828 
    29 - Besides, the current system also has another case I don't like: if the user  
     29- Besides, the current system also has another case I don't like: if the user 
    3030  specifies an inverse (on one or both sides) but also set a table name on one 
    31   side (or two different table names), it will consider the relation as being  
    32   different even though the user explicitly told it was the same. This should  
    33   not happen. The system should rather throw an exception in that case. But  
     31  side (or two different table names), it will consider the relation as being 
     32  different even though the user explicitly told it was the same. This should 
     33  not happen. The system should rather throw an exception in that case. But 
    3434  this last part should be easilty fixable, I think (it'a a matter of tweaking 
    3535  the is_inverse method of the HasAndBelongsToMany class)... 
     
    5151  http://www.sqlalchemy.org/trac/attachment/ticket/547 
    5252  * the provided "_find_class_descriptor" seem overly complex though 
    53   * the (unlikely?) case where a parent who is not inheriting from XFBase (or  
     53  * the (unlikely?) case where a parent who is not inheriting from XFBase (or 
    5454    Entity) defines a property will probably fail because of line 16. 
    5555 
     
    5858- support all mapper arguments which take column arguments in a generic way 
    5959 
    60 - instead of linking the descriptor in the entity (cls._descriptor) we could  
    61   do it externally, like the mappers in SA. This would solve some of the  
     60- instead of linking the descriptor in the entity (cls._descriptor) we could 
     61  do it externally, like the mappers in SA. This would solve some of the 
    6262  ugliness we have in the current implementation (mostly in target). 
    6363 
     
    8181  http://www.sqlalchemy.org/trac/browser/sqlalchemy/trunk/examples/poly_assoc 
    8282 
    83 - investigate whether it would be possible to do a generic acts_as(xxx)  
     83- investigate whether it would be possible to do a generic acts_as(xxx) 
    8484  instead of the acts_as_taggable Jonathan demonstrated 
    85    
     85 
    8686- implement something like: 
    8787 
     
    9898  ie relations using any "selectable" instead of the normal object table. 
    9999 
    100     mapper(User, users,  
     100    mapper(User, users, 
    101101        properties={ 
    102102           'orders': relation(mapper(Order, orders), order_by=orders.c.id), 
  • elixir/trunk/elixir/__init__.py

    r343 r347  
    77database tables (this pattern is often referred to as the Active Record design 
    88pattern), providing many of the benefits of traditional databases 
    9 without losing the convenience of Python objects.  
     9without losing the convenience of Python objects. 
    1010 
    1111Elixir is intended to replace the ActiveMapper SQLAlchemy extension, and the 
     
    4545 
    4646__all__ = ['Entity', 'EntityMeta', 'EntityCollection', 'entities', 
    47            'Field', 'has_field', 'with_fields',  
     47           'Field', 'has_field', 'with_fields', 
    4848           'has_property', 'GenericProperty', 'ColumnProperty', 'Synonym', 
    4949           'belongs_to', 'has_one', 'has_many', 'has_and_belongs_to_many', 
     
    6262 
    6363class Objectstore(object): 
    64     """a wrapper for a SQLAlchemy session-making object, such as  
     64    """a wrapper for a SQLAlchemy session-making object, such as 
    6565    SessionContext or ScopedSession. 
    66      
     66 
    6767    Uses the ``registry`` attribute present on both objects 
    6868    (versions 0.3 and 0.4) in order to return the current 
    6969    contextual session. 
    7070    """ 
    71      
     71 
    7272    def __init__(self, ctx): 
    7373        self.context = ctx 
     
    7575    def __getattr__(self, name): 
    7676        return getattr(self.context.registry(), name) 
    77      
     77 
    7878    session = property(lambda s:s.context.registry()) 
    7979 
    8080# default session 
    81 try:  
     81try: 
    8282    from sqlalchemy.orm import scoped_session 
    8383    session = scoped_session(sqlalchemy.orm.create_session) 
    84 except ImportError:  
     84except ImportError: 
    8585    # Not on version 0.4 of sqlalchemy 
    8686    from sqlalchemy.ext.sessioncontext import SessionContext 
     
    9898class EntityCollection(list): 
    9999    def __init__(self): 
    100         # _entities is a dict of entities for each frame where there are  
     100        # _entities is a dict of entities for each frame where there are 
    101101        # entities defined. 
    102102        self._entities = {} 
    103103#        self._entity_map = {} 
    104104        list.__init__(self) 
    105      
     105 
    106106    def map_entity(self, entity): 
    107107        self.append(entity) 
     
    121121        caller_entities[key] = entity 
    122122 
    123         # Append all entities which are currently visible by the entity. This  
    124         # will find more entities only if some of them where imported from  
     123        # Append all entities which are currently visible by the entity. This 
     124        # will find more entities only if some of them where imported from 
    125125        # another module. 
    126         for ent in [e for e in caller_frame.f_locals.values()  
     126        for ent in [e for e in caller_frame.f_locals.values() 
    127127                         if isinstance(e, EntityMeta)]: 
    128128            caller_entities[ent.__name__] = ent 
    129          
     129 
    130130#        self._entity_map[key] = entity 
    131      
     131 
    132132    def resolve(self, key, entity=None): 
    133133        ''' 
     
    144144            return getattr(module, classname, None) 
    145145        else: 
    146             # If not, try the list of entities of the "caller" of the  
    147             # source class. Most of the time, this will be the module  
    148             # the class is defined in. But it could also be a method  
     146            # If not, try the list of entities of the "caller" of the 
     147            # source class. Most of the time, this will be the module 
     148            # the class is defined in. But it could also be a method 
    149149            # (inner classes). 
    150150            caller_entities = self._entities[entity._caller] 
     
    189189 
    190190def cleanup_all(drop_tables=False, *args, **kwargs): 
    191     '''Clear all mappers, clear the session, and clear all metadatas.  
     191    '''Clear all mappers, clear the session, and clear all metadatas. 
    192192    Optionally drops the tables. 
    193193    ''' 
  • elixir/trunk/elixir/entity.py

    r343 r347  
    2929 
    3030 
    31 try:  
     31try: 
    3232    from sqlalchemy.orm import ScopedSession 
    33 except ImportError:  
     33except ImportError: 
    3434    # Not on sqlalchemy version 0.4 
    3535    ScopedSession = type(None) 
     
    5757                return session.registry().query(cls) 
    5858 
    59         if not 'query' in cls.__dict__:  
     59        if not 'query' in cls.__dict__: 
    6060            cls.query = query() 
    6161 
     
    7070    EntityDescriptor describes fields and options needed for table creation. 
    7171    ''' 
    72      
     72 
    7373    def __init__(self, entity): 
    7474        entity.table = None 
     
    9090                if self.parent: 
    9191                    raise Exception('%s entity inherits from several entities,' 
    92                                     ' and this is not supported.'  
     92                                    ' and this is not supported.' 
    9393                                    % self.entity.__name__) 
    9494                else: 
     
    117117 
    118118        for option in ('autosetup', 'inheritance', 'polymorphic', 'identity', 
    119                        'autoload', 'tablename', 'shortnames',  
    120                        'auto_primarykey', 'version_id_col',  
     119                       'autoload', 'tablename', 'shortnames', 
     120                       'auto_primarykey', 'version_id_col', 
    121121                       'allowcoloverride'): 
    122122            setattr(self, option, options.options_defaults[option]) 
    123123 
    124124        for option_dict in ('mapper_options', 'table_options'): 
    125             setattr(self, option_dict,  
     125            setattr(self, option_dict, 
    126126                    options.options_defaults[option_dict].copy()) 
    127127 
    128128    def setup_options(self): 
    129129        ''' 
    130         Setup any values that might depend on using_options. For example, the  
     130        Setup any values that might depend on using_options. For example, the 
    131131        tablename or the metadata. 
    132132        ''' 
     
    144144        elif not hasattr(session, 'registry'): 
    145145            # Both SessionContext and ScopedSession have a registry attribute, 
    146             # but objectstores (whether Elixir's or Activemapper's) don't, so  
     146            # but objectstores (whether Elixir's or Activemapper's) don't, so 
    147147            # if we are here, it means an Objectstore is used for the session. 
    148 #XXX: still true for activemapper post 0.4?             
     148#XXX: still true for activemapper post 0.4? 
    149149            objectstore = session 
    150150            session = objectstore.context 
     
    193193    def create_pk_cols(self): 
    194194        """ 
    195         Create primary_key columns. That is, call the 'create_pk_cols'  
    196         builders then add a primary key to the table if it hasn't already got  
    197         one and needs one.  
    198          
    199         This method is "semi-recursive" in some cases: it calls the  
     195        Create primary_key columns. That is, call the 'create_pk_cols' 
     196        builders then add a primary key to the table if it hasn't already got 
     197        one and needs one. 
     198 
     199        This method is "semi-recursive" in some cases: it calls the 
    200200        create_keys method on ManyToOne relationships and those in turn call 
    201         create_pk_cols on their target. It shouldn't be possible to have an  
     201        create_pk_cols on their target. It shouldn't be possible to have an 
    202202        infinite loop since a loop of primary_keys is not a valid situation. 
    203203        """ 
     
    210210            if self.parent: 
    211211                if self.inheritance == 'multi': 
    212                     # Add columns with foreign keys to the parent's primary  
    213                     # key columns  
     212                    # Add columns with foreign keys to the parent's primary 
     213                    # key columns 
    214214                    parent_desc = self.parent._descriptor 
    215215                    schema = parent_desc.table_options.get('schema', None) 
    216                     tablename = parent_desc.tablename  
     216                    tablename = parent_desc.tablename 
    217217                    if schema is not None: 
    218218                        tablename = "%s.%s" % (schema, tablename) 
     
    223223 
    224224                        # It seems like SA ForeignKey is not happy being given 
    225                         # a real column object when said column is not yet  
     225                        # a real column object when said column is not yet 
    226226                        # attached to a table 
    227227                        pk_col_name = "%s.%s" % (tablename, pk_col.key) 
     
    242242 
    243243                self.add_column( 
    244                     Column(colname, options.DEFAULT_AUTO_PRIMARYKEY_TYPE,  
     244                    Column(colname, options.DEFAULT_AUTO_PRIMARYKEY_TYPE, 
    245245                           primary_key=True)) 
    246246        self._pk_col_done = True 
     
    251251    def before_table(self): 
    252252        self.call_builders('before_table') 
    253          
     253 
    254254    def setup_table(self, only_autoloaded=False): 
    255255        ''' 
    256         Create a SQLAlchemy table-object with all columns that have been  
     256        Create a SQLAlchemy table-object with all columns that have been 
    257257        defined up to this point. 
    258258        ''' 
     
    271271                if self.inheritance == 'single': 
    272272                    # we know the parent is setup before the child 
    273                     self.entity.table = self.parent.table  
    274  
    275                     # re-add the entity columns to the parent entity so that  
    276                     # they are added to the parent's table (whether the  
     273                    self.entity.table = self.parent.table 
     274 
     275                    # re-add the entity columns to the parent entity so that 
     276                    # they are added to the parent's table (whether the 
    277277                    # parent's table is already setup or not). 
    278278                    for col in self.columns: 
     
    281281                        self.parent._descriptor.add_constraint(constraint) 
    282282                    return 
    283                 elif self.inheritance == 'concrete':  
    284                     #TODO: we should also copy columns from the parent table  
     283                elif self.inheritance == 'concrete': 
     284                    #TODO: we should also copy columns from the parent table 
    285285                    # if the parent is a base (abstract?) entity (whatever the 
    286286                    # inheritance type -> elif will need to be changed) 
    287287 
    288                     # Copy all non-primary key columns from parent table  
     288                    # Copy all non-primary key columns from parent table 
    289289                    # (primary key columns have already been copied earlier). 
    290290                    for col in self.parent._descriptor.columns: 
     
    292292                            self.add_column(col.copy()) 
    293293 
    294                     #FIXME: use the public equivalent of _get_colspec when  
    295                     #available  
     294                    #FIXME: use the public equivalent of _get_colspec when 
     295                    #available 
    296296                    for con in self.parent._descriptor.constraints: 
    297297                        self.add_constraint( 
     
    306306               self.inheritance in ('single', 'multi') and \ 
    307307               self.children and not self.parent: 
    308                 self.add_column(Column(self.polymorphic,  
     308                self.add_column(Column(self.polymorphic, 
    309309                                       options.POLYMORPHIC_COL_TYPE)) 
    310310 
     
    315315 
    316316            args = self.columns + self.constraints + self.table_args 
    317          
    318         self.entity.table = Table(self.tablename, self.metadata,  
     317 
     318        self.entity.table = Table(self.tablename, self.metadata, 
    319319                                  *args, **kwargs) 
    320320 
     
    330330                for func in methods: 
    331331                    ret = func(instance) 
    332                     # I couldn't commit myself to force people to  
    333                     # systematicaly return EXT_CONTINUE in all their event  
     332                    # I couldn't commit myself to force people to 
     333                    # systematicaly return EXT_CONTINUE in all their event 
    334334                    # methods. 
    335335                    # But not doing that diverge to how SQLAlchemy works. 
    336                     # I should try to convince Mike to do EXT_CONTINUE by  
     336                    # I should try to convince Mike to do EXT_CONTINUE by 
    337337                    # default, and stop processing as the special case. 
    338338#                    if ret != EXT_CONTINUE: 
     
    351351        if not methods: 
    352352            return 
    353          
     353 
    354354        # transform that list into methods themselves 
    355355        for event in methods: 
    356356            methods[event] = make_proxy_method(methods[event]) 
    357          
     357 
    358358        # create a custom mapper extension class, tailored to our entity 
    359359        ext = type('EventMapperExtension', (MapperExtension,), methods)() 
    360          
     360 
    361361        # then, make sure that the entity's mapper has our mapper extension 
    362362        self.add_mapper_extension(ext) 
     
    374374        if isinstance(order_by, basestring): 
    375375            order_by = [order_by] 
    376          
     376 
    377377        order = list() 
    378378        for colname in order_by: 
     
    397397        if self.order_by: 
    398398            kwargs['order_by'] = self.translate_order_by(self.order_by) 
    399          
     399 
    400400        if self.version_id_col: 
    401401            kwargs['version_id_col'] = self.get_column(self.version_id_col) 
     
    416416                    if self.inheritance == 'concrete': 
    417417                        keys = [(self.identity, self.entity.table)] 
    418                         keys.extend([(child._descriptor.identity, child.table)  
     418                        keys.extend([(child._descriptor.identity, child.table) 
    419419                                     for child in self._get_children()]) 
    420                         #XXX: we might need to change the alias name so that  
     420                        #XXX: we might need to change the alias name so that 
    421421                        # children (which are parent themselves) don't end up 
    422422                        # with the same alias than their parent? 
     
    432432 
    433433                    #TODO: this is an optimization, and it breaks the multi 
    434                     # table polymorphic inheritance test with a relation.  
    435                     # So I turn it off for now. We might want to provide an  
     434                    # table polymorphic inheritance test with a relation. 
     435                    # So I turn it off for now. We might want to provide an 
    436436                    # option to turn it on. 
    437437#                    if self.inheritance == 'multi': 
     
    486486        if check_duplicate is None: 
    487487            check_duplicate = not self.allowcoloverride 
    488          
     488 
    489489        if check_duplicate and self.get_column(col.key, False) is not None: 
    490             raise Exception("Column '%s' already exist in '%s' ! " %  
     490            raise Exception("Column '%s' already exist in '%s' ! " % 
    491491                            (col.key, self.entity.__name__)) 
    492492        self._columns.append(col) 
    493          
     493 
    494494        if col.primary_key: 
    495495            self.has_pk = True 
    496496 
    497497        # Autosetup triggers shouldn't be active anymore at this point, so we 
    498         # can theoretically access the entity's table safely. But the problem  
    499         # is that if, for some reason, the trigger removal phase didn't  
    500         # happen, we'll get an infinite loop. So we just make sure we don't  
     498        # can theoretically access the entity's table safely. But the problem 
     499        # is that if, for some reason, the trigger removal phase didn't 
     500        # happen, we'll get an infinite loop. So we just make sure we don't 
    501501        # get one in any case. 
    502502        table = type.__getattribute__(self.entity, 'table') 
    503503        if table: 
    504504            if check_duplicate and col.key in table.columns.keys(): 
    505                 raise Exception("Column '%s' already exist in table '%s' ! " %  
     505                raise Exception("Column '%s' already exist in table '%s' ! " % 
    506506                                (col.key, table.name)) 
    507507            table.append_column(col) 
     
    509509    def add_constraint(self, constraint): 
    510510        self.constraints.append(constraint) 
    511          
     511 
    512512        table = self.entity.table 
    513513        if table: 
     
    516516    def add_property(self, name, property, check_duplicate=True): 
    517517        if check_duplicate and name in self.properties: 
    518             raise Exception("property '%s' already exist in '%s' ! " %  
     518            raise Exception("property '%s' already exist in '%s' ! " % 
    519519                            (name, self.entity.__name__)) 
    520520        self.properties[name] = property 
    521521 
    522 #FIXME: something like this is needed to propagate the relationships from  
    523 # parent entities to their children in a concrete inheritance scenario. But  
     522#FIXME: something like this is needed to propagate the relationships from 
     523# parent entities to their children in a concrete inheritance scenario. But 
    524524# this doesn't work because of the backref matching code. 
    525525#        if self.children and self.inheritance == 'concrete': 
     
    530530        if mapper: 
    531531            mapper.add_property(name, property) 
    532          
     532 
    533533    def add_mapper_extension(self, extension): 
    534534        extensions = self.mapper_options.get('extension', []) 
     
    585585 
    586586    def columns(self): 
    587         #FIXME: this would be more correct but it breaks inheritance, so I'll  
     587        #FIXME: this would be more correct but it breaks inheritance, so I'll 
    588588        # use the old test for now. 
    589589#        if self.entity.table: 
    590         if self.autoload:  
     590        if self.autoload: 
    591591            return self.entity.table.columns 
    592592        else: 
    593             #FIXME: depending on the type of inheritance, we should also  
    594             # return the parent entity's columns (for example for order_by  
     593            #FIXME: depending on the type of inheritance, we should also 
     594            # return the parent entity's columns (for example for order_by 
    595595            # using a column defined in the parent. 
    596596            return self._columns 
     
    652652def is_entity(cls): 
    653653    """ 
    654     Scan the bases classes of `cls` to see if any is an instance of  
     654    Scan the bases classes of `cls` to see if any is an instance of 
    655655    EntityMeta. If we don't find any, it means it is either an unrelated class 
    656656    or an entity base class (like the 'Entity' class). 
     
    663663class EntityMeta(type): 
    664664    """ 
    665     Entity meta class.  
    666     You should only use it directly if you want to define your own base class  
     665    Entity meta class. 
     666    You should only use it directly if you want to define your own base class 
    667667    for your entities (ie you don't want to use the provided 'Entity' class). 
    668668    """ 
     
    670670    def __init__(cls, name, bases, dict_): 
    671671        # Only process further subclasses of the base classes (Entity et al.), 
    672         # not the base classes themselves. We don't want the base entities to  
    673         # be registered in an entity collection, nor to have a table name and  
    674         # so on.  
     672        # not the base classes themselves. We don't want the base entities to 
     673        # be registered in an entity collection, nor to have a table name and 
     674        # so on. 
    675675        if not is_entity(cls): 
    676676            return 
     
    679679        desc = cls._descriptor = EntityDescriptor(cls) 
    680680 
    681         # Process attributes (using the assignment syntax), looking for  
     681        # Process attributes (using the assignment syntax), looking for 
    682682        # 'Property' instances and attaching them to this entity. 
    683         properties = [(name, attr) for name, attr in dict_.iteritems()  
     683        properties = [(name, attr) for name, attr in dict_.iteritems() 
    684684                                   if isinstance(attr, Property)] 
    685685        sorted_props = sorted(properties, key=lambda i: i[1]._counter) 
     
    696696 
    697697        # create trigger proxies 
    698         # TODO: support entity_name... It makes sense only for autoloaded  
     698        # TODO: support entity_name... It makes sense only for autoloaded 
    699699        # tables for now, and would make more sense if we support "external" 
    700700        # tables 
     
    730730    md.tables[cls._table_key] = table_proxy 
    731731 
    732     # We need to monkeypatch the metadata's table iterator method because  
    733     # otherwise it doesn't work if the setup is triggered by the  
     732    # We need to monkeypatch the metadata's table iterator method because 
     733    # otherwise it doesn't work if the setup is triggered by the 
    734734    # metadata.create_all(). 
    735     # This is because ManyToMany relationships add tables AFTER the list  
    736     # of tables that are going to be created is "computed"  
     735    # This is because ManyToMany relationships add tables AFTER the list 
     736    # of tables that are going to be created is "computed" 
    737737    # (metadata.tables.values()). 
    738738    # see: 
    739     # - table_iterator method in MetaData class in sqlalchemy/schema.py  
     739    # - table_iterator method in MetaData class in sqlalchemy/schema.py 
    740740    # - visit_metadata method in sqlalchemy/ansisql.py 
    741741    original_table_iterator = md.table_iterator 
    742     if not hasattr(original_table_iterator,  
     742    if not hasattr(original_table_iterator, 
    743743                   '_non_elixir_patched_iterator'): 
    744744        def table_iterator(*args, **kwargs): 
     
    773773    md = desc.metadata 
    774774 
    775     # the fake table could have already been removed (namely in a  
     775    # the fake table could have already been removed (namely in a 
    776776    # single table inheritance scenario) 
    777777    md.tables.pop(cls._table_key, None) 
     
    784784    del cls._has_triggers 
    785785 
    786      
     786 
    787787def setup_entities(entities): 
    788788    '''Setup all entities in the list passed as argument''' 
     
    808808def cleanup_entities(entities): 
    809809    """ 
    810     Try to revert back the list of entities passed as argument to the state  
    811     they had just before their setup phase. It will not work entirely for  
     810    Try to revert back the list of entities passed as argument to the state 
     811    they had just before their setup phase. It will not work entirely for 
    812812    autosetup entities as we need to remove the autosetup triggers. 
    813813 
    814     As of now, this function is *not* functional in that it doesn't revert to  
    815     the exact same state the entities were before setup. For example, the  
     814    As of now, this function is *not* functional in that it doesn't revert to 
     815    the exact same state the entities were before setup. For example, the 
    816816    properties do not work yet as those would need to be regenerated (since the 
    817     columns they are based on are regenerated too -- and as such the  
    818     corresponding joins are not correct) but this doesn't happen because of  
    819     the way relationship setup is designed to be called only once (especially  
     817    columns they are based on are regenerated too -- and as such the 
     818    corresponding joins are not correct) but this doesn't happen because of 
     819    the way relationship setup is designed to be called only once (especially 
    820820    the backref stuff in create_properties). 
    821821    """ 
     
    830830        entity.table = None 
    831831        entity.mapper = None 
    832          
     832 
    833833        desc._pk_col_done = False 
    834834        desc.has_pk = False 
     
    841841    ''' 
    842842    The base class for all entities 
    843      
     843 
    844844    All Elixir model objects should inherit from this class. Statements can 
    845845    appear within the body of the definition of an entity to define its 
    846846    fields, relationships, and other options. 
    847      
     847 
    848848    Here is an example: 
    849849 
    850850    .. sourcecode:: python 
    851      
     851 
    852852        class Person(Entity): 
    853853            name = Field(Unicode(128)) 
    854854            birthdate = Field(DateTime, default=datetime.now) 
    855      
     855 
    856856    Please note, that if you don't specify any primary keys, Elixir will 
    857857    automatically create one called ``id``. 
    858      
     858 
    859859    For further information, please refer to the provided examples or 
    860860    tutorial. 
    861861    ''' 
    862862    __metaclass__ = EntityMeta 
    863      
     863 
    864864    def __init__(self, **kwargs): 
    865865        for key, value in kwargs.items(): 
     
    891891                    # Build a lookup dict: {(pk1, pk2): value} 
    892892                    lookup = dict([ 
    893                         (tuple([getattr(o, c.name) for c in pkey]), o)  
     893                        (tuple([getattr(o, c.name) for c in pkey]), o) 
    894894                        for o in dbdata]) 
    895895                    for row in data[rname]: 
    896                         # If any primary key columns are missing or None,  
     896                        # If any primary key columns are missing or None, 
    897897                        # create a new object 
    898898                        if [1 for c in pkey if not row.get(c.name)]: 
     
    904904 
    905905                            # If the row isn't found, we must fail the request 
    906                             # in a web scenario, this could be a parameter  
     906                            # in a web scenario, this could be a parameter 
    907907                            # tampering attack 
    908908                            if not subobj: 
     
    932932            for col in table.c: 
    933933                columns.append(col) 
    934              
     934 
    935935        data = dict([(col.name, getattr(self, col.name)) 
    936936                     for col in columns if col.name not in exclude]) 
     
    961961        return object_session(self).expunge(self, *args, **kwargs) 
    962962 
    963     # This bunch of session methods, along with all the query methods below  
     963    # This bunch of session methods, along with all the query methods below 
    964964    # only make sense when using a global/scoped/contextual session. 
    965965    def _global_session(self): 
  • elixir/trunk/elixir/fields.py

    r320 r347  
    11''' 
    2 This module provides support for defining the fields (columns) of your  
     2This module provides support for defining the fields (columns) of your 
    33entities. Elixir currently supports two syntaxes to do so: the default 
    44`Attribute-based syntax`_ as well as the has_field_ DSL statement. 
    55 
    6 Note that the old with_fields_ statement is currently deprecated in favor of  
     6Note that the old with_fields_ statement is currently deprecated in favor of 
    77the `Attribute-based syntax`_. 
    88 
     
    2828 
    2929 
    30 The Field class takes one mandatory argument, which is its type. Please refer  
    31 to SQLAlchemy documentation for a list of `types supported by SQLAlchemy  
     30The Field class takes one mandatory argument, which is its type. Please refer 
     31to SQLAlchemy documentation for a list of `types supported by SQLAlchemy 
    3232<http://www.sqlalchemy.org/docs/04/types.html>`_. 
    3333 
    34 Following that first mandatory argument, fields can take any number of  
    35 optional keyword arguments. Please note that all the **arguments** that are  
    36 **not specifically processed by Elixir**, as mentioned in the documentation  
    37 below **are passed on to the SQLAlchemy ``Column`` object**. Please refer to  
    38 the `SQLAlchemy Column object's documentation  
     34Following that first mandatory argument, fields can take any number of 
     35optional keyword arguments. Please note that all the **arguments** that are 
     36**not specifically processed by Elixir**, as mentioned in the documentation 
     37below **are passed on to the SQLAlchemy ``Column`` object**. Please refer to 
     38the `SQLAlchemy Column object's documentation 
    3939<http://www.sqlalchemy.org/docs/04/sqlalchemy_schema.html 
    40 #docstrings_sqlalchemy.schema_Column>`_ for more details about other  
     40#docstrings_sqlalchemy.schema_Column>`_ for more details about other 
    4141supported keyword arguments. 
    4242 
     
    7878 
    7979The first argument is the name of the field, the second is its type. Following 
    80 these, any number of keyword arguments can be specified for additional  
     80these, any number of keyword arguments can be specified for additional 
    8181behavior. The following arguments are supported: 
    8282 
     
    111111----------- 
    112112The `with_fields` statement is **deprecated** in favor of the `attribute-based 
    113 syntax`_.  
    114  
    115 It allows you to define all fields of an entity at once.  
     113syntax`_. 
     114 
     115It allows you to define all fields of an entity at once. 
    116116Each keyword argument to this statement represents one field, which should 
    117 be a `Field` object. The first argument to a Field object is its type.  
     117be a `Field` object. The first argument to a Field object is its type. 
    118118Following it, any number of keyword arguments can be specified for 
    119 additional behavior. The `with_fields` statement supports the same keyword  
     119additional behavior. The `with_fields` statement supports the same keyword 
    120120arguments than the `has_field` statement. 
    121121 
     
    145145    ''' 
    146146    Represents the definition of a 'field' on an entity. 
    147      
     147 
    148148    This class represents a column on the table where the entity is stored. 
    149149    This object is only used with the `with_fields` syntax for defining all 
     
    151151    require the manual creation of this object. 
    152152    ''' 
    153      
     153 
    154154    def __init__(self, type, *args, **kwargs): 
    155155        super(Field, self).__init__() 
    156          
     156 
    157157        self.colname = kwargs.pop('colname', None) 
    158158        self.synonym = kwargs.pop('synonym', None) 
     
    165165        self.column = None 
    166166        self.property = None 
    167          
     167 
    168168        self.args = args 
    169169        self.kwargs = kwargs 
     
    205205 
    206206        if self.synonym: 
    207             self.entity._descriptor.add_property(self.synonym,  
     207            self.entity._descriptor.add_property(self.synonym, 
    208208                                                 synonym(self.name)) 
    209209 
     
    211211def has_field_handler(entity, name, *args, **kwargs): 
    212212    if 'through' in kwargs: 
    213         setattr(entity, name,  
    214                 association_proxy(kwargs.pop('through'),  
     213        setattr(entity, name, 
     214                association_proxy(kwargs.pop('through'), 
    215215                                  kwargs.pop('attribute', name), 
    216216                                  **kwargs)) 
  • elixir/trunk/elixir/options.py

    r336 r347  
    11''' 
    22This module provides support for defining several options on your Elixir 
    3 entities.  There are three different kinds of options that can be set  
     3entities.  There are three different kinds of options that can be set 
    44up, and for this there are three different statements: using_options_, 
    55using_table_options_ and using_mapper_options_. 
    66 
    7 Alternatively, these options can be set on all Elixir entities by modifying  
     7Alternatively, these options can be set on all Elixir entities by modifying 
    88the `options_defaults` dictionary before defining any entity. 
    99 
     
    1212The 'using_options' DSL statement allows you to set up some additional 
    1313behaviors on your model objects, including table names, ordering, and 
    14 more.  To specify an option, simply supply the option as a keyword  
     14more.  To specify an option, simply supply the option as a keyword 
    1515argument onto the statement, as follows: 
    1616 
     
    139139`using_table_options` 
    140140--------------------- 
    141 The 'using_table_options' DSL statement allows you to set up some  
    142 additional options on your entity table. It is meant only to handle the  
     141The 'using_table_options' DSL statement allows you to set up some 
     142additional options on your entity table. It is meant only to handle the 
    143143options which are not supported directly by the 'using_options' statement. 
    144 By opposition to the 'using_options' statement, these options are passed  
     144By opposition to the 'using_options' statement, these options are passed 
    145145directly to the underlying SQLAlchemy Table object (both non-keyword arguments 
    146146and keyword arguments) without any processing. 
     
    150150#docstrings_sqlalchemy.schema_Table>`_. 
    151151 
    152 You might also be interested in the section about `constraints  
     152You might also be interested in the section about `constraints 
    153153<http://www.sqlalchemy.org/docs/04/metadata.html#metadata_constraints>`_. 
    154154 
    155155`using_mapper_options` 
    156156---------------------- 
    157 The 'using_mapper_options' DSL statement allows you to set up some  
    158 additional options on your entity mapper. It is meant only to handle the  
     157The 'using_mapper_options' DSL statement allows you to set up some 
     158additional options on your entity mapper. It is meant only to handle the 
    159159options which are not supported directly by the 'using_options' statement. 
    160 By opposition to the 'using_options' statement, these options are passed  
    161 directly to the underlying SQLAlchemy mapper (as keyword arguments)  
     160By opposition to the 'using_options' statement, these options are passed 
     161directly to the underlying SQLAlchemy mapper (as keyword arguments) 
    162162without any processing. 
    163163 
    164 For further information, please refer to the `SQLAlchemy mapper  
    165 function's documentation  
     164For further information, please refer to the `SQLAlchemy mapper 
     165function's documentation 
    166166<http://www.sqlalchemy.org/docs/04/sqlalchemy_orm.html 
    167167#docstrings_sqlalchemy.orm_modfunc_mapper>`_. 
     
    187187POLYMORPHIC_COL_TYPE = String(POLYMORPHIC_COL_SIZE) 
    188188 
    189 #  
     189# 
    190190options_defaults = dict( 
    191191    autosetup=False, 
     
    217217            setattr(entity._descriptor, kwarg, kwargs[kwarg]) 
    218218        else: 
    219             raise Exception("'%s' is not a valid option for Elixir entities."  
     219            raise Exception("'%s' is not a valid option for Elixir entities." 
    220220                            % kwarg) 
    221221 
  • elixir/trunk/elixir/properties.py

    r327 r347  
    11''' 
    22This module provides support for defining properties on your entities. It both 
    3 provides, the `Property` class which acts as a building block for common  
    4 properties such as fields and relationships (for those, please consult the  
    5 corresponding modules), but also provides some more specialized properties,  
    6 such as `ColumnProperty` and `Synonym`. It also provides the GenericProperty  
    7 class which allows you to wrap any SQLAlchemy property, and its DSL-syntax  
     3provides, the `Property` class which acts as a building block for common 
     4properties such as fields and relationships (for those, please consult the 
     5corresponding modules), but also provides some more specialized properties, 
     6such as `ColumnProperty` and `Synonym`. It also provides the GenericProperty 
     7class which allows you to wrap any SQLAlchemy property, and its DSL-syntax 
    88equivalent: has_property_. 
    99 
    1010`has_property` 
    1111-------------- 
    12 The ``has_property`` statement allows you to define properties which rely on  
     12The ``has_property`` statement allows you to define properties which rely on 
    1313their entity's table (and columns) being defined before they can be declared 
    1414themselves. The `has_property` statement takes two arguments: first the name of 
     
    1616lambda) taking one argument and returning the desired SQLAlchemy property. That 
    1717function will be called whenever the entity table is completely defined, and 
    18 will be given the .c attribute of the entity as argument (as a way to access  
     18will be given the .c attribute of the entity as argument (as a way to access 
    1919the entity columns). 
    2020 
     
    2626        has_field('quantity', Float) 
    2727        has_field('unit_price', Float) 
    28         has_property('price',  
     28        has_property('price', 
    2929                     lambda c: column_property( 
    3030                         (c.quantity * c.unit_price).label('price'))) 
     
    3434from sqlalchemy.orm import column_property, synonym 
    3535 
    36 __doc_all__ = ['EntityBuilder', 'Property', 'GenericProperty',  
     36__doc_all__ = ['EntityBuilder', 'Property', 'GenericProperty', 
    3737               'ColumnProperty'] 
    3838 
    3939class EntityBuilder(object): 
    4040    ''' 
    41     Abstract base class for all entity builders. An Entity builder is a class  
    42     of objects which can be added to an Entity (usually by using special  
     41    Abstract base class for all entity builders. An Entity builder is a class 
     42    of objects which can be added to an Entity (usually by using special 
    4343    properties or statements) to "build" that entity. Building an entity, 
    4444    meaning to add columns to its "main" table, create other tables, add 
     
    6666 
    6767    def create_properties(self): 
    68         pass     
     68        pass 
    6969 
    7070    def before_mapper(self): 
    71         pass     
     71        pass 
    7272 
    7373    def after_mapper(self): 
    74         pass     
     74        pass 
    7575 
    7676    def finalize(self): 
    77         pass     
     77        pass 
    7878 
    7979 
     
    9898    ''' 
    9999    __metaclass__ = CounterMeta 
    100      
     100 
    101101    def __init__(self, *args, **kwargs): 
    102102        self.entity = None 
     
    135135                             (c.quantity * c.unit_price).label('price'))) 
    136136    ''' 
    137      
     137 
    138138    def __init__(self, prop): 
    139139        super(GenericProperty, self).__init__() 
     
    154154class ColumnProperty(GenericProperty): 
    155155    ''' 
    156     A specialized form of the GenericProperty to generate SQLAlchemy  
    157     ``column_property``'s.  
    158      
    159     It takes a single argument, which is a function (often  
    160     given as an anonymous lambda) taking one argument and returning the  
     156    A specialized form of the GenericProperty to generate SQLAlchemy 
     157    ``column_property``'s. 
     158 
     159    It takes a single argument, which is a function (often 
     160    given as an anonymous lambda) taking one argument and returning the 
    161161    desired (scalar-returning) SQLAlchemy ClauseElement. That function will be 
    162     called whenever the entity table is completely defined, and will be given  
    163     the .c attribute of the entity as argument (as a way to access the entity  
     162    called whenever the entity table is completely defined, and will be given 
     163    the .c attribute of the entity as argument (as a way to access the entity 
    164164    columns). The ColumnProperty will first wrap your ClauseElement in a label 
    165165    with the same name as the property, then wrap that in a column_property. 
     
    172172            price = ColumnProperty(lambda c: c.quantity * c.unit_price) 
    173173 
    174     Please look at the `corresponding SQLAlchemy  
     174    Please look at the `corresponding SQLAlchemy 
    175175    documentation <http://www.sqlalchemy.org/docs/04/mappers.html 
    176176    #advdatamapping_mapper_expressions>`_ for details. 
     
    184184    ''' 
    185185    This class represents a synonym property of another property (column, ...) 
    186     of an entity.  As opposed to the `synonym` kwarg to the Field class (which  
    187     share the same goal), this class can be used to define a synonym of a  
     186    of an entity.  As opposed to the `synonym` kwarg to the Field class (which 
     187    share the same goal), this class can be used to define a synonym of a 
    188188    property defined in a parent class (of the current class). On the other 
    189     hand, it cannot define a synonym for the purpose of using a standard python  
     189    hand, it cannot define a synonym for the purpose of using a standard python 
    190190    property in queries. See the Field class for details on that usage. 
    191191 
  • elixir/trunk/elixir/py23compat.py

    r313 r347  
    2323        if reverse: 
    2424            cmp = lambda self, other, cmp=cmp: -cmp(self,other) 
    25         l.sort(cmp)  
     25        l.sort(cmp) 
    2626 
    2727# sorted 
     
    6363        """ 
    6464        assert maxsplit >= 0 
    65          
     65 
    6666        if maxsplit == 0: return [s] 
    67          
     67 
    6868        # the following lines perform the function, but inefficiently. 
    6969        #  This may be adequate for compatibility purposes 
  • elixir/trunk/elixir/relationships.py

    r346 r347  
    11''' 
    2 This module provides support for defining relationships between your Elixir  
     2This module provides support for defining relationships between your Elixir 
    33entities.  Elixir currently supports two syntaxes to do so: the default 
    44`Attribute-based syntax`_ which supports the following types of relationships: 
    5 ManyToOne_, OneToMany_, OneToOne_ and ManyToMany_, as well as a  
    6 `DSL-based syntax`_ which provides the following statements: belongs_to_,  
    7 has_many_, has_one_ and has_and_belongs_to_many_.  
     5ManyToOne_, OneToMany_, OneToOne_ and ManyToMany_, as well as a 
     6`DSL-based syntax`_ which provides the following statements: belongs_to_, 
     7has_many_, has_one_ and has_and_belongs_to_many_. 
    88 
    99====================== 
     
    1111====================== 
    1212 
    13 The first argument to all these "normal" relationship classes is the name of  
    14 the class (entity) you are relating to.  
    15  
    16 Following that first mandatory argument, any number of additional keyword  
    17 arguments can be specified for advanced behavior. See each relationship type  
     13The first argument to all these "normal" relationship classes is the name of 
     14the class (entity) you are relating to. 
     15 
     16Following that first mandatory argument, any number of additional keyword 
     17arguments can be specified for advanced behavior. See each relationship type 
    1818for a list of their specific keyword arguments. At this point, we'll just note 
    19 that all the arguments that are not specifically processed by Elixir, as  
    20 mentioned in the documentation below are passed on to the SQLAlchemy  
    21 ``relation`` function. So, please refer to the `SQLAlchemy relation function's  
     19that all the arguments that are not specifically processed by Elixir, as 
     20mentioned in the documentation below are passed on to the SQLAlchemy 
     21``relation`` function. So, please refer to the `SQLAlchemy relation function's 
    2222documentation <http://www.sqlalchemy.org/docs/04/sqlalchemy_orm.html 
    23 #docstrings_sqlalchemy.orm_modfunc_relation>`_ for further detail about which  
    24 keyword arguments are supported.  
    25  
    26 You should keep in mind that the following  
    27 keyword arguments are automatically generated by Elixir and should not be used  
    28 unless you want to override the value provided by Elixir: ``uselist``,  
     23#docstrings_sqlalchemy.orm_modfunc_relation>`_ for further detail about which 
     24keyword arguments are supported. 
     25 
     26You should keep in mind that the following 
     27keyword arguments are automatically generated by Elixir and should not be used 
     28unless you want to override the value provided by Elixir: ``uselist``, 
    2929``remote_side``, ``secondary``, ``primaryjoin`` and ``secondaryjoin``. 
    3030 
    3131Additionally, if you want a bidirectionnal relationship, you should define the 
    3232inverse relationship on the other entity explicitly (as opposed to how 
    33 SQLAlchemy's backrefs are defined). In non-ambiguous situations, Elixir will  
     33SQLAlchemy's backrefs are defined). In non-ambiguous situations, Elixir will 
    3434match relationships together automatically. If there are several relationships 
    35 of the same type between two entities, Elixir is not able to determine which  
    36 relationship is the inverse of which, so you have to disambiguate the  
    37 situation by giving the name of the inverse relationship in the ``inverse``  
     35of the same type between two entities, Elixir is not able to determine which 
     36relationship is the inverse of which, so you have to disambiguate the 
     37situation by giving the name of the inverse relationship in the ``inverse`` 
    3838keyword argument. 
    3939 
     
    4343----------- 
    4444 
    45 Describes the child's side of a parent-child relationship.  For example,  
     45Describes the child's side of a parent-child relationship.  For example, 
    4646a `Pet` object may belong to its owner, who is a `Person`.  This could be 
    4747expressed like so: 
     
    5252        owner = ManyToOne('Person') 
    5353 
    54 Behind the scene, assuming the primary key of the `Person` entity is  
    55 an integer column named `id`, the ``ManyToOne`` relationship will  
    56 automatically add an integer column named `owner_id` to the entity, with a  
     54Behind the scene, assuming the primary key of the `Person` entity is 
     55an integer column named `id`, the ``ManyToOne`` relationship will 
     56automatically add an integer column named `owner_id` to the entity, with a 
    5757foreign key referencing the `id` column of the `Person` entity. 
    5858 
    59 In addition to the keyword arguments inherited from SQLAlchemy's relation  
     59In addition to the keyword arguments inherited from SQLAlchemy's relation 
    6060function, ``ManyToOne`` relationships accept the following optional arguments 
    6161which will be directed to the created column: 
     
    102102+----------------------+------------------------------------------------------+ 
    103103 
    104 Additionally, Elixir supports the belongs_to_ statement as an alternative,  
     104Additionally, Elixir supports the belongs_to_ statement as an alternative, 
    105105DSL-based, syntax to define ManyToOne_ relationships. 
    106106 
     
    119119        children = OneToMany('Person') 
    120120 
    121 Note that a ``OneToMany`` relationship **cannot exist** without a  
     121Note that a ``OneToMany`` relationship **cannot exist** without a 
    122122corresponding ``ManyToOne`` relationship in the other way. This is because the 
    123 ``OneToMany`` relationship needs the foreign key created by the ``ManyToOne``  
     123``OneToMany`` relationship needs the foreign key created by the ``ManyToOne`` 
    124124relationship. 
    125125 
    126 In addition to keyword arguments inherited from SQLAlchemy, ``OneToMany``  
     126In addition to keyword arguments inherited from SQLAlchemy, ``OneToMany`` 
    127127relationships accept the following optional (keyword) arguments: 
    128128 
     
    141141OneToMany_ relationships, with the has_many_ statement. 
    142142 
    143 Also, as for standard SQLAlchemy relations, the ``order_by`` keyword argument  
     143Also, as for standard SQLAlchemy relations, the ``order_by`` keyword argument 
    144144 
    145145 
     
    148148 
    149149Describes the parent's side of a parent-child relationship when there is only 
    150 one child.  For example, a `Car` object has one gear stick, which is  
     150one child.  For example, a `Car` object has one gear stick, which is 
    151151represented as a `GearStick` object. This could be expressed like so: 
    152152 
     
    159159        car = ManyToOne('Car') 
    160160 
    161 Note that a ``OneToOne`` relationship **cannot exist** without a corresponding  
     161Note that a ``OneToOne`` relationship **cannot exist** without a corresponding 
    162162``ManyToOne`` relationship in the other way. This is because the ``OneToOne`` 
    163163relationship needs the foreign_key created by the ``ManyToOne`` relationship. 
     
    183183        articles = ManyToMany('Article') 
    184184 
    185 Behind the scene, the ``ManyToMany`` relationship will  
     185Behind the scene, the ``ManyToMany`` relationship will 
    186186automatically create an intermediate table to host its data. 
    187187 
    188188Note that you don't necessarily need to define the inverse relationship.  In 
    189 our example, even though we want tags to be usable on several articles, we  
     189our example, even though we want tags to be usable on several articles, we 
    190190might not be interested in which articles correspond to a particular tag.  In 
    191191that case, we could have omitted the `Tag` side of the relationship. 
    192192 
    193 If the entity containing your ``ManyToMany`` relationship is  
     193If the entity containing your ``ManyToMany`` relationship is 
    194194autoloaded, you **must** specify at least one of either the ``remote_side`` or 
    195195``local_side`` argument. 
    196196 
    197 In addition to keyword arguments inherited from SQLAlchemy, ``ManyToMany``  
     197In addition to keyword arguments inherited from SQLAlchemy, ``ManyToMany`` 
    198198relationships accept the following optional (keyword) arguments: 
    199199 
     
    238238 
    239239The following DSL statements provide an alternative way to define relationships 
    240 between your entities. The first argument to all those statements is the name  
    241 of the relationship, the second is the 'kind' of object you are relating to  
    242 (it is usually given using the ``of_kind`` keyword).  
     240between your entities. The first argument to all those statements is the name 
     241of the relationship, the second is the 'kind' of object you are relating to 
     242(it is usually given using the ``of_kind`` keyword). 
    243243 
    244244`belongs_to` 
     
    269269        has_many('children', of_kind='Person') 
    270270 
    271 There is also an alternate form of the ``has_many`` relationship that takes  
    272 only two keyword arguments: ``through`` and ``via`` in order to encourage a  
    273 richer form of many-to-many relationship that is an alternative to the  
     271There is also an alternate form of the ``has_many`` relationship that takes 
     272only two keyword arguments: ``through`` and ``via`` in order to encourage a 
     273richer form of many-to-many relationship that is an alternative to the 
    274274``has_and_belongs_to_many`` statement.  Here is an example: 
    275275 
     
    313313------------------------- 
    314314 
    315 The ``has_and_belongs_to_many`` statement is the DSL syntax equivalent to the  
     315The ``has_and_belongs_to_many`` statement is the DSL syntax equivalent to the 
    316316ManyToMany_ relationship. As such, it supports all the same arguments as 
    317317ManyToMany_ relationships. 
     
    347347    Base class for relationships. 
    348348    ''' 
    349      
     349 
    350350    def __init__(self, of_kind, *args, **kwargs): 
    351351        super(Relationship, self).__init__() 
     
    357357        self._target = None 
    358358        self._inverse = None 
    359          
     359 
    360360        self.property = None # sqlalchemy property 
    361361        self.backref = None  # sqlalchemy backref 
     
    368368        super(Relationship, self).attach(entity, name) 
    369369        entity._descriptor.relationships.append(self) 
    370      
     370 
    371371    def create_pk_cols(self): 
    372372        self.create_keys(True) 
     
    377377    def create_keys(self, pk): 
    378378        ''' 
    379         Subclasses (ie. concrete relationships) may override this method to  
     379        Subclasses (ie. concrete relationships) may override this method to 
    380380        create foreign keys. 
    381381        ''' 
    382      
     382 
    383383    def create_tables(self): 
    384384        ''' 
    385         Subclasses (ie. concrete relationships) may override this method to  
     385        Subclasses (ie. concrete relationships) may override this method to 
    386386        create secondary tables. 
    387387        ''' 
    388      
     388 
    389389    def create_properties(self): 
    390390        ''' 
     
    425425        return self._target 
    426426    target = property(target) 
    427      
     427 
    428428    def inverse(self): 
    429429        if not self._inverse: 
     
    434434                    raise Exception( 
    435435                              "Couldn't find a relationship named '%s' in " 
    436                               "entity '%s' or its parent entities."  
     436                              "entity '%s' or its parent entities." 
    437437                              % (self.inverse_name, self.target.__name__)) 
    438438                assert self.match_type_of(inverse) 
     
    443443                self._inverse = inverse 
    444444                inverse._inverse = self 
    445          
     445 
    446446        return self._inverse 
    447447    inverse = property(inverse) 
    448      
     448 
    449449    def match_type_of(self, other): 
    450450        return False 
     
    461461class ManyToOne(Relationship): 
    462462    ''' 
    463      
     463 
    464464    ''' 
    465      
     465 
    466466    def __init__(self, *args, **kwargs): 
    467467        self.colname = kwargs.pop('colname', []) 
     
    480480        if 'use_alter' in kwargs: 
    481481            self.constraint_kwargs['use_alter'] = kwargs.pop('use_alter') 
    482          
     482 
    483483        if 'ondelete' in kwargs: 
    484484            self.constraint_kwargs['ondelete'] = kwargs.pop('ondelete') 
    485485        if 'onupdate' in kwargs: 
    486486            self.constraint_kwargs['onupdate'] = kwargs.pop('onupdate') 
    487          
     487 
    488488        self.foreign_key = list() 
    489489        self.primaryjoin_clauses = list() 
    490490 
    491491        super(ManyToOne, self).__init__(*args, **kwargs) 
    492      
     492 
    493493    def match_type_of(self, other): 
    494494        return isinstance(other, (OneToMany, OneToOne)) 
     
    496496    def create_keys(self, pk): 
    497497        ''' 
    498         Find all primary keys on the target and create foreign keys on the  
     498        Find all primary keys on the target and create foreign keys on the 
    499499        source accordingly. 
    500500        ''' 
     
    508508        source_desc = self.entity._descriptor 
    509509        #TODO: make this work if target is a pure SA-mapped class 
    510         # for that, I need:  
     510        # for that, I need: 
    511511        # - the list of primary key columns of the target table (type and name) 
    512512        # - the name of the target table 
     
    520520            if self.colname: 
    521521                self.primaryjoin_clauses = \ 
    522                     _get_join_clauses(self.entity.table,  
    523                                       self.colname, None,  
     522                    _get_join_clauses(self.entity.table, 
     523                                      self.colname, None, 
    524524                                      self.target.table)[0] 
    525525                if not self.primaryjoin_clauses: 
     
    539539                        "'%s' entity is not the same as the number of columns " 
    540540                        "of the primary key of '%s'." 
    541                         % (self.name, self.entity.__name__,  
     541                        % (self.name, self.entity.__name__, 
    542542                           self.target.__name__)) 
    543543 
     
    546546                raise Exception("No primary key found in target table ('%s') " 
    547547                                "for the '%s' relationship of the '%s' entity." 
    548                                 % (self.target.tablename, self.name,  
     548                                % (self.target.tablename, self.name, 
    549549                                   self.entity.__name__)) 
    550550 
     
    554554                else: 
    555555                    colname = options.FKCOL_NAMEFORMAT % \ 
    556                               {'relname': self.name,  
     556                              {'relname': self.name, 
    557557                               'key': pk_col.key} 
    558558 
     
    569569                fk_colnames.append(col.key) 
    570570 
    571                 # Build the list of column "paths" the foreign key will  
     571                # Build the list of column "paths" the foreign key will 
    572572                # point to 
    573573                target_path = "%s.%s" % (target_desc.tablename, pk_col.key) 
     
    577577                fk_refcols.append(target_path) 
    578578 
    579                 # Build up the primary join. This is needed when you have  
     579                # Build up the primary join. This is needed when you have 
    580580                # several belongs_to relationships between two objects 
    581581                self.primaryjoin_clauses.append(col == pk_col) 
    582              
     582 
    583583            if 'name' not in self.constraint_kwargs: 
    584584                # In some databases (at least MySQL) the constraint name needs 
    585585                # to be unique for the whole database, instead of per table. 
    586586                fk_name = options.CONSTRAINT_NAMEFORMAT % \ 
    587                           {'tablename': source_desc.tablename,  
     587                          {'tablename': source_desc.tablename, 
    588588                           'colnames': '_'.join(fk_colnames)} 
    589589                self.constraint_kwargs['name'] = fk_name 
    590                  
     590 
    591591            source_desc.add_constraint( 
    592592                ForeignKeyConstraint(fk_colnames, fk_refcols, 
     
    595595    def get_prop_kwargs(self): 
    596596        kwargs = {'uselist': False} 
    597          
     597 
    598598        if self.entity.table is self.target.table: 
    599599            kwargs['remote_side'] = \ 
     
    626626                      % (self.target.__name__, self.name, 
    627627                         self.entity.__name__)) 
    628      
     628 
    629629    def get_prop_kwargs(self): 
    630630        kwargs = {'uselist': self.uselist} 
    631          
     631 
    632632        #TODO: for now, we don't break any test if we remove those 2 lines. 
    633633        # So, we should either complete the selfref test to prove that they 
     
    639639            # autoloaded tables 
    640640            kwargs['remote_side'] = self.inverse.foreign_key 
    641          
     641 
    642642        if self.inverse.primaryjoin_clauses: 
    643643            kwargs['primaryjoin'] = and_(*self.inverse.primaryjoin_clauses) 
     
    650650class OneToMany(OneToOne): 
    651651    uselist = True 
    652      
     652 
    653653    def get_prop_kwargs(self): 
    654654        kwargs = super(OneToMany, self).get_prop_kwargs() 
     
    688688    def create_tables(self): 
    689689        # Warning: if the table was specified manually, the join clauses won't 
    690         # be computed. We might want to autodetect joins based on fk, as for  
     690        # be computed. We might want to autodetect joins based on fk, as for 
    691691        # autoloaded entities 
    692692        if self.secondary_table: 
    693693            return 
    694          
     694 
    695695        if self.inverse: 
    696696            if self.inverse.secondary_table: 
     
    707707        assert e1_schema == e2_schema, \ 
    708708               "Schema %r for entity %s differs from schema %r of entity %s" \ 
    709                % (e1_schema, self.entity.__name__,  
     709               % (e1_schema, self.entity.__name__, 
    710710                  e2_schema, self.target.__name__) 
    711         
    712         # First, we compute the name of the table. Note that some of the  
    713         # intermediary variables are reused later for the constraint  
     711 
     712        # First, we compute the name of the table. Note that some of the 
     713        # intermediary variables are reused later for the constraint 
    714714        # names. 
    715          
    716         # We use the name of the relation for the first entity  
    717         # (instead of the name of its primary key), so that we can  
    718         # have two many-to-many relations between the same objects  
    719         # without having a table name collision.  
     715 
     716        # We use the name of the relation for the first entity 
     717        # (instead of the name of its primary key), so that we can 
     718        # have two many-to-many relations between the same objects 
     719        # without having a table name collision. 
    720720        source_part = "%s_%s" % (e1_desc.tablename, self.name) 
    721721 
    722722        # And we use only the name of the table of the second entity 
    723         # when there is no inverse, so that a many-to-many relation  
     723        # when there is no inverse, so that a many-to-many relation 
    724724        # can be defined without an inverse. 
    725725        if self.inverse: 
     
    727727        else: 
    728728            target_part = e2_desc.tablename 
    729          
     729 
    730730        if self.user_tablename: 
    731731            tablename = self.user_tablename 
    732732        else: 
    733             # We need to keep the table name consistent (independant of  
     733            # We need to keep the table name consistent (independant of 
    734734            # whether this relation or its inverse is setup first). 
    735735            if self.inverse and e1_desc.tablename < e2_desc.tablename: 
     
    741741            self._reflect_table(tablename) 
    742742        else: 
    743             # We pre-compute the names of the foreign key constraints  
    744             # pointing to the source (local) entity's table and to the  
     743            # We pre-compute the names of the foreign key constraints 
     744            # pointing to the source (local) entity's table and to the 
    745745            # target's table 
    746746 
    747             # In some databases (at least MySQL) the constraint names need  
     747            # In some databases (at least MySQL) the constraint names need 
    748748            # to be unique for the whole database, instead of per table. 
    749749            source_fk_name = "%s_fk" % source_part 
     
    758758            joins = (self.primaryjoin_clauses, self.secondaryjoin_clauses) 
    759759            for num, desc, fk_name, m2m in ( 
    760                     (0, e1_desc, source_fk_name, self),  
     760                    (0, e1_desc, source_fk_name, self), 
    761761                    (1, e2_desc, target_fk_name, self.inverse)): 
    762762                fk_colnames = list() 
    763763                fk_refcols = list() 
    764              
     764 
    765765                for pk_col in desc.primary_keys: 
    766766                    colname = self.column_format % \ 
     
    768768                               'key': pk_col.key, 
    769769                               'entity': desc.entity.__name__.lower()} 
    770                      
    771                     # In case we have a many-to-many self-reference, we  
    772                     # need to tweak the names of the columns so that we  
     770 
     771                    # In case we have a many-to-many self-reference, we 
     772                    # need to tweak the names of the columns so that we 
    773773                    # don't end up with twice the same column name. 
    774774                    if self.entity is self.target: 
    775775                        colname += str(num + 1) 
    776                      
     776 
    777777                    col = Column(colname, pk_col.type, primary_key=True) 
    778778                    columns.append(col) 
    779779 
    780                     # Build the list of local columns which will be part  
     780                    # Build the list of local columns which will be part 
    781781                    # of the foreign key. 
    782782                    fk_colnames.append(colname) 
    783783 
    784                     # Build the list of column "paths" the foreign key will  
     784                    # Build the list of column "paths" the foreign key will 
    785785                    # point to 
    786786                    target_path = "%s.%s" % (desc.tablename, pk_col.key) 
     
    793793                    if self.entity is self.target: 
    794794                        joins[num].append(col == pk_col) 
    795                  
     795 
    796796                onupdate = m2m and m2m.onupdate 
    797797                ondelete = m2m and m2m.ondelete 
    798                  
     798 
    799799                constraints.append( 
    800800                    ForeignKeyConstraint(fk_colnames, fk_refcols, 
    801                                          name=fk_name, onupdate=onupdate,  
     801                                         name=fk_name, onupdate=onupdate, 
    802802                                         ondelete=ondelete)) 
    803803 
    804804            args = columns + constraints 
    805              
    806             self.secondary_table = Table(tablename, e1_desc.metadata,  
     805 
     806            self.secondary_table = Table(tablename, e1_desc.metadata, 
    807807                                         schema=e1_schema, *args) 
    808808 
     
    815815                % (self.entity.__name__, self.name, 
    816816                   self.target.__name__)) 
    817                  
    818         self.secondary_table = Table(tablename,  
     817 
     818        self.secondary_table = Table(tablename, 
    819819                                     self.entity._descriptor.metadata, 
    820820                                     autoload=True) 
     
    822822        # In the case we have a self-reference, we need to build join clauses 
    823823        if self.entity is self.target: 
    824             #CHECKME: maybe we should try even harder by checking if that  
     824            #CHECKME: maybe we should try even harder by checking if that 
    825825            # information was defined on the inverse relationship) 
    826826            if not self.local_side and not self.remote_side: 
     
    834834 
    835835            self.primaryjoin_clauses, self.secondaryjoin_clauses = \ 
    836                 _get_join_clauses(self.secondary_table,  
    837                                   self.local_side, self.remote_side,  
     836                _get_join_clauses(self.secondary_table, 
     837                                  self.local_side, self.remote_side, 
    838838                                  self.entity.table) 
    839839 
    840840    def get_prop_kwargs(self): 
    841         kwargs = {'secondary': self.secondary_table,  
     841        kwargs = {'secondary': self.secondary_table, 
    842842                  'uselist': self.uselist} 
    843843 
     
    856856    def is_inverse(self, other): 
    857857        return super(ManyToMany, self).is_inverse(other) and \ 
    858                (self.user_tablename == other.user_tablename or  
     858               (self.user_tablename == other.user_tablename or 
    859859                (not self.user_tablename and not other.user_tablename)) 
    860860 
     
    895895    # one of the joins (primary or secondary), or we assume the current 
    896896    # columns match because the columns for this join were not given and we 
    897     # know the other join is either not used (is None) or has an explicit  
     897    # know the other join is either not used (is None) or has an explicit 
    898898    # match. 
    899          
     899 
    900900#TODO: rewrite this. Even with the comment, I don't even understand it myself. 
    901901    for cols, constraint in constraint_map.iteritems(): 
    902         if cols == cols1 or (cols != cols2 and  
     902        if cols == cols1 or (cols != cols2 and 
    903903                             not cols1 and (cols2 in constraint_map or 
    904904                                            cols2 is None)): 
     
    916916    def handler(entity, name, *args, **kwargs): 
    917917        if 'through' in kwargs and 'via' in kwargs: 
    918             setattr(entity, name,  
    919                     association_proxy(kwargs.pop('through'),  
     918            setattr(entity, name, 
     919                    association_proxy(kwargs.pop('through'), 
    920920                                      kwargs.pop('via'), 
    921921                                      **kwargs)) 
  • elixir/trunk/elixir/statements.py

    r311 r347  
    33MUTATORS = '__elixir_mutators__' 
    44 
    5 class ClassMutator(object):     
     5class ClassMutator(object): 
    66    ''' 
    77    DSL-style syntax 
    8      
     8 
    99    A ``ClassMutator`` object represents a DSL term. 
    1010    ''' 
    11      
     11 
    1212    def __init__(self, handler): 
    1313        ''' 
     
    1616        ''' 
    1717        self.handler = handler 
    18      
     18 
    1919    # called when a mutator (eg. "has_field(...)") is parsed 
    2020    def __call__(self, *args, **kwargs): 
     
    2828    def process(self, entity, *args, **kwargs): 
    2929        ''' 
    30         Process one mutator. This version simply calls the handler callable,  
     30        Process one mutator. This version simply calls the handler callable, 
    3131        but another mutator (sub)class could do more processing. 
    3232        ''' 
     
    3737def process_mutators(entity): 
    3838    ''' 
    39     Apply all mutators of the given entity. That is, loop over all mutators  
     39    Apply all mutators of the given entity. That is, loop over all mutators 
    4040    in the class's mutator list and process them. 
    4141    ''' 
  • elixir/trunk/release.howto

    r326 r347  
    2424 
    2525- announce release on those lists: 
    26     - Elixir <sqlelixir@googlegroups.com>  
     26    - Elixir <sqlelixir@googlegroups.com> 
    2727    - SQLAlchemy <sqlalchemy@googlegroups.com> 
    2828    - TurboGears <turbogears@googlegroups.com> 
  • elixir/trunk/setup.py

    r336 r347  
    1414databases without losing the convenience of Python objects. 
    1515 
    16 Elixir is intended to replace the ActiveMapper SQLAlchemy extension, and the  
     16Elixir is intended to replace the ActiveMapper SQLAlchemy extension, and the 
    1717TurboEntity project but does not intend to replace SQLAlchemy's core features, 
    18 and instead focuses on providing a simpler syntax for defining model objects  
    19 when you do not need the full expressiveness of SQLAlchemy's manual mapper  
     18and instead focuses on providing a simpler syntax for defining model objects 
     19when you do not need the full expressiveness of SQLAlchemy's manual mapper 
    2020definitions. 
    2121""", 
  • elixir/trunk/tests/a.py

    r234 r347  
    44    name = Field(String(30)) 
    55    b = ManyToOne('tests.b.B') 
    6  
  • elixir/trunk/tests/test_acts_as_list.py

    r293 r347  
    44def setup(): 
    55    global ToDo, Person 
    6      
     6 
    77    class ToDo(Entity): 
    88        subject = Field(String(128)) 
     
    1111        def qualify(self): 
    1212            return ToDo.owner_id == self.owner_id 
    13              
     13 
    1414        acts_as_list(qualifier=qualify, column_name='position') 
    15          
     15 
    1616        def __repr__(self): 
    1717            return '<%d:%s>' % (self.position, self.subject) 
    18      
     18 
    1919    class Person(Entity): 
    2020        name = Field(String(64)) 
    2121        todos = OneToMany('ToDo', order_by='position') 
    22      
    23      
     22 
     23 
    2424    setup_all() 
    2525    metadata.bind = 'sqlite:///' 
     
    3333    def setup(self): 
    3434        create_all() 
    35      
     35 
    3636    def teardown(self): 
    3737        drop_all() 
    3838        session.clear() 
    39      
     39 
    4040    def test_acts_as_list(self): 
    4141        # create a person 
    4242        # you must create and flush this _before_ you attach todo's to it 
    43         # because of the way that the plugin is implemented  
     43        # because of the way that the plugin is implemented 
    4444        p = Person(name='Jonathan') 
    4545        session.flush(); session.clear() 
    46          
     46 
    4747        # add three todos, in the reverse order that we want them 
    4848        p = Person.get(1) 
     
    5151        p.todos.append(ToDo(subject='One')) 
    5252        session.flush(); session.clear() 
    53          
     53 
    5454        # move the first item lower 
    5555        p = Person.get(1) 
    56         p.todos[0].move_lower()  
     56        p.todos[0].move_lower() 
    5757        session.flush(); session.clear() 
    58          
     58 
    5959        # validate it worked 
    6060        p = Person.get(1) 
     
    6262        assert p.todos[1].subject == 'Three' 
    6363        assert p.todos[2].subject == 'One' 
    64          
     64 
    6565        # move the last item to the top to put things in correct order 
    6666        p.todos[2].move_to_top() 
    6767        session.flush(); session.clear() 
    68          
     68 
    6969        # validate it worked 
    7070        p = Person.get(1) 
     
    7272        assert p.todos[1].subject == 'Two' 
    7373        assert p.todos[2].subject == 'Three' 
    74          
     74 
    7575        # lets shuffle them again for the sake of testing move_to_bottom 
    7676        # and move_to_top 
    7777        p.todos[2].move_to_top() 
    7878        session.flush(); session.clear() 
    79          
     79 
    8080        p = Person.get(1) 
    8181        assert p.todos[0].subject == 'Three' 
    8282        assert p.todos[1].subject == 'One' 
    8383        assert p.todos[2].subject == 'Two' 
    84          
     84 
    8585        p.todos[1].move_to_bottom() 
    8686        session.flush(); session.clear() 
    87          
     87 
    8888        p = Person.get(1) 
    8989        assert p.todos[0].subject == 'Three' 
    9090        assert p.todos[1].subject == 'Two' 
    9191        assert p.todos[2].subject == 'One' 
    92                  
     92 
    9393        # lets move everything back to how it should be 
    9494        p = Person.get(1) 
     
    9696        p.todos[2].move_to(1) 
    9797        session.flush(); session.clear() 
    98          
     98 
    9999        # validate it worked 
    100100        p = Person.get(1) 
     
    105105        assert p.todos[2].subject == 'Three' 
    106106        assert p.todos[2].position == 3 
    107                  
     107 
    108108        # delete the second todo list item 
    109109        p.todos[1].delete() 
    110110        session.flush(); session.clear() 
    111          
     111 
    112112        # validate that the deletion worked, and sequence numebers 
    113         # were properly managed         
     113        # were properly managed 
    114114        p = Person.get(1) 
    115115        assert p.todos[0].subject == 'One' 
  • elixir/trunk/tests/test_associable.py

    r336 r347  
    1515    def teardown(self): 
    1616        cleanup_all(True) 
    17      
     17 
    1818    def test_empty(self): 
    1919        class Foo(Entity): 
     
    8383        assert '243 Hooper st.' in streets 
    8484        assert '123 Elm St.' in streets 
    85          
     85 
    8686        people = Person.select_addresses(and_(Address.street=='132 Elm St', 
    8787                                              Address.city=='Smallville')) 
  • elixir/trunk/tests/test_autoload.py

    r327 r347  
    5656        pets = OneToMany('Animal', inverse='owner') 
    5757        animals = OneToMany('Animal', inverse='feeder') 
    58         categories = ManyToMany('Category',  
     58        categories = ManyToMany('Category', 
    5959                                tablename='person_category') 
    6060        appreciate = ManyToMany('Person', 
     
    7171 
    7272    class Category(Entity): 
    73         persons = ManyToMany('Person',  
     73        persons = ManyToMany('Person', 
    7474                                tablename='person_category') 
    7575 
     
    8787    def setup(self): 
    8888        create_all() 
    89          
     89 
    9090    def teardown(self): 
    9191        drop_all() 
    9292        session.clear() 
    93      
     93 
    9494    def test_simple(self): 
    9595        snowball = Animal(name="Snowball II") 
     
    9797        homer = Person(name="Homer", animals=[snowball, slh], pets=[slh]) 
    9898        lisa = Person(name="Lisa", pets=[snowball]) 
    99          
    100         session.flush() 
    101         session.clear() 
    102          
     99 
     100        session.flush() 
     101        session.clear() 
     102 
    103103        homer = Person.get_by(name="Homer") 
    104104        lisa = Person.get_by(name="Lisa") 
    105105        slh = Animal.get_by(name="Santa's Little Helper") 
    106          
     106 
    107107        assert len(homer.animals) == 2 
    108108        assert homer == lisa.pets[0].feeder 
     
    114114        bart = Person(name="Bart") 
    115115        lisa = Person(name="Lisa") 
    116          
     116 
    117117        grampa.children.append(homer) 
    118118        homer.children.append(bart) 
    119119        lisa.father = homer 
    120          
    121         session.flush() 
    122         session.clear() 
    123          
     120 
     121        session.flush() 
     122        session.clear() 
     123 
    124124        p = Person.get_by(name="Homer") 
    125          
     125 
    126126        assert p in p.father.children 
    127127        assert p.father.name == "Abe" 
     
    138138        bart = Person(name="Bart") 
    139139        lisa = Person(name="Lisa") 
    140          
     140 
    141141        simpson.persons.extend([bart, lisa]) 
    142          
    143         session.flush() 
    144         session.clear() 
    145          
     142 
     143        session.flush() 
     144        session.clear() 
     145 
    146146        c = Category.get_by(name="Simpson") 
    147147        grampa = Person.get_by(name="Abe") 
    148          
     148 
    149149        print "Persons in the '%s' category: %s." % ( 
    150                 c.name,  
     150                c.name, 
    151151                ", ".join([p.name for p in c.persons])) 
    152          
     152 
    153153        assert len(c.persons) == 4 
    154154        assert c in grampa.categories 
     
    160160        session.flush() 
    161161        session.clear() 
    162          
     162 
    163163        homer = Person.get_by(name="Homer") 
    164164        barney = Person.get_by(name="Barney") 
     
    208208            Column('id', Integer), 
    209209            Column('name', String(32))) 
    210          
     210 
    211211        local_meta.create_all() 
    212212 
     
    243243            using_options(tablename='father') 
    244244 
    245         class Son(Father):  
     245        class Son(Father): 
    246246            pass 
    247247 
  • elixir/trunk/tests/test_autoload_mixed.py

    r272 r347  
    1313        (user_id INTEGER PRIMARY KEY AUTOINCREMENT)""") 
    1414        conn.close() 
    15          
     15 
    1616    def test_belongs_to(self): 
    1717        class User(Entity): 
  • elixir/trunk/tests/test_autosetup.py

    r275 r347  
    1717    def teardown(self): 
    1818        cleanup_all() 
    19      
     19 
    2020    def test_autosetup_manual_setup_all(self): 
    2121        class Person(Entity): 
     
    2525        # check that we have a fake table installed 
    2626        assert 'person' in metadata.tables 
    27         assert isinstance(metadata.tables['person'],  
     27        assert isinstance(metadata.tables['person'], 
    2828                          elixir.entity.TriggerProxy) 
    2929 
     
    4040        # check that we have a fake table installed 
    4141        assert 'person' in metadata.tables 
    42         assert isinstance(metadata.tables['person'],  
     42        assert isinstance(metadata.tables['person'], 
    4343                          elixir.entity.TriggerProxy) 
    4444 
     
    4646 
    4747        assert 'person' not in metadata.tables 
    48      
     48 
    4949    def test_drop_create_drop(self): 
    5050        class User(Entity): 
     
    7272        # check that accessing the table didn't trigger the setup 
    7373        assert 'person' not in metadata.tables 
    74          
     74 
    7575        setup_all() 
    7676 
     
    8585        homer = Person(name="Homer Simpson") 
    8686        assert isinstance(metadata.tables['person'], Table) 
    87          
     87 
    8888    def test_getattr(self): 
    8989        class Person(Entity): 
     
    9292 
    9393        tablename = Person.table.name 
    94         assert tablename == 'person'  
     94        assert tablename == 'person' 
    9595        assert isinstance(metadata.tables['person'], Table) 
    9696 
     
    123123#    def test_mapper(self): 
    124124        # we want to hit the mapper directly (without hitting any of the 
    125         # other triggers first). We do so by getting a query object using a  
     125        # other triggers first). We do so by getting a query object using a 
    126126        # manual session. 
    127127#        class Person(Entity): 
  • elixir/trunk/tests/test_class_methods.py

    r271 r347  
    1010    def setup(self): 
    1111        metadata.bind = 'sqlite:///' 
    12      
     12 
    1313    def teardown(self): 
    1414        cleanup_all() 
    15      
     15 
    1616    def test_get(self): 
    1717        class A(Entity): 
     
    2121 
    2222        a1 = A(name="a1") 
    23          
     23 
    2424        session.flush() 
    2525        session.clear() 
    26          
     26 
    2727        assert A.get(1).name == "a1" 
    2828 
  • elixir/trunk/tests/test_collections.py

    r343 r347  
    1717    def teardown(self): 
    1818        cleanup_all() 
    19      
     19 
    2020    def test_no_collection(self): 
    2121        class Person(Entity): 
  • elixir/trunk/tests/test_custombase.py

    r330 r347  
    2626 
    2727        setup_all(True) 
    28          
     28 
    2929        a1 = A(name="a1") 
    30          
     30 
    3131        session.flush() 
    3232        session.clear() 
    33          
     33 
    3434        a = A.query.filter_by(name="a1").one() 
    35          
     35 
    3636        assert a.name == 'a1' 
    3737 
     
    4242        class B(A): 
    4343            data = Field(String(30)) 
    44              
     44 
    4545        setup_all(True) 
    46          
     46 
    4747        a1 = A(name="a1") 
    4848        b1 = B(name="b1", data="-b1-") 
    49          
     49 
    5050        session.flush() 
    5151        session.clear() 
    52          
     52 
    5353        b = A.query.filter_by(name="b1").one() 
    54          
     54 
    5555        assert b.data == '-b1-' 
    5656 
     
    6767 
    6868        setup_all(True) 
    69          
     69 
    7070        a1 = A() 
    71         a1.name = "a1"  
    72          
     71        a1.name = "a1" 
     72 
    7373        session.flush() 
    7474        session.clear() 
    75          
     75 
    7676        a = A.query.filter_by(name="a1").one() 
    77          
     77 
    7878        assert a.name == 'a1' 
    7979        assert a.test() == "success" 
    80          
     80 
     81    def test_base_with_fields(self): 
     82        class FieldBase(object): 
     83            __metaclass__ = EntityMeta 
     84 
     85            common = Field(String(32)) 
     86 
     87            def __init__(self, **kwargs): 
     88                for key, value in kwargs.items(): 
     89                    setattr(self, key, value) 
     90 
     91        class A(FieldBase): 
     92            name = Field(String(32)) 
     93 
     94        class B(FieldBase): 
     95            pass 
     96 
     97        setup_all(True) 
     98 
     99        assert 'name' in A.table.columns.keys() 
     100        assert 'common' in A.table.columns.keys() 
     101        assert 'common' in B.table.columns.keys() 
  • elixir/trunk/tests/test_encryption.py

    r310 r347  
    11from elixir import * 
    22from elixir.ext.encrypted import acts_as_encrypted 
    3   
     3 
    44 
    55def setup(): 
    66    global Person, Pet 
    7      
     7 
    88    class Person(Entity): 
    99        name = Field(String(50)) 
     
    1919        owner = ManyToOne('Person') 
    2020 
    21      
     21 
    2222    metadata.bind = 'sqlite:///' 
    2323 
     
    3030    def setup(self): 
    3131        setup_all(True) 
    32      
     32 
    3333    def teardown(self): 
    3434        drop_all() 
    3535        session.clear() 
    36      
    37     def test_encryption(self):     
     36 
     37    def test_encryption(self): 
    3838        jonathan = Person( 
    39             name=u'Jonathan LaCour',  
    40             password=u's3cr3tw0RD',  
     39            name=u'Jonathan LaCour', 
     40            password=u's3cr3tw0RD', 
    4141            ssn=u'123-45-6789' 
    4242        ) 
  • elixir/trunk/tests/test_events.py

    r313 r347  
    1515def setup(): 
    1616    global Document 
    17      
     17 
    1818    class Document(Entity): 
    1919        name = Field(String(50)) 
    20          
     20 
    2121        def pre_insert(self): 
    2222            global before_insert_called 
    2323            before_insert_called += 1 
    2424        pre_insert = before_insert(pre_insert) 
    25          
     25 
    2626        def post_insert(self): 
    2727            global after_insert_called 
     
    3333            before_update_called += 1 
    3434        pre_update = before_update(pre_update) 
    35          
     35 
    3636        def post_update(self): 
    3737            global after_update_called 
     
    4343            before_delete_called += 1 
    4444        pre_delete = before_delete(pre_delete) 
    45          
     45 
    4646        def post_delete(self): 
    4747            global after_delete_called 
    4848            after_delete_called += 1 
    4949        post_delete = after_delete(post_delete) 
    50      
     50 
    5151        def pre_any(self): 
    5252            global before_any_called 
     
    6565    def setup(self): 
    6666        create_all() 
    67      
     67 
    6868    def teardown(self): 
    6969        drop_all() 
    7070        session.clear() 
    71      
     71 
    7272    def test_events(self): 
    7373        d = Document(name='My Document') 
    7474        session.flush(); session.clear() 
    75          
     75 
    7676        d = Document.query.get(1) 
    7777        d.name = 'My Document Updated' 
    7878        session.flush(); session.clear() 
    79          
     79 
    8080        d = Document.query.get(1) 
    8181        d.delete() 
    8282        session.flush(); session.clear() 
    83          
     83 
    8484        assert before_insert_called == 1 
    8585        assert before_update_called == 1 
  • elixir/trunk/tests/test_fields.py

    r271 r347  
    77def setup(): 
    88    metadata.bind = 'sqlite:///' 
    9          
     9 
    1010class TestFields(object): 
    1111    def teardown(self): 
    1212        cleanup_all(True) 
    13      
     13 
    1414    def test_attr_syntax(self): 
    1515        class Person(Entity): 
     
    1818 
    1919        setup_all(True) 
    20          
     20 
    2121        homer = Person(firstname="Homer", surname="Simpson") 
    2222        bart = Person(firstname="Bart", surname="Simpson") 
    23          
     23 
    2424        session.flush() 
    2525        session.clear() 
    26          
     26 
    2727        p = Person.get_by(firstname="Homer") 
    28          
     28 
    2929        assert p.surname == 'Simpson' 
    3030 
     
    3535 
    3636        setup_all(True) 
    37          
     37 
    3838        homer = Person(firstname="Homer", surname="Simpson") 
    3939        bart = Person(firstname="Bart", surname="Simpson") 
    40          
     40 
    4141        session.flush() 
    4242        session.clear() 
    43          
     43 
    4444        p = Person.get_by(firstname="Homer") 
    45          
     45 
    4646        assert p.surname == 'Simpson' 
    47          
     47 
    4848    def test_with_fields(self): 
    4949        class Person(Entity): 
     
    5252                surname = Field(String(30)) 
    5353            ) 
    54              
     54 
    5555        setup_all(True) 
    56          
     56 
    5757        homer = Person(firstname="Homer", surname="Simpson") 
    5858        bart = Person(firstname="Bart", surname="Simpson") 
    59          
     59 
    6060        session.flush() 
    6161        session.clear() 
    62          
     62 
    6363        p = Person.get_by(firstname="Homer") 
    64          
     64 
    6565        assert p.surname == 'Simpson' 
    6666 
  • elixir/trunk/tests/test_inherit.py

    r341 r347  
    5252    for class_ in (A, B, C, D, E): 
    5353        res[class_.__name__] = class_.query.all() 
    54         sort_list(res[class_.__name__], key=lambda o: o.__class__.__name__)  
     54        sort_list(res[class_.__name__], key=lambda o: o.__class__.__name__) 
    5555 
    5656    for query_class in ('A', 'B', 'C', 'D', 'E'): 
     
    6565        cleanup_all(True) 
    6666 
    67     # this is related to SA ticket 866  
     67    # this is related to SA ticket 866 
    6868    # http://www.sqlalchemy.org/trac/ticket/866 
    6969    # the problem was caused by the fact that the attribute-based syntax left 
     
    7373            pass 
    7474 
    75         class B(A):  
     75        class B(A): 
    7676            name = Field(String(30)) 
    7777            other = Field(Text) 
     
    103103        # enforcing the foreign key constraint cascade rule). 
    104104#        assert not B.table.select().execute().fetchall() 
    105      
     105 
    106106    def test_inheritance_wh_schema(self): 
    107107        # I can only test schema stuff on postgres 
     
    109109            print "schema test skipped" 
    110110            return 
    111          
     111 
    112112        class A(Entity): 
    113113            using_options(inheritance="multi") 
     
    175175#    def test_polymorphic_concrete_inheritance(self): 
    176176        # to get this test to work, I need to duplicate parent relationships in 
    177         # the children. The problem is that the properties are setup post  
    178         # mapper setup, so I'll need to add some logic into the add_property  
     177        # the children. The problem is that the properties are setup post 
     178        # mapper setup, so I'll need to add some logic into the add_property 
    179179        # method which I'm reluctant to do. 
    180180#        do_tst('concrete', True, { 
     
    194194            'E': ('E',), 
    195195        }) 
    196   
     196 
    197197    def test_polymorphic_multitable_inheritance(self): 
    198198        do_tst('multi', True, { 
  • elixir/trunk/tests/test_m2m.py

    r297 r347  
    1010    def setup(self): 
    1111        metadata.bind = 'sqlite:///' 
    12      
     12 
    1313    def teardown(self): 
    1414        cleanup_all(True) 
    15          
     15 
    1616    def test_simple(self): 
    1717        class A(Entity): 
     
    3535        assert a in b.as_ 
    3636        assert b in a.bs_ 
    37      
     37 
    3838    def test_column_format(self): 
    3939        class A(Entity): 
     
    5959        assert a in b.as_ 
    6060        assert b in a.bs_ 
    61          
     61 
    6262        found_a = False 
    6363        found_b = False 
     
    6767        assert found_a 
    6868        assert found_b 
    69      
     69 
    7070    def test_multi_pk_in_target(self): 
    7171        class A(Entity): 
     
    9898            rel1 = ManyToMany('B') 
    9999            rel2 = ManyToMany('B') 
    100              
     100 
    101101        class B(Entity): 
    102102            name = Field(String(20), primary_key=True) 
    103103 
    104104        setup_all(True) 
    105          
     105 
    106106        b1 = B(name='b1') 
    107107        a1 = A(name='a1', rel1=[B(name='b2'), b1], 
     
    110110        session.flush() 
    111111        session.clear() 
    112          
     112 
    113113        a1 = A.query.one() 
    114114        b1 = B.get_by(name='b1') 
    115115        b2 = B.get_by(name='b2') 
    116116 
    117         assert b1 in a1.rel1  
     117        assert b1 in a1.rel1 
    118118        assert b1 in a1.rel2 
    119119        assert b2 in a1.rel1 
     
    122122        class Person(Entity): 
    123123            name = Field(String(30)) 
    124              
     124 
    125125            friends = ManyToMany('Person') 
    126126 
     
    133133        session.flush() 
    134134        session.clear() 
    135          
     135 
    136136        homer = Person.get_by(name="Homer") 
    137137        barney = Person.get_by(name="Barney") 
     
    145145 
    146146            has_and_belongs_to_many('bs', of_kind='B') 
    147              
     147 
    148148        class B(Entity): 
    149149            has_field('name', String(100), primary_key=True) 
    150150 
    151151        setup_all(True) 
    152          
     152 
    153153        b1 = B(name='b1') 
    154154        a1 = A(name='a1', bs=[B(name='b2'), b1]) 
     
    158158        session.flush() 
    159159        session.clear() 
    160          
     160 
    161161        a1 = A.get_by(name='a1') 
    162162        a2 = A.get_by(name='a2') 
  • elixir/trunk/tests/test_m2o.py

    r319 r347  
    1111    def teardown(self): 
    1212        cleanup_all(True) 
    13      
     13 
    1414    def test_simple(self): 
    1515        class A(Entity): 
     
    4040        class B(Entity): 
    4141            a = ManyToOne('A') 
    42              
     42 
    4343        setup_all(True) 
    44          
     44 
    4545        b1 = B(a=A(testx=1)) 
    46          
     46 
    4747        session.flush() 
    4848        session.clear() 
    49          
     49 
    5050        b = B.query.one() 
    5151 
     
    5555        class A(Entity): 
    5656            name = Field(String(128), default="foo") 
    57          
     57 
    5858        class B(Entity): 
    5959            # specify a different key for the column so that 
     
    6262            a = ManyToOne('A', colname='a', 
    6363                          column_kwargs=dict(key='a_id')) 
    64           
     64 
    6565        setup_all(True) 
    6666 
     
    7979        class A(Entity): 
    8080            name = Field(String(40), primary_key=True) 
    81           
     81 
    8282        class B(Entity): 
    8383            a = ManyToOne('A', primary_key=True) 
    84           
     84 
    8585        class C(Entity): 
    8686            b = ManyToOne('B', primary_key=True) 
     
    9595        class A(Entity): 
    9696            pass 
    97           
     97 
    9898        class B(Entity): 
    9999            a = ManyToOne('A', primary_key=True) 
    100           
     100 
    101101        setup_all() 
    102102 
     
    109109            key1 = Field(Integer, primary_key=True) 
    110110            key2 = Field(String(40), primary_key=True) 
    111           
     111 
    112112        class B(Entity): 
    113113            num = Field(Integer, primary_key=True) 
    114114            a = ManyToOne('A', primary_key=True) 
    115           
     115 
    116116        class C(Entity): 
    117117            num = Field(Integer, primary_key=True) 
     
    135135        class A(Entity): 
    136136            c = ManyToOne('C', use_alter=True) 
    137           
     137 
    138138        class B(Entity): 
    139139            a = ManyToOne('A', primary_key=True) 
    140           
     140 
    141141        class C(Entity): 
    142142            b = ManyToOne('B', primary_key=True) 
     
    152152        class A(Entity): 
    153153            name = Field(String(32)) 
    154              
     154 
    155155        class B(Entity): 
    156156            name = Field(String(15)) 
    157              
     157 
    158158            a_rel1 = ManyToOne('A') 
    159159            a_rel2 = ManyToOne('A') 
     
    165165        b1 = B(name="b1", a_rel1=a1, a_rel2=a2) 
    166166        b2 = B(name="b2", a_rel1=a1, a_rel2=a1) 
    167          
     167 
    168168        session.flush() 
    169169        session.clear() 
    170          
     170 
    171171        a1 = A.get_by(name="a1") 
    172172        a2 = A.get_by(name="a2") 
    173173        b1 = B.get_by(name="b1") 
    174174        b2 = B.get_by(name="b2") 
    175          
     175 
    176176        assert a1 == b2.a_rel1 
    177177        assert a2 == b1.a_rel2 
     
    186186 
    187187        setup_all(True) 
    188          
     188 
    189189        santa = Person(name="Santa Claus") 
    190190        rudolph = Animal(name="Rudolph", owner=santa) 
    191          
     191 
    192192        session.flush() 
    193193        session.clear() 
    194          
     194 
    195195        assert "Claus" in Animal.get_by(name="Rudolph").owner.name 
  • elixir/trunk/tests/test_nestedclass.py

    r245 r347  
    77        name = Field(String(40)) 
    88        type = Field(String(40)) 
    9          
     9 
    1010        class Stuff(Entity): 
    1111            ping = Field(String(32)) 
    1212            pong = Field(String(32)) 
    13          
     13 
    1414        other = Field(String(40)) 
    15          
    16     setup_all()         
    17      
     15 
     16    setup_all() 
     17 
    1818class TestNestedClass(object): 
    1919    def test_nestedclass(self): 
  • elixir/trunk/tests/test_o2m.py

    r313 r347  
    1111    def teardown(self): 
    1212        cleanup_all(True) 
    13      
     13 
    1414    def test_simple(self): 
    1515        class A(Entity): 
     
    4040        class Person(Entity): 
    4141            name = Field(String(30)) 
    42              
     42 
    4343            father = ManyToOne('Person', inverse='children') 
    4444            children = OneToMany('Person', inverse='father') 
     
    5050        bart = Person(name="Bart") 
    5151        lisa = Person(name="Lisa") 
    52          
    53         grampa.children.append(homer)         
     52 
     53        grampa.children.append(homer) 
    5454        homer.children.append(bart) 
    5555        lisa.father = homer 
    56          
     56 
    5757        session.flush() 
    5858        session.clear() 
    59          
     59 
    6060        p = Person.get_by(name="Homer") 
    61          
    62         print "%s is %s's child." % (p.name, p.father.name)         
     61 
     62        print "%s is %s's child." % (p.name, p.father.name) 
    6363        print "His children are: %s." % ( 
    6464                " and ".join([c.name for c in p.children])) 
    65          
     65 
    6666        assert p in p.father.children 
    6767        assert p.father is Person.get_by(name="Abe") 
     
    9898        node.children.append(node2) 
    9999        node.children.append(TreeNode(name='node3')) 
    100              
     100 
    101101        session.flush() 
    102102        session.clear() 
    103          
     103 
    104104        root = TreeNode.get_by(name='rootnode') 
    105105        print root 
     
    115115 
    116116        setup_all(True) 
    117          
     117 
    118118        santa = Person(name="Santa Claus") 
    119119        rudolph = Animal(name="Rudolph", owner=santa) 
    120          
     120 
    121121        session.flush() 
    122122        session.clear() 
    123        
     123 
    124124        santa = Person.get_by(name="Santa Claus") 
    125125 
  • elixir/trunk/tests/test_options.py

    r341 r347  
    55from sqlalchemy import UniqueConstraint, create_engine, Column 
    66from sqlalchemy.orm import create_session 
    7 from sqlalchemy.exceptions import SQLError, ConcurrentModificationError  
     7from sqlalchemy.exceptions import SQLError, ConcurrentModificationError 
    88from elixir import * 
    99 
     
    1919        class Person(Entity): 
    2020            name = Field(String(30)) 
    21   
     21 
    2222            using_options(version_id_col=True) 
    23   
     23 
    2424        setup_all() 
    2525        Person.table.create() 
    26   
     26 
    2727        p1 = Person(name='Daniel') 
    2828        session.flush() 
    2929        session.clear() 
    30          
     30 
    3131        person = Person.query.first() 
    3232        person.name = 'Gaetan' 
     
    3434        session.clear() 
    3535        assert person.row_version == 2 
    36   
     36 
    3737        person = Person.query.first() 
    3838        person.name = 'Jonathan' 
     
    4040        session.clear() 
    4141        assert person.row_version == 3 
    42   
     42 
    4343        # check that a concurrent modification raises exception 
    4444        p1 = Person.query.first() 
     
    7777 
    7878        # Note that this test is bogus as you cannot just change a column this 
    79         # way since the mapper is already constructed at this point and will  
     79        # way since the mapper is already constructed at this point and will 
    8080        # use the old column!!! This test is only meant as a way to check no 
    8181        # exception is raised. 
     
    121121 
    122122        engine = create_engine('sqlite:///') 
    123          
     123 
    124124        ctx = SessionContext(lambda: create_session(bind=engine)) 
    125          
     125 
    126126        class Person(Entity): 
    127127            using_options(session=ctx) 
     
    135135        bart = Person(firstname="Bart", surname='Simpson') 
    136136        ctx.current.flush() 
    137          
     137 
    138138        assert Person.query.session is ctx.current 
    139139        assert Person.query.filter_by(firstname='Homer').one() is homer 
     
    141141    def test_manual_session(self): 
    142142        engine = create_engine('sqlite:///') 
    143          
     143 
    144144        class Person(Entity): 
    145145            using_options(session=None) 
     
    158158        session.save(bart) 
    159159        session.flush() 
    160         
     160 
    161161        bart.delete() 
    162162        session.flush() 
     
    168168        try: 
    169169            from sqlalchemy.orm import scoped_session, sessionmaker 
    170             #TODO: this test, as-is has no sense on SA 0.4 since activemapper  
    171             # session uses scoped_session, but we need to provide a new  
     170            #TODO: this test, as-is has no sense on SA 0.4 since activemapper 
     171            # session uses scoped_session, but we need to provide a new 
    172172            # test for that. 
    173173            return 
     
    179179        except ImportError: 
    180180            return 
    181              
     181 
    182182        engine = create_engine('sqlite:///') 
    183183 
     
    206206            print "Not on version 0.4 or later of sqlalchemy" 
    207207            return 
    208              
     208 
    209209        engine = create_engine('sqlite:///') 
    210210 
     
    225225        assert Person.query.session is Session() 
    226226        assert Person.query.filter_by(firstname='Homer').one() is homer 
    227          
     227 
    228228    def test_global_scoped_session(self): 
    229229        try: 
     
    234234 
    235235        global __session__ 
    236          
    237         engine = create_engine('sqlite:///') 
    238          
     236 
     237        engine = create_engine('sqlite:///') 
     238 
    239239        session = scoped_session(sessionmaker(bind=engine)) 
    240240        __session__ = session 
    241          
    242         class Person(Entity): 
    243             firstname = Field(String(30)) 
    244             surname = Field(String(30)) 
    245  
    246         setup_all() 
    247         create_all(engine) 
    248  
    249         homer = Person(firstname="Homer", surname='Simpson') 
    250         bart = Person(firstname="Bart", surname='Simpson') 
    251         session.flush() 
    252          
     241 
     242        class Person(Entity): 
     243            firstname = Field(String(30)) 
     244            surname = Field(String(30)) 
     245 
     246        setup_all() 
     247        create_all(engine) 
     248 
     249        homer = Person(firstname="Homer", surname='Simpson') 
     250        bart = Person(firstname="Bart", surname='Simpson') 
     251        session.flush() 
     252 
    253253        assert Person.query.session is session() 
    254254        assert Person.query.filter_by(firstname='Homer').one() is homer 
    255255 
    256256        del __session__ 
    257          
     257 
    258258class TestTableOptions(object): 
    259259    def setup(self): 
     
    264264 
    265265    def test_unique_constraint(self): 
    266          
     266 
    267267        class Person(Entity): 
    268268            firstname = Field(String(30)) 
  • elixir/trunk/tests/test_order_by.py

    r271 r347  
    88def setup(): 
    99    global Record, Artist, Genre 
    10      
     10 
    1111    class Record(Entity): 
    1212        title = Field(String(100)) 
     
    4343        ("Octavarium", 2005), 
    4444        # 2005 is a mistake to make the test more interesting 
    45         ("Six Degrees Of Inner Turbulence", 2005),  
     45        ("Six Degrees Of Inner Turbulence", 2005), 
    4646        ("Train Of Thought", 2003), 
    4747        ("When Dream And Day Unite", 1989) 
    4848    ) 
    49      
     49 
    5050    for title, year in titles: 
    5151        Record(title=title, artist=artist, year=year, genres=[genre]) 
     
    6262    def teardown(self): 
    6363        session.clear() 
    64      
     64 
    6565    def test_mapper_order_by(self): 
    6666        records = Record.query.all() 
  • elixir/trunk/tests/test_packages.py

    r344 r347  
    1212    def teardown(self): 
    1313        cleanup_all(True) 
    14      
     14 
    1515    def test_packages(self): 
    1616        # This is an ugly workaround because when nosetest is run globally (ie 
     
    1818        # all modules, including a and b. Then when any other test calls 
    1919        # setup_all(), A and B are also setup, but then the other test also 
    20         # calls cleanup_all(), so when we get here, A and B are already dead  
     20        # calls cleanup_all(), so when we get here, A and B are already dead 
    2121        # and reimporting their modules does nothing because they were already 
    2222        # imported. 
  • elixir/trunk/tests/test_properties.py

    r338 r347  
    1414    def teardown(self): 
    1515        cleanup_all(True) 
    16      
     16 
    1717    def test_generic_property(self): 
    1818        class Tag(Entity): 
     
    2020            score2 = Field(Float) 
    2121 
    22             score = GenericProperty(  
     22            score = GenericProperty( 
    2323                         lambda c: column_property( 
    2424                             (c.score1 * c.score2).label('score'))) 
     
    3030        session.flush() 
    3131        session.clear() 
    32          
     32 
    3333        for tag in Tag.query.all(): 
    3434            assert tag.score == tag.score1 * tag.score2 
     
    4848        session.flush() 
    4949        session.clear() 
    50          
     50 
    5151        for tag in Tag.query.all(): 
    5252            assert tag.score == tag.score1 * tag.score2 
     
    5656            score1 = Field(Float) 
    5757            score2 = Field(Float) 
    58   
     58 
    5959            user = ManyToOne('User') 
    60   
     60 
    6161            score = ColumnProperty(lambda c: c.score1 * c.score2) 
    62   
     62 
    6363        class User(Entity): 
    6464            name = Field(String(16)) 
    6565            category = ManyToOne('Category') 
    6666            tags = OneToMany('Tag', lazy=False) 
    67             score = ColumnProperty(lambda c:  
     67            score = ColumnProperty(lambda c: 
    6868                                   select([func.sum(Tag.score)], 
    6969                                          Tag.user_id == c.id).as_scalar()) 
    70   
     70 
    7171        class Category(Entity): 
    7272            name = Field(String(16)) 
    73             users = OneToMany('User', lazy=False)  
    74   
    75             score = ColumnProperty(lambda c:  
     73            users = OneToMany('User', lazy=False) 
     74 
     75            score = ColumnProperty(lambda c: 
    7676                                   select([func.avg(User.score)], 
    7777                                          User.category_id == c.id 
    7878                                         ).as_scalar()) 
    7979        setup_all(True) 
    80   
    81         u1 = User(name='joe', tags=[Tag(score1=5.0, score2=3.0),  
     80 
     81        u1 = User(name='joe', tags=[Tag(score1=5.0, score2=3.0), 
    8282                                    Tag(score1=55.0, score2=1.0)]) 
    83   
    84         u2 = User(name='bar', tags=[Tag(score1=5.0, score2=4.0),  
     83 
     84        u2 = User(name='bar', tags=[Tag(score1=5.0, score2=4.0), 
    8585                                    Tag(score1=50.0, score2=1.0), 
    8686                                    Tag(score1=15.0, score2=2.0)]) 
    87   
     87 
    8888        c1 = Category(name='dummy', users=[u1, u2]) 
    8989 
     
    102102            has_field('score1', Float) 
    103103            has_field('score2', Float) 
    104             has_property('score',  
     104            has_property('score', 
    105105                         lambda c: column_property( 
    106106                             (c.score1 * c.score2).label('score'))) 
     
    113113        session.flush() 
    114114        session.clear() 
    115          
     115 
    116116        for tag in Tag.query.all(): 
    117117            assert tag.score == tag.score1 * tag.score2 
     
    157157        session.flush() 
    158158        session.clear() 
    159         
     159 
    160160        p = Person.get_by(email='x@z.com') 
    161          
     161 
    162162        assert p.name == 'Mr. X' 
    163163 
     
    213213 
    214214        assert a.name == 'a1' 
    215          
     215 
  • elixir/trunk/tests/test_sa_integration.py

    r336 r347  
    1313    def teardown(self): 
    1414        cleanup_all(True) 
    15      
     15 
    1616    def test_sa_to_elixir(self): 
    1717        class A(Entity): 
     
    2222        setup_all(True) 
    2323 
    24         b_table = Table('b', metadata,  
     24        b_table = Table('b', metadata, 
    2525            Column('id', Integer, primary_key=True), 
    2626            Column('name', String(60)), 
     
    4949 
    5050#    def test_elxir_to_sa(self): 
    51 #        a_table = Table('a', metadata,  
     51#        a_table = Table('a', metadata, 
    5252#            Column('id', Integer, primary_key=True), 
    5353#            Column('name', String(60)), 
     
    6363#            name = Field(String(60)) 
    6464#            a = ManyToOne('A') 
    65 #     
     65# 
    6666#        setup_all(True) 
    6767# 
  • elixir/trunk/tests/test_through.py

    r319 r347  
    1313    def teardown(self): 
    1414        cleanup_all(True) 
    15      
     15 
    1616    def test_rel_through(self): 
    1717        # converted from http://www.sqlalchemy.org/docs/04/plugins.html#plugins_associationproxy 
  • elixir/trunk/tests/test_versioning.py

    r312 r347  
    4949    def setup(self): 
    5050        create_all() 
    51      
     51 
    5252    def teardown(self): 
    5353        drop_all() 
    5454        session.clear() 
    55      
    56     def test_versioning(self):     
     55 
     56    def test_versioning(self): 
    5757        gilliam = Director(name='Terry Gilliam') 
    5858        monkeys = Movie(id=1, title='12 Monkeys', description='draft description', director=gilliam) 
    5959        bruce = Actor(name='Bruce Willis', movies=[monkeys]) 
    6060        session.flush(); session.clear() 
    61      
     61 
    6262        time.sleep(1) 
    6363        after_create = datetime.now() 
    6464        time.sleep(1) 
    65      
     65 
    6666        movie = Movie.get_by(title='12 Monkeys') 
    6767        assert movie.version == 1 
     
    7171        movie.description = 'description two' 
    7272        session.flush(); session.clear() 
    73      
     73 
    7474        time.sleep(1) 
    7575        after_update_one = datetime.now() 
    7676        time.sleep(1) 
    77      
     77 
    7878        movie = Movie.get_by(title='12 Monkeys') 
    7979        movie.description = 'description three' 
     
    8484        monkeys.ignoreme = 1 
    8585        session.flush(); session.clear() 
    86      
     86 
    8787        time.sleep(1) 
    8888        after_update_two = datetime.now() 
    8989        time.sleep(1) 
    90      
     90 
    9191        movie = Movie.get_by(title='12 Monkeys') 
    9292        assert movie.autoupd == 8, movie.autoupd 
     
    9494        middle_version = movie.get_as_of(after_update_one) 
    9595        latest_version = movie.get_as_of(after_update_two) 
    96      
     96 
    9797        initial_timestamp = oldest_version.timestamp 
    98      
     98 
    9999        assert oldest_version.version == 1 
    100100        assert oldest_version.description == 'draft description' 
     
    102102        assert oldest_version.autoupd is not None 
    103103        assert oldest_version.autoupd > 0 
    104      
     104 
    105105        assert middle_version.version == 2 
    106106        assert middle_version.description == 'description two' 
    107107        assert middle_version.autoupd > oldest_version.autoupd 
    108      
     108 
    109109        assert latest_version.version == 3, 'version=%i' % latest_version.version 
    110110        assert latest_version.description == 'description three' 
    111111        assert latest_version.ignoreme == 1 
    112112        assert latest_version.autoupd > middle_version.autoupd 
    113      
     113 
    114114        differences = latest_version.compare_with(oldest_version) 
    115115        assert differences['description'] == ('description three', 'draft description') 
    116      
     116 
    117117        assert len(movie.versions) == 3 
    118118        assert movie.versions[0] == oldest_version 
     
    124124        movie.revert_to(2) 
    125125        session.flush(); session.clear() 
    126      
     126 
    127127        movie = Movie.get_by(title='12 Monkeys') 
    128128        assert movie.version == 2, "version=%i, should be 2" % movie.version 
     
    150150        movie = Movie(id=3, title='Foo', description='1') 
    151151        session.commit(); 
    152          
     152 
    153153        session.begin() 
    154154        movie.description = '2' 
     
    156156        session.rollback() 
    157157        session.clear() 
    158          
     158 
    159159        session.begin() 
    160160        movie = Movie.get_by(title='Foo')