Changes between Version 8 and Version 9 of TestManagerForTracPluginGenericClass


Ignore:
Timestamp:
Oct 15, 2012 11:06:29 AM (23 months ago)
Author:
seccanj
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • TestManagerForTracPluginGenericClass

    v8 v9  
    183183                              Column('state')], 
    184184                     'has_custom': True, 
    185                      'has_change': True} 
     185                     'has_change': True, 
     186                     'version': 1}} 
    186187            } 
    187188 
     
    231232        pass 
    232233 
     234 
    233235    # IEnvironmentSetupParticipant methods 
    234236    def environment_created(self): 
    235         self.upgrade_environment(self.env.get_db_cnx()) 
    236  
    237     def environment_needs_upgrade(self, db): 
    238         return self._need_initialization(db) 
    239  
    240     def upgrade_environment(self, db): 
    241         # Create db 
    242         if self._need_initialization(db): 
    243             upgrade_db(self.env, self.SCHEMA, db) 
    244  
    245     def _need_initialization(self, db): 
    246         return need_db_upgrade(self.env, self.SCHEMA, db) 
    247 }}} 
    248  
     237        self.upgrade_environment() 
     238 
     239    def environment_needs_upgrade(self, db=None): 
     240        for realm in self.SCHEMA: 
     241            realm_metadata = self.SCHEMA[realm] 
     242 
     243            if need_db_create_for_realm(self.env, realm, realm_metadata, db) or \ 
     244                need_db_upgrade_for_realm(self.env, realm, realm_metadata, db): 
     245                 
     246                return True 
     247                 
     248        return False 
     249 
     250    def upgrade_environment(self, db=None): 
     251        # Create or update db 
     252        @self.env.with_transaction(db) 
     253        def do_upgrade_environment(db): 
     254            for realm in self.SCHEMA: 
     255                realm_metadata = self.SCHEMA[realm] 
     256 
     257                if need_db_create_for_realm(self.env, realm, realm_metadata, db): 
     258                    create_db_for_realm(self.env, realm, realm_metadata, db) 
     259 
     260                elif need_db_upgrade_for_realm(self.env, realm, realm_metadata, db): 
     261                    upgrade_db_for_realm(self.env, 'tracgenericworkflow.upgrades', realm, realm_metadata, db) 
     262}}} 
     263 
     264[[BR]] 
    249265First of all, this should be a component, because it must implement Trac interfaces. 
    250266The interfaces to implement are the following: 
     
    252268 * IEnvironmentSetupParticipant, to be queried by Trac about any needs regarding database creation or upgrade. 
    253269 
    254 Then here comes the declaration of the database schema, class fields and class metadata, to be later returned in the corresponding methods of the interface. 
    255  
     270Then comes the declaration of the database schema, class fields and class metadata, to be later returned in the corresponding methods of the interface. 
     271 
     272[[BR]] 
     273One note about upgrade. The call to "upgrade_db_for_realm" specifies, as the second parameter, the subdirectory where all of the DB upgrade scripts will be provided. 
     274 
     275This directory will contain simple python files, one for each table and for each DB version upgrade, with a structure similar to the following. 
     276See a detailed explanation after the code box. 
     277Refer to [http://testman4trac.svn.sourceforge.net/viewvc/testman4trac/testman4trac/trunk/testmanager/upgrades/ the TestManager plugin upgrade directory] for a more complete example. 
     278{{{ 
     279from trac.db import Table, Column, Index, DatabaseManager 
     280from tracgenericclass.util import * 
     281from testmanager.model import TestManagerModelProvider 
     282 
     283def do_upgrade(env, ver, db_backend, db): 
     284    """ 
     285    Add 'page_version' column to testcaseinplan table 
     286    """ 
     287    cursor = db.cursor() 
     288     
     289    realm = 'testcaseinplan' 
     290    cursor.execute("CREATE TEMPORARY TABLE %(realm)s_old AS SELECT * FROM %(realm)s" % {'realm': realm}) 
     291    cursor.execute("DROP TABLE %(realm)s" % {'realm': realm}) 
     292 
     293    table_metadata = Table('testcaseinplan', key = ('id', 'planid'))[ 
     294                              Column('id'), 
     295                              Column('planid'), 
     296                              Column('page_name'), 
     297                              Column('page_version', type='int'), 
     298                              Column('status')] 
     299 
     300    env.log.info("Updating table for class %s" % realm) 
     301    for stmt in db_backend.to_sql(table_metadata): 
     302        env.log.debug(stmt) 
     303        cursor.execute(stmt) 
     304 
     305    cursor = db.cursor() 
     306 
     307    cursor.execute("INSERT INTO %(realm)s (id,planid,page_name,page_version,status) " 
     308                   "SELECT id,planid,page_name,-1,status FROM %(realm)s_old" % {'realm': realm}) 
     309 
     310    cursor.execute("DROP TABLE %(realm)s_old" % {'realm': realm}) 
     311 
     312}}} 
     313 
     314[[BR]] 
     315In case you need to update a table's structure (e.g. add columns), you will: 
     316 
     317 1) Create a temporary table with the same structure as the old table 
     318 2) Copy all the contents of the current table into the temporary table 
     319 3) Drop the original table 
     320 4) Recreate the original table with the new structure 
     321 5) Copy back all the contents from the temporary table into the updated original table 
     322 6) Drop the temporary table 
     323 
     324[[BR]] 
    256325Following is the '''IConcreteClassProvider interface''' documentation, which is pretty explanatory about the format and meaning of the required data. 
    257326 
     
    683752 
    684753'''Note''': The object being passed along here is the actual object, not a copy. The listener would then be able to alter the object and then save it, thus affecting the system. I didn't want to prevent this (and probably it wouldn't be even possible), because this may be a powerful mechanism of further hacking the environment for achieving higher functionalities. 
    685