Changeset 83

Show
Ignore:
Timestamp:
03/05/07 21:32:53 (6 years ago)
Author:
ged
Message:

- Added support for the "version_id_col" option on entities. This option adds

a column to the table which will be used to prevent concurrent modifications
on any row of the entity's table (i.e. it will raise an error if it happens).

- Using invalid options on entities will now raise an exception

Location:
elixir/trunk
Files:
4 modified

Legend:

Unmodified
Added
Removed
  • elixir/trunk/CHANGES

    r82 r83  
    1 0.2.1 
     10.3.0 
    22- Added support for autoloading/reflecting databases with 
    33  has_and_belongs_to_many relationships. The tablename argument is now 
    44  optional, but still recommended, otherwise you'll have to use the same exact 
    55  name for your intermediary table than the one generated. 
     6- Added support for the "version_id_col" option on entities. This option adds  
     7  a column to the table which will be used to prevent concurrent modifications 
     8  on any row of the entity's table (i.e. it will raise an error if it happens). 
    69- Made the colname argument optional for belongs_to relationships in 
    710  autoloaded entities. It is only required to specify it if you have several 
     
    1922- Actually make the code python 2.3 compatible (Robin's patch was based on 
    2023  0.1.0 while I had introduced more decorators in the trunk in the mean time). 
     24- Using invalid options on entities will now raise an exception 
    2125- Some cleanup/useless code removal 
    2226 
  • elixir/trunk/elixir/entity.py

    r82 r83  
    2525DEFAULT_AUTO_PRIMARYKEY_NAME = "id" 
    2626DEFAULT_AUTO_PRIMARYKEY_TYPE = Integer 
    27  
     27DEFAULT_VERSION_ID_COL = "row_version" 
    2828 
    2929class EntityDescriptor(object): 
     
    7070 
    7171        for option in ('inheritance',  
    72                        'autoload', 'shortnames', 'auto_primarykey'): 
     72                       'autoload', 'shortnames', 'auto_primarykey', 
     73                       'version_id_col'): 
    7374            setattr(self, option, options_defaults[option]) 
    7475 
     
    141142            kwargs['order_by'] = self.translate_order_by(self.order_by) 
    142143         
     144        if self.version_id_col: 
     145            kwargs['version_id_col'] = self.fields[self.version_id_col].column 
     146 
    143147        if self.parent: 
    144148            if self.inheritance == 'single': 
     
    192196#                for field in self.fields.itervalues(): 
    193197#                    self.add_field(field) 
     198 
     199        if self.version_id_col: 
     200            if not isinstance(self.version_id_col, basestring): 
     201                self.version_id_col = DEFAULT_VERSION_ID_COL 
     202            self.add_field(Field(Integer, colname=self.version_id_col)) 
    194203 
    195204        if not self.autoload: 
  • elixir/trunk/elixir/options.py

    r82 r83  
    4040+---------------------+-------------------------------------------------------+ 
    4141| ``autoload``        | Automatically load column definitions from the        | 
    42 |                     | existing database table.  For now, it does **not**    | 
    43 |                     | work for ``has_and_belongs_to_many`` relationships.   | 
     42|                     | existing database table.                              | 
    4443|                     | Using autoloaded tables implies setting               | 
    4544|                     | ``delay_setup`` to ``True`` before defining your      | 
     
    6261|                     | corresponding entity.  If this option is False,       | 
    6362|                     | it will disallow auto-creation of a primary key.      | 
     63+---------------------+-------------------------------------------------------+ 
     64| ``version_id_col``  | If this option is True, it will create a version      | 
     65|                     | column automatically using the default name. If given | 
     66|                     | as string, it will create the column using that name. | 
     67|                     | This can be used to prevent concurrent modifications  | 
     68|                     | to the entity's table rows (i.e. it will raise an     | 
     69|                     | exception if it happens).                             | 
    6470+---------------------+-------------------------------------------------------+ 
    6571| ``order_by``        | How to order select results. Either a string or a     | 
     
    112118    shortnames=False, 
    113119    auto_primarykey=True, 
     120    version_id_col=False, 
    114121    mapper_options=dict(), 
    115122    table_options=dict(), 
     
    124131        'shortnames', 
    125132        'auto_primarykey', 
     133        'version_id_col', 
    126134        'order_by', 
    127135    ) 
     
    133141            if kwarg in UsingOptions.valid_options: 
    134142                setattr(desc, kwarg, kwargs[kwarg]) 
     143            else: 
     144                raise Exception("'%s' is not a valid option for Elixir " 
     145                                "entities." % kwarg) 
    135146 
    136147 
  • elixir/trunk/tests/test_options.py

    r71 r83  
    33""" 
    44 
    5 from sqlalchemy import create_engine, UniqueConstraint  
    6 from sqlalchemy.exceptions import SQLError 
     5from sqlalchemy import create_engine, create_session, UniqueConstraint  
     6from sqlalchemy.exceptions import SQLError, ConcurrentModificationError  
    77from elixir     import * 
    88 
    9 #TODO: complete this test.  
    10  
    11 # The order_by option is already tested in test_order_by.py 
    12  
    13 class Record(Entity): 
    14     with_fields( 
    15         title = Field(Unicode(30)), 
    16         artist = Field(Unicode(30)), 
    17         year = Field(Integer) 
    18     ) 
    19      
    20     using_options(tablename="records") 
    219 
    2210class TestOptions(object): 
     
    2412        engine = create_engine('sqlite:///') 
    2513        metadata.connect(engine) 
    26         create_all() 
    27      
     14 
     15    # this test is a rip-off SQLAlchemy's activemapper's update test 
     16    def test_version_id_col(self): 
     17        class Person(Entity): 
     18            has_field('name', Unicode(30)) 
     19 
     20            using_options(version_id_col=True) 
     21 
     22        Person.table.create() 
     23 
     24        p1 = Person(name='Daniel') 
     25        objectstore.flush() 
     26        objectstore.clear() 
     27         
     28        person = Person.select()[0] 
     29        person.name = 'Gaetan' 
     30        objectstore.flush() 
     31        objectstore.clear() 
     32        assert person.row_version == 2 
     33 
     34        person = Person.select()[0] 
     35        person.name = 'Jonathan' 
     36        objectstore.flush() 
     37        objectstore.clear() 
     38        assert person.row_version == 3 
     39 
     40        # check that a concurrent modification raises exception 
     41        p1 = Person.select()[0] 
     42        s1 = objectstore.session 
     43        s2 = create_session() 
     44        objectstore.context.current = s2 
     45        p2 = Person.select()[0] 
     46        p1.name = "Daniel" 
     47        p2.name = "Gaetan" 
     48        objectstore.flush() 
     49        try: 
     50            objectstore.context.current = s1 
     51            objectstore.flush() 
     52            assert False 
     53        except ConcurrentModificationError: 
     54            pass 
     55 
    2856    def teardown(self): 
    2957        cleanup_all() 
    30      
     58 
     59 
    3160class TestTableOptions(object): 
    3261    def setup(self):