diff -r f6ef988fda71 CHANGELOG
--- a/CHANGELOG	Fri Sep 21 16:04:02 2012 +0200
+++ b/CHANGELOG	Sun Sep 23 17:30:03 2012 +0200
@@ -1,3 +1,10 @@
+* Add RPC definition
+* Repace BrowseRecord by Model instance
+* Replace Cache decorator by a simple LRU Cache
+* Remove Cacheable
+* Remove _description
+* Rename _name by __name__
+* Use class in Pool
 * Fix search button clause in ModelButton.get_groups (CVE-2012-2238)
 * Merge all kind of buttons
 * Use XML id for board action instead of id
diff -r f6ef988fda71 TODO
--- a/TODO	Fri Sep 21 16:04:02 2012 +0200
+++ b/TODO	Sun Sep 23 17:30:03 2012 +0200
@@ -1,7 +1,7 @@
+- Allow to pickle Model instance
 - Add check on action groups when executing wizard
 - Allow to create fields from the client interface
 - Improve encoding of default properties
 - Add check for index and constraint name lenght (limit 31)
-- Change _constraint to pass a BrowseRecordList instead of a list of ids
 - Add order attribute on page tag
 - Find the on_change method for inherited fields
diff -r f6ef988fda71 doc/ref/index.rst
--- a/doc/ref/index.rst	Fri Sep 21 16:04:02 2012 +0200
+++ b/doc/ref/index.rst	Sun Sep 23 17:30:03 2012 +0200
@@ -13,3 +13,4 @@
     transaction
     tools/singleton
     pool
+    rpc
diff -r f6ef988fda71 doc/ref/models/fields.rst
--- a/doc/ref/models/fields.rst	Fri Sep 21 16:04:02 2012 +0200
+++ b/doc/ref/models/fields.rst	Sun Sep 23 17:30:03 2012 +0200
@@ -146,6 +146,13 @@
 
 Define how the field must be loaded: ``lazy`` or ``eager``.
 
+``name``
+--------
+
+.. attribute:: Field.name
+
+The name of the field.
+
 Default value
 =============
 
diff -r f6ef988fda71 doc/ref/models/models.rst
--- a/doc/ref/models/models.rst	Fri Sep 21 16:04:02 2012 +0200
+++ b/doc/ref/models/models.rst	Sun Sep 23 17:30:03 2012 +0200
@@ -5,14 +5,14 @@
 Model
 =====
 
-.. class:: Model
+.. class:: Model([id[, \**kwargs]])
 
 This is the base class that every kind of :ref:`model <topics-models>`
 inherits. It defines common attributes of all models.
 
 Class attributes are:
 
-.. attribute:: Model._name
+.. attribute:: Model.__name__
 
     It contains the a unique name to reference the model throughout the
     platform.
@@ -28,15 +28,10 @@
 
     .. _generalization: http://en.wikipedia.org/wiki/Class_diagram#Generalization
 
-.. attribute:: Model._description
+.. attribute:: Model.__rpc__
 
-    It contains a description of the model.
-
-.. attribute:: Model._rpc
-
-    It contains a dictionary with method name as key and a boolean as value. If
-    a method name is in the dictionary then it is allowed to call it remotely.
-    If the value is ``True`` then the transaction will be committed.
+    It contains a dictionary with method name as key and an instance of
+    :class:`trytond.rpc.RPC` as value.
 
 .. attribute:: Model._error_messages
 
@@ -57,13 +52,21 @@
 
     The definition of the field ``id`` of records.
 
-Instance methods:
+Class methods:
 
-.. method:: Model.init(module_name)
+.. classmethod:: Model.__setup__()
+
+    Setup the class before adding into the :class:`trytond.pool.Pool`.
+
+.. classmethod:: Model.__post_setup__()
+
+    Setup the class after added into the :class:`trytond.pool.Pool`.
+
+.. classmethod:: Model.__register__(module_name)
 
     Registers the model in ``ir.model`` and ``ir.model.field``.
 
-.. method:: Model.raise_user_error(error[, error_args[, error_description[, error_description_args[, raise_exception]]]])
+.. classmethod:: Model.raise_user_error(error[, error_args[, error_description[, error_description_args[, raise_exception]]]])
 
     Raises an exception that will be displayed as an error message in the
     client.  ``error`` is the key of the error message in ``_error_messages``
@@ -72,7 +75,7 @@
     The boolean ``raise_exception`` can be set to ``False`` to retrieve the
     error message strings.
 
-.. method:: Model.raise_user_warning(warning_name, warning[, warning_args[, warning_description[, warning_description_args]]])
+.. classmethod:: Model.raise_user_warning(warning_name, warning[, warning_args[, warning_description[, warning_description_args]]])
 
     Raises an exception that will be displayed as a warning message on the
     client, if the user has not yet bypassed it. ``warning_name`` is used to
@@ -84,14 +87,14 @@
         warning states by users.
     ..
 
-.. method:: Model.default_get(fields_names[, with_rec_name])
+.. classmethod:: Model.default_get(fields_names[, with_rec_name])
 
     Return a dictionary with the default values for each field in
     ``fields_names``. Default values are defined by the returned value of each
     instance method with the pattern ``default_`field_name`()``.
     ``with_rec_name`` allow to add `rec_name` value for each many2one field.
 
-.. method:: Model.fields_get([fields_names])
+.. classmethod:: Model.fields_get([fields_names])
 
     Return the definition of each field on the model.
 
@@ -122,9 +125,9 @@
     Same as :stat:`ModelView.button` but return the action id of the XML `id`
     action.
 
-Instance methods:
+Class methods:
 
-.. method:: ModelView.fields_view_get([view_id[, view_type[, toolbar]]])
+.. classmethod:: ModelView.fields_view_get([view_id[, view_type[, toolbar]]])
 
     Return a view definition used by the client. The definition is::
 
@@ -149,9 +152,16 @@
             },
         }
 
-.. method:: ModelView.view_header_get(value[, view_type])
+.. classmethod:: ModelView.view_toolbar_get()
 
-    Return the window title used by the client for the specific view type.
+    Returns the model specific actions in a dictionary with keys:
+        - `print`: a list of available reports
+        - `action`: a list of available actions
+        - `relate`: a list of available relations
+
+.. classmethod:: ModelView.view_header_get(value[, view_type])
+
+    Returns the window title used by the client for the specific view type.
 
 ============
 ModelStorage
@@ -199,121 +209,124 @@
 
         [ ('function name', 'error keyword'), ... ]
 
-    where ``function name`` is the name of an instance method of the class
+    where ``function name`` is the name of an instance or a class method of the
     which must return a boolean (``False`` when the constraint is violated) and
-    ``error keyword`` is a key of
-    :attr:`Model._error_messages`.
+    ``error keyword`` is a key of :attr:`Model._error_messages`.
 
-Instance methods:
+Static methods:
 
-.. method:: ModelStorage.default_create_uid()
+.. staticmethod:: ModelStorage.default_create_uid()
 
     Return the default value for :attr:`create_uid`.
 
-.. method:: ModelStorage.default_create_date()
+.. staticmethod:: ModelStorage.default_create_date()
 
     Return the default value for :attr:`create_date`.
 
-.. method:: ModelStorage.create(values)
+CLass methods:
+
+.. classmethod:: ModelStorage.create(values)
 
     Create a record. ``values`` is a dictionary with fields names as key and
-    created values as value.
+    created values as value and return the new instance.
 
-.. method:: ModelStorage.trigger_create(id)
+.. classmethod:: ModelStorage.trigger_create(record)
 
     Trigger create actions. It will call actions defined in ``ir.trigger`` if
     ``on_create`` is set and ``condition`` is true.
 
-.. method:: ModelStorage.read(ids[, fields_names])
+.. classmethod:: ModelStorage.read(ids[, fields_names])
 
-    Return values for the ids. If ``fields_names`` is set, there will be only
-    values for these fields otherwise it will be for all fields.
-    If ``ids`` is a list of ids, the returned value will be a list of
-    dictionaries.
-    If ``ids`` is an integer, the returned value will be a dictionary.
+    Return a list of values for the ids. If ``fields_names`` is set, there will
+    be only values for these fields otherwise it will be for all fields.
 
-.. method:: ModelStorage.write(ids, values)
+.. classmethod:: ModelStorage.write(records, values)
 
-    Write ``values`` on records. ``ids`` can be a list of ids or an id.
-    ``values`` is a dictionary with fields names as key and writen values as
-    value.
+    Write ``values`` on the list of records.  ``values`` is a dictionary with
+    fields names as key and writen values as value.
 
-.. method:: ModelStorage.trigger_write_get_eligibles(ids)
+.. classmethod:: ModelStorage.trigger_write_get_eligibles(records)
 
-    Return eligible record ids for write actions by triggers. This dictionary
+    Return eligible records for write actions by triggers. This dictionary
     is to pass to :meth:`~ModelStorage.trigger_write`.
 
-.. method:: ModelStorage.trigger_write(eligibles)
+.. classmethod:: ModelStorage.trigger_write(eligibles)
 
     Trigger write actions. It will call actions defined in ``ir.trigger`` if
     ``on_write`` is set and ``condition`` was false before
     :meth:`~ModelStorage.write` and true after.
 
-.. method:: ModelStorage.delete(ids)
+.. classmethod:: ModelStorage.delete(records)
 
-    Delete records. ``ids`` can be a list of ids or an id.
+    Delete records.
 
-.. method:: ModelStorage.trigger_delete(ids)
+.. classmethod:: ModelStorage.trigger_delete(records)
 
     Trigger delete actions. It will call actions defined in ``ir.trigger`` if
     ``on_delete`` is set and ``condition`` is true.
 
-.. method:: ModelStorage.copy(ids[, default])
+.. classmethod:: ModelStorage.copy(records[, default])
 
-    Duplicate the records. ``ids`` can be a list of ids or an id. ``default``
-    is a dictionary of default value for the created records.
+    Duplicate the records. ``default`` is a dictionary of default value for the
+    created records.
 
-.. method:: ModelStorage.search(domain[, offset[, limit[, order[, count]]]])
+.. classmethod:: ModelStorage.search(domain[, offset[, limit[, order[, count]]]])
 
-    Return a list of ids that match the :ref:`domain <topics-domain>`.
+    Return a list of records that match the :ref:`domain <topics-domain>`.
 
-.. method:: ModelStorage.search_count(domain)
+.. classmethod:: ModelStorage.search_count(domain)
 
     Return the number of records that match the :ref:`domain <topics-domain>`.
 
-.. method:: ModelStorage.search_read(domain[, offset[, limit[, order[, fields_names]]]])
+.. classmethod:: ModelStorage.search_read(domain[, offset[, limit[, order[, fields_names]]]])
 
     Call :meth:`search` and :meth:`read` at once.
     Useful for the client to reduce the number of calls.
 
-.. method:: ModelStorage.get_rec_name(ids, name)
-
-    Getter for the :class:`trytond.model.fields.Function` field
-    :attr:`rec_name`.
-
-.. method:: ModelStorage.search_rec_name(name, clause)
+.. classmethod:: ModelStorage.search_rec_name(name, clause)
 
     Searcher for the :class:`trytond.model.fields.Function` field
     :attr:`rec_name`.
 
-.. method:: ModelStorage.browse(ids)
+.. classmethod:: ModelStorage.browse(ids)
 
-    Return a :class:`BrowseRecordList` or a :class:`BrowseRecord` for the ``ids``.
+    Return a list of record instance for the ``ids``.
 
-.. method:: ModelStorage.export_data(ids, fields_names)
+.. classmethod:: ModelStorage.export_data(records, fields_names)
 
-    Return a list of list of values for each ``ids``.
+    Return a list of list of values for each ``records``.
     The list of values follows ``fields_names``.
     Relational fields are defined with ``/`` at any depth.
 
-.. method:: ModelStorage.import_data(fields_names, datas)
+.. classmethod:: ModelStorage.import_data(fields_names, data)
 
     Create records for all values in ``datas``.
     The field names of values must be defined in ``fields_names``.
     It returns a tuple containing: the number of records imported, the last values
     if failed, the exception if failed and the warning if failed.
 
-.. method:: ModelStorage.check_xml_record(ids, values)
+.. classmethod:: ModelStorage.check_xml_record(records, values)
 
-    Verify if the ids are originating from XML data. It is used to prevent
+    Verify if the records are originating from XML data. It is used to prevent
     modification of data coming from XML files. This method must be overiden to
     change this behavior.
 
-.. method:: ModelStorage.check_recursion(ids[, parent])
+.. classmethod:: ModelStorage.check_recursion(records[, parent])
 
     Helper method that checks if there is no recursion in the tree composed
     with ``parent`` as parent field name.
 
+Instance methods:
+
+.. method:: ModelStorage.get_rec_name(name)
+
+    Getter for the :class:`trytond.model.fields.Function` field
+    :attr:`rec_name`.
+
+.. method:: ModelStorage.save()
+
+    Save the modification made on the record instance.
+
 ========
 ModelSQL
 ========
@@ -367,18 +380,18 @@
 
     Like :attr:`Model._error_messages` but for :attr:`_sql_constraints`
 
-Instance methods:
+Class methods:
 
-.. method:: ModelSQL.default_sequence()
+.. classmethod:: ModelSQL.default_sequence()
 
     Return default value for sequence field if the model has one.
 
-.. method:: ModelSQL.table_query()
+.. classmethod:: ModelSQL.table_query()
 
     Could be overrided to use a custom SQL query instead of a table of the
     database. It should return a tuple containing SQL query and arguments.
 
-.. method:: ModelSQL.search_domain(domain[, active_test])
+.. classmethod:: ModelSQL.search_domain(domain[, active_test])
 
     Convert a :ref:`domain <topics-domain>` into a tuple containing:
 
@@ -427,8 +440,8 @@
 
 .. _singleton: http://en.wikipedia.org/wiki/Singleton_pattern
 
-Instance methods:
+Class methods:
 
-.. method:: ModelSingleton.get_singleton_id()
+.. classmethod:: ModelSingleton.get_singleton()
 
-    Return the id of the unique record if there is one.
+    Return the instance of the unique record if there is one.
diff -r f6ef988fda71 doc/ref/pool.rst
--- a/doc/ref/pool.rst	Fri Sep 21 16:04:02 2012 +0200
+++ b/doc/ref/pool.rst	Sun Sep 23 17:30:03 2012 +0200
@@ -13,32 +13,46 @@
 
 .. staticmethod:: Pool.register(klass[, type])
 
-Register a class of type (default: `model`).
+    Register a class of type (default: `model`).
 
 Class methods:
 
 .. classmethod:: Pool.start()
 
-Start the pool by registering all Tryton modules found.
+    Start the pool by registering all Tryton modules found.
 
 .. classmethod:: Pool.stop(database_name)
 
-Stop the pool by removing instances for the database.
+    Stop the pool by removing instances for the database.
 
 .. classmethod:: Pool.database_list()
 
-List all started database.
+    List all started database.
 
 Instance methods:
 
 .. method:: Pool.get(name[, type])
 
-Return the named instance of type from the pool.
+    Return the named instance of type from the pool.
 
 .. method:: Pool.object_name_list([type])
 
-Return the list of instances names.
+    Return the list of instances names.
 
 .. method:: Pool.iterobject([type])
 
-Return an interator over instances names.
+    Return an interator over instances names.
+
+.. method:: Pool.setup(module)
+
+    Setup classes for module and return a list of classes for each type in a
+    dictionary.
+
+========
+PoolMeta
+========
+
+.. class:: PoolMeta
+
+The PoolMeta is a metaclass helper to setup __name__ on class to be registered
+in the Pool.
diff -r f6ef988fda71 doc/ref/rpc.rst
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/ref/rpc.rst	Sun Sep 23 17:30:03 2012 +0200
@@ -0,0 +1,24 @@
+.. _ref-rpc:
+.. module:: trytond.rpc
+
+===
+RPC
+===
+
+.. class:: RPC([readonly[, instantiate[, result]]])
+
+RPC is an object to define the behavior of Remote Procedur Call.
+
+Instance attributes are:
+
+.. attribute:: RPC.readonly
+
+    The transaction mode
+
+.. attribute:: RPC.instantiate
+
+    The position of the argument to be instanciated
+
+.. attribute:: RPC.result
+
+    The function to transform the result
diff -r f6ef988fda71 doc/ref/transaction.rst
--- a/doc/ref/transaction.rst	Fri Sep 21 16:04:02 2012 +0200
+++ b/doc/ref/transaction.rst	Sun Sep 23 17:30:03 2012 +0200
@@ -35,6 +35,10 @@
 
     The language code defines in the context.
 
+.. attribute:: Transaction.counter
+
+    Count the number of modification made in this transaction.
+
 .. method:: Transaction.start(database_name, user[, context])
 
     Start a new transaction and return a `context manager`_.
diff -r f6ef988fda71 doc/ref/wizard.rst
--- a/doc/ref/wizard.rst	Fri Sep 21 16:04:02 2012 +0200
+++ b/doc/ref/wizard.rst	Sun Sep 23 17:30:03 2012 +0200
@@ -12,7 +12,7 @@
 
 .. _`finite state machine`: http://en.wikipedia.org/wiki/Finite-state_machine
 
-.. class:: Wizard()
+.. class:: Wizard(session_id)
 
     This is the base for any wizard. It contains the engine for the finite
     state machine. A wizard must have some :class:`State` instance attributes
@@ -20,7 +20,7 @@
 
 Class attributes are:
 
-.. attribute:: Wizard._name
+.. attribute:: Wizard.__name__
 
     It contains the unique name to reference the wizard throughout the
     platform.
@@ -33,31 +33,39 @@
 
     It contains the name of the ending state.
 
-.. attribute:: Wizard._rpc
+.. attribute:: Wizard.__rpc__
 
-    Same as :attr:`trytond.model.Model._rpc`.
+    Same as :attr:`trytond.model.Model.__rpc__`.
 
 .. attribute:: Wizard.states
 
     It contains a dictionary with state name as key and :class:`State` as
     value.
 
-Instance methods are:
+Class methods are:
 
-.. method:: Wizard.init(module_name)
+.. classmethod:: Wizard.__setup__()
+
+    Setup the class before adding into the :class:`trytond.pool.Pool`.
+
+.. classmethod:: Wizard.__post_setup__()
+
+    Setup the class after added into the :class:`trytond.pool.Pool`.
+
+.. classmethod:: Wizard.__register__(module_name)
 
     Register the wizard.
 
-.. method:: Wizard.create()
+.. classmethod:: Wizard.create()
 
     Create a session for the wizard and returns a tuple containing the session
     id, the starting and ending state.
 
-.. method:: Wizard.delete(session_id)
+.. classmethod:: Wizard.delete(session_id)
 
     Delete the session.
 
-.. method:: Wizard.execute(session, data, state_name)
+.. classmethod:: Wizard.execute(session, data, state_name)
 
     Execute the wizard for the state.
     `session` can be an instance of :class:`Session` or a session id.
@@ -65,27 +73,6 @@
     `active_id`, `active_ids` and `active_model` must be set in the context
     according to the records on which the wizard is run.
 
-=======
-Session
-=======
-
-.. class:: Session(wizard, session_id)
-
-    A wizard session contains values of each :class:`StateView` associated to
-    the wizard.
-
-Instance attributes are:
-
-.. attribute:: Session.data
-
-    Raw storage of session data.
-
-Instance methods are:
-
-.. method:: Session.save()
-
-    Save the session in database.
-
 =====
 State
 =====
@@ -94,6 +81,12 @@
 
     This is the base for any wizard state.
 
+Instance attributes are:
+
+.. attribute:: State.name
+
+    The name of the state.
+
 =========
 StateView
 =========
@@ -120,7 +113,7 @@
 
 Instance methods are:
 
-.. method:: StateView.get_view
+.. method:: StateView.get_view()
 
     Returns the view definition like
     :meth:`~trytond.model.ModelView.fields_view_get`.
diff -r f6ef988fda71 doc/topics/models/index.rst
--- a/doc/topics/models/index.rst	Fri Sep 21 16:04:02 2012 +0200
+++ b/doc/topics/models/index.rst	Sun Sep 23 17:30:03 2012 +0200
@@ -5,8 +5,8 @@
 ======
 
 A model represents a single business logic or concept. It contains fields and
-defines the behaviors of the data. Most of the time, each model stores data in
-a single database table.
+defines the behaviors of the record. Most of the time, each model stores record
+in a single database table.
 
 The basics:
 
@@ -15,8 +15,11 @@
 
     * :ref:`Fields <ref-models-fields>` are defined as model attributes.
 
-    * Tryton generates the table definitions and provides an API to access the
-      data.
+    * Tryton generates the table definitions
+
+    * Tryton provides an API following the `active record pattern`_ to access the records.
+
+.. _active record pattern: http://en.wikipedia.org/wiki/Active_record
 
 Example
 =======
@@ -25,19 +28,18 @@
 fields::
 
     from trytond.model import ModelView, ModelSQL, fields
+    from trytond.pool import Pool
 
     class Party(ModelSQL, ModelView):
         "Party"
-        _description = __doc__
-        _name = "party.party"
+        __name__ = "party.party"
         name = fields.Char('Name')
         code = fields.Char('Code')
 
-    Party()
+    Pool.register(Party)
 
-Instantiating the class registers the model class in the framework.  Later the
-class will be instantiated once per database and stored in the
-:ref:`Pool <ref-pool>`.  Model instances are essentially accessors to
+The class must be registered in the :ref:`Pool <ref-pool>`.
+Model classes are essentially data mappers to records and Model instances are
 records.
 
 Model attributes define meta-information of the model. They are class
diff -r f6ef988fda71 doc/topics/modules/index.rst
--- a/doc/topics/modules/index.rst	Fri Sep 21 16:04:02 2012 +0200
+++ b/doc/topics/modules/index.rst	Sun Sep 23 17:30:03 2012 +0200
@@ -20,8 +20,8 @@
 ``__init__.py`` file
 --------------------
 
-It is the Python ``__init__.py`` to define a module. It must import all the
-Python files from the module.
+It is the Python ``__init__.py`` to define a module. It must contains a method
+named `register()` that must register to the pool all the objects of the module.
 
 
 ``tryton.cfg`` file
diff -r f6ef988fda71 doc/topics/reports/index.rst
--- a/doc/topics/reports/index.rst	Fri Sep 21 16:04:02 2012 +0200
+++ b/doc/topics/reports/index.rst	Sun Sep 23 17:30:03 2012 +0200
@@ -228,17 +228,21 @@
 
 ::
 
-  class InvoiceReport(Report):
-      _name = 'account.invoice'
-      def parse(self, report, objects, datas, localcontext):
-          employee_obj = Pool().get('company.employee')
-          employee = False
-          if Transaction().context.get('employee'):
-              employee = employee_obj.browse(Transaction().context['employee'])
-          localcontext['employee'] = employee
-          return super(InvoiceReport, self).parse(report, objects, datas,
-                   localcontext)
-  InvoiceReport()
+    from trytond.report import Report
+    from tryton.pool import Pool
+
+    class InvoiceReport(Report):
+        __name__ = 'account.invoice'
+
+        def parse(self, report, objects, datas, localcontext):
+            employee_obj = Pool().get('company.employee')
+            employee = False
+            if Transaction().context.get('employee'):
+                employee = employee_obj.browse(Transaction().context['employee'])
+            localcontext['employee'] = employee
+            return super(InvoiceReport, self).parse(report, objects, datas,
+                     localcontext)
+    Pool.register(InvoiceReport, type_='report')
 
 
 Replacing existing Tryton styles
diff -r f6ef988fda71 doc/topics/wizard.rst
--- a/doc/topics/wizard.rst	Fri Sep 21 16:04:02 2012 +0200
+++ b/doc/topics/wizard.rst	Sun Sep 23 17:30:03 2012 +0200
@@ -5,7 +5,7 @@
 ======
 
 A wizard describes a series of steps defined as :class:`trytond.wizard.State`.
-The wizard uses a :class:`trytond.wizard.Session` to store data between states.
+The wizard stores data in `ir.session.wizard` between states.
 
 The basics:
 
@@ -18,13 +18,18 @@
 Example
 =======
 
-This example defines a wizard which export translations::
+This example defines a wizard which export translations
+
+.. highlight:: python
+
+::
 
     from trytond.wizard import Wizard, StateView, StateTransition, Button
+    from trytond.pool import Pool
 
     class TranslationExport(Wizard):
         "Export translation"
-        _name = "ir.translation.export"
+        __name__ = "ir.translation.export"
 
         start = StateView('ir.translation.export.start',
             'ir.translation_export_start_view_form', [
@@ -37,21 +42,19 @@
                 Button('Close', 'end', 'tryton-close'),
                 ])
 
-        def transition_export(self, session):
+        def transition_export(self):
             pool = Pool()
             translation_obj = pool.get('ir.translation')
             file_data = translation_obj.translation_export(
-                session.start.language.code, session.start.module.name)
-            session.result.file = buffer(file_data)
+                self.start.language.code, self.start.module.name)
+            self.result.file = buffer(file_data)
             return 'result'
 
-        def default_result(self, session, fields):
+        def default_result(self, fields):
             return {
-                'file': session.result.file,
+                'file': self.result.file,
                 }
 
-    TranslationExport()
+    Pool.register(TranslationExport, type_='wizard')
 
-Instantiating the class registers the wizard class in the framework. Later the
-class will be instantiated once per database and stored in the
-:ref:`Pool <topics-pool>`.
+The class must be registered in the :ref:`Pool <ref-pool>`.
diff -r f6ef988fda71 trytond/backend/database.py
--- a/trytond/backend/database.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/backend/database.py	Sun Sep 23 17:30:03 2012 +0200
@@ -148,13 +148,15 @@
         '''
         Commit the cursor
         '''
-        self.cache = {}
+        for cache in self.cache.itervalues():
+            cache.clear()
 
     def rollback(self):
         '''
         Rollback the cursor
         '''
-        self.cache = {}
+        for cache in self.cache.itervalues():
+            cache.clear()
 
     def test(self):
         '''
diff -r f6ef988fda71 trytond/backend/postgresql/table.py
--- a/trytond/backend/postgresql/table.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/backend/postgresql/table.py	Sun Sep 23 17:30:03 2012 +0200
@@ -24,8 +24,9 @@
         if not self.table_exist(self.cursor, self.table_name):
             self.cursor.execute('CREATE TABLE "%s" ()' % self.table_name)
 
-        self.cursor.execute('COMMENT ON TABLE "%s" IS \'%s\'' %
-                (self.table_name, model._description.replace("'", "''")))
+        if model.__doc__:
+            self.cursor.execute('COMMENT ON TABLE "%s" IS \'%s\'' %
+                (self.table_name, model.__doc__.replace("'", "''")))
 
         self._update_definitions()
         if 'id' not in self._columns:
diff -r f6ef988fda71 trytond/backend/table.py
--- a/trytond/backend/table.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/backend/table.py	Sun Sep 23 17:30:03 2012 +0200
@@ -20,7 +20,7 @@
             self.table_name = model._table + '__history'
         else:
             self.table_name = model._table
-        self.object_name = model._name
+        self.object_name = model.__name__
         if history:
             self.sequence_name = self.table_name + '___id_seq'
         else:
diff -r f6ef988fda71 trytond/cache.py
--- a/trytond/cache.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/cache.py	Sun Sep 23 17:30:03 2012 +0200
@@ -1,7 +1,5 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
-import inspect
-import time
 import datetime
 from threading import Lock
 from trytond.transaction import Transaction
@@ -12,101 +10,51 @@
 
 class Cache(object):
     """
-    Use it as a decorator of the function you plan to cache
-    Timeout: 0 = no timeout, otherwise in seconds
+    A key value LRU cache with size limit.
     """
     _cache_instance = []
     _resets = {}
     _resets_lock = Lock()
 
-    def __init__(self, name, timeout=3600, max_len=1024):
-        self.timeout = timeout
-        self.max_len = max_len
+    def __init__(self, name, size_limit=1024, context=True):
+        self.size_limit = size_limit
+        self.context = context
         self._cache = {}
         self._cache_instance.append(self)
         self._name = name
         self._timestamp = None
         self._lock = Lock()
 
-    def __call__(self, function):
-        arg_names = inspect.getargspec(function)[0]
+    def _key(self, key):
+        if self.context:
+            return (key, Transaction().user, repr(Transaction.context))
+        return key
 
-        def reset():
-            dbname = Transaction().cursor.dbname
-            Cache.reset(dbname, self._name)
-            self._lock.acquire()
+    def get(self, key, default=None):
+        cursor = Transaction().cursor
+        key = self._key(key)
+        with self._lock:
+            cache = self._cache.setdefault(cursor.dbname,
+                LRUDict(self.size_limit))
             try:
-                self._cache[dbname] = {}
-            finally:
-                self._lock.release()
-            return True
+                result = cache[key] = cache.pop(key)
+                return result
+            except KeyError:
+                return default
 
-        def call(model, *args, **kwargs):
-            result = None
-            find = False
-            cursor = Transaction().cursor
-            # Update named arguments with positional argument values
-            kwargs_origin = kwargs.copy()
-            kwargs.update(dict(zip(arg_names, args)))
-            kwargs = kwargs.items()
-            kwargs.sort()
+    def set(self, key, value):
+        cursor = Transaction().cursor
+        key = self._key(key)
+        with self._lock:
+            cache = self._cache.setdefault(cursor.dbname,
+                LRUDict(self.size_limit))
+            cache[key] = value
 
-            self._lock.acquire()
-            try:
-                self._cache.setdefault(cursor.dbname, {})
-            finally:
-                self._lock.release()
-
-            lower = None
-            self._lock.acquire()
-            try:
-                if len(self._cache[cursor.dbname]) > self.max_len:
-                    mintime = time.time() - self.timeout
-                    for key in self._cache[cursor.dbname].keys():
-                        last_time = self._cache[cursor.dbname][key][1]
-                        if mintime > last_time:
-                            del self._cache[cursor.dbname][key]
-                        else:
-                            if not lower or lower[1] > last_time:
-                                lower = (key, last_time)
-                if (len(self._cache[cursor.dbname]) > self.max_len
-                        and lower):
-                    del self._cache[cursor.dbname][lower[0]]
-            finally:
-                self._lock.release()
-
-            # Work out key as a tuple
-            key = (id(model), Transaction().user, repr(Transaction().context),
-                    repr(kwargs))
-
-            # Check cache and return cached value if possible
-            self._lock.acquire()
-            try:
-                if key in self._cache[cursor.dbname]:
-                    (value, last_time) = self._cache[cursor.dbname][key]
-                    mintime = time.time() - self.timeout
-                    if self.timeout <= 0 or mintime <= last_time:
-                        self._cache[cursor.dbname][key] = (value,
-                                time.time())
-                        result = value
-                        find = True
-            finally:
-                self._lock.release()
-
-            if not find:
-                # Work out new value, cache it and return it
-                result = function(model, *args, **kwargs_origin)
-
-                self._lock.acquire()
-                try:
-                    self._cache[cursor.dbname][key] = (result, time.time())
-                finally:
-                    self._lock.release()
-            return result
-
-        call.__doc__ = function.__doc__
-        call.reset = reset
-        return call
+    def clear(self):
+        cursor = Transaction().cursor
+        Cache.reset(cursor.dbname, self._name)
+        with self._lock:
+            self._cache[cursor.dbname] = LRUDict(self.size_limit)
 
     @staticmethod
     def clean(dbname):
@@ -122,28 +70,21 @@
         finally:
             cursor.commit()
             cursor.close()
-        for obj in Cache._cache_instance:
-            if obj._name in timestamps:
-                if (not obj._timestamp
-                        or timestamps[obj._name] > obj._timestamp):
-                    obj._timestamp = timestamps[obj._name]
-                    obj._lock.acquire()
-                    try:
-                        obj._cache[dbname] = {}
-                    finally:
-                        obj._lock.release()
+        for inst in Cache._cache_instance:
+            if inst._name in timestamps:
+                with inst._lock:
+                    if (not inst._timestamp
+                            or timestamps[inst._name] > inst._timestamp):
+                        inst._timestamp = timestamps[inst._name]
+                        inst._cache[dbname] = LRUDict(inst.size_limit)
 
     @staticmethod
     def reset(dbname, name):
         if not CONFIG['multi_server']:
             return
-        Cache._resets_lock.acquire()
-        try:
+        with Cache._resets_lock:
             Cache._resets.setdefault(dbname, set())
             Cache._resets[dbname].add(name)
-        finally:
-            Cache._resets_lock.release()
-        return
 
     @staticmethod
     def resets(dbname):
@@ -151,24 +92,24 @@
             return
         database = Database(dbname).connect()
         cursor = database.cursor()
-        Cache._resets_lock.acquire()
-        Cache._resets.setdefault(dbname, set())
         try:
-            for name in Cache._resets[dbname]:
-                cursor.execute('SELECT name FROM ir_cache WHERE name = %s',
-                            (name,))
-                if cursor.fetchone():
-                    cursor.execute('UPDATE ir_cache SET "timestamp" = %s '\
+            with Cache._resets_lock:
+                Cache._resets.setdefault(dbname, set())
+                for name in Cache._resets[dbname]:
+                    cursor.execute('SELECT name FROM ir_cache WHERE name = %s',
+                        (name,))
+                    if cursor.fetchone():
+                        # It would be better to insert only
+                        cursor.execute('UPDATE ir_cache SET "timestamp" = %s '
                             'WHERE name = %s', (datetime.datetime.now(), name))
-                else:
-                    cursor.execute('INSERT INTO ir_cache ' \
-                            '("timestamp", "name") ' \
+                    else:
+                        cursor.execute('INSERT INTO ir_cache '
+                            '("timestamp", "name") '
                             'VALUES (%s, %s)', (datetime.datetime.now(), name))
-            Cache._resets[dbname].clear()
+                Cache._resets[dbname].clear()
         finally:
             cursor.commit()
             cursor.close()
-            Cache._resets_lock.release()
 
 
 class LRUDict(OrderedDict):
diff -r f6ef988fda71 trytond/convert.py
--- a/trytond/convert.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/convert.py	Sun Sep 23 17:30:03 2012 +0200
@@ -191,11 +191,11 @@
             eval_attr = attributes.get('eval', '')
 
             if search_attr:
-                search_model = self.model._columns[field_name].model_name
-                f_obj = self.mh.pool.get(search_model)
+                search_model = self.model._fields[field_name].model_name
+                SearchModel = self.mh.pool.get(search_model)
                 with Transaction().set_context(active_test=False):
-                    self.values[field_name], = \
-                            f_obj.search(safe_eval(search_attr))
+                    found, = SearchModel.search(safe_eval(search_attr))
+                    self.values[field_name] = found.id
 
             elif ref_attr:
                 self.values[field_name] = self.mh.get_id(ref_attr)
@@ -255,7 +255,8 @@
         elif name == "record":
             if self.xml_id in self.xml_ids and not self.update:
                 raise Exception('Duplicate id: "%s".' % (self.xml_id,))
-            self.mh.import_record(self.model._name, self.values, self.xml_id)
+            self.mh.import_record(
+                self.model.__name__, self.values, self.xml_id)
             self.xml_ids.append(self.xml_id)
             return None
         else:
@@ -263,7 +264,7 @@
 
     def current_state(self):
         return "In tag record: model %s with id %s." % \
-               (self.model and self.model._name or "?", self.xml_id)
+               (self.model and self.model.__name__ or "?", self.xml_id)
 
 
 # Custom exception:
@@ -280,10 +281,10 @@
     Provide some helper function to ease cache access and management.
     """
 
-    def __init__(self, modeldata_obj, pool):
+    def __init__(self, ModelData, pool):
         self.fs2db = {}
         self.fetched_modules = []
-        self.modeldata_obj = modeldata_obj
+        self.ModelData = ModelData
         self.browserecord = {}
         self.pool = pool
 
@@ -317,15 +318,10 @@
         if module not in self.fetched_modules:
             return
         self.browserecord[module].setdefault(model_name, {})
-        model_obj = self.pool.get(model_name)
+        Model = self.pool.get(model_name)
         if not ids:
-            object_ids = self.browserecord[module][model_name].keys()
-        else:
-            if isinstance(ids, (int, long)):
-                object_ids = [ids]
-            else:
-                object_ids = ids
-        models = model_obj.browse(object_ids)
+            ids = self.browserecord[module][model_name].keys()
+        models = Model.browse(ids)
         for model in models:
             if model.id in self.browserecord[module][model_name]:
                 for cache in Transaction().cursor.cache.values():
@@ -339,13 +335,13 @@
     def fetch_new_module(self, module):
         cursor = Transaction().cursor
         self.fs2db[module] = {}
-        module_data_ids = self.modeldata_obj.search([
+        module_data_ids = self.ModelData.search([
                 ('module', '=', module),
                 ('inherit', '=', None),
                 ], order=[('db_id', 'ASC')])
 
         record_ids = {}
-        for rec in self.modeldata_obj.browse(module_data_ids):
+        for rec in self.ModelData.browse(module_data_ids):
             self.fs2db[rec.module][rec.fs_id] = {
                 "db_id": rec.db_id, "model": rec.model,
                 "id": rec.id, "values": rec.values
@@ -359,16 +355,16 @@
         for model_name in record_ids.keys():
             if model_name not in object_name_list:
                 continue
-            model_obj = self.pool.get(model_name)
+            Model = self.pool.get(model_name)
             self.browserecord[module][model_name] = {}
             for i in range(0, len(record_ids[model_name]), cursor.IN_MAX):
                 sub_record_ids = record_ids[model_name][i:i + cursor.IN_MAX]
                 with Transaction().set_context(active_test=False):
-                    ids = model_obj.search([
+                    records = Model.search([
                         ('id', 'in', sub_record_ids),
                         ])
                 with Transaction().set_context(language='en_US'):
-                    models = model_obj.browse(ids)
+                    models = Model.browse(map(int, records))
                 for model in models:
                     self.browserecord[module][model_name][model.id] = model
         self.fetched_modules.append(module)
@@ -382,8 +378,8 @@
 
         self.pool = pool
         self.module = module
-        self.modeldata_obj = pool.get('ir.model.data')
-        self.fs2db = Fs2bdAccessor(self.modeldata_obj, pool)
+        self.ModelData = pool.get('ir.model.data')
+        self.fs2db = Fs2bdAccessor(self.ModelData, pool)
         self.to_delete = self.populate_to_delete()
         self.noupdate = None
         self.skip_data = False
@@ -481,26 +477,28 @@
         return self.fs2db.get(module, xml_id)["db_id"]
 
     @staticmethod
-    def _clean_value(key, browse_record, object_ref):
+    def _clean_value(key, record):
         """
         Take a field name, a browse_record, and a reference to the
         corresponding object.  Return a raw value has it must look on the
         db.
         """
-
+        Model = record.__class__
         # search the field type in the object or in a parent
-        if key in object_ref._columns:
-            field_type = object_ref._columns[key]._type
+        if key in Model._fields:
+            field_type = Model._fields[key]._type
         else:
-            field_type = object_ref._inherit_fields[key][2]._type
+            field_type = Model._inherit_fields[key][2]._type
 
         # handle the value regarding to the type
         if field_type == 'many2one':
-            return browse_record[key] and browse_record[key].id or None
+            return getattr(record, key).id if getattr(record, key) else None
         elif field_type == 'reference':
-            if not browse_record[key]:
+            if not getattr(record, key):
                 return None
-            ref_mode, ref_id = browse_record[key].split(',', 1)
+            elif not isinstance(getattr(record, key), basestring):
+                return str(getattr(record, key))
+            ref_mode, ref_id = getattr(record, key).split(',', 1)
             try:
                 ref_id = safe_eval(ref_id)
             except Exception:
@@ -511,7 +509,7 @@
         elif field_type in ['one2many', 'many2many']:
             raise Unhandled_field("Unhandled field %s" % key)
         else:
-            return browse_record[key]
+            return getattr(record, key)
 
     def populate_to_delete(self):
         """Create a list of all the records that whe should met in the update
@@ -520,12 +518,11 @@
 
         # Fetch the data in id descending order to avoid depedendcy
         # problem when the corresponding recordds will be deleted:
-        module_data_ids = self.modeldata_obj.search([
+        module_data = self.ModelData.search([
                 ('module', '=', self.module),
                 ('inherit', '=', None),
                 ], order=[('id', 'DESC')])
-        return set(rec.fs_id for rec in self.modeldata_obj.browse(
-            module_data_ids))
+        return set(rec.fs_id for rec in module_data)
 
     def import_record(self, model, values, fs_id):
         module = self.module
@@ -544,7 +541,7 @@
                 raise Exception('Reference to %s.%s not found' % \
                         (module, fs_id))
 
-        object_ref = self.pool.get(model)
+        Model = self.pool.get(model)
 
         if self.fs2db.get(module, fs_id):
 
@@ -581,62 +578,58 @@
                 raise Exception("This record try to overwrite " \
                 "data with the wrong model: %s (module: %s)" % (fs_id, module))
 
+            record = self.fs2db.get_browserecord(module, Model.__name__, db_id)
             #Re-create record if it was deleted
-            if not self.fs2db.get_browserecord(
-                    module, object_ref._name, db_id):
+            if not record:
                 with Transaction().set_context(
                         module=module, language='en_US'):
-                    db_id = object_ref.create(values)
+                    record = Model.create(values)
 
                 # reset_browsercord
-                self.fs2db.reset_browsercord(module, object_ref._name, db_id)
+                self.fs2db.reset_browsercord(
+                    module, Model.__name__, [record.id])
 
-                record = self.fs2db.get_browserecord(module, object_ref._name,
-                        db_id)
+                record = self.fs2db.get_browserecord(
+                    module, Model.__name__, record.id)
                 for table, field_name, field in \
-                        object_ref._inherit_fields.values():
-                    inherit_db_ids[table] = record[field_name].id
+                        Model._inherit_fields.values():
+                    inherit_db_ids[table] = getattr(record, field_name).id
 
                 for table in inherit_db_ids.keys():
-                    data_id = self.modeldata_obj.search([
+                    data = self.ModelData.search([
                         ('fs_id', '=', fs_id),
                         ('module', '=', module),
                         ('model', '=', table),
                         ], limit=1)
-                    if data_id:
-                        self.modeldata_obj.write(data_id, {
+                    if data:
+                        self.ModelData.write(data, {
                             'db_id': inherit_db_ids[table],
                             'inherit': True,
                             })
                     else:
-                        data_id = self.modeldata_obj.create({
+                        data = self.ModelData.create({
                             'fs_id': fs_id,
                             'module': module,
                             'model': table,
                             'db_id': inherit_db_ids[table],
                             'inherit': True,
                             })
-                    inherit_mdata_ids.append((table, data_id))
+                    inherit_mdata_ids.append((table, data.id))
 
-                data_id = self.modeldata_obj.search([
+                data = self.ModelData.search([
                     ('fs_id', '=', fs_id),
                     ('module', '=', module),
-                    ('model', '=', object_ref._name),
-                    ], limit=1)[0]
-                self.modeldata_obj.write(data_id, {
-                    'db_id': db_id,
+                    ('model', '=', Model.__name__),
+                    ], limit=1)
+                self.ModelData.write(data, {
+                    'db_id': record.id,
                     })
-                self.fs2db.get(module, fs_id)["db_id"] = db_id
-
-            db_val = self.fs2db.get_browserecord(module, object_ref._name,
-                    db_id)
-            if not db_val:
-                db_val = object_ref.browse(db_id)
+                self.fs2db.get(module, fs_id)["db_id"] = record.id
 
             to_update = {}
             for key in values:
 
-                db_field = self._clean_value(key, db_val, object_ref)
+                db_field = self._clean_value(key, record)
 
                 # if the fs value is the same has in the db, whe ignore it
                 val = values[key]
@@ -648,22 +641,22 @@
 
                 # we cannot update a field if it was changed by a user...
                 if key not in old_values:
-                    if key in object_ref._columns:
-                        expected_value = object_ref._defaults.get(
+                    if key in Model._fields:
+                        expected_value = Model._defaults.get(
                             key, lambda *a: None)()
                     else:
-                        inherit_obj = self.pool.get(
-                            object_ref._inherit_fields[key][0])
-                        expected_value = inherit_obj._defaults.get(
+                        Inherit = self.pool.get(
+                            Model._inherit_fields[key][0])
+                        expected_value = Inherit._defaults.get(
                             key, lambda *a: None)()
                 else:
                     expected_value = old_values[key]
 
                 # Migration from 2.0: Reference field change value
-                if key in object_ref._columns:
-                    field_type = object_ref._columns[key]._type
+                if key in Model._fields:
+                    field_type = Model._fields[key]._type
                 else:
-                    field_type = object_ref._inherit_fields[key][2]._type
+                    field_type = Model._inherit_fields[key][2]._type
                 if field_type == 'reference':
                     if (expected_value and expected_value.endswith(',0')
                             and not db_field):
@@ -677,7 +670,7 @@
                     logging.getLogger("convert").warning(
                         "Field %s of %s@%s not updated (id: %s), because "\
                         "it has changed since the last update" % \
-                        (key, db_id, model, fs_id))
+                        (key, record.id, model, fs_id))
                     continue
 
                 # so, the field in the fs and in the db are different,
@@ -689,32 +682,32 @@
                 # write the values in the db:
                 with Transaction().set_context(
                         module=module, language='en_US'):
-                    object_ref.write(db_id, to_update)
-                self.fs2db.reset_browsercord(module, object_ref._name, db_id)
+                    Model.write([record], to_update)
+                self.fs2db.reset_browsercord(
+                    module, Model.__name__, [record.id])
 
             if not inherit_db_ids:
-                record = object_ref.browse(db_id)
                 for table, field_name, field in \
-                        object_ref._inherit_fields.values():
-                    inherit_db_ids[table] = record[field_name].id
+                        Model._inherit_fields.values():
+                    inherit_db_ids[table] = getattr(record, field_name).id
             if not inherit_mdata_ids:
                 for table in inherit_db_ids.keys():
-                    data_id = self.modeldata_obj.search([
+                    data, = self.ModelData.search([
                         ('fs_id', '=', fs_id),
                         ('module', '=', module),
                         ('model', '=', table),
                         ], limit=1)
-                    inherit_mdata_ids.append((table, data_id))
+                    inherit_mdata_ids.append((table, data.id))
 
             if to_update:
                 # re-read it: this ensure that we store the real value
                 # in the model_data table:
-                db_val = self.fs2db.get_browserecord(module, object_ref._name,
-                        db_id)
-                if not db_val:
-                    db_val = object_ref.browse(db_id)
+                record = self.fs2db.get_browserecord(
+                    module, Model.__name__, record.id)
+                if not record:
+                    record = Model(record.id)
                 for key in to_update:
-                    values[key] = self._clean_value(key, db_val, object_ref)
+                    values[key] = self._clean_value(key, record)
 
             if module != self.module:
                 temp_values = old_values.copy()
@@ -722,16 +715,17 @@
                 values = temp_values
 
             if values != old_values:
-                self.modeldata_obj.write(mdata_id, {
+                self.ModelData.write([self.ModelData(mdata_id)], {
                     'fs_id': fs_id,
                     'model': model,
                     'module': module,
-                    'db_id': db_id,
+                    'db_id': record.id,
                     'values': str(values),
                     'date_update': datetime.datetime.now(),
                     })
                 for table, inherit_mdata_id in inherit_mdata_ids:
-                    self.modeldata_obj.write(inherit_mdata_id, {
+                    self.ModelData.write(
+                        [self.ModelData(inherit_mdata_id)], {
                         'fs_id': fs_id,
                         'model': table,
                         'module': module,
@@ -740,27 +734,26 @@
                         'date_update': datetime.datetime.now(),
                         })
             # reset_browsercord to keep cache memory low
-            self.fs2db.reset_browsercord(module, object_ref._name, db_id)
+            self.fs2db.reset_browsercord(module, Model.__name__, [record.id])
 
         else:
             # this record is new, create it in the db:
             with Transaction().set_context(module=module, language='en_US'):
-                db_id = object_ref.create(values)
+                record = Model.create(values)
             inherit_db_ids = {}
 
-            record = object_ref.browse(db_id)
             for table, field_name, field in (
-                    object_ref._inherit_fields.values()):
-                inherit_db_ids[table] = record[field_name].id
+                    Model._inherit_fields.values()):
+                inherit_db_ids[table] = getattr(record, field_name).id
 
             # re-read it: this ensure that we store the real value
             # in the model_data table:
-            db_val = object_ref.browse(db_id)
+            record = Model(record.id)
             for key in values:
-                values[key] = self._clean_value(key, db_val, object_ref)
+                values[key] = self._clean_value(key, record)
 
             for table in inherit_db_ids.keys():
-                self.modeldata_obj.create({
+                self.ModelData.create({
                     'fs_id': fs_id,
                     'model': table,
                     'module': module,
@@ -770,20 +763,20 @@
                     'noupdate': self.noupdate,
                     })
 
-            mdata_id = self.modeldata_obj.create({
+            mdata = self.ModelData.create({
                 'fs_id': fs_id,
                 'model': model,
                 'module': module,
-                'db_id': db_id,
+                'db_id': record.id,
                 'values': str(values),
                 'noupdate': self.noupdate,
                 })
 
             # update fs2db:
             self.fs2db.set(module, fs_id, {
-                    "db_id": db_id, "model": model,
-                    "id": mdata_id, "values": str(values)})
-            self.fs2db.reset_browsercord(module, model, db_id)
+                    "db_id": record.id, "model": model,
+                    "id": mdata.id, "values": str(values)})
+            self.fs2db.reset_browsercord(module, model, [record.id])
 
 
 def post_import(pool, module, to_delete):
@@ -792,26 +785,26 @@
     """
     cursor = Transaction().cursor
     mdata_delete = []
-    modeldata_obj = pool.get("ir.model.data")
+    ModelData = pool.get("ir.model.data")
 
     with Transaction().set_context(active_test=False):
-        mdata_ids = modeldata_obj.search([
+        mdata = ModelData.search([
             ('fs_id', 'in', to_delete),
             ('module', '=', module),
             ], order=[('id', 'DESC')])
 
     object_name_list = set(pool.object_name_list())
-    for mrec in modeldata_obj.browse(mdata_ids):
-        mdata_id, model, db_id = mrec.id, mrec.model, mrec.db_id
+    for mrec in mdata:
+        model, db_id = mrec.model, mrec.db_id
 
         logging.getLogger("convert").info(
                 'Deleting %s@%s' % (db_id, model))
         try:
             # Deletion of the record
             if model in object_name_list:
-                model_obj = pool.get(model)
-                model_obj.delete(db_id)
-                mdata_delete.append(mdata_id)
+                Model = pool.get(model)
+                Model.delete([Model(db_id)])
+                mdata_delete.append(mrec)
             else:
                 logging.getLogger("convert").warning(
                         'Could not delete id %d of model %s because model no '
@@ -831,7 +824,7 @@
 
     # Clean model_data:
     if mdata_delete:
-        modeldata_obj.delete(mdata_delete)
+        ModelData.delete(mdata_delete)
         cursor.commit()
 
     return True
diff -r f6ef988fda71 trytond/error.py
--- a/trytond/error.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/error.py	Sun Sep 23 17:30:03 2012 +0200
@@ -7,7 +7,8 @@
 
 class WarningErrorMixin(object):
 
-    def raise_user_error(self, error, error_args=None,
+    @classmethod
+    def raise_user_error(cls, error, error_args=None,
             error_description='', error_description_args=None,
             raise_exception=True):
         '''
@@ -26,14 +27,14 @@
             (or tuple if error_description is not empty) instead of raising an
             exception.
         '''
-        translation_obj = Pool().get('ir.translation')
+        Translation = Pool().get('ir.translation')
 
-        error = self._error_messages.get(error, error)
+        error = cls._error_messages.get(error, error)
 
         language = Transaction().language
-        res = translation_obj.get_source(self._name, 'error', language, error)
+        res = Translation.get_source(cls.__name__, 'error', language, error)
         if not res:
-            res = translation_obj.get_source(error, 'error', language)
+            res = Translation.get_source(error, 'error', language)
         if res:
             error = res
 
@@ -44,13 +45,13 @@
                 pass
 
         if error_description:
-            error_description = self._error_messages.get(error_description,
+            error_description = cls._error_messages.get(error_description,
                     error_description)
 
-            res = translation_obj.get_source(self._name, 'error', language,
-                    error_description)
+            res = Translation.get_source(cls.__name__, 'error', language,
+                error_description)
             if not res:
-                res = translation_obj.get_source(error_description, 'error',
+                res = Translation.get_source(error_description, 'error',
                     language)
             if res:
                 error_description = res
@@ -70,7 +71,8 @@
         else:
             return error
 
-    def raise_user_warning(self, warning_name, warning,
+    @classmethod
+    def raise_user_warning(cls, warning_name, warning,
             warning_args=None, warning_description='',
             warning_description_args=None):
         '''
@@ -87,16 +89,16 @@
         :param warning_description_args: the arguments that will be used
             for "%"-based substitution
         '''
-        warning_obj = Pool().get('res.user.warning')
-        if warning_obj.check(warning_name):
+        Warning_ = Pool().get('res.user.warning')
+        if Warning_.check(warning_name):
             if warning_description:
-                warning, warning_description = self.raise_user_error(warning,
+                warning, warning_description = cls.raise_user_error(warning,
                         error_args=warning_args,
                         error_description=warning_description,
                         error_description_args=warning_description_args,
                         raise_exception=False)
                 raise UserWarning(warning_name, warning, warning_description)
             else:
-                warning = self.raise_user_error(warning,
+                warning = cls.raise_user_error(warning,
                         error_args=warning_args, raise_exception=False)
                 raise UserWarning(warning_name, warning)
diff -r f6ef988fda71 trytond/ir/__init__.py
--- a/trytond/ir/__init__.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/ir/__init__.py	Sun Sep 23 17:30:03 2012 +0200
@@ -1,8 +1,11 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
+from ..pool import Pool
 from .translation import *
 from .sequence import *
-from . import ui
+from .ui.menu import *
+from .ui.view import *
+from .ui.icon import *
 from .property import *
 from .action import *
 from .model import *
@@ -11,8 +14,81 @@
 from .lang import *
 from .export import *
 from .rule import *
-from . import module
+from .module import *
 from .cache import *
 from .date import *
 from .trigger import *
 from .session import *
+
+
+def register():
+    Pool.register(
+        Translation,
+        ReportTranslationSetStart,
+        ReportTranslationSetSucceed,
+        TranslationCleanStart,
+        TranslationCleanSucceed,
+        TranslationUpdateStart,
+        TranslationExportStart,
+        TranslationExportResult,
+        SequenceType,
+        Sequence,
+        SequenceStrict,
+        UIMenu,
+        View,
+        ShowViewStart,
+        ViewShortcut,
+        ViewTreeWidth,
+        ViewTreeExpandedState,
+        Icon,
+        Property,
+        Action,
+        ActionKeyword,
+        ActionReport,
+        ActionActWindow,
+        ActionActWindowView,
+        ActionWizard,
+        ActionURL,
+        Model,
+        ModelField,
+        ModelAccess,
+        ModelFieldAccess,
+        ModelButton,
+        ModelData,
+        PrintModelGraphStart,
+        Attachment,
+        Cron,
+        Lang,
+        Export,
+        ExportLine,
+        RuleGroup,
+        Rule,
+        Module,
+        ModuleDependency,
+        ModuleConfigWizardItem,
+        ModuleConfigWizardFirst,
+        ModuleConfigWizardOther,
+        ModuleInstallUpgradeStart,
+        ModuleInstallUpgradeDone,
+        Cache,
+        Date,
+        Trigger,
+        TriggerLog,
+        Session,
+        SessionWizard,
+        module='ir', type_='model')
+    Pool.register(
+        ReportTranslationSet,
+        TranslationClean,
+        TranslationUpdate,
+        TranslationExport,
+        OpenShortcut,
+        ShowView,
+        PrintModelGraph,
+        ModuleConfigWizard,
+        ModuleInstallUpgrade,
+        ModuleConfig,
+        module='ir', type_='wizard')
+    Pool.register(
+        ModelGraph,
+        module='ir', type_='report')
diff -r f6ef988fda71 trytond/ir/action.py
--- a/trytond/ir/action.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/ir/action.py	Sun Sep 23 17:30:03 2012 +0200
@@ -5,21 +5,26 @@
 from operator import itemgetter
 from collections import defaultdict
 
-from ..model import ModelView, ModelSQL, ModelStorage, fields
+from ..model import ModelView, ModelSQL, fields
 from ..tools import file_open, safe_eval
 from ..backend import TableHandler
 from ..pyson import PYSONEncoder, CONTEXT, PYSON
 from ..transaction import Transaction
 from ..pool import Pool
 from ..cache import Cache
+from ..rpc import RPC
+
+__all__ = [
+    'Action', 'ActionKeyword', 'ActionReport', 'ActionActWindow',
+    'ActionActWindowView', 'ActionWizard', 'ActionURL',
+    ]
 
 EMAIL_REFKEYS = set(('cc', 'to', 'subject'))
 
 
 class Action(ModelSQL, ModelView):
     "Action"
-    _name = 'ir.action'
-    _description = __doc__
+    __name__ = 'ir.action'
     name = fields.Char('Name', required=True, translate=True)
     type = fields.Char('Type', required=True, readonly=True)
     usage = fields.Char('Usage')
@@ -30,22 +35,26 @@
     icon = fields.Many2One('ir.ui.icon', 'Icon')
     active = fields.Boolean('Active', select=True)
 
-    def __init__(self):
-        super(Action, self).__init__()
-        self._rpc.update({
-            'get_action_id': False,
-        })
+    @classmethod
+    def __setup__(cls):
+        super(Action, cls).__setup__()
+        cls.__rpc__.update({
+                'get_action_id': RPC(),
+                })
 
-    def default_usage(self):
+    @staticmethod
+    def default_usage():
         return None
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def get_action_id(self, action_id):
+    @classmethod
+    def get_action_id(cls, action_id):
         pool = Pool()
         with Transaction().set_context(active_test=False):
-            if self.search([
+            if cls.search([
                 ('id', '=', action_id),
                 ]):
                 return action_id
@@ -55,18 +64,18 @@
                     'ir.action.wizard',
                     'ir.action.url',
                     ):
-                action_obj = pool.get(action_type)
-                action_id2 = action_obj.search([
+                Action = pool.get(action_type)
+                action_id2 = Action.search([
                     ('id', '=', action_id),
                     ])
                 if action_id2:
-                    action = action_obj.browse(action_id2[0])
+                    action = Action.browse(action_id2[0])
                     return action.action.id
 
-    def get_action_values(self, type_, action_ids):
-        action_obj = Pool().get(type_)
-        columns = set(action_obj._columns.keys()
-            + action_obj._inherit_fields.keys())
+    @classmethod
+    def get_action_values(cls, type_, action_ids):
+        Action = Pool().get(type_)
+        columns = set(Action._fields.keys() + Action._inherit_fields.keys())
         columns.add('icon.rec_name')
         to_remove = ()
         if type_ == 'ir.action.report':
@@ -75,15 +84,12 @@
         elif type_ == 'ir.action.act_window':
             to_remove = ('domain', 'context', 'search_value')
         columns.difference_update(to_remove)
-        return action_obj.read(action_ids, list(columns))
-
-Action()
+        return Action.read(action_ids, list(columns))
 
 
 class ActionKeyword(ModelSQL, ModelView):
     "Action keyword"
-    _name = 'ir.action.keyword'
-    _description = __doc__
+    __name__ = 'ir.action.keyword'
     keyword = fields.Selection([
             ('tree_open', 'Open tree'),
             ('tree_action', 'Action tree'),
@@ -97,141 +103,93 @@
         ondelete='CASCADE', select=True)
     groups = fields.Function(fields.One2Many('res.group', None, 'Groups'),
         'get_groups', searcher='search_groups')
+    _get_keyword_cache = Cache('ir_action_keyword.get_keyword')
 
-    def __init__(self):
-        super(ActionKeyword, self).__init__()
-        self._rpc.update({'get_keyword': False})
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(ActionKeyword, cls).__setup__()
+        cls.__rpc__.update({'get_keyword': RPC()})
+        cls._constraints += [
             ('check_wizard_model', 'wrong_wizard_model'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'wrong_wizard_model': 'Wrong wizard model!',
         })
 
-    def init(self, module_name):
-        super(ActionKeyword, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(ActionKeyword, cls).__register__(module_name)
 
-        table = TableHandler(Transaction().cursor, self, module_name)
+        table = TableHandler(Transaction().cursor, cls, module_name)
         table.index_action(['keyword', 'model'], 'add')
 
-    def get_groups(self, ids, name):
-        groups = {}
-        for keyword in self.browse(ids):
-            groups[keyword.id] = [g.id for g in keyword.action.groups]
-        return groups
+    def get_groups(self, name):
+        return [g.id for g in self.action.groups]
 
-    def search_groups(self, name, clause):
+    @classmethod
+    def search_groups(cls, name, clause):
         return [('action.groups',) + tuple(clause[1:])]
 
-    def check_wizard_model(self, ids):
-        pool = Pool()
-        action_wizard_obj = pool.get('ir.action.wizard')
-        for action_keyword in self.browse(ids):
-            if action_keyword.action.type == 'ir.action.wizard':
-                action_wizard_ids = action_wizard_obj.search([
-                    ('action', '=', action_keyword.action.id),
-                    ], limit=1)
-                if not action_wizard_ids:
-                    continue
-                action_wizard_id, = action_wizard_ids
-                action_wizard = action_wizard_obj.browse(action_wizard_id)
-                if action_wizard.model:
-                    model, record_id = action_keyword.model.split(',', 1)
-                    if model != action_wizard.model:
-                        return False
+    def check_wizard_model(self):
+        ActionWizard = Pool().get('ir.action.wizard')
+        if self.action.type == 'ir.action.wizard':
+            action_wizard, = ActionWizard.search([
+                ('action', '=', self.action.id),
+                ], limit=1)
+            if action_wizard.model:
+                if self.model.__name__ != action_wizard.model:
+                    return False
         return True
 
-    def _convert_vals(self, vals):
+    @staticmethod
+    def _convert_vals(vals):
         vals = vals.copy()
         pool = Pool()
-        action_obj = pool.get('ir.action')
+        Action = pool.get('ir.action')
         if 'action' in vals:
-            vals['action'] = action_obj.get_action_id(vals['action'])
+            vals['action'] = Action.get_action_id(vals['action'])
         return vals
 
-    def models_get(self):
+    @staticmethod
+    def models_get():
         pool = Pool()
-        model_obj = pool.get('ir.model')
-        model_ids = model_obj.search([])
+        Model = pool.get('ir.model')
+        models = Model.search([])
         res = []
-        for model in model_obj.browse(model_ids):
+        for model in models:
             res.append([model.model, model.name])
         return res
 
-    def delete(self, ids):
-        pool = Pool()
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        for keyword in self.browse(ids):
-            # Restart the cache view
-            try:
-                pool.get(keyword.model.split(',')[0]
-                        ).fields_view_get.reset()
-            except Exception:
-                pass
-            try:
-                pool.get(keyword.model.split(',')[0]
-                    ).view_toolbar_get.reset()
-            except Exception:
-                pass
-        self.get_keyword.reset()
-        return super(ActionKeyword, self).delete(ids)
+    @classmethod
+    def delete(cls, keywords):
+        ModelView._fields_view_get_cache.clear()
+        ModelView._view_toolbar_get_cache.clear()
+        cls._get_keyword_cache.clear()
+        super(ActionKeyword, cls).delete(keywords)
 
-    def create(self, vals):
-        pool = Pool()
-        vals = self._convert_vals(vals)
-        if 'model' in vals:
-            # Restart the cache view
-            try:
-                pool.get(vals['model'].split(',')[0]
-                        ).fields_view_get.reset()
-            except Exception:
-                pass
-            try:
-                pool.get(vals['model'].split(',')[0]
-                    ).view_toolbar_get.reset()
-            except Exception:
-                pass
-        self.get_keyword.reset()
-        return super(ActionKeyword, self).create(vals)
+    @classmethod
+    def create(cls, vals):
+        ModelView._fields_view_get_cache.clear()
+        ModelView._view_toolbar_get_cache.clear()
+        cls._get_keyword_cache.clear()
+        vals = cls._convert_vals(vals)
+        return super(ActionKeyword, cls).create(vals)
 
-    def write(self, ids, vals):
-        pool = Pool()
-        vals = self._convert_vals(vals)
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        for keyword in self.browse(ids):
-            # Restart the cache view
-            try:
-                pool.get(keyword.model.split(',')[0]
-                        ).fields_view_get.reset()
-            except Exception:
-                pass
-            try:
-                pool.get(keyword.model.split(',')[0]
-                    ).view_toolbar_get.reset()
-            except Exception:
-                pass
-        self.get_keyword.reset()
-        res = super(ActionKeyword, self).write(ids, vals)
-        for keyword in self.browse(ids):
-            # Restart the cache view
-            try:
-                pool.get(keyword.model.split(',')[0]
-                        ).fields_view_get.reset()
-            except Exception:
-                pass
-            try:
-                pool.get(keyword.model.split(',')[0]
-                    ).view_toolbar_get.reset()
-            except Exception:
-                pass
-        return res
+    @classmethod
+    def write(cls, keywords, vals):
+        vals = cls._convert_vals(vals)
+        super(ActionKeyword, cls).write(keywords, vals)
+        ModelView._fields_view_get_cache.clear()
+        ModelView._view_toolbar_get_cache.clear()
+        cls._get_keyword_cache.clear()
 
-    @Cache('ir_action_keyword.get_keyword')
-    def get_keyword(self, keyword, value):
-        pool = Pool()
-        action_obj = pool.get('ir.action')
+    @classmethod
+    def get_keyword(cls, keyword, value):
+        Action = Pool().get('ir.action')
+        key = (keyword, tuple(value))
+        keywords = cls._get_keyword_cache.get(key)
+        if keywords is not None:
+            return keywords
         keywords = []
         model, model_id = value
 
@@ -247,23 +205,21 @@
                     ('model', '=', model + ',' + str(model_id)),
                     ],
                 ]
-        action_keyword_ids = self.search(clause, order=[])
+        action_keywords = cls.search(clause, order=[])
         types = defaultdict(list)
-        for action_keyword in self.browse(action_keyword_ids):
+        for action_keyword in action_keywords:
             type_ = action_keyword.action.type
             types[type_].append(action_keyword.action.id)
         for type_, action_ids in types.iteritems():
-            keywords.extend(action_obj.get_action_values(type_, action_ids))
+            keywords.extend(Action.get_action_values(type_, action_ids))
         keywords.sort(key=itemgetter('name'))
+        cls._get_keyword_cache.set(key, keywords)
         return keywords
 
-ActionKeyword()
-
 
 class ActionReport(ModelSQL, ModelView):
     "Action report"
-    _name = 'ir.action.report'
-    _description = __doc__
+    __name__ = 'ir.action.report'
     _inherits = {'ir.action': 'action'}
     model = fields.Char('Model')
     report_name = fields.Char('Internal Name', required=True)
@@ -355,24 +311,26 @@
         "Example: {'to': 'test@example.com', 'cc': 'user@example.com'}")
     pyson_email = fields.Function(fields.Char('PySON Email'), 'get_pyson')
 
-    def __init__(self):
-        super(ActionReport, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(ActionReport, cls).__setup__()
+        cls._sql_constraints += [
             ('report_name_module_uniq', 'UNIQUE(report_name, module)',
                 'The internal name must be unique by module!'),
         ]
-        self._constraints += [
+        cls._constraints += [
             ('check_email', 'invalid_email'),
             ]
-        self._error_messages.update({
+        cls._error_messages.update({
                 'invalid_email': 'Invalid email!',
                 })
 
-    def init(self, module_name):
-        super(ActionReport, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(ActionReport, cls).__register__(module_name)
 
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
         # Migration from 1.0 report_name_uniq has been removed
         table.drop_constraint('report_name_uniq')
@@ -380,16 +338,16 @@
         # Migration from 1.0 output_format (m2o) is now extension (selection)
         if table.column_exist('output_format'):
             cursor.execute(
-                'SELECT report.id FROM "' + self._table + '" report '
+                'SELECT report.id FROM "' + cls._table + '" report '
                 'JOIN ir_action_report_outputformat of '
                     'ON (report.output_format = of.id) '
                 'WHERE of.format = \'pdf\'')
 
             ids = [x[0] for x in cursor.fetchall()]
             with Transaction().set_user(0):
-                self.write(ids, {'extension': 'pdf'})
-                ids = self.search([('id', 'not in', ids)])
-                self.write(ids, {'extension': 'odt'})
+                cls.write(cls.browse(ids), {'extension': 'pdf'})
+                ids = cls.search([('id', 'not in', ids)])
+                cls.write(cls.browse(ids), {'extension': 'odt'})
 
             table.drop_column("output_format")
             TableHandler.dropTable(cursor, 'ir.action.report.outputformat',
@@ -397,7 +355,7 @@
 
         # Migrate from 2.0 remove required on extension
         table.not_null_action('extension', action='remove')
-        cursor.execute('UPDATE "' + self._table + '" '
+        cursor.execute('UPDATE "' + cls._table + '" '
             'SET extension = %s '
             'WHERE extension = %s', ('', 'odt'))
 
@@ -407,43 +365,50 @@
                 and table.column_exist('report_content_custom')):
             limit = cursor.IN_MAX
             cursor.execute('SELECT COUNT(id) '
-                'FROM "' + self._table + '"')
+                'FROM "' + cls._table + '"')
             report_count, = cursor.fetchone()
             for offset in range(0, report_count, limit):
                 cursor.execute(cursor.limit_clause(
                     'SELECT id, report_content_data '
-                    'FROM "' + self._table + '"'
+                    'FROM "' + cls._table + '"'
                     'ORDER BY id',
                     limit, offset))
                 for report_id, report in cursor.fetchall():
                     if report:
                         report = buffer(base64.decodestring(str(report)))
-                        cursor.execute('UPDATE "' + self._table + '" '
+                        cursor.execute('UPDATE "' + cls._table + '" '
                             'SET report_content_custom = %s '
                             'WHERE id = %s', (report, report_id))
             table.drop_column('report_content_data')
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return 'ir.action.report'
 
-    def default_report_content(self):
+    @staticmethod
+    def default_report_content():
         return None
 
-    def default_direct_print(self):
+    @staticmethod
+    def default_direct_print():
         return False
 
-    def default_template_extension(self):
+    @staticmethod
+    def default_template_extension():
         return 'odt'
 
-    def default_extension(self):
+    @staticmethod
+    def default_extension():
         return ''
 
-    def default_module(self):
+    @staticmethod
+    def default_module():
         return Transaction().context.get('module') or ''
 
-    def check_email(self, ids):
+    @classmethod
+    def check_email(cls, reports):
         "Check email"
-        for report in self.browse(ids):
+        for report in reports:
             if report.email:
                 try:
                     value = safe_eval(report.email, CONTEXT)
@@ -457,134 +422,128 @@
                     return False
         return True
 
-    def get_report_content(self, ids, name):
-        res = {}
+    @classmethod
+    def get_report_content(cls, reports, name):
+        contents = {}
         converter = buffer
         default = None
-        format_ = Transaction().context.pop('%s.%s' % (self._name, name), '')
+        format_ = Transaction().context.pop('%s.%s'
+            % (cls.__name__, name), '')
         if format_ == 'size':
             converter = len
             default = 0
-        for report in self.browse(ids):
-            data = report[name + '_custom']
-            if not data and report[name[:-8]]:
+        for report in reports:
+            data = getattr(report, name + '_custom')
+            if not data and getattr(report, name[:-8]):
                 try:
-                    with file_open(report[name[:-8]].replace('/', os.sep),
+                    with file_open(
+                            getattr(report, name[:-8]).replace('/', os.sep),
                             mode='rb') as fp:
                         data = fp.read()
                 except Exception:
                     data = None
-            res[report.id] = converter(data) if data else default
-        return res
+            contents[report.id] = converter(data) if data else default
+        return contents
 
-    def set_report_content(self, ids, name, value):
-        self.write(ids, {'%s_custom' % name: value})
+    @classmethod
+    def set_report_content(cls, records, name, value):
+        cls.write(records, {'%s_custom' % name: value})
 
-    def get_style_content(self, ids, name):
-        res = {}
+    @classmethod
+    def get_style_content(cls, reports, name):
+        contents = {}
         converter = buffer
         default = None
-        format_ = Transaction().context.pop('%s.%s' % (self._name, name), '')
+        format_ = Transaction().context.pop('%s.%s'
+            % (cls.__name__, name), '')
         if format_ == 'size':
             converter = len
             default = 0
-        for report in self.browse(ids):
+        for report in reports:
             try:
                 with file_open(report.style.replace('/', os.sep),
                         mode='rb') as fp:
                     data = fp.read()
             except Exception:
                 data = None
-            res[report.id] = converter(data) if data else default
-        return res
+            contents[report.id] = converter(data) if data else default
+        return contents
 
-    def get_pyson(self, ids, name):
-        res = {}
+    @classmethod
+    def get_pyson(cls, reports, name):
+        pysons = {}
         encoder = PYSONEncoder()
         field = name[6:]
         defaults = {
             'email': '{}',
-        }
-        for act in self.browse(ids):
-            res[act.id] = encoder.encode(safe_eval(act[field] or
-                defaults.get(field, 'None'), CONTEXT))
-        return res
+            }
+        for report in reports:
+            pysons[report.id] = encoder.encode(safe_eval(getattr(report, field)
+                    or defaults.get(field, 'None'), CONTEXT))
+        return pysons
 
-    def copy(self, ids, default=None):
-        action_obj = Pool().get('ir.action')
+    @classmethod
+    def copy(cls, reports, default=None):
+        Action = Pool().get('ir.action')
 
         if default is None:
             default = {}
         default = default.copy()
         default.setdefault('module', None)
 
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
-
-        new_ids = []
-        reports = self.browse(ids)
+        new_reports = []
         for report in reports:
             if report.report:
                 default['report_content'] = None
             default['report_name'] = report.report_name
-            default['action'] = action_obj.copy(report.action.id)
-            new_ids.append(super(ActionReport, self).copy(report.id,
-                default=default))
-        if int_id:
-            return new_ids[0]
-        return new_ids
+            default['action'] = Action.copy([report.action])[0].id
+            new_reports.extend(super(ActionReport, cls).copy([report],
+                    default=default))
+        return new_reports
 
-    def create(self, vals):
+    @classmethod
+    def create(cls, vals):
         later = {}
         vals = vals.copy()
         for field in vals:
-            if field in self._columns \
-                    and hasattr(self._columns[field], 'set'):
+            if (field in cls._fields
+                    and hasattr(cls._fields[field], 'set')):
                 later[field] = vals[field]
         for field in later:
             del vals[field]
         cursor = Transaction().cursor
-        if cursor.nextid(self._table):
-            cursor.setnextid(self._table, cursor.currid('ir_action'))
-        new_id = super(ActionReport, self).create(vals)
-        report = self.browse(new_id)
+        if cursor.nextid(cls._table):
+            cursor.setnextid(cls._table, cursor.currid('ir_action'))
+        report = super(ActionReport, cls).create(vals)
         new_id = report.action.id
-        cursor.execute('UPDATE "' + self._table + '" SET id = %s ' \
+        cursor.execute('UPDATE "' + cls._table + '" SET id = %s ' \
                 'WHERE id = %s', (report.action.id, report.id))
-        cursor.update_auto_increment(self._table, report.action.id)
-        ModelStorage.delete(self, report.id)
-        self.write(new_id, later)
-        return new_id
+        cursor.update_auto_increment(cls._table, report.action.id)
+        report = cls(new_id)
+        cls.write([report], later)
+        return report
 
-    def write(self, ids, vals):
+    @classmethod
+    def write(cls, reports, vals):
         context = Transaction().context
         if 'module' in context:
             vals = vals.copy()
             vals['module'] = context['module']
 
-        return super(ActionReport, self).write(ids, vals)
+        super(ActionReport, cls).write(reports, vals)
 
-    def delete(self, ids):
-        pool = Pool()
-        action_obj = pool.get('ir.action')
+    @classmethod
+    def delete(cls, reports):
+        Action = Pool().get('ir.action')
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        action_ids = [x.action.id for x in self.browse(ids)]
-
-        res = super(ActionReport, self).delete(ids)
-        action_obj.delete(action_ids)
-        return res
-
-ActionReport()
+        actions = [x.action for x in reports]
+        super(ActionReport, cls).delete(reports)
+        Action.delete(actions)
 
 
 class ActionActWindow(ModelSQL, ModelView):
     "Action act window"
-    _name = 'ir.action.act_window'
-    _description = __doc__
+    __name__ = 'ir.action.act_window'
     _inherits = {'ir.action': 'action'}
     domain = fields.Char('Domain Value')
     context = fields.Char('Context Value')
@@ -608,53 +567,62 @@
     pyson_search_value = fields.Function(fields.Char(
         'PySON Search Criteria'), 'get_pyson')
 
-    def __init__(self):
-        super(ActionActWindow, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(ActionActWindow, cls).__setup__()
+        cls._constraints += [
             ('check_views', 'invalid_views'),
             ('check_domain', 'invalid_domain'),
             ('check_context', 'invalid_context'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'invalid_views': 'Invalid views!',
             'invalid_domain': 'Invalid domain or search criteria!',
             'invalid_context': 'Invalid context!',
         })
-        self._rpc.update({
-                'get': False,
+        cls.__rpc__.update({
+                'get': RPC(),
                 })
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
-        super(ActionActWindow, self).init(module_name)
+        super(ActionActWindow, cls).__register__(module_name)
 
         # Migration from 2.0: new search_value format
         cursor.execute('UPDATE "%s" '
             'SET search_value = %%s '
-            'WHERE search_value = %%s' % self._table,
+            'WHERE search_value = %%s' % cls._table,
             ('[]', '{}'))
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return 'ir.action.act_window'
 
-    def default_context(self):
+    @staticmethod
+    def default_context():
         return '{}'
 
-    def default_limit(self):
+    @staticmethod
+    def default_limit():
         return 0
 
-    def default_auto_refresh(self):
+    @staticmethod
+    def default_auto_refresh():
         return 0
 
-    def default_window_name(self):
+    @staticmethod
+    def default_window_name():
         return True
 
-    def default_search_value(self):
+    @staticmethod
+    def default_search_value():
         return '[]'
 
-    def check_views(self, ids):
+    @classmethod
+    def check_views(cls, actions):
         "Check views"
-        for action in self.browse(ids):
+        for action in actions:
             if action.res_model:
                 for act_window_view in action.act_window_views:
                     view = act_window_view.view
@@ -671,9 +639,10 @@
                         return False
         return True
 
-    def check_domain(self, ids):
+    @classmethod
+    def check_domain(cls, actions):
         "Check domain and search_value"
-        for action in self.browse(ids):
+        for action in actions:
             for domain in (action.domain, action.search_value):
                 if not domain:
                     continue
@@ -693,9 +662,10 @@
                         return False
         return True
 
-    def check_context(self, ids):
+    @classmethod
+    def check_context(cls, actions):
         "Check context"
-        for action in self.browse(ids):
+        for action in actions:
             if action.context:
                 try:
                     value = safe_eval(action.context, CONTEXT)
@@ -713,122 +683,105 @@
                         return False
         return True
 
-    def get_views(self, ids, name):
-        res = {}
-        for act in self.browse(ids):
-            res[act.id] = [(view.view.id, view.view.type)
-                    for view in act.act_window_views]
-        return res
+    def get_views(self, name):
+        return [(view.view.id, view.view.type)
+            for view in self.act_window_views]
 
-    def get_pyson(self, ids, name):
-        res = {}
+    @classmethod
+    def get_pyson(cls, windows, name):
+        pysons = {}
         encoder = PYSONEncoder()
         field = name[6:]
         defaults = {
             'domain': '[]',
             'context': '{}',
             'search_value': '{}',
-        }
-        for act in self.browse(ids):
-            res[act.id] = encoder.encode(safe_eval(act[field] or
-                defaults.get(field, 'None'), CONTEXT))
-        return res
+            }
+        for window in windows:
+            pysons[window.id] = encoder.encode(safe_eval(getattr(window, field)
+                    or defaults.get(field, 'None'), CONTEXT))
+        return pysons
 
-    def create(self, vals):
+    @classmethod
+    def create(cls, vals):
         later = {}
         vals = vals.copy()
         for field in vals:
-            if field in self._columns \
-                    and hasattr(self._columns[field], 'set'):
+            if (field in cls._fields
+                    and hasattr(cls._fields[field], 'set')):
                 later[field] = vals[field]
         for field in later:
             del vals[field]
         cursor = Transaction().cursor
-        if cursor.nextid(self._table):
-            cursor.setnextid(self._table, cursor.currid('ir_action'))
-        new_id = super(ActionActWindow, self).create(vals)
-        act_window = self.browse(new_id)
+        if cursor.nextid(cls._table):
+            cursor.setnextid(cls._table, cursor.currid('ir_action'))
+        act_window = super(ActionActWindow, cls).create(vals)
         new_id = act_window.action.id
-        cursor.execute('UPDATE "' + self._table + '" SET id = %s ' \
+        cursor.execute('UPDATE "' + cls._table + '" SET id = %s ' \
                 'WHERE id = %s', (act_window.action.id, act_window.id))
-        cursor.update_auto_increment(self._table, act_window.action.id)
-        ModelStorage.delete(self, act_window.id)
-        self.write(new_id, later)
-        return new_id
+        cursor.update_auto_increment(cls._table, act_window.action.id)
+        act_window = cls(new_id)
+        cls.write([act_window], later)
+        return act_window
 
-    def delete(self, ids):
-        pool = Pool()
-        action_obj = pool.get('ir.action')
+    @classmethod
+    def delete(cls, act_windows):
+        Action = Pool().get('ir.action')
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        action_ids = [x.action.id for x in self.browse(ids)]
+        actions = [x.action.id for x in act_windows]
+        super(ActionActWindow, cls).delete(act_windows)
+        Action.delete(actions)
 
-        res = super(ActionActWindow, self).delete(ids)
-        action_obj.delete(action_ids)
-        return res
-
-    def get(self, xml_id):
+    @classmethod
+    def get(cls, xml_id):
         'Get values from XML id'
         pool = Pool()
-        model_data_obj = pool.get('ir.model.data')
-        action_obj = pool.get('ir.action')
-        action_id = model_data_obj.get_id(*xml_id.split('.'))
-        return action_obj.get_action_values(self._name, action_id)
+        ModelData = pool.get('ir.model.data')
+        Action = pool.get('ir.action')
+        action_id = ModelData.get_id(*xml_id.split('.'))
+        return Action.get_action_values(cls.__name__, [action_id])[0]
 
-    def copy(self, ids, default=None):
-        action_obj = Pool().get('ir.action')
-
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
+    @classmethod
+    def copy(cls, actions, default=None):
+        Action = Pool().get('ir.action')
         if default is None:
             default = {}
         default = default.copy()
-        new_ids = []
-        for act_window in self.browse(ids):
-            default['action'] = action_obj.copy(act_window.action.id)
-            new_id = super(ActionActWindow, self).copy(act_window.id,
-                default=default)
-            new_ids.append(new_id)
-
-        if int_id:
-            return new_ids[0]
-        return new_ids
-
-ActionActWindow()
+        new_actions = []
+        for act_window in actions:
+            default['action'] = Action.copy([act_window.action])[0].id
+            new_actions.extend(super(ActionActWindow, cls).copy([act_window],
+                    default=default))
+        return new_actions
 
 
 class ActionActWindowView(ModelSQL, ModelView):
     "Action act window view"
-    _name = 'ir.action.act_window.view'
+    __name__ = 'ir.action.act_window.view'
     _rec_name = 'view'
-    _description = __doc__
     sequence = fields.Integer('Sequence', required=True)
     view = fields.Many2One('ir.ui.view', 'View', required=True,
             ondelete='CASCADE')
     act_window = fields.Many2One('ir.action.act_window', 'Action',
             ondelete='CASCADE')
 
-    def __init__(self):
-        super(ActionActWindowView, self).__init__()
-        self._order.insert(0, ('sequence', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(ActionActWindowView, cls).__setup__()
+        cls._order.insert(0, ('sequence', 'ASC'))
 
-    def init(self, module_name):
-        super(ActionActWindowView, self).init(module_name)
-        table = TableHandler(Transaction().cursor, self, module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(ActionActWindowView, cls).__register__(module_name)
+        table = TableHandler(Transaction().cursor, cls, module_name)
 
         # Migration from 1.0 remove multi
         table.drop_column('multi')
 
-ActionActWindowView()
-
 
 class ActionWizard(ModelSQL, ModelView):
     "Action wizard"
-    _name = 'ir.action.wizard'
-    _description = __doc__
+    __name__ = 'ir.action.wizard'
     _inherits = {'ir.action': 'action'}
     wiz_name = fields.Char('Wizard name', required=True)
     action = fields.Many2One('ir.action', 'Action', required=True,
@@ -837,134 +790,115 @@
     email = fields.Char('Email')
     window = fields.Boolean('Window', help='Run wizard in a new window')
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return 'ir.action.wizard'
 
-    def create(self, vals):
+    @classmethod
+    def create(cls, vals):
         later = {}
         vals = vals.copy()
         for field in vals:
-            if field in self._columns \
-                    and hasattr(self._columns[field], 'set'):
+            if (field in cls._fields
+                    and hasattr(cls._fields[field], 'set')):
                 later[field] = vals[field]
         for field in later:
             del vals[field]
         cursor = Transaction().cursor
-        if cursor.nextid(self._table):
-            cursor.setnextid(self._table, cursor.currid('ir_action'))
-        new_id = super(ActionWizard, self).create(vals)
-        wizard = self.browse(new_id)
+        if cursor.nextid(cls._table):
+            cursor.setnextid(cls._table, cursor.currid('ir_action'))
+        wizard = super(ActionWizard, cls).create(vals)
         new_id = wizard.action.id
-        cursor.execute('UPDATE "' + self._table + '" SET id = %s ' \
+        cursor.execute('UPDATE "' + cls._table + '" SET id = %s ' \
                 'WHERE id = %s', (wizard.action.id, wizard.id))
-        cursor.update_auto_increment(self._table, wizard.action.id)
-        ModelStorage.delete(self, wizard.id)
-        self.write(new_id, later)
-        return new_id
+        cursor.update_auto_increment(cls._table, wizard.action.id)
+        wizard = cls(new_id)
+        cls.write([wizard], later)
+        return wizard
 
-    def delete(self, ids):
+    @classmethod
+    def delete(cls, wizards):
         pool = Pool()
-        action_obj = pool.get('ir.action')
+        Action = pool.get('ir.action')
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        action_ids = [x.action.id for x in self.browse(ids)]
+        actions = [x.action.id for x in wizards]
 
-        res = super(ActionWizard, self).delete(ids)
-        action_obj.delete(action_ids)
-        return res
+        super(ActionWizard, cls).delete(wizards)
+        Action.delete(actions)
 
-    def copy(self, ids, default=None):
-        action_obj = Pool().get('ir.action')
+    @classmethod
+    def copy(cls, wizards, default=None):
+        Action = Pool().get('ir.action')
 
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
         if default is None:
             default = {}
         default = default.copy()
-        new_ids = []
-        for wizard in self.browse(ids):
-            default['action'] = action_obj.copy(wizard.action.id)
-            new_id = super(ActionWizard, self).copy(wizard.id,
-                default=default)
-            new_ids.append(new_id)
-
-        if int_id:
-            return new_ids[0]
-        return new_ids
-
-ActionWizard()
+        new_wizards = []
+        for wizard in wizards:
+            default['action'] = Action.copy([wizard.action])[0].id
+            new_wizards.extend(super(ActionWizard, cls).copy([wizard],
+                    default=default))
+        return new_wizards
 
 
 class ActionURL(ModelSQL, ModelView):
     "Action URL"
-    _name = 'ir.action.url'
-    _description = __doc__
+    __name__ = 'ir.action.url'
     _inherits = {'ir.action': 'action'}
     url = fields.Char('Action Url', required=True)
     action = fields.Many2One('ir.action', 'Action', required=True,
             ondelete='CASCADE')
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return 'ir.action.url'
 
-    def default_target(self):
+    @staticmethod
+    def default_target():
         return 'new'
 
-    def create(self, vals):
+    @classmethod
+    def create(cls, vals):
         later = {}
         vals = vals.copy()
         for field in vals:
-            if field in self._columns \
-                    and hasattr(self._columns[field], 'set'):
+            if (field in cls._fields
+                    and hasattr(cls._fields[field], 'set')):
                 later[field] = vals[field]
         for field in later:
             del vals[field]
         cursor = Transaction().cursor
-        if cursor.nextid(self._table):
-            cursor.setnextid(self._table, cursor.currid('ir_action'))
-        new_id = super(ActionURL, self).create(vals)
-        url = self.browse(new_id)
+        if cursor.nextid(cls._table):
+            cursor.setnextid(cls._table, cursor.currid('ir_action'))
+        url = super(ActionURL, cls).create(vals)
         new_id = url.action.id
-        cursor.execute('UPDATE "' + self._table + '" SET id = %s ' \
+        cursor.execute('UPDATE "' + cls._table + '" SET id = %s ' \
                 'WHERE id = %s', (url.action.id, url.id))
-        cursor.update_auto_increment(self._table, url.action.id)
-        ModelStorage.delete(self, url.id)
-        self.write(new_id, later)
-        return new_id
+        cursor.update_auto_increment(cls._table, url.action.id)
+        url = cls(new_id)
+        cls.write([url], later)
+        return url
 
-    def delete(self, ids):
+    @classmethod
+    def delete(cls, urls):
         pool = Pool()
-        action_obj = pool.get('ir.action')
+        Action = pool.get('ir.action')
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        action_ids = [x.action.id for x in self.browse(ids)]
+        actions = [x.action for x in urls]
 
-        res = super(ActionURL, self).delete(ids)
-        action_obj.delete(action_ids)
-        return res
+        super(ActionURL, cls).delete(urls)
+        Action.delete(actions)
 
-    def copy(self, ids, default=None):
-        action_obj = Pool().get('ir.action')
+    @classmethod
+    def copy(cls, urls, default=None):
+        Action = Pool().get('ir.action')
 
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
         if default is None:
             default = {}
         default = default.copy()
-        new_ids = []
-        for url in self.browse(ids):
-            default['action'] = action_obj.copy(url.action.id)
-            new_id = super(ActionURL, self).copy(url.id, default=default)
-            new_ids.append(new_id)
-
-        if int_id:
-            return new_ids[0]
-        return new_ids
-
-ActionURL()
+        new_urls = []
+        for url in urls:
+            default['action'] = Action.copy([url.action])[0].id
+            new_urls.extend(super(ActionURL, cls).copy([url],
+                    default=default))
+        return new_urls
diff -r f6ef988fda71 trytond/ir/attachment.py
--- a/trytond/ir/attachment.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/ir/attachment.py	Sun Sep 23 17:30:03 2012 +0200
@@ -13,6 +13,10 @@
 from ..pyson import Eval
 from ..pool import Pool
 
+__all__ = [
+    'Attachment',
+    ]
+
 
 def firstline(description):
     try:
@@ -23,8 +27,7 @@
 
 class Attachment(ModelSQL, ModelView):
     "Attachment"
-    _name = 'ir.attachment'
-    _description = __doc__
+    __name__ = 'ir.attachment'
     name = fields.Char('Name', required=True)
     type = fields.Selection([
         ('data', 'Data'),
@@ -35,7 +38,7 @@
                 }, depends=['type']), 'get_data', setter='set_data')
     description = fields.Text('Description')
     summary = fields.Function(fields.Char('Summary',
-        on_change_with=['description']), 'get_summary')
+        on_change_with=['description']), 'on_change_with_summary')
     resource = fields.Reference('Resource', selection='models_get',
         select=True)
     link = fields.Char('Link', states={
@@ -47,23 +50,25 @@
                 'invisible': Eval('type') != 'data',
                 }, depends=['type']), 'get_data')
     last_modification = fields.Function(fields.DateTime('Last Modification'),
-            'get_last_modification')
+        'get_last_modification')
     last_user = fields.Function(fields.Char('Last User'),
         'get_last_user')
 
-    def __init__(self):
-        super(Attachment, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Attachment, cls).__setup__()
+        cls._sql_constraints += [
             ('resource_name', 'UNIQUE(resource, name)',
                 'The  names of attachments must be unique by resource!'),
         ]
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
 
-        super(Attachment, self).init(module_name)
+        super(Attachment, cls).__register__(module_name)
 
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
         # Migration from 1.4 res_model and res_id merged into resource
         # Reference
@@ -71,58 +76,61 @@
                 table.column_exist('res_id'):
             table.drop_constraint('res_model_res_id_name')
             cursor.execute('UPDATE "%s" '
-            'SET "resource" = "res_model"||\',\'||"res_id"' % self._table)
+            'SET "resource" = "res_model"||\',\'||"res_id"' % cls._table)
             table.drop_column('res_model')
             table.drop_column('res_id')
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return 'data'
 
-    def default_resource(self):
+    @staticmethod
+    def default_resource():
         return Transaction().context.get('resource')
 
-    def default_collision(self):
+    @staticmethod
+    def default_collision():
         return 0
 
-    def models_get(self):
+    @staticmethod
+    def models_get():
         pool = Pool()
-        model_obj = pool.get('ir.model')
-        model_ids = model_obj.search([])
+        Model = pool.get('ir.model')
+        models = Model.search([])
         res = []
-        for model in model_obj.browse(model_ids):
+        for model in models:
             res.append([model.model, model.name])
         return res
 
-    def get_data(self, ids, name):
-        res = {}
+    def get_data(self, name):
         db_name = Transaction().cursor.dbname
-        format_ = Transaction().context.pop('%s.%s' % (self._name, name), '')
-        for attachment in self.browse(ids):
-            value = None
+        format_ = Transaction().context.pop('%s.%s'
+            % (self.__name__, name), '')
+        value = None
+        if name == 'data_size' or format_ == 'size':
+            value = 0
+        if self.digest:
+            filename = self.digest
+            if self.collision:
+                filename = filename + '-' + str(self.collision)
+            filename = os.path.join(CONFIG['data_path'], db_name,
+                    filename[0:2], filename[2:4], filename)
             if name == 'data_size' or format_ == 'size':
-                value = 0
-            if attachment.digest:
-                filename = attachment.digest
-                if attachment.collision:
-                    filename = filename + '-' + str(attachment.collision)
-                filename = os.path.join(CONFIG['data_path'], db_name,
-                        filename[0:2], filename[2:4], filename)
-                if name == 'data_size' or format_ == 'size':
-                    try:
-                        statinfo = os.stat(filename)
-                        value = statinfo.st_size
-                    except OSError:
-                        pass
-                else:
-                    try:
-                        with open(filename, 'rb') as file_p:
-                            value = buffer(file_p.read())
-                    except IOError:
-                        pass
-            res[attachment.id] = value
-        return res
+                try:
+                    statinfo = os.stat(filename)
+                    value = statinfo.st_size
+                except OSError:
+                    pass
+            else:
+                try:
+                    with open(filename, 'rb') as file_p:
+                        value = buffer(file_p.read())
+                except IOError:
+                    pass
+        return value
 
-    def set_data(self, ids, name, value):
+    @classmethod
+    def set_data(cls, attachments, name, value):
         if value is None:
             return
         cursor = Transaction().cursor
@@ -168,77 +176,73 @@
         else:
             with open(filename, 'wb') as file_p:
                 file_p.write(value)
-        self.write(ids, {
+        cls.write(attachments, {
             'digest': digest,
             'collision': collision,
             })
 
-    def get_summary(self, ids, name):
-        return dict((x.id, firstline(x.description or ''))
-            for x in self.browse(ids))
+    def on_change_with_summary(self, name=None):
+        return firstline(self.description or '')
 
-    def on_change_with_summary(self, values):
-        return firstline(values.get('description') or '')
+    def get_last_modification(self, name):
+        return (self.write_date if self.write_date else self.create_date
+            ).replace(microsecond=0)
 
-    def get_last_modification(self, ids, name):
-        return dict((x.id, (x.write_date if x.write_date else x.create_date
-            ).replace(microsecond=0))
-            for x in self.browse(ids))
+    @classmethod
+    def get_last_user(cls, attachments, name):
+        with Transaction().set_user(0):
+            return dict(
+                (x.id, x.write_uid.rec_name
+                    if x.write_uid else x.create_uid.rec_name)
+                for x in cls.browse(attachments))
 
-    def get_last_user(self, ids, name):
-        with Transaction().set_user(0):
-            return dict((x.id, x.write_uid.rec_name if x.write_uid
-                else x.create_uid.rec_name) for x in self.browse(ids))
-
-    def check_access(self, ids, mode='read'):
+    @classmethod
+    def check_access(cls, ids, mode='read'):
         pool = Pool()
-        model_access_obj = pool.get('ir.model.access')
+        ModelAccess = pool.get('ir.model.access')
         if Transaction().user == 0:
             return
-        if not ids:
-            return
-        if isinstance(ids, (int, long)):
-            ids = [ids]
         model_names = set()
         with Transaction().set_user(0):
-            for attachment in self.browse(ids):
+            for attachment in cls.browse(ids):
                 if attachment.resource:
                     model_names.add(attachment.resource.split(',')[0])
         for model_name in model_names:
-            model_access_obj.check(model_name, mode=mode)
+            ModelAccess.check(model_name, mode=mode)
 
-    def read(self, ids, fields_names=None):
-        self.check_access(ids, mode='read')
-        return super(Attachment, self).read(ids, fields_names=fields_names)
+    @classmethod
+    def read(cls, ids, fields_names=None):
+        cls.check_access(ids, mode='read')
+        return super(Attachment, cls).read(ids, fields_names=fields_names)
 
-    def delete(self, ids):
-        self.check_access(ids, mode='delete')
-        return super(Attachment, self).delete(ids)
+    @classmethod
+    def delete(cls, attachments):
+        cls.check_access([a.id for a in attachments], mode='delete')
+        super(Attachment, cls).delete(attachments)
 
-    def write(self, ids, vals):
-        self.check_access(ids, mode='write')
-        res = super(Attachment, self).write(ids, vals)
-        self.check_access(ids, mode='write')
-        return res
+    @classmethod
+    def write(cls, attachments, vals):
+        cls.check_access([a.id for a in attachments], mode='write')
+        super(Attachment, cls).write(attachments, vals)
+        cls.check_access(attachments, mode='write')
 
-    def create(self, vals):
-        res = super(Attachment, self).create(vals)
-        self.check_access(res, mode='create')
-        return res
+    @classmethod
+    def create(cls, vals):
+        attachment = super(Attachment, cls).create(vals)
+        cls.check_access([attachment.id], mode='create')
+        return attachment
 
-    def view_header_get(self, value, view_type='form'):
+    @classmethod
+    def view_header_get(cls, value, view_type='form'):
         pool = Pool()
-        ir_model_obj = pool.get('ir.model')
-        value = super(Attachment, self).view_header_get(value,
+        Model = pool.get('ir.model')
+        value = super(Attachment, cls).view_header_get(value,
                 view_type=view_type)
         resource = Transaction().context.get('resource')
         if resource:
             model_name, record_id = resource.split(',', 1)
-            ir_model_id, = ir_model_obj.search([('model', '=', model_name)])
-            ir_model = ir_model_obj.browse(ir_model_id)
-            model_obj = pool.get(model_name)
-            record = model_obj.browse(int(record_id))
+            ir_model, = Model.search([('model', '=', model_name)])
+            Resource = pool.get(model_name)
+            record = Resource(int(record_id))
             value = '%s - %s - %s' % (ir_model.name, record.rec_name, value)
         return value
-
-Attachment()
diff -r f6ef988fda71 trytond/ir/cache.py
--- a/trytond/ir/cache.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/ir/cache.py	Sun Sep 23 17:30:03 2012 +0200
@@ -2,12 +2,13 @@
 #this repository contains the full copyright notices and license terms.
 from ..model import ModelSQL, fields
 
+__all__ = [
+    'Cache',
+    ]
+
 
 class Cache(ModelSQL):
     "Cache"
-    _name = 'ir.cache'
-    _description = __doc__
+    __name__ = 'ir.cache'
     name = fields.Char('Name', required=True)
     timestamp = fields.DateTime('Timestamp')
-
-Cache()
diff -r f6ef988fda71 trytond/ir/cron.py
--- a/trytond/ir/cron.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/ir/cron.py	Sun Sep 23 17:30:03 2012 +0200
@@ -13,6 +13,10 @@
 from ..pool import Pool
 from ..backend import TableHandler
 
+__all__ = [
+    'Cron',
+    ]
+
 _INTERVALTYPES = {
     'days': lambda interval: relativedelta(days=interval),
     'hours': lambda interval: relativedelta(hours=interval),
@@ -24,8 +28,7 @@
 
 class Cron(ModelSQL, ModelView):
     "Cron"
-    _name = "ir.cron"
-    _description = __doc__
+    __name__ = "ir.cron"
     name = fields.Char('Name', required=True, translate=True)
     user = fields.Many2One('res.user', 'Execution User', required=True,
         domain=[('active', '=', False)],
@@ -53,65 +56,76 @@
     function = fields.Char('Function')
     args = fields.Text('Arguments')
 
-    def __init__(self):
-        super(Cron, self).__init__()
-        self._error_messages.update({
+    @classmethod
+    def __setup__(cls):
+        super(Cron, cls).__setup__()
+        cls._error_messages.update({
             'request_title': 'Scheduled action failed',
             'request_body': "The following action failed to execute "
                             "properly: \"%s\"\n Traceback: \n\n%s\n"
             })
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
 
         # Migration from 2.0: rename numbercall, doall and nextcall
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.column_rename('numbercall', 'number_calls')
         table.column_rename('doall', 'repeat_missed')
         table.column_rename('nextcall', 'next_call')
         table.drop_column('running')
 
-        super(Cron, self).init(module_name)
+        super(Cron, cls).__register__(module_name)
 
         # Migration from 2.0: work_days removed
         cursor.execute('UPDATE "%s" '
             'SET interval_type = %%s '
-            'WHERE interval_type = %%s' % self._table,
+            'WHERE interval_type = %%s' % cls._table,
             ('days', 'work_days'))
 
-    def default_next_call(self):
+    @staticmethod
+    def default_next_call():
         return datetime.datetime.now()
 
-    def default_interval_number(self):
+    @staticmethod
+    def default_interval_number():
         return 1
 
-    def default_interval_type(self):
+    @staticmethod
+    def default_interval_type():
         return 'months'
 
-    def default_number_calls(self):
+    @staticmethod
+    def default_number_calls():
         return -1
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_repeat_missed(self):
+    @staticmethod
+    def default_repeat_missed():
         return True
 
-    def check_xml_record(self, ids, values):
+    @staticmethod
+    def check_xml_record(crons, values):
         return True
 
-    def get_delta(self, cron):
+    @staticmethod
+    def get_delta(cron):
         '''
         Return the relativedelta for the next call
         '''
         return _INTERVALTYPES[cron.interval_type](cron.interval_number)
 
-    def _get_request_values(self, cron):
+    @classmethod
+    def _get_request_values(cls, cron):
         tb_s = ''.join(traceback.format_exception(*sys.exc_info()))
         tb_s = tb_s.decode('utf-8', 'ignore')
-        name = self.raise_user_error('request_title',
+        name = cls.raise_user_error('request_title',
             raise_exception=False)
-        body = self.raise_user_error('request_body', (cron.name, tb_s),
+        body = cls.raise_user_error('request_body', (cron.name, tb_s),
             raise_exception=False)
         values = {
             'name': name,
@@ -122,7 +136,7 @@
             'date_sent': datetime.datetime.now(),
             'references': [
                 ('create', {
-                    'reference': '%s,%s' % (self._name, cron.id),
+                    'reference': '%s,%s' % (cls.__name__, cron.id),
                 }),
             ],
             'state': 'waiting',
@@ -130,35 +144,36 @@
         }
         return values
 
-    def _callback(self, cron):
+    @classmethod
+    def _callback(cls, cron):
         pool = Pool()
         try:
             args = (cron.args or []) and safe_eval(cron.args)
-            model_obj = pool.get(cron.model)
+            Model = pool.get(cron.model)
             with Transaction().set_user(cron.user.id):
-                getattr(model_obj, cron.function)(*args)
+                getattr(Model, cron.function)(*args)
         except Exception:
             Transaction().cursor.rollback()
 
-            request_obj = pool.get('res.request')
+            Request = pool.get('res.request')
             req_user = cron.request_user
             language = (req_user.language.code if req_user.language
                     else CONFIG['language'])
             with contextlib.nested(Transaction().set_user(cron.user.id),
                     Transaction().set_context(language=language)):
-                values = self._get_request_values(cron)
-                request_obj.create(values)
+                values = cls._get_request_values(cron)
+                Request.create(values)
             Transaction().cursor.commit()
 
-    def run(self, db_name):
+    @classmethod
+    def run(cls, db_name):
         now = datetime.datetime.now()
         with Transaction().start(db_name, 0) as transaction:
-            transaction.cursor.lock(self._table)
-            cron_ids = self.search([
-                ('number_calls', '!=', 0),
-                ('next_call', '<=', datetime.datetime.now()),
-            ])
-            crons = self.browse(cron_ids)
+            transaction.cursor.lock(cls._table)
+            crons = cls.search([
+                    ('number_calls', '!=', 0),
+                    ('next_call', '<=', datetime.datetime.now()),
+                    ])
 
             for cron in crons:
                 try:
@@ -167,19 +182,17 @@
                     first = True
                     while next_call < now and number_calls != 0:
                         if first or cron.repeat_missed:
-                            self._callback(cron)
-                        next_call += self.get_delta(cron)
+                            cls._callback(cron)
+                        next_call += cls.get_delta(cron)
                         if number_calls > 0:
                             number_calls -= 1
                         first = False
 
-                    values = {
-                        'next_call': next_call,
-                        'number_calls': number_calls,
-                    }
+                    cron.next_call = next_call
+                    cron.number_calls = number_calls
                     if not number_calls:
-                        values['active'] = False
-                    self.write(cron.id, values)
+                        cron.active = False
+                    cron.save()
                     transaction.cursor.commit()
                 except Exception:
                     transaction.cursor.rollback()
@@ -188,5 +201,3 @@
                     tb_s = tb_s.decode('utf-8', 'ignore')
                     logger = logging.getLogger('cron')
                     logger.error('Exception:\n%s' % tb_s)
-
-Cron()
diff -r f6ef988fda71 trytond/ir/date.py
--- a/trytond/ir/date.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/ir/date.py	Sun Sep 23 17:30:03 2012 +0200
@@ -1,26 +1,29 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level
 #of this repository contains the full copyright notices and license terms.
+import datetime
+
 from ..model import Model
-import datetime
+from ..rpc import RPC
+
+__all__ = [
+    'Date',
+    ]
 
 
 class Date(Model):
     'Date'
-    _name = 'ir.date'
-    _description = __doc__
+    __name__ = 'ir.date'
 
-    def __init__(self):
-        super(Date, self).__init__()
-        self._rpc.update({
-            'today': False,
-            })
+    @classmethod
+    def __setup__(cls):
+        super(Date, cls).__setup__()
+        cls.__rpc__.update({
+                'today': RPC(),
+                })
 
-    def today(self):
+    @staticmethod
+    def today():
         '''
-        Current date
-
-        :return: a current datetime.date
+        Return the current date
         '''
         return datetime.date.today()
-
-Date()
diff -r f6ef988fda71 trytond/ir/export.py
--- a/trytond/ir/export.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/ir/export.py	Sun Sep 23 17:30:03 2012 +0200
@@ -3,25 +3,23 @@
 "Exports"
 from ..model import ModelView, ModelSQL, fields
 
+__all__ = [
+    'Export', 'ExportLine',
+    ]
+
 
 class Export(ModelSQL, ModelView):
     "Export"
-    _name = "ir.export"
-    _description = __doc__
+    __name__ = "ir.export"
     name = fields.Char('Name')
     resource = fields.Char('Resource')
     export_fields = fields.One2Many('ir.export.line', 'export',
        'Fields')
 
-Export()
-
 
 class ExportLine(ModelSQL, ModelView):
     "Export line"
-    _name = 'ir.export.line'
-    _description = __doc__
+    __name__ = 'ir.export.line'
     name = fields.Char('Name')
     export = fields.Many2One('ir.export', 'Export',
        select=True)
-
-ExportLine()
diff -r f6ef988fda71 trytond/ir/lang.py
--- a/trytond/ir/lang.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/ir/lang.py	Sun Sep 23 17:30:03 2012 +0200
@@ -4,7 +4,7 @@
 import warnings
 
 from ..model import ModelView, ModelSQL, fields
-from ..model.cacheable import Cacheable
+from ..cache import Cache
 from ..tools import safe_eval, datetime_strftime
 from ..transaction import Transaction
 from ..pool import Pool
@@ -15,11 +15,14 @@
 from locale import CHAR_MAX
 warnings.resetwarnings()
 
+__all__ = [
+    'Lang',
+    ]
 
-class Lang(ModelSQL, ModelView, Cacheable):
+
+class Lang(ModelSQL, ModelView):
     "Language"
-    _name = "ir.lang"
-    _description = __doc__
+    __name__ = "ir.lang"
     name = fields.Char('Name', required=True, translate=True)
     code = fields.Char('Code', required=True,
             help="RFC 4646 tag: http://tools.ietf.org/html/rfc4646")
@@ -38,75 +41,88 @@
     decimal_point = fields.Char('Decimal Separator', required=True)
     thousands_sep = fields.Char('Thousands Separator')
 
-    def __init__(self):
-        super(Lang, self).__init__()
-        self._constraints += [
+    _lang_cache = Cache('ir.lang')
+
+    @classmethod
+    def __setup__(cls):
+        super(Lang, cls).__setup__()
+        cls._constraints += [
             ('check_grouping', 'invalid_grouping'),
             ('check_date', 'invalid_date'),
         ]
-        self._sql_constraints += [
+        cls._sql_constraints += [
             ('check_decimal_point_thousands_sep',
                 'CHECK(decimal_point != thousands_sep)',
                 'decimal_point and thousands_sep must be different!'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'invalid_grouping': 'Invalid Grouping!',
             'invalid_date': 'The date format is not valid!',
         })
 
-    def search_rec_name(self, name, clause):
-        ids = self.search([('code',) + clause[1:]], order=[])
-        if ids:
-            ids += self.search([('name',) + clause[1:]], order=[])
-            return [('id', 'in', ids)]
+    @classmethod
+    def search_rec_name(cls, name, clause):
+        langs = cls.search([('code',) + clause[1:]], order=[])
+        if langs:
+            langs += cls.search([('name',) + clause[1:]], order=[])
+            return [('id', 'in', [l.id for l in langs])]
         return [('name',) + tuple(clause[1:])]
 
-    def read(self, ids, fields_names=None):
+    @classmethod
+    def read(cls, ids, fields_names=None):
         pool = Pool()
-        translation_obj = pool.get('ir.translation')
-        res = super(Lang, self).read(ids, fields_names=fields_names)
+        Translation = pool.get('ir.translation')
+        res = super(Lang, cls).read(ids, fields_names=fields_names)
         if (Transaction().context.get('translate_name')
                 and (not fields_names or 'name' in fields_names)):
             with Transaction().set_context(
                     language=CONFIG['language'],
                     translate_name=False):
-                res2 = self.read(ids, fields_names=['id', 'code', 'name'])
+                res2 = cls.read(ids, fields_names=['id', 'code', 'name'])
             for record2 in res2:
                 for record in res:
                     if record['id'] == record2['id']:
                         break
-                res_trans = translation_obj.get_ids(self._name + ',name',
+                res_trans = Translation.get_ids(cls.__name__ + ',name',
                         'model', record2['code'], [record2['id']])
                 record['name'] = res_trans.get(record2['id'], False) \
                         or record2['name']
         return res
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_translatable(self):
+    @staticmethod
+    def default_translatable():
         return False
 
-    def default_direction(self):
+    @staticmethod
+    def default_direction():
         return 'ltr'
 
-    def default_date(self):
+    @staticmethod
+    def default_date():
         return '%m/%d/%Y'
 
-    def default_grouping(self):
+    @staticmethod
+    def default_grouping():
         return '[]'
 
-    def default_decimal_point(self):
+    @staticmethod
+    def default_decimal_point():
         return '.'
 
-    def default_thousands_sep(self):
+    @staticmethod
+    def default_thousands_sep():
         return ','
 
-    def check_grouping(self, ids):
+    @classmethod
+    def check_grouping(cls, langs):
         '''
         Check if grouping is list of numbers
         '''
-        for lang in self.browse(ids):
+        for lang in langs:
             try:
                 grouping = safe_eval(lang.grouping)
                 for i in grouping:
@@ -116,11 +132,12 @@
                 return False
         return True
 
-    def check_date(self, ids):
+    @classmethod
+    def check_date(cls, langs):
         '''
         Check the date format
         '''
-        for lang in self.browse(ids):
+        for lang in langs:
             try:
                 datetime_strftime(datetime.datetime.now(),
                         lang.date.encode('utf-8'))
@@ -145,38 +162,41 @@
                 return False
         return True
 
-    def check_xml_record(self, ids, values):
+    @staticmethod
+    def check_xml_record(langs, values):
         return True
 
-    def get_translatable_languages(self):
-        res = self.get('translatable_languages')
+    @classmethod
+    def get_translatable_languages(cls):
+        res = cls._lang_cache.get('translatable_languages')
         if res is None:
-            lang_ids = self.search([
+            langs = cls.search([
                     ('translatable', '=', True),
                     ])
-            res = [x.code for x in self.browse(lang_ids)]
-            self.add('translatable_languages', res)
+            res = [x.code for x in langs]
+            cls._lang_cache.set('translatable_languages', res)
         return res
 
-    def create(self, vals):
+    @classmethod
+    def create(cls, vals):
         # Clear cache
-        if self.get('translatable_languages'):
-            self.invalidate('translatable_languages')
-        return super(Lang, self).create(vals)
+        cls._lang_cache.clear()
+        return super(Lang, cls).create(vals)
 
-    def write(self, ids, vals):
+    @classmethod
+    def write(cls, langs, vals):
         # Clear cache
-        if self.get('translatable_languages'):
-            self.invalidate('translatable_languages')
-        return super(Lang, self).write(ids, vals)
+        cls._lang_cache.clear()
+        super(Lang, cls).write(langs, vals)
 
-    def delete(self, ids):
+    @classmethod
+    def delete(cls, langs):
         # Clear cache
-        if self.get('translatable_languages'):
-            self.invalidate('translatable_languages')
-        return super(Lang, self).delete(ids)
+        cls._lang_cache.clear()
+        super(Lang, cls).delete(langs)
 
-    def _group(self, lang, s, monetary=False):
+    @staticmethod
+    def _group(lang, s, monetary=None):
         # Code from _group in locale.py
 
         # Iterate over grouping intervals
@@ -194,11 +214,11 @@
                 last_interval = interval
 
         if monetary:
-            thousands_sep = monetary['mon_thousands_sep']
-            grouping = safe_eval(monetary['mon_grouping'])
+            thousands_sep = monetary.mon_thousands_sep
+            grouping = safe_eval(monetary.mon_grouping)
         else:
-            thousands_sep = lang['thousands_sep']
-            grouping = safe_eval(lang['grouping'])
+            thousands_sep = lang.thousands_sep
+            grouping = safe_eval(lang.grouping)
         if not grouping:
             return (s, 0)
         if s[-1] == ' ':
@@ -225,20 +245,11 @@
             len(thousands_sep) * (len(groups) - 1)
         )
 
-    def format(self, lang, percent, value, grouping=False, monetary=None,
+    @classmethod
+    def format(cls, lang, percent, value, grouping=False, monetary=None,
             *additional):
         '''
         Returns the lang-aware substitution of a %? specifier (percent).
-
-        :param lang: the BrowseRecord of the language
-        :param percent: the string with %? specifier
-        :param value: the value
-        :param grouping: a boolean to take grouping into account
-        :param monetary: a BrowseRecord of the currency or None
-        :param additional: for format strings which contain one or more
-            modifiers
-
-        :return: the formatted string
         '''
         # Code from format in locale.py
 
@@ -255,11 +266,11 @@
             return s[lpos:rpos + 1]
 
         if not lang:
-            lang = {
-                'decimal_point': self.default_decimal_point(),
-                'thousands_sep': self.default_thousands_sep(),
-                'grouping': self.default_grouping(),
-            }
+            lang = cls(
+                decimal_point=cls.default_decimal_point(),
+                thousands_sep=cls.default_thousands_sep(),
+                grouping=cls.default_grouping(),
+                )
 
         # this is only for one-percent-specifier strings
         # and this should be checked
@@ -275,41 +286,34 @@
             seps = 0
             parts = formatted.split('.')
             if grouping:
-                parts[0], seps = self._group(lang, parts[0], monetary=monetary)
+                parts[0], seps = cls._group(lang, parts[0], monetary=monetary)
             if monetary:
-                decimal_point = monetary['mon_decimal_point']
+                decimal_point = monetary.mon_decimal_point
             else:
-                decimal_point = lang['decimal_point']
+                decimal_point = lang.decimal_point
             formatted = decimal_point.join(parts)
             if seps:
                 formatted = _strip_padding(formatted, seps)
         elif percent[-1] in 'diu':
             if grouping:
-                formatted, seps = self._group(lang, formatted,
+                formatted, seps = cls._group(lang, formatted,
                     monetary=monetary)
             if seps:
                 formatted = _strip_padding(formatted, seps)
         return formatted
 
-    def currency(self, lang, val, currency, symbol=True, grouping=False):
+    @classmethod
+    def currency(cls, lang, val, currency, symbol=True, grouping=False):
         """
         Formats val according to the currency settings in lang.
-
-        :param lang: the BrowseRecord of the language
-        :param val: the value to format
-        :param currency: the BrowseRecord of the currency
-        :param symbol: a boolean to include currency symbol
-        :param grouping: a boolean to take grouping into account
-
-        :return: the formatted string
         """
         # Code from currency in locale.py
         if not lang:
-            lang = {
-                'decimal_point': self.default_decimal_point(),
-                'thousands_sep': self.default_thousands_sep(),
-                'grouping': self.default_grouping(),
-            }
+            lang = cls(
+                decimal_point=cls.default_decimal_point(),
+                thousands_sep=cls.default_thousands_sep(),
+                grouping=cls.default_grouping(),
+                )
 
         # check for illegal values
         digits = currency.digits
@@ -317,7 +321,7 @@
             raise ValueError("Currency formatting is not possible using "
                              "the 'C' locale.")
 
-        s = self.format(lang, '%%.%if' % digits, abs(val), grouping,
+        s = cls.format(lang, '%%.%if' % digits, abs(val), grouping,
                 monetary=currency)
         # '<' and '>' are markers if the sign must be inserted
         # between symbol and value
@@ -355,14 +359,10 @@
 
         return s.replace('<', '').replace('>', '')
 
-    def strftime(self, datetime, code, format):
+    @staticmethod
+    def strftime(datetime, code, format):
         '''
         Convert datetime to a string as specified by the format argument.
-
-        :param datetime: a datetime
-        :param code: locale code
-        :param format: a string
-        :return: a unicode string
         '''
         if code in TIME_LOCALE:
             for f, i in (('%a', 6), ('%A', 6), ('%b', 1), ('%B', 1)):
@@ -374,5 +374,3 @@
         else:
             format = format.encode('utf-8')
         return datetime_strftime(datetime, format).decode('utf-8')
-
-Lang()
diff -r f6ef988fda71 trytond/ir/model.py
--- a/trytond/ir/model.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/ir/model.py	Sun Sep 23 17:30:03 2012 +0200
@@ -9,13 +9,19 @@
 from ..cache import Cache
 from ..pool import Pool
 from ..pyson import Bool, Eval
+from ..rpc import RPC
+
+__all__ = [
+    'Model', 'ModelField', 'ModelAccess', 'ModelFieldAccess', 'ModelButton',
+    'ModelData', 'PrintModelGraphStart', 'PrintModelGraph', 'ModelGraph',
+    ]
+
 IDENTIFIER = re.compile(r'^[a-zA-z_][a-zA-Z0-9_]*$')
 
 
 class Model(ModelSQL, ModelView):
     "Model"
-    _name = 'ir.model'
-    _description = __doc__
+    __name__ = 'ir.model'
     name = fields.Char('Model Description', translate=True, loading='lazy',
         states={
             'readonly': Bool(Eval('module')),
@@ -36,74 +42,74 @@
     fields = fields.One2Many('ir.model.field', 'model', 'Fields',
        required=True)
 
-    def __init__(self):
-        super(Model, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Model, cls).__setup__()
+        cls._sql_constraints += [
             ('model_uniq', 'UNIQUE(model)',
                 'The model must be unique!'),
         ]
-        self._constraints += [
+        cls._constraints += [
             ('check_module', 'invalid_module'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'invalid_module': 'Module Name must be a python identifier!',
         })
-        self._order.insert(0, ('model', 'ASC'))
-        self._rpc.update({
-                'list_models': False,
+        cls._order.insert(0, ('model', 'ASC'))
+        cls.__rpc__.update({
+                'list_models': RPC(),
                 })
 
-    def check_module(self, ids):
+    @classmethod
+    def check_module(cls, models):
         '''
         Check module
         '''
-        for model in self.browse(ids):
+        for model in models:
             if model.module and not IDENTIFIER.match(model.module):
                 return False
         return True
 
-    def list_models(self):
+    @classmethod
+    def list_models(cls):
         'Return a list of all models names'
         with Transaction().set_user(0):
-            model_ids = self.search([], order=[
+            models = cls.search([], order=[
                     ('module', 'ASC'),  # Optimization assumption
                     ('model', 'ASC'),
                     ('id', 'ASC'),
                     ])
-            models = self.browse(model_ids)
             return [m.model for m in models]
 
-    def create(self, vals):
+    @classmethod
+    def create(cls, vals):
         pool = Pool()
-        property_obj = pool.get('ir.property')
-        res = super(Model, self).create(vals)
+        Property = pool.get('ir.property')
+        res = super(Model, cls).create(vals)
         # Restart the cache of models_get
-        property_obj.models_get.reset()
+        Property._models_get_cache.clear()
         return res
 
-    def write(self, ids, vals):
+    @classmethod
+    def write(cls, models, vals):
         pool = Pool()
-        property_obj = pool.get('ir.property')
-        res = super(Model, self).write(ids, vals)
+        Property = pool.get('ir.property')
+        super(Model, cls).write(models, vals)
         # Restart the cache of models_get
-        property_obj.models_get.reset()
-        return res
+        Property._models_get_cache.clear()
 
-    def delete(self, ids):
+    @classmethod
+    def delete(cls, models):
         pool = Pool()
-        property_obj = pool.get('ir.property')
-        res = super(Model, self).delete(ids)
+        Property = pool.get('ir.property')
+        super(Model, cls).delete(models)
         # Restart the cache of models_get
-        property_obj.models_get.reset()
-        return res
-
-Model()
+        Property._models_get_cache.clear()
 
 
 class ModelField(ModelSQL, ModelView):
     "Model field"
-    _name = 'ir.model.field'
-    _description = __doc__
+    __name__ = 'ir.model.field'
     name = fields.Char('Name', required=True,
         states={
             'readonly': Bool(Eval('module')),
@@ -141,43 +147,48 @@
     module = fields.Char('Module',
        help="Module in which this field is defined")
 
-    def __init__(self):
-        super(ModelField, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(ModelField, cls).__setup__()
+        cls._sql_constraints += [
             ('name_model_uniq', 'UNIQUE(name, model)',
                 'The field name in model must be unique!'),
         ]
-        self._constraints += [
+        cls._constraints += [
             ('check_name', 'invalid_name'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'invalid_name': 'Model Field Name must be a python identifier!',
         })
-        self._order.insert(0, ('name', 'ASC'))
+        cls._order.insert(0, ('name', 'ASC'))
 
-    def default_name(self):
+    @staticmethod
+    def default_name():
         return 'No Name'
 
-    def default_field_description(self):
+    @staticmethod
+    def default_field_description():
         return 'No description available'
 
-    def check_name(self, ids):
+    @classmethod
+    def check_name(cls, fields):
         '''
         Check name
         '''
-        for field in self.browse(ids):
+        for field in fields:
             if not IDENTIFIER.match(field.name):
                 return False
         return True
 
-    def read(self, ids, fields_names=None):
+    @classmethod
+    def read(cls, ids, fields_names=None):
         pool = Pool()
-        translation_obj = pool.get('ir.translation')
+        Translation = pool.get('ir.translation')
 
         to_delete = []
         if Transaction().context.get('language'):
             if fields_names is None:
-                fields_names = self._columns.keys()
+                fields_names = cls._fields.keys()
 
             if 'field_description' in fields_names \
                     or 'help' in fields_names:
@@ -188,11 +199,7 @@
                     fields_names.append('name')
                     to_delete.append('name')
 
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
-        res = super(ModelField, self).read(ids, fields_names=fields_names)
+        res = super(ModelField, cls).read(ids, fields_names=fields_names)
 
         if (Transaction().context.get('language')
                 and ('field_description' in fields_names
@@ -221,20 +228,20 @@
                 if 'help' in fields_names:
                     trans_args.append((id2model[model_id] + ',' + rec['name'],
                             'help', Transaction().language, None))
-            translation_obj.get_sources(trans_args)
+            Translation.get_sources(trans_args)
             for rec in res:
                 if isinstance(rec['model'], (list, tuple)):
                     model_id = rec['model'][0]
                 else:
                     model_id = rec['model']
                 if 'field_description' in fields_names:
-                    res_trans = translation_obj.get_source(
+                    res_trans = Translation.get_source(
                             id2model[model_id] + ',' + rec['name'],
                             'field', Transaction().language)
                     if res_trans:
                         rec['field_description'] = res_trans
                 if 'help' in fields_names:
-                    res_trans = translation_obj.get_source(
+                    res_trans = Translation.get_source(
                             id2model[model_id] + ',' + rec['name'],
                             'help', Transaction().language)
                     if res_trans:
@@ -244,17 +251,12 @@
             for rec in res:
                 for field in to_delete:
                     del rec[field]
-        if int_id:
-            res = res[0]
         return res
 
-ModelField()
-
 
 class ModelAccess(ModelSQL, ModelView):
     "Model access"
-    _name = 'ir.model.access'
-    _description = __doc__
+    __name__ = 'ir.model.access'
     _rec_name = 'model'
     model = fields.Many2One('ir.model', 'Model', required=True,
             ondelete="CASCADE")
@@ -265,44 +267,62 @@
     perm_create = fields.Boolean('Create Access')
     perm_delete = fields.Boolean('Delete Access')
     description = fields.Text('Description')
+    _get_access_cache = Cache('ir_model_access.get_access', context=False)
 
-    def __init__(self):
-        super(ModelAccess, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(ModelAccess, cls).__setup__()
+        cls._sql_constraints += [
             ('model_group_uniq', 'UNIQUE("model", "group")',
                 'Only one record by model and group is allowed!'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'read': 'You can not read this document! (%s)',
             'write': 'You can not write in this document! (%s)',
             'create': 'You can not create this kind of document! (%s)',
             'delete': 'You can not delete this document! (%s)',
             })
-        self._rpc.update({
-                'get_access': False,
+        cls.__rpc__.update({
+                'get_access': RPC(),
                 })
 
-    def check_xml_record(self, ids, values):
+    @staticmethod
+    def check_xml_record(accesses, values):
         return True
 
-    def default_perm_read(self):
+    @staticmethod
+    def default_perm_read():
         return False
 
-    def default_perm_write(self):
+    @staticmethod
+    def default_perm_write():
         return False
 
-    def default_perm_create(self):
+    @staticmethod
+    def default_perm_create():
         return False
 
-    def default_perm_delete(self):
+    @staticmethod
+    def default_perm_delete():
         return False
 
-    def get_access(self, models):
+    @classmethod
+    def get_access(cls, models):
         'Return access for models'
         pool = Pool()
-        ir_model_obj = pool.get('ir.model')
-        user_group_obj = pool.get('res.user-res.group')
+        Model = pool.get('ir.model')
+        UserGroup = pool.get('res.user-res.group')
         cursor = Transaction().cursor
+
+        access = {}
+        for model in models:
+            maccess = cls._get_access_cache.get(model, default=-1)
+            if maccess == -1:
+                break
+            access[model] = maccess
+        else:
+            return access
+
         default = {'read': True, 'write': True, 'create': True, 'delete': True}
         access = dict((m, default) for m in models)
         cursor.execute(('SELECT '
@@ -319,72 +339,57 @@
                 'WHERE m.model IN (' + ','.join(('%%s',) * len(models)) + ') '
                     'AND (gu."user" = %%s OR a."group" IS NULL) '
                 'GROUP BY m.model')
-            % (self._table, ir_model_obj._table, user_group_obj._table),
+            % (cls._table, Model._table, UserGroup._table),
             list(models) + [Transaction().user])
         access.update(dict(
                 (m, {'read': r, 'write': w, 'create': c, 'delete': d})
                 for m, r, w, c, d in cursor.fetchall()))
+        for model, maccess in access.iteritems():
+            cls._get_access_cache.set(model, maccess)
         return access
 
-    @Cache('ir_model_access.check')
-    def check(self, model_name, mode='read', raise_exception=True):
+    @classmethod
+    def check(cls, model_name, mode='read', raise_exception=True):
         'Check access for model_name and mode'
         assert mode in ['read', 'write', 'create', 'delete'], \
                 'Invalid access mode for security'
         if Transaction().user == 0:
             return True
 
-        access = self.get_access([model_name])[model_name][mode]
-        if not access:
+        access = cls.get_access([model_name])[model_name][mode]
+        if not access and access is not None:
             if raise_exception:
-                self.raise_user_error(mode, model_name)
+                cls.raise_user_error(mode, model_name)
             else:
                 return False
         return True
 
-    def write(self, ids, vals):
-        res = super(ModelAccess, self).write(ids, vals)
+    @classmethod
+    def write(cls, accesses, vals):
+        super(ModelAccess, cls).write(accesses, vals)
         # Restart the cache
-        self.check.reset()
-        pool = Pool()
-        for _, model in pool.iterobject():
-            try:
-                model.fields_view_get.reset()
-            except Exception:
-                pass
+        cls._get_access_cache.clear()
+        ModelView._fields_view_get_cache.clear()
+
+    @classmethod
+    def create(cls, vals):
+        res = super(ModelAccess, cls).create(vals)
+        # Restart the cache
+        cls._get_access_cache.clear()
+        ModelView._fields_view_get_cache.clear()
         return res
 
-    def create(self, vals):
-        res = super(ModelAccess, self).create(vals)
+    @classmethod
+    def delete(cls, accesses):
+        super(ModelAccess, cls).delete(accesses)
         # Restart the cache
-        self.check.reset()
-        pool = Pool()
-        for _, model in pool.iterobject():
-            try:
-                model.fields_view_get.reset()
-            except Exception:
-                pass
-        return res
-
-    def delete(self, ids):
-        res = super(ModelAccess, self).delete(ids)
-        # Restart the cache
-        self.check.reset()
-        pool = Pool()
-        for _, model in pool.iterobject():
-            try:
-                model.fields_view_get.reset()
-            except Exception:
-                pass
-        return res
-
-ModelAccess()
+        cls._get_access_cache.clear()
+        ModelView._fields_view_get_cache.clear()
 
 
 class ModelFieldAccess(ModelSQL, ModelView):
     "Model Field Access"
-    _name = 'ir.model.field.access'
-    _description = __doc__
+    __name__ = 'ir.model.field.access'
     _rec_name = 'field'
     field = fields.Many2One('ir.model.field', 'Field', required=True,
             ondelete='CASCADE')
@@ -394,46 +399,45 @@
     perm_create = fields.Boolean('Create Access')
     perm_delete = fields.Boolean('Delete Access')
     description = fields.Text('Description')
+    _get_access_cache = Cache('ir_model_field_access.check')
 
-    def __init__(self):
-        super(ModelFieldAccess, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(ModelFieldAccess, cls).__setup__()
+        cls._sql_constraints += [
             ('field_group_uniq', 'UNIQUE("field", "group")',
                 'Only one record by field and group is allowed!'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'read': 'You can not read the field! (%s.%s)',
             'write': 'You can not write on the field! (%s.%s)',
             })
 
-    def check_xml_record(self, ids, values):
+    @staticmethod
+    def check_xml_record(field_accesses, values):
         return True
 
-    def default_perm_read(self):
+    @staticmethod
+    def default_perm_read():
         return False
 
-    def default_perm_write(self):
+    @staticmethod
+    def default_perm_write():
         return False
 
-    def default_perm_create(self):
+    @staticmethod
+    def default_perm_create():
         return True
 
-    def default_perm_delete(self):
+    @staticmethod
+    def default_perm_delete():
         return True
 
-    @Cache('ir_model_field_access.check')
-    def check(self, model_name, fields, mode='read', raise_exception=True,
+    @classmethod
+    def check(cls, model_name, fields, mode='read', raise_exception=True,
             access=False):
         '''
         Check access for fields on model_name.
-
-        :param model_name: the model name
-        :param fields: a list of fields
-        :param mode: 'read', 'write', 'create' or 'delete'
-        :param raise_exception: raise an exception if the test failed
-        :param access: return a dictionary with access right instead of boolean
-
-        :return: a boolean
         '''
         assert mode in ('read', 'write', 'create', 'delete'), \
             'Invalid access mode'
@@ -443,134 +447,128 @@
             return True
 
         pool = Pool()
-        ir_model_obj = pool.get('ir.model')
-        ir_model_field_obj = pool.get('ir.model.field')
-        user_group_obj = pool.get('res.user-res.group')
+        Model = pool.get('ir.model')
+        ModelField = pool.get('ir.model.field')
+        UserGroup = pool.get('res.user-res.group')
 
         cursor = Transaction().cursor
+        key = (model_name, mode, access, Transaction().user)
+        accesses = cls._get_access_cache.get(key)
 
-        cursor.execute('SELECT f.name, '
-            'MAX(CASE WHEN a.perm_%s THEN 1 ELSE 0 END) '
-            'FROM "%s" AS a '
-            'JOIN "%s" AS f '
-                'ON (a.field = f.id) '
-            'JOIN "%s" AS m '
-                'ON (f.model = m.id) '
-            'LEFT JOIN "%s" AS gu '
-                'ON (gu."group" = a."group") '
-            'WHERE m.model = %%s AND (gu."user" = %%s OR a."group" IS NULL) '
-            'GROUP BY f.name'
-            % (mode, self._table, ir_model_field_obj._table,
-                ir_model_obj._table, user_group_obj._table), (model_name,
-                Transaction().user))
-        accesses = dict(cursor.fetchall())
+        if accesses is None:
+            cursor.execute('SELECT f.name, '
+                'MAX(CASE WHEN a.perm_%s THEN 1 ELSE 0 END) '
+                'FROM "%s" AS a '
+                'JOIN "%s" AS f '
+                    'ON (a.field = f.id) '
+                'JOIN "%s" AS m '
+                    'ON (f.model = m.id) '
+                'LEFT JOIN "%s" AS gu '
+                    'ON (gu."group" = a."group") '
+                'WHERE m.model = %%s '
+                    'AND (gu."user" = %%s OR a."group" IS NULL) '
+                'GROUP BY f.name'
+                % (mode, cls._table, ModelField._table,
+                    Model._table, UserGroup._table), (model_name,
+                    Transaction().user))
+            accesses = dict(cursor.fetchall())
+            cls._get_access_cache.set(key, accesses)
         if access:
             return accesses
         for field in fields:
             if not accesses.get(field, True):
                 if raise_exception:
-                    self.raise_user_error(mode, (model_name, field))
+                    cls.raise_user_error(mode, (model_name, field))
                 else:
                     return False
         return True
 
-    def write(self, ids, vals):
-        res = super(ModelFieldAccess, self).write(ids, vals)
+    @classmethod
+    def write(cls, field_accesses, vals):
+        super(ModelFieldAccess, cls).write(field_accesses, vals)
         # Restart the cache
-        self.check.reset()
-        pool = Pool()
-        for _, model in pool.iterobject():
-            try:
-                model.fields_view_get.reset()
-            except Exception:
-                pass
+        cls._get_access_cache.clear()
+        ModelView._fields_view_get_cache.clear()
+
+    @classmethod
+    def create(cls, vals):
+        res = super(ModelFieldAccess, cls).create(vals)
+        # Restart the cache
+        cls._get_access_cache.clear()
+        ModelView._fields_view_get_cache.clear()
         return res
 
-    def create(self, vals):
-        res = super(ModelFieldAccess, self).create(vals)
+    @classmethod
+    def delete(cls, field_accesses):
+        super(ModelFieldAccess, cls).delete(field_accesses)
         # Restart the cache
-        self.check.reset()
-        pool = Pool()
-        for _, model in pool.iterobject():
-            try:
-                model.fields_view_get.reset()
-            except Exception:
-                pass
-        return res
-
-    def delete(self, ids):
-        res = super(ModelFieldAccess, self).delete(ids)
-        # Restart the cache
-        self.check.reset()
-        pool = Pool()
-        for _, model in pool.iterobject():
-            try:
-                model.fields_view_get.reset()
-            except Exception:
-                pass
-        return res
-
-ModelFieldAccess()
+        cls._get_access_cache.clear()
+        ModelView._fields_view_get_cache.clear()
 
 
 class ModelButton(ModelSQL, ModelView):
     "Model Button"
-    _name = 'ir.model.button'
-    _description = __doc__
+    __name__ = 'ir.model.button'
     name = fields.Char('Name', required=True, readonly=True)
     model = fields.Many2One('ir.model', 'Model', required=True, readonly=True,
         ondelete='CASCADE', select=True)
     groups = fields.Many2Many('ir.model.button-res.group', 'button', 'group',
         'Groups')
+    _groups_cache = Cache('ir.model.button.groups')
 
-    def __init__(self):
-        super(ModelButton, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(ModelButton, cls).__setup__()
+        cls._sql_constraints += [
             ('name_model_uniq', 'UNIQUE(name, model)',
                 'The button name in model must be unique!'),
             ]
-        self._order.insert(0, ('model', 'ASC'))
+        cls._order.insert(0, ('model', 'ASC'))
 
-    def create(self, values):
-        result = super(ModelButton, self).create(values)
+    @classmethod
+    def create(cls, values):
+        result = super(ModelButton, cls).create(values)
         # Restart the cache for get_groups
-        self.get_groups.reset()
+        cls._groups_cache.clear()
         return result
 
-    def write(self, ids, values):
-        result = super(ModelButton, self).write(ids, values)
+    @classmethod
+    def write(cls, buttons, values):
+        super(ModelButton, cls).write(buttons, values)
         # Restart the cache for get_groups
-        self.get_groups.reset()
-        return result
+        cls._groups_cache.clear()
 
-    def delete(self, ids):
-        result = super(ModelButton, self).delete(ids)
+    @classmethod
+    def delete(cls, buttons):
+        super(ModelButton, cls).delete(buttons)
         # Restart the cache for get_groups
-        self.get_groups.reset()
-        return result
+        cls._groups_cache.clear()
 
-    @Cache('ir.model.button')
-    def get_groups(self, model, name):
+    @classmethod
+    def get_groups(cls, model, name):
         '''
         Return a set of group ids for the named button on the model.
         '''
-        button_ids = self.search([
+        key = (model, name)
+        groups = cls._groups_cache.get(key)
+        if groups is not None:
+            return groups
+        buttons = cls.search([
                 ('model.model', '=', model),
                 ('name', '=', name),
                 ])
-        if not button_ids:
-            return set()
-        button_id, = button_ids
-        button = self.browse(button_id)
-        return set(g.id for g in button.groups)
-
-ModelButton()
+        if not buttons:
+            groups = set()
+        else:
+            button, = buttons
+            groups = set(g.id for g in button.groups)
+        cls._groups_cache.set(key, groups)
+        return groups
 
 
 class ModelData(ModelSQL, ModelView):
     "Model data"
-    _name = 'ir.model.data'
-    _description = __doc__
+    __name__ = 'ir.model.data'
     fs_id = fields.Char('Identifier on File System', required=True,
         help="The id of the record as known on the file system.",
         select=True)
@@ -584,68 +582,70 @@
     values = fields.Text('Values')
     inherit = fields.Boolean('Inherit')
     noupdate = fields.Boolean('No Update')
+    _get_id_cache = Cache('ir_model_data.get_id', context=False)
 
-    def __init__(self):
-        super(ModelData, self).__init__()
-        self._sql_constraints = [
+    @classmethod
+    def __setup__(cls):
+        super(ModelData, cls).__setup__()
+        cls._sql_constraints = [
             ('fs_id_module_model_uniq', 'UNIQUE("fs_id", "module", "model")',
                 'The triple (fs_id, module, model) must be unique!'),
         ]
 
-    def default_date_init(self):
+    @staticmethod
+    def default_date_init():
         return datetime.datetime.now()
 
-    def default_inherit(self):
+    @staticmethod
+    def default_inherit():
         return False
 
-    def default_noupdate(self):
+    @staticmethod
+    def default_noupdate():
         return False
 
-    def write(self, ids, values):
-        result = super(ModelData, self).write(ids, values)
+    @classmethod
+    def write(cls, data, values):
+        super(ModelData, cls).write(data, values)
         # Restart the cache for get_id
-        self.get_id.reset()
-        return result
+        cls._get_id_cache.clear()
 
-    @Cache('ir_model_data.get_id')
-    def get_id(self, module, fs_id):
+    @classmethod
+    def get_id(cls, module, fs_id):
         """
         Return for an fs_id the corresponding db_id.
-
-        :param module: the module name
-        :param fs_id: the id in the xml file
-
-        :return: the database id
         """
-        ids = self.search([
+        key = (module, fs_id)
+        id_ = cls._get_id_cache.get(key)
+        if id_ is not None:
+            return id_
+        data = cls.search([
             ('module', '=', module),
             ('fs_id', '=', fs_id),
             ('inherit', '=', False),
             ], limit=1)
-        if not ids:
+        if not data:
             raise Exception("Reference to %s not found"
                 % ".".join([module, fs_id]))
-        return self.read(ids[0], ['db_id'])['db_id']
-
-ModelData()
+        id_ = cls.read([d.id for d in data], ['db_id'])[0]['db_id']
+        cls._get_id_cache.set(key, id_)
+        return id_
 
 
 class PrintModelGraphStart(ModelView):
     'Print Model Graph'
-    _name = 'ir.model.print_model_graph.start'
-    _description = __doc__
+    __name__ = 'ir.model.print_model_graph.start'
     level = fields.Integer('Level', required=True)
     filter = fields.Text('Filter', help="Entering a Python "
             "Regular Expression will exclude matching models from the graph.")
 
-    def default_level(self):
+    @staticmethod
+    def default_level():
         return 1
 
-PrintModelGraphStart()
-
 
 class PrintModelGraph(Wizard):
-    _name = 'ir.model.print_model_graph'
+    __name__ = 'ir.model.print_model_graph'
 
     start = StateView('ir.model.print_model_graph.start',
         'ir.print_model_graph_start_view_form', [
@@ -654,62 +654,56 @@
             ])
     print_ = StateAction('ir.report_model_graph')
 
-    def transition_print_(self, session):
+    def transition_print_(self):
         return 'end'
 
-    def do_print_(self, session, action):
+    def do_print_(self, action):
         return action, {
             'id': Transaction().context.get('active_id'),
             'ids': Transaction().context.get('active_ids'),
-            'level': session.start.level,
-            'filter': session.start.filter,
+            'level': self.start.level,
+            'filter': self.start.filter,
             }
 
-PrintModelGraph()
-
 
 class ModelGraph(Report):
-    _name = 'ir.model.graph'
+    __name__ = 'ir.model.graph'
 
-    def execute(self, ids, data):
+    @classmethod
+    def execute(cls, ids, data):
         import pydot
         pool = Pool()
-        model_obj = pool.get('ir.model')
-        action_report_obj = pool.get('ir.action.report')
+        Model = pool.get('ir.model')
+        ActionReport = pool.get('ir.action.report')
 
         if not data['filter']:
             filter = None
         else:
             filter = re.compile(data['filter'], re.VERBOSE)
-        action_report_ids = action_report_obj.search([
-            ('report_name', '=', self._name)
+        action_report_ids = ActionReport.search([
+            ('report_name', '=', cls.__name__)
             ])
         if not action_report_ids:
-            raise Exception('Error', 'Report (%s) not find!' % self._name)
-        action_report = action_report_obj.browse(action_report_ids[0])
+            raise Exception('Error', 'Report (%s) not find!' % cls.__name__)
+        action_report = ActionReport.browse(action_report_ids[0])
 
-        models = model_obj.browse(ids)
+        models = Model.browse(ids)
 
         graph = pydot.Dot(fontsize="8")
         graph.set('center', '1')
         graph.set('ratio', 'auto')
-        self.fill_graph(models, graph, level=data['level'], filter=filter)
+        cls.fill_graph(models, graph, level=data['level'], filter=filter)
         data = graph.create(prog='dot', format='png')
         return ('png', buffer(data), False, action_report.name)
 
-    def fill_graph(self, models, graph, level=1, filter=None):
+    @classmethod
+    def fill_graph(cls, models, graph, level=1, filter=None):
         '''
         Fills a pydot graph with a models structure.
-
-        :param models: a BrowseRecordList of ir.model
-        :param graph: a pydot.Graph
-        :param level: the depth to dive into model reationships
-        :param filter: a compiled regular expression object to filter specific
-            models
         '''
         import pydot
         pool = Pool()
-        model_obj = pool.get('ir.model')
+        Model = pool.get('ir.model')
 
         sub_models = set()
         if level > 0:
@@ -720,12 +714,12 @@
                     if field.relation and not graph.get_node(field.relation):
                         sub_models.add(field.relation)
             if sub_models:
-                model_ids = model_obj.search([
+                model_ids = Model.search([
                     ('model', 'in', list(sub_models)),
                     ])
-                sub_models = model_obj.browse(model_ids)
+                sub_models = Model.browse(model_ids)
                 if set(sub_models) != set(models):
-                    self.fill_graph(sub_models, graph, level=level - 1,
+                    cls.fill_graph(sub_models, graph, level=level - 1,
                             filter=filter)
 
         for model in models:
@@ -792,5 +786,3 @@
 
                     edge = pydot.Edge(str(tail), str(head), **args)
                     graph.add_edge(edge)
-
-ModelGraph()
diff -r f6ef988fda71 trytond/ir/module/module.py
--- a/trytond/ir/module/module.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/ir/module/module.py	Sun Sep 23 17:30:03 2012 +0200
@@ -1,5 +1,10 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
+try:
+    import simplejson as json
+except ImportError:
+    import json
+
 from trytond.model import ModelView, ModelSQL, fields
 from trytond.modules import create_graph, get_module_list, get_module_info
 from trytond.wizard import Wizard, StateView, Button, StateTransition, \
@@ -8,12 +13,20 @@
 from trytond.pool import Pool
 from trytond.transaction import Transaction
 from trytond.pyson import Eval
+from trytond.protocols.jsonrpc import object_hook
+from trytond.rpc import RPC
+
+__all__ = [
+    'Module', 'ModuleDependency', 'ModuleConfigWizardItem',
+    'ModuleConfigWizardFirst', 'ModuleConfigWizardOther', 'ModuleConfigWizard',
+    'ModuleInstallUpgradeStart', 'ModuleInstallUpgradeDone',
+    'ModuleInstallUpgrade', 'ModuleConfig',
+    ]
 
 
 class Module(ModelSQL, ModelView):
     "Module"
-    _name = "ir.module.module"
-    _description = __doc__
+    __name__ = "ir.module.module"
     name = fields.Char("Name", readonly=True, required=True)
     version = fields.Function(fields.Char('Version'), 'get_version')
     dependencies = fields.One2Many('ir.module.module.dependency',
@@ -30,24 +43,25 @@
         ('to install', 'To be installed'),
         ], string='State', readonly=True)
 
-    def __init__(self):
-        super(Module, self).__init__()
-        self._sql_constraints = [
+    @classmethod
+    def __setup__(cls):
+        super(Module, cls).__setup__()
+        cls._sql_constraints = [
             ('name_uniq', 'unique (name)',
                 'The name of the module must be unique!'),
         ]
-        self._order.insert(0, ('name', 'ASC'))
-        self._rpc.update({
-            'on_write': False,
-        })
-        self._error_messages.update({
+        cls._order.insert(0, ('name', 'ASC'))
+        cls.__rpc__.update({
+                'on_write': RPC(instantiate=0),
+                })
+        cls._error_messages.update({
             'delete_state': 'You can not remove a module that is installed ' \
                     'or will be installed',
             'missing_dep': 'Missing dependencies %s for module "%s"',
             'uninstall_dep': 'The modules you are trying to uninstall ' \
                     'depends on installed modules:',
             })
-        self._buttons.update({
+        cls._buttons.update({
                 'install': {
                     'invisible': Eval('state') != 'uninstalled',
                     },
@@ -68,62 +82,54 @@
                     },
                 })
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'uninstalled'
 
-    def get_version(self, ids, name):
-        res = {}
-        for module in self.browse(ids):
-            res[module.id] = get_module_info(module.name).get('version', '')
-        return res
+    def get_version(self, name):
+        return get_module_info(self.name).get('version', '')
 
-    def get_parents(self, ids, name):
-        parents = {}
-        modules = self.browse(ids)
+    @classmethod
+    def get_parents(cls, modules, name):
         parent_names = list(set(d.name for m in modules
                     for d in m.dependencies))
-        parent_ids = self.search([
+        parents = cls.search([
                 ('name', 'in', parent_names),
                 ])
-        name2id = dict((m.name, m.id) for m in self.browse(parent_ids))
-        for module in self.browse(ids):
-            parents[module.id] = [name2id[d.name] for d in module.dependencies]
-        return parents
+        name2id = dict((m.name, m.id) for m in parents)
+        return dict((m.id, [name2id[d.name] for d in m.dependencies])
+            for m in modules)
 
-    def get_childs(self, ids, name):
-        childs = dict((i, []) for i in ids)
-        modules = self.browse(ids)
+    @classmethod
+    def get_childs(cls, modules, name):
+        child_ids = dict((m.id, []) for m in modules)
         names = [m.name for m in modules]
-        child_ids = self.search([
+        childs = cls.search([
                 ('dependencies.name', 'in', names),
                 ])
-        for child in self.browse(child_ids):
+        for child in childs:
             for dep in child.dependencies:
-                if dep.module.id in childs:
-                    childs[dep.module.id].append(child.id)
-        return childs
+                if dep.module.id in child_ids:
+                    child_ids[dep.module.id].append(child.id)
+        return child_ids
 
-    def delete(self, ids):
-        if not ids:
-            return True
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        for module in self.browse(ids):
+    @classmethod
+    def delete(cls, records):
+        for module in records:
             if module.state in (
                     'installed',
                     'to upgrade',
                     'to remove',
                     'to install',
                     ):
-                self.raise_user_error('delete_state')
-        return super(Module, self).delete(ids)
+                cls.raise_user_error('delete_state')
+        return super(Module, cls).delete(records)
 
-    def on_write(self, ids):
-        if not ids:
-            return []
-        res = []
+    @classmethod
+    def on_write(cls, modules):
+        ids = set()
         graph, packages, later = create_graph(get_module_list())
-        for module in self.browse(ids):
+        for module in modules:
             if module.name not in graph:
                 continue
 
@@ -140,20 +146,22 @@
                     childs.update(get_childs(c))
                 return childs
             dependencies.update(get_childs(module))
-            res += self.search([
-                ('name', 'in', list(dependencies)),
-                ])
-        return list({}.fromkeys(res))
+            ids |= set(x.id for x in cls.search([
+                        ('name', 'in', list(dependencies)),
+                        ]))
+        return list(ids)
 
-    def state_install(self, ids):
+    @classmethod
+    @ModelView.button
+    def install(cls, modules):
         graph, packages, later = create_graph(get_module_list())
-        for module in self.browse(ids):
+        for module in modules:
             if module.name not in graph:
                 missings = []
                 for package, deps, xdep, info in packages:
                     if package == module.name:
                         missings = [x for x in deps if x not in graph]
-                self.raise_user_error('missing_dep', (missings, module.name))
+                cls.raise_user_error('missing_dep', (missings, module.name))
 
             def get_parents(module):
                 parents = set(p.name for p in module.parents)
@@ -161,23 +169,25 @@
                     parents.update(get_parents(p))
                 return parents
             dependencies = list(get_parents(module))
-            module_install_ids = self.search([
-                ('name', 'in', dependencies),
-                ('state', '=', 'uninstalled'),
-                ])
-            self.write(module_install_ids + [module.id], {
-                'state': 'to install',
-                })
+            modules_install = cls.search([
+                    ('name', 'in', dependencies),
+                    ('state', '=', 'uninstalled'),
+                    ])
+            cls.write(modules_install + [module], {
+                    'state': 'to install',
+                    })
 
-    def state_upgrade(self, ids):
+    @classmethod
+    @ModelView.button
+    def upgrade(cls, modules):
         graph, packages, later = create_graph(get_module_list())
-        for module in self.browse(ids):
+        for module in modules:
             if module.name not in graph:
                 missings = []
                 for package, deps, xdep, info in packages:
                     if package == module.name:
                         missings = [x for x in deps if x not in graph]
-                self.raise_user_error('missing_dep', (missings, module.name))
+                cls.raise_user_error('missing_dep', (missings, module.name))
 
             def get_childs(name, graph):
                 childs = set(x.name for x in graph[name].childs)
@@ -187,29 +197,26 @@
                 childs.update(childs2)
                 return childs
             dependencies = list(get_childs(module.name, graph))
-            module_installed_ids = self.search([
-                ('name', 'in', dependencies),
-                ('state', '=', 'installed'),
-                ])
-            self.write(module_installed_ids + [module.id], {
-                'state': 'to upgrade',
+            modules_installed = cls.search([
+                    ('name', 'in', dependencies),
+                    ('state', '=', 'installed'),
+                    ])
+            cls.write(modules_installed + [module], {
+                    'state': 'to upgrade',
+                    })
+
+    @classmethod
+    @ModelView.button
+    def install_cancel(cls, modules):
+        cls.write(modules, {
+                'state': 'uninstalled',
                 })
 
+    @classmethod
     @ModelView.button
-    def install(self, ids):
-        return self.state_install(ids)
-
-    @ModelView.button
-    def install_cancel(self, ids):
-        self.write(ids, {
-            'state': 'uninstalled',
-            })
-        return True
-
-    @ModelView.button
-    def uninstall(self, ids):
+    def uninstall(cls, modules):
         cursor = Transaction().cursor
-        for module in self.browse(ids):
+        for module in modules:
             cursor.execute('SELECT m.state, m.name ' \
                     'FROM ir_module_module_dependency d ' \
                     'JOIN ir_module_module m on (d.module = m.id) ' \
@@ -219,33 +226,28 @@
                             (module.name,))
             res = cursor.fetchall()
             if res:
-                self.raise_user_error('uninstall_dep',
+                cls.raise_user_error('uninstall_dep',
                         error_description='\n'.join(
                             '\t%s: %s' % (x[0], x[1]) for x in res))
-        self.write(ids, {'state': 'to remove'})
-        return True
+        cls.write(modules, {'state': 'to remove'})
 
+    @classmethod
     @ModelView.button
-    def uninstall_cancel(self, ids):
-        self.write(ids, {'state': 'installed'})
-        return True
+    def uninstall_cancel(cls, modules):
+        cls.write(modules, {'state': 'installed'})
 
+    @classmethod
     @ModelView.button
-    def upgrade(self, ids):
-        return self.state_upgrade(ids)
+    def upgrade_cancel(cls, modules):
+        cls.write(modules, {'state': 'installed'})
 
-    @ModelView.button
-    def upgrade_cancel(self, ids):
-        self.write(ids, {'state': 'installed'})
-        return True
-
-    # update the list of available packages
-    def update_list(self):
+    @classmethod
+    def update_list(cls):
+        'Update the list of available packages'
         count = 0
         module_names = get_module_list()
 
-        module_ids = self.search([])
-        modules = self.browse(module_ids)
+        modules = cls.search([])
         name2module = dict((m.name, m) for m in modules)
 
         # iterate through installed modules and mark them as being so
@@ -253,45 +255,42 @@
             if name in name2module:
                 module = name2module[name]
                 tryton = get_module_info(name)
-                self._update_dependencies(module, tryton.get('depends', []))
+                cls._update_dependencies(module, tryton.get('depends', []))
                 continue
 
             tryton = get_module_info(name)
             if not tryton:
                 continue
-            module_id = self.create({
+            module = cls.create({
                     'name': name,
                     'state': 'uninstalled',
                     })
             count += 1
-            module = self.browse(module_id)
-            self._update_dependencies(module, tryton.get('depends', []))
+            cls._update_dependencies(module, tryton.get('depends', []))
         return count
 
-    def _update_dependencies(self, module, depends=None):
+    @classmethod
+    def _update_dependencies(cls, module, depends=None):
         pool = Pool()
-        dependency_obj = pool.get('ir.module.module.dependency')
-        dependency_obj.delete([x.id for x in module.dependencies
-                if x.name not in depends])
+        Dependency = pool.get('ir.module.module.dependency')
+        Dependency.delete([x for x in module.dependencies
+            if x.name not in depends])
         if depends is None:
             depends = []
         # Restart Browse Cache for deleted dependencies
-        module = self.browse(module.id)
+        module = cls(module.id)
         dependency_names = [x.name for x in module.dependencies]
         for depend in depends:
             if depend not in dependency_names:
-                dependency_obj.create({
+                Dependency.create({
                         'module': module.id,
                         'name': depend,
                         })
 
-Module()
-
 
 class ModuleDependency(ModelSQL, ModelView):
     "Module dependency"
-    _name = "ir.module.module.dependency"
-    _description = __doc__
+    __name__ = "ir.module.module.dependency"
     name = fields.Char('Name')
     module = fields.Many2One('ir.module.module', 'Module', select=True,
        ondelete='CASCADE', required=True)
@@ -304,34 +303,29 @@
                 ('unknown', 'Unknown'),
                 ], 'State', readonly=True), 'get_state')
 
-    def __init__(self):
-        super(ModuleDependency, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(ModuleDependency, cls).__setup__()
+        cls._sql_constraints += [
             ('name_module_uniq', 'UNIQUE(name, module)',
                 'Dependency must be unique by module!'),
         ]
 
-    def get_state(self, ids, name):
-        result = {}
+    def get_state(self, name):
         pool = Pool()
-        module_obj = pool.get('ir.module.module')
-        for dependency in self.browse(ids):
-            ids = module_obj.search([
-                ('name', '=', dependency.name),
+        Module = pool.get('ir.module.module')
+        dependencies = Module.search([
+                ('name', '=', self.name),
                 ])
-            if ids:
-                result[dependency.id] = module_obj.browse(ids[0]).state
-            else:
-                result[dependency.id] = 'unknown'
-        return result
-
-ModuleDependency()
+        if dependencies:
+            return dependencies[0].state
+        else:
+            return 'unknown'
 
 
 class ModuleConfigWizardItem(ModelSQL, ModelView):
     "Config wizard to run after installing module"
-    _name = 'ir.module.module.config_wizard.item'
-    _description = __doc__
+    __name__ = 'ir.module.module.config_wizard.item'
     _rec_name = 'action'
     action = fields.Many2One('ir.action', 'Action', required=True,
         readonly=True)
@@ -341,57 +335,54 @@
         ('done', 'Done'),
         ], string='State', required=True, select=True)
 
-    def __init__(self):
-        super(ModuleConfigWizardItem, self).__init__()
-        self._order.insert(0, ('sequence', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(ModuleConfigWizardItem, cls).__setup__()
+        cls._order.insert(0, ('sequence', 'ASC'))
 
-    def init(self, module_name):
-        table = TableHandler(Transaction().cursor, self, module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        table = TableHandler(Transaction().cursor, cls, module_name)
 
         # Migrate from 2.2 remove name
         table.drop_column('name')
 
-        super(ModuleConfigWizardItem, self).init(module_name)
+        super(ModuleConfigWizardItem, cls).__register__(module_name)
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'open'
 
-    def default_sequence(self):
+    @staticmethod
+    def default_sequence():
         return 10
 
-ModuleConfigWizardItem()
-
 
 class ModuleConfigWizardFirst(ModelView):
     'Module Config Wizard First'
-    _name = 'ir.module.module.config_wizard.first'
-    _description = __doc__
-
-ModuleConfigWizardFirst()
+    __name__ = 'ir.module.module.config_wizard.first'
 
 
 class ModuleConfigWizardOther(ModelView):
     'Module Config Wizard Other'
-    _name = 'ir.module.module.config_wizard.other'
-    _description = __doc__
+    __name__ = 'ir.module.module.config_wizard.other'
 
     percentage = fields.Float('Percentage', readonly=True)
 
-    def default_percentage(self):
+    @staticmethod
+    def default_percentage():
         pool = Pool()
-        item_obj = pool.get('ir.module.module.config_wizard.item')
-        done = item_obj.search([
+        Item = pool.get('ir.module.module.config_wizard.item')
+        done = Item.search([
             ('state', '=', 'done'),
             ], count=True)
-        all = item_obj.search([], count=True)
+        all = Item.search([], count=True)
         return 100.0 * done / all
 
-ModuleConfigWizardOther()
-
 
 class ModuleConfigWizard(Wizard):
     'Run config wizards'
-    _name = 'ir.module.module.config_wizard'
+    __name__ = 'ir.module.module.config_wizard'
 
     class ConfigStateAction(StateAction):
 
@@ -400,18 +391,18 @@
 
         def get_action(self):
             pool = Pool()
-            item_obj = pool.get('ir.module.module.config_wizard.item')
-            action_obj = pool.get('ir.action')
-            item_ids = item_obj.search([
+            Item = pool.get('ir.module.module.config_wizard.item')
+            Action = pool.get('ir.action')
+            items = Item.search([
                 ('state', '=', 'open'),
                 ], limit=1)
-            if item_ids:
-                item = item_obj.browse(item_ids[0])
-                item_obj.write(item.id, {
-                    'state': 'done',
-                    })
-                return action_obj.get_action_values(item.action.type,
-                    item.action.id)
+            if items:
+                item = items[0]
+                Item.write([item], {
+                        'state': 'done',
+                        })
+                return Action.get_action_values(item.action.type,
+                    [item.action.id])[0]
 
     start = StateTransition()
     first = StateView('ir.module.module.config_wizard.first',
@@ -426,45 +417,37 @@
             ])
     action = ConfigStateAction()
 
-    def transition_start(self, session):
-        res = self.transition_action(session)
+    def transition_start(self):
+        res = self.transition_action()
         if res == 'other':
             return 'first'
         return res
 
-    def transition_action(self, session):
+    def transition_action(self):
         pool = Pool()
-        item_obj = pool.get('ir.module.module.config_wizard.item')
-        item_ids = item_obj.search([
-            ('state', '=', 'open'),
-            ])
-        if item_ids:
+        Item = pool.get('ir.module.module.config_wizard.item')
+        items = Item.search([
+                ('state', '=', 'open'),
+                ])
+        if items:
             return 'other'
         return 'end'
 
-ModuleConfigWizard()
-
 
 class ModuleInstallUpgradeStart(ModelView):
     'Module Install Upgrade Start'
-    _name = 'ir.module.module.install_upgrade.start'
-    _description = __doc__
+    __name__ = 'ir.module.module.install_upgrade.start'
     module_info = fields.Text('Modules to update', readonly=True)
 
-ModuleInstallUpgradeStart()
-
 
 class ModuleInstallUpgradeDone(ModelView):
     'Module Install Upgrade Done'
-    _name = 'ir.module.module.install_upgrade.done'
-    _description = __doc__
-
-ModuleInstallUpgradeDone()
+    __name__ = 'ir.module.module.install_upgrade.done'
 
 
 class ModuleInstallUpgrade(Wizard):
     "Install / Upgrade modules"
-    _name = 'ir.module.module.install_upgrade'
+    __name__ = 'ir.module.module.install_upgrade'
 
     start = StateView('ir.module.module.install_upgrade.start',
         'ir.module_install_upgrade_start_view_form', [
@@ -478,49 +461,52 @@
             ])
     config = StateAction('ir.act_module_config_wizard')
 
-    def default_start(self, session, fields):
+    @staticmethod
+    def default_start(fields):
         pool = Pool()
-        module_obj = pool.get('ir.module.module')
-        module_ids = module_obj.search([
-            ('state', 'in', ['to upgrade', 'to remove', 'to install']),
-            ])
-        modules = module_obj.browse(module_ids)
+        Module = pool.get('ir.module.module')
+        modules = Module.search([
+                ('state', 'in', ['to upgrade', 'to remove', 'to install']),
+                ])
         return {
             'module_info': '\n'.join(x.name + ': ' + x.state \
-                    for x in modules),
-        }
+                for x in modules),
+            }
 
-    def transition_upgrade(self, session):
+    def transition_upgrade(self):
         pool = Pool()
-        module_obj = pool.get('ir.module.module')
-        lang_obj = pool.get('ir.lang')
+        Module = pool.get('ir.module.module')
+        Lang = pool.get('ir.lang')
+        Session = pool.get('ir.session.wizard')
         with Transaction().new_cursor() as transaction:
-            module_ids = module_obj.search([
+            modules = Module.search([
                 ('state', 'in', ['to upgrade', 'to remove', 'to install']),
                 ])
-            lang_ids = lang_obj.search([
+            langs = Lang.search([
                 ('translatable', '=', True),
                 ])
-            lang = [x.code for x in lang_obj.browse(lang_ids)]
+            lang = [x.code for x in langs]
             transaction.cursor.commit()
-        if module_ids:
+        if modules:
             pool.init(update=True, lang=lang)
-        if session:
-            # Don't store session to prevent concurrent update
-            for state_name in session.data:
-                getattr(session, state_name).dirty = False
+        # Don't store session to prevent concurrent update
+        session = Session(self._session_id)
+        data = json.loads(session.data.encode('utf-8'),
+            object_hook=object_hook)
+        for state_name, state in self.states.iteritems():
+            if isinstance(state, StateView):
+                Target = pool.get(state.model_name)
+                data.setdefault(state_name, {})
+                setattr(self, state_name, Target(*data[state_name]))
         return 'done'
 
-ModuleInstallUpgrade()
-
 
 class ModuleConfig(Wizard):
     'Configure Modules'
-    _name = 'ir.module.module.config'
+    __name__ = 'ir.module.module.config'
 
     start = StateAction('ir.act_module_form')
 
-    def transition_start(self, session):
+    @staticmethod
+    def transition_start():
         return 'end'
-
-ModuleConfig()
diff -r f6ef988fda71 trytond/ir/property.py
--- a/trytond/ir/property.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/ir/property.py	Sun Sep 23 17:30:03 2012 +0200
@@ -6,37 +6,42 @@
 from ..cache import Cache
 from ..pool import Pool
 
+__all__ = [
+    'Property',
+    ]
+
 
 class Property(ModelSQL, ModelView):
     "Property"
-    _name = 'ir.property'
-    _description = __doc__
+    __name__ = 'ir.property'
     _rec_name = 'field'
     value = fields.Reference('Value', selection='models_get')
     res = fields.Reference('Resource', selection='models_get', select=True)
     field = fields.Many2One('ir.model.field', 'Field',
         ondelete='CASCADE', required=True, select=True)
+    _models_get_cache = Cache('ir_property.models_get', context=False)
 
-    @Cache('ir_property.models_get')
-    def models_get(self):
+    @classmethod
+    def models_get(cls):
+        models = cls._models_get_cache.get(None)
+        if models:
+            return models
         cursor = Transaction().cursor
         cursor.execute('SELECT model, name FROM ir_model ORDER BY name ASC')
-        res = cursor.fetchall() + [('', '')]
-        return res
+        models = cursor.fetchall() + [('', '')]
+        cls._models_get_cache.set(None, models)
+        return models
 
-    def get(self, names, model, res_ids=None):
+    @classmethod
+    def get(cls, names, model, res_ids=None):
         """
-        Return property value for each res_ids
-        :param names: property name or a list of property names
-        :param model: object name
-        :param res_ids: a list of record ids
-        :return: a dictionary
+        Return named property values for each res_ids of model
         """
         pool = Pool()
-        model_access_obj = pool.get('ir.model.access')
+        ModelAccess = pool.get('ir.model.access')
         res = {}
 
-        model_access_obj.check(model, 'read')
+        ModelAccess.check(model, 'read')
 
         names_list = True
         if not isinstance(names, list):
@@ -45,34 +50,29 @@
         if res_ids is None:
             res_ids = []
 
-        property_ids = self.search([
+        properties = cls.search([
             ('field.name', 'in', names),
             ['OR',
                 ('res', '=', None),
                 ('res', 'in', ['%s,%s' % (model, x) for x in res_ids]),
                 ],
             ], order=[])
-        properties = self.browse(property_ids)
 
         default_vals = dict((x, None) for x in names)
-        for property in (x for x in properties if not x.res):
-            value = property.value
+        for property_ in (x for x in properties if not x.res):
+            value = property_.value
             val = None
-            if value:
-                if value.split(',')[0]:
-                    try:
-                        val = int(value.split(',')[1]\
-                                .split(',')[0].strip('('))
-                    except ValueError:
-                        val = None
+            if value is not None:
+                if not isinstance(value, basestring):
+                    val = int(value)
                 else:
-                    if property.field.ttype == 'numeric':
+                    if property_.field.ttype == 'numeric':
                         val = Decimal(value.split(',')[1])
-                    elif property.field.ttype in ('char', 'selection'):
+                    elif property_.field.ttype in ('char', 'selection'):
                         val = value.split(',')[1]
                     else:
                         raise Exception('Not implemented')
-            default_vals[property.field.name] = val
+            default_vals[property_.field.name] = val
 
         if not res_ids:
             if not names_list:
@@ -82,80 +82,69 @@
         for name in names:
             res[name] = dict((x, default_vals[name]) for x in res_ids)
 
-        for property in (x for x in properties if x.res):
+        for property_ in (x for x in properties if x.res):
             val = None
-            if property.value:
-                if property.value.split(',')[0]:
-                    try:
-                        val = int(property.value.split(',')[1]\
-                                .split(',')[0].strip('('))
-                    except ValueError:
-                        val = None
+            if property_.value is not None:
+                if not isinstance(property_.value, basestring):
+                    val = int(property_.value)
                 else:
-                    if property.field.ttype == 'numeric':
-                        val = Decimal(property.value.split(',')[1])
-                    elif property.field.ttype in ('char', 'selection'):
-                        val = property.value.split(',')[1]
+                    if property_.field.ttype == 'numeric':
+                        val = Decimal(property_.value.split(',')[1])
+                    elif property_.field.ttype in ('char', 'selection'):
+                        val = property_.value.split(',')[1]
                     else:
                         raise Exception('Not implemented')
-            res[property.field.name][
-                int(property.res.split(',')[1].split(',')[0].strip('('))] = val
+            res[property_.field.name][int(property_.res)] = val
 
         if not names_list:
             return res[names[0]]
         return res
 
-    def _set_values(self, model, res_id, val, field_id):
+    @staticmethod
+    def _set_values(model, res_id, val, field_id):
         return {
             'value': val,
             'res': model + ',' + str(res_id),
             'field': field_id,
         }
 
-    def set(self, name, model, ids, val):
+    @classmethod
+    def set(cls, name, model, ids, val):
         """
-        Set property value for ids
-        :param name: property name
-        :param model: object name
-        :param ids: a list of ids
-        :param val: the value
-        :return: the id of the record created
+        Set named property value for ids of model
+        Return the id of the record created
         """
         pool = Pool()
-        model_field_obj = pool.get('ir.model.field')
-        model_access_obj = pool.get('ir.model.access')
+        ModelField = pool.get('ir.model.field')
+        ModelAccess = pool.get('ir.model.access')
 
-        model_access_obj.check(model, 'write')
+        ModelAccess.check(model, 'write')
 
-        field_id = model_field_obj.search([
+        model_field, = ModelField.search([
             ('name', '=', name),
             ('model.model', '=', model),
-            ], order=[], limit=1)[0]
-        model_obj = pool.get(model)
-        field = model_obj._columns[name]
+            ], order=[], limit=1)
+        Model = pool.get(model)
+        field = Model._fields[name]
 
-        property_ids = self.search([
-            ('field', '=', field_id),
+        properties = cls.search([
+            ('field', '=', model_field.id),
             ('res', 'in', [model + ',' + str(res_id) for res_id in ids]),
             ], order=[])
         with Transaction().set_user(0, set_context=True):
-            self.delete(property_ids)
+            cls.delete(properties)
 
-        default_id = self.search([
-            ('field', '=', field_id),
+        defaults = cls.search([
+            ('field', '=', model_field.id),
             ('res', '=', None),
             ], order=[], limit=1)
         default_val = None
-        if default_id:
-            value = self.browse(default_id[0]).value
+        if defaults:
+            value = cls(defaults[0].id).value
             default_val = None
-            if value:
-                if value.split(',')[0]:
-                    try:
-                        default_val = int(value.split(',')[1]\
-                                .split(',')[0].strip('('))
-                    except ValueError:
-                        default_val = None
+            if value is not None:
+                if not isinstance(value, basestring):
+                    default_val = int(value)
                 else:
                     if field._type == 'numeric':
                         default_val = Decimal(value.split(',')[1])
@@ -164,12 +153,8 @@
                     else:
                         raise Exception('Not implemented')
 
-        res = None
         if (val != default_val):
             for res_id in ids:
-                vals = self._set_values(model, res_id, val, field_id)
+                vals = cls._set_values(model, res_id, val, model_field.id)
                 with Transaction().set_user(0, set_context=True):
-                    res = self.create(vals)
-        return res
-
-Property()
+                    cls.create(vals).id
diff -r f6ef988fda71 trytond/ir/rule.py
--- a/trytond/ir/rule.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/ir/rule.py	Sun Sep 23 17:30:03 2012 +0200
@@ -10,11 +10,14 @@
 from ..const import OPERATORS
 from ..pool import Pool
 
+__all__ = [
+    'RuleGroup', 'Rule',
+    ]
+
 
 class RuleGroup(ModelSQL, ModelView):
     "Rule group"
-    _name = 'ir.rule.group'
-    _description = __doc__
+    __name__ = 'ir.rule.group'
     name = fields.Char('Name', select=True)
     model = fields.Many2One('ir.model', 'Model', select=True,
             required=True)
@@ -34,60 +37,65 @@
     perm_create = fields.Boolean('Create Access')
     perm_delete = fields.Boolean('Delete Access')
 
-    def __init__(self):
-        super(RuleGroup, self).__init__()
-        self._order.insert(0, ('model', 'ASC'))
-        self._order.insert(1, ('global_p', 'ASC'))
-        self._order.insert(2, ('default_p', 'ASC'))
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(RuleGroup, cls).__setup__()
+        cls._order.insert(0, ('model', 'ASC'))
+        cls._order.insert(1, ('global_p', 'ASC'))
+        cls._order.insert(2, ('default_p', 'ASC'))
+        cls._sql_constraints += [
             ('global_default_exclusive', 'CHECK(NOT(global_p AND default_p))',
                 'Global and Default are mutually exclusive!'),
         ]
 
-    def default_global_p(self):
+    @staticmethod
+    def default_global_p():
         return True
 
-    def default_default_p(self):
+    @staticmethod
+    def default_default_p():
         return False
 
-    def default_perm_read(self):
+    @staticmethod
+    def default_perm_read():
         return True
 
-    def default_perm_write(self):
+    @staticmethod
+    def default_perm_write():
         return True
 
-    def default_perm_create(self):
+    @staticmethod
+    def default_perm_create():
         return True
 
-    def default_perm_delete(self):
+    @staticmethod
+    def default_perm_delete():
         return True
 
-    def delete(self, ids):
-        res = super(RuleGroup, self).delete(ids)
+    @classmethod
+    def delete(cls, groups):
+        super(RuleGroup, cls).delete(groups)
         # Restart the cache on the domain_get method of ir.rule
-        Pool().get('ir.rule').domain_get.reset()
+        Pool().get('ir.rule')._domain_get_cache.clear()
+
+    @classmethod
+    def create(cls, vals):
+        res = super(RuleGroup, cls).create(vals)
+        # Restart the cache on the domain_get method of ir.rule
+        Pool().get('ir.rule')._domain_get_cache.clear()
         return res
 
-    def create(self, vals):
-        res = super(RuleGroup, self).create(vals)
+    @classmethod
+    def write(cls, groups, vals):
+        super(RuleGroup, cls).write(groups, vals)
         # Restart the cache on the domain_get method of ir.rule
-        Pool().get('ir.rule').domain_get.reset()
-        return res
-
-    def write(self, ids, vals):
-        res = super(RuleGroup, self).write(ids, vals)
-        # Restart the cache on the domain_get method of ir.rule
-        Pool().get('ir.rule').domain_get.reset()
-        return res
-
-RuleGroup()
+        Pool().get('ir.rule')._domain_get_cache.clear()
 
 
 class Rule(ModelSQL, ModelView):
     "Rule"
-    _name = 'ir.rule'
+    __name__ = 'ir.rule'
     _rec_name = 'field'
-    _description = __doc__
     field = fields.Many2One('ir.model.field', 'Field',
         domain=[('model', '=', Eval('_parent_rule_group', {}).get('model'))],
         select=True, required=True)
@@ -96,18 +104,21 @@
     operand = fields.Selection('get_operand', 'Operand', required=True)
     rule_group = fields.Many2One('ir.rule.group', 'Group', select=True,
        required=True, ondelete="CASCADE")
+    _domain_get_cache = Cache('ir_rule.domain_get')
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
 
-        super(Rule, self).init(module_name)
+        super(Rule, cls).__register__(module_name)
 
         # Migration from 2.0: rename operator '<>' into '!='
         cursor.execute('UPDATE "%s" '
             'SET operator = %%s '
-            'WHERE operator = %%s' % self._table, ('!=', '<>'))
+            'WHERE operator = %%s' % cls._table, ('!=', '<>'))
 
-    def _operand_get(self, obj_name='', level=3, recur=None, root_tech='',
+    @classmethod
+    def _operand_get(cls, obj_name='', level=3, recur=None, root_tech='',
             root=''):
         res = {}
         if not obj_name:
@@ -123,33 +134,36 @@
 
             if obj_fields[k]['type'] in ('many2one'):
                 res[root + '/' + obj_fields[k]['string']] = (
-                    '(' + root_tech + '.' + k + '.id '
-                    'if ' + root_tech + ' else None)')
+                    '(%s.%s.id if %s.%s else None)'
+                    % (root_tech, k, root_tech, k))
 
             elif obj_fields[k]['type'] in ('many2many', 'one2many'):
-                res[root + '/' + obj_fields[k]['string']] = \
-                        '[x.id for x in ' + root_tech + '.' + k + ']'
+                res[root + '/' + obj_fields[k]['string']] = (
+                    '[x.id for x in (%s.%s or [])]'
+                    % (root_tech, k))
             else:
                 res[root + '/' + obj_fields[k]['string']] = \
-                        root_tech + '.' + k
+                    root_tech + '.' + k
 
             if (obj_fields[k]['type'] in recur) and (level > 0):
-                res.update(self._operand_get(obj_fields[k]['relation'],
-                    level - 1, recur, root_tech + '.' + k,
-                    root + '/' + obj_fields[k]['string']))
+                res.update(cls._operand_get(obj_fields[k]['relation'],
+                        level - 1, recur,
+                        '(%s.%s if %s else None)' % (root_tech,  k, root_tech),
+                        root + '/' + obj_fields[k]['string']))
 
         return res
 
-    def get_operand(self):
+    @classmethod
+    def get_operand(cls):
         res = []
-        operands = self._operand_get('res.user', level=1, recur=['many2one'],
+        operands = cls._operand_get('res.user', level=1, recur=['many2one'],
                 root_tech='user', root='User')
         for i in operands.keys():
             res.append((i, i))
         return res
 
-    @Cache('ir_rule.domain_get')
-    def domain_get(self, model_name, mode='read'):
+    @classmethod
+    def domain_get(cls, model_name, mode='read'):
         assert mode in ['read', 'write', 'create', 'delete'], \
                 'Invalid domain mode for security'
 
@@ -158,30 +172,35 @@
             if not Transaction().context.get('user'):
                 return '', []
             with Transaction().set_user(Transaction().context['user']):
-                return self.domain_get(model_name, mode=mode)
+                return cls.domain_get(model_name, mode=mode)
+
+        key = (model_name, mode)
+        domain = cls._domain_get_cache.get(key)
+        if domain:
+            return domain
 
         pool = Pool()
-        rule_group_obj = pool.get('ir.rule.group')
-        model_obj = pool.get('ir.model')
-        rule_group_user_obj = pool.get('ir.rule.group-res.user')
-        rule_group_group_obj = pool.get('ir.rule.group-res.group')
-        user_group_obj = pool.get('res.user-res.group')
-        user_obj = pool.get('res.user')
+        RuleGroup = pool.get('ir.rule.group')
+        Model = pool.get('ir.model')
+        RuleGroup_User = pool.get('ir.rule.group-res.user')
+        RuleGroup_Group = pool.get('ir.rule.group-res.group')
+        User_Group = pool.get('res.user-res.group')
+        User = pool.get('res.user')
 
         cursor = Transaction().cursor
-        cursor.execute('SELECT r.id FROM "' + self._table + '" r '
-            'JOIN "' + rule_group_obj._table + '" g '
+        cursor.execute('SELECT r.id FROM "' + cls._table + '" r '
+            'JOIN "' + RuleGroup._table + '" g '
                 "ON (g.id = r.rule_group) "
-            'JOIN "' + model_obj._table + '" m ON (g.model = m.id) '
+            'JOIN "' + Model._table + '" m ON (g.model = m.id) '
             "WHERE m.model = %s "
                 "AND g.perm_" + mode + " "
                 "AND (g.id IN ("
                         'SELECT rule_group '
-                        'FROM "' + rule_group_user_obj._table + '" '
+                        'FROM "' + RuleGroup_User._table + '" '
                         'WHERE "user" = %s '
                         "UNION SELECT rule_group "
-                        'FROM "' + rule_group_group_obj._table + '" g_rel '
-                        'JOIN "' + user_group_obj._table + '" u_rel '
+                        'FROM "' + RuleGroup_Group._table + '" g_rel '
+                        'JOIN "' + User_Group._table + '" u_rel '
                             'ON (g_rel."group" = u_rel."group") '
                         'WHERE u_rel."user" = %s) '
                     "OR default_p "
@@ -189,19 +208,20 @@
                 (model_name, Transaction().user, Transaction().user))
         ids = [x[0] for x in cursor.fetchall()]
         if not ids:
+            cls._domain_get_cache.set(key, ('', []))
             return '', []
         obj = pool.get(model_name)
         clause = {}
         clause_global = {}
-        operand2query = self._operand_get('res.user', level=1,
+        operand2query = cls._operand_get('res.user', level=1,
                 recur=['many2one'], root_tech='user', root='User')
         user_id = Transaction().user
         with Transaction().set_user(0, set_context=True):
-            user = user_obj.browse(user_id)
+            user = User(user_id)
         # Use root user without context to prevent recursion
         with contextlib.nested(Transaction().set_user(0),
                 Transaction().set_context(user=0)):
-            for rule in self.browse(ids):
+            for rule in cls.browse(ids):
                 dom = safe_eval("[('%s', '%s', %s)]" % \
                         (rule.field.name, rule.operator,
                             operand2query[rule.operand]), {
@@ -209,7 +229,7 @@
                                 'time': time,
                                 })
 
-                if rule.rule_group['global_p']:
+                if rule.rule_group.global_p:
                     clause_global.setdefault(rule.rule_group.id, ['OR'])
                     clause_global[rule.rule_group.id].append(dom)
                 else:
@@ -220,17 +240,17 @@
         val = []
 
         # Test if there is no rule_group that have no rule
-        cursor.execute('SELECT g.id FROM "' + rule_group_obj._table + '" g ' \
-                'JOIN "' + model_obj._table + '" m ON (g.model = m.id) ' \
+        cursor.execute('SELECT g.id FROM "' + RuleGroup._table + '" g ' \
+                'JOIN "' + Model._table + '" m ON (g.model = m.id) ' \
             'WHERE m.model = %s ' \
                 'AND (g.id NOT IN (SELECT rule_group ' \
-                        'FROM "' + self._table + '")) ' \
+                        'FROM "' + cls._table + '")) ' \
                 'AND (g.id IN (SELECT rule_group ' \
-                        'FROM "' + rule_group_user_obj._table + '" ' \
+                        'FROM "' + RuleGroup_User._table + '" ' \
                         'WHERE "user" = %s ' \
                         'UNION SELECT rule_group ' \
-                        'FROM "' + rule_group_group_obj._table + '" g_rel ' \
-                            'JOIN "' + user_group_obj._table + '" u_rel ' \
+                        'FROM "' + RuleGroup_Group._table + '" g_rel ' \
+                            'JOIN "' + User_Group._table + '" u_rel ' \
                                 'ON g_rel."group" = u_rel."group" ' \
                         'WHERE u_rel."user" = %s))',
             (model_name, user_id, user_id))
@@ -249,24 +269,24 @@
 
         query, val, _, _ = obj.search_domain(clause, active_test=False)
 
+        cls._domain_get_cache.set(key, (query, val))
         return query, val
 
-    def delete(self, ids):
-        res = super(Rule, self).delete(ids)
+    @classmethod
+    def delete(cls, rules):
+        super(Rule, cls).delete(rules)
         # Restart the cache on the domain_get method of ir.rule
-        self.domain_get.reset()
+        cls._domain_get_cache.clear()
+
+    @classmethod
+    def create(cls, vals):
+        res = super(Rule, cls).create(vals)
+        # Restart the cache on the domain_get method of ir.rule
+        cls._domain_get_cache.clear()
         return res
 
-    def create(self, vals):
-        res = super(Rule, self).create(vals)
-        # Restart the cache on the domain_get method of ir.rule
-        self.domain_get.reset()
-        return res
-
-    def write(self, ids, vals):
-        res = super(Rule, self).write(ids, vals)
+    @classmethod
+    def write(cls, rules, vals):
+        super(Rule, cls).write(rules, vals)
         # Restart the cache on the domain_get method
-        self.domain_get.reset()
-        return res
-
-Rule()
+        cls._domain_get_cache.clear()
diff -r f6ef988fda71 trytond/ir/sequence.py
--- a/trytond/ir/sequence.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/ir/sequence.py	Sun Sep 23 17:30:03 2012 +0200
@@ -10,23 +10,25 @@
 from ..config import CONFIG
 from ..backend import TableHandler
 
+__all__ = [
+    'SequenceType', 'Sequence', 'SequenceStrict',
+    ]
+
 sql_sequence = CONFIG.options['db_type'] == 'postgresql'
 
 
 class SequenceType(ModelSQL, ModelView):
     "Sequence type"
-    _name = 'ir.sequence.type'
-    _description = __doc__
+    __name__ = 'ir.sequence.type'
+
     name = fields.Char('Sequence Name', required=True, translate=True)
     code = fields.Char('Sequence Code', required=True)
 
-SequenceType()
-
 
 class Sequence(ModelSQL, ModelView):
     "Sequence"
-    _name = 'ir.sequence'
-    _description = __doc__
+    __name__ = 'ir.sequence'
+
     _strict = False
     name = fields.Char('Sequence Name', required=True, translate=True)
     code = fields.Selection('code_get', 'Sequence Code', required=True,
@@ -77,188 +79,197 @@
                 ['decimal timestamp', 'hexadecimal timestamp']),
             }, depends=['type'])
 
-    def __init__(self):
-        super(Sequence, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Sequence, cls).__setup__()
+        cls._constraints += [
             ('check_prefix_suffix', 'invalid_prefix_suffix'),
             ('check_last_timestamp', 'future_last_timestamp'),
         ]
-        self._sql_constraints += [
+        cls._sql_constraints += [
             ('check_timestamp_rounding', 'CHECK(timestamp_rounding > 0)',
                 'Timestamp rounding should be greater than 0'),
             ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'missing': 'Missing sequence!',
             'invalid_prefix_suffix': 'Invalid prefix/suffix!',
             'future_last_timestamp': 'Last Timestamp could not be in future!',
             })
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
 
         # Migration from 2.0 rename number_next into number_next_internal
         table.column_rename('number_next', 'number_next_internal')
 
-        super(Sequence, self).init(module_name)
+        super(Sequence, cls).__register__(module_name)
 
         # Migration from 2.0 create sql_sequence
-        if sql_sequence and not self._strict:
-            sequence_ids = self.search([])
-            for sequence in self.browse(sequence_ids):
+        if sql_sequence and not cls._strict:
+            sequences = cls.search([])
+            for sequence in sequences:
                 if sequence.type != 'incremental':
                     continue
                 if not TableHandler.sequence_exist(cursor,
-                        self._sql_sequence_name(sequence)):
-                    self.create_sql_sequence(sequence,
-                        sequence.number_next_internal)
+                        sequence._sql_sequence_name):
+                    sequence.create_sql_sequence(sequence.number_next_internal)
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_type(self):
+    @staticmethod
+    def default_type():
         return 'incremental'
 
-    def default_number_increment(self):
+    @staticmethod
+    def default_number_increment():
         return 1
 
-    def default_number_next(self):
+    @staticmethod
+    def default_number_next():
         return 1
 
-    def default_padding(self):
+    @staticmethod
+    def default_padding():
         return 0
 
-    def default_timestamp_rounding(self):
+    @staticmethod
+    def default_timestamp_rounding():
         return 1.0
 
-    def default_timestamp_offset(self):
+    @staticmethod
+    def default_timestamp_offset():
         return 946681200.0  # Offset for 2000-01-01
 
-    def default_last_timestamp(self):
+    @staticmethod
+    def default_last_timestamp():
         return 0.0
 
-    def default_code(self):
+    @staticmethod
+    def default_code():
         return Transaction().context.get('code')
 
-    def get_number_next(self, ids, name):
+    def get_number_next(self, name):
         cursor = Transaction().cursor
-        result = {}
-        for sequence in self.browse(ids):
-            sql_name = self._sql_sequence_name(sequence)
-            if sql_sequence and not self._strict:
-                cursor.execute('SELECT '
-                    'CASE WHEN NOT is_called THEN last_value '
-                        'ELSE last_value + increment_by '
-                    'END FROM "%s"' % sql_name)
-                value, = cursor.fetchone()
-            else:
-                value = sequence.number_next_internal
-            result[sequence.id] = value
-        return result
+        sql_name = self._sql_sequence_name
+        if sql_sequence and not self._strict:
+            cursor.execute('SELECT '
+                'CASE WHEN NOT is_called THEN last_value '
+                    'ELSE last_value + increment_by '
+                'END FROM "%s"' % sql_name)
+            return cursor.fetchone()[0]
+        else:
+            return self.number_next_internal
 
-    def set_number_next(self, ids, name, value):
-        super(Sequence, self).write(ids, {
+    @classmethod
+    def set_number_next(cls, sequences, name, value):
+        super(Sequence, cls).write(sequences, {
                 'number_next_internal': value,
                 })
 
-    def create(self, values):
-        sequence_id = super(Sequence, self).create(values)
-        if sql_sequence and not self._strict:
-            sequence = self.browse(sequence_id)
-            self.update_sql_sequence(sequence, values.get('number_next',
-                    self.default_number_next()))
+    @classmethod
+    def create(cls, values):
+        sequence_id = super(Sequence, cls).create(values)
+        if sql_sequence and not cls._strict:
+            sequence = cls(sequence_id)
+            sequence.update_sql_sequence(values.get('number_next',
+                    cls.default_number_next()))
         return sequence_id
 
-    def write(self, ids, values):
-        result = super(Sequence, self).write(ids, values)
-        if sql_sequence and not self._strict:
-            ids = [ids] if isinstance(ids, (int, long)) else ids
-            sequences = self.browse(ids)
+    @classmethod
+    def write(cls, sequences, values):
+        result = super(Sequence, cls).write(sequences, values)
+        if sql_sequence and not cls._strict:
             for sequence in sequences:
-                self.update_sql_sequence(sequence, values.get('number_next'))
+                sequence.update_sql_sequence(values.get('number_next'))
         return result
 
-    def delete(self, ids):
-        if sql_sequence and not self._strict:
-            ids = [ids] if isinstance(ids, (int, long)) else ids
-            sequences = self.browse(ids)
+    @classmethod
+    def delete(cls, sequences):
+        if sql_sequence and not cls._strict:
             for sequence in sequences:
-                self.delete_sql_sequence(sequence)
-        return super(Sequence, self).delete(ids)
+                sequence.delete_sql_sequence()
+        return super(Sequence, cls).delete(sequences)
 
-    def code_get(self):
+    @classmethod
+    def code_get(cls):
         pool = Pool()
-        sequence_type_obj = pool.get('ir.sequence.type')
-        sequence_type_ids = sequence_type_obj.search([])
-        sequence_types = sequence_type_obj.browse(sequence_type_ids)
+        SequenceType = pool.get('ir.sequence.type')
+        sequence_types = SequenceType.search([])
         return [(x.code, x.name) for x in sequence_types]
 
-    def check_prefix_suffix(self, ids):
+    @classmethod
+    def check_prefix_suffix(cls, sequences):
         "Check prefix and suffix"
 
-        for sequence in self.browse(ids):
+        for sequence in sequences:
             try:
-                self._process(sequence.prefix)
-                self._process(sequence.suffix)
+                cls._process(sequence.prefix)
+                cls._process(sequence.suffix)
             except Exception:
                 return False
         return True
 
-    def check_last_timestamp(self, ids):
+    @classmethod
+    def check_last_timestamp(cls, sequences):
         "Check last_timestamp"
 
-        for sequence in self.browse(ids):
-            next_timestamp = self._timestamp(sequence)
+        for sequence in sequences:
+            next_timestamp = cls._timestamp(sequence)
             if sequence.last_timestamp > next_timestamp:
                 return False
         return True
 
-    def _sql_sequence_name(self, sequence):
+    @property
+    def _sql_sequence_name(self):
         'Return SQL sequence name'
-        return '%s_%s' % (self._table, sequence.id)
+        return '%s_%s' % (self._table, self.id)
 
-    def create_sql_sequence(self, sequence, number_next=None):
+    def create_sql_sequence(self, number_next=None):
         'Create the SQL sequence'
         cursor = Transaction().cursor
-        if sequence.type != 'incremental':
+        if self.type != 'incremental':
             return
         if number_next is None:
-            number_next = sequence.number_next
-        cursor.execute('CREATE SEQUENCE "' + self._sql_sequence_name(sequence)
-            + '" INCREMENT BY %s START WITH %s', (sequence.number_increment,
-                number_next))
+            number_next = self.number_next
+        cursor.execute('CREATE SEQUENCE "' + self._sql_sequence_name
+            + '" INCREMENT BY %s START WITH %s',
+            (self.number_increment, number_next))
 
-    def update_sql_sequence(self, sequence, number_next=None):
+    def update_sql_sequence(self, number_next=None):
         'Update the SQL sequence'
         cursor = Transaction().cursor
-        exist = TableHandler.sequence_exist(cursor,
-            self._sql_sequence_name(sequence))
-        if sequence.type != 'incremental':
+        exist = TableHandler.sequence_exist(cursor, self._sql_sequence_name)
+        if self.type != 'incremental':
             if exist:
-                self.delete_sql_sequence(sequence)
+                self.delete_sql_sequence()
             return
         if not exist:
-            self.create_sql_sequence(sequence, number_next)
+            self.create_sql_sequence(number_next)
             return
         if number_next is None:
-            number_next = sequence.number_next
-        cursor.execute('ALTER SEQUENCE "' + self._sql_sequence_name(sequence)
-            + '" INCREMENT BY %s RESTART WITH %s', (sequence.number_increment,
-                number_next))
+            number_next = self.number_next
+        cursor.execute('ALTER SEQUENCE "' + self._sql_sequence_name
+            + '" INCREMENT BY %s RESTART WITH %s',
+            (self.number_increment, number_next))
 
-    def delete_sql_sequence(self, sequence):
+    def delete_sql_sequence(self):
         'Delete the SQL sequence'
         cursor = Transaction().cursor
-        if sequence.type != 'incremental':
+        if self.type != 'incremental':
             return
         cursor.execute('DROP SEQUENCE "%s"'
-            % self._sql_sequence_name(sequence))
+            % self._sql_sequence_name)
 
-    def _process(self, string, date=None):
+    @staticmethod
+    def _process(string, date=None):
         pool = Pool()
-        date_obj = pool.get('ir.date')
+        Date = pool.get('ir.date')
         if not date:
-            date = date_obj.today()
+            date = Date.today()
         year = datetime_strftime(date, '%Y')
         month = datetime_strftime(date, '%m')
         day = datetime_strftime(date, '%d')
@@ -268,23 +279,25 @@
                 day=day,
                 )
 
-    def _timestamp(self, sequence):
+    @staticmethod
+    def _timestamp(sequence):
         return int((time.time() - sequence.timestamp_offset)
                 / sequence.timestamp_rounding)
 
-    def _get_sequence(self, sequence):
+    @classmethod
+    def _get_sequence(cls, sequence):
         if sequence.type == 'incremental':
-            if sql_sequence and not self._strict:
+            if sql_sequence and not cls._strict:
                 cursor = Transaction().cursor
                 cursor.execute('SELECT nextval(\'"%s"\')'
-                    % self._sql_sequence_name(sequence))
+                    % sequence._sql_sequence_name)
                 number_next, = cursor.fetchone()
             else:
                 #Pre-fetch number_next
                 number_next = sequence.number_next_internal
 
                 with Transaction().set_user(0):
-                    self.write(sequence.id, {
+                    cls.write([sequence], {
                             'number_next_internal': (number_next
                                 + sequence.number_increment),
                             })
@@ -292,9 +305,9 @@
         elif sequence.type in ('decimal timestamp', 'hexadecimal timestamp'):
             timestamp = sequence.last_timestamp
             while timestamp == sequence.last_timestamp:
-                timestamp = self._timestamp(sequence)
+                timestamp = cls._timestamp(sequence)
             with Transaction().set_user(0):
-                self.write(sequence.id, {
+                cls.write([sequence], {
                     'last_timestamp': timestamp,
                     })
             if sequence.type == 'decimal timestamp':
@@ -303,45 +316,42 @@
                 return hex(timestamp)[2:].upper()
         return ''
 
-    def get_id(self, domain):
+    @classmethod
+    def get_id(cls, domain):
         '''
         Return sequence value for the domain
-
-        :param domain: a domain or a sequence id
-        :return: the sequence value
         '''
+        if isinstance(domain, cls):
+            domain = domain.id
         if isinstance(domain, (int, long)):
             domain = [('id', '=', domain)]
 
         # bypass rules on sequences
         with Transaction().set_context(user=False):
             with Transaction().set_user(0):
-                sequence_ids = self.search(domain, limit=1)
+                try:
+                    sequence, = cls.search(domain, limit=1)
+                except TypeError:
+                    cls.raise_user_error('missing')
             date = Transaction().context.get('date')
-            if sequence_ids:
-                with Transaction().set_user(0):
-                    sequence = self.browse(sequence_ids[0])
-                return '%s%s%s' % (
-                        self._process(sequence.prefix, date=date),
-                        self._get_sequence(sequence),
-                        self._process(sequence.suffix, date=date),
-                        )
-        self.raise_user_error('missing')
+            return '%s%s%s' % (
+                cls._process(sequence.prefix, date=date),
+                cls._get_sequence(sequence),
+                cls._process(sequence.suffix, date=date),
+                )
 
-    def get(self, code):
-        return self.get_id([('code', '=', code)])
-
-Sequence()
+    @classmethod
+    def get(cls, code):
+        return cls.get_id([('code', '=', code)])
 
 
 class SequenceStrict(Sequence):
     "Sequence Strict"
-    _name = 'ir.sequence.strict'
-    _description = __doc__
+    __name__ = 'ir.sequence.strict'
+    _table = 'ir_sequence_strict'  # Needed to override Sequence._table
     _strict = True
 
-    def get_id(self, clause):
-        Transaction().cursor.lock(self._table)
-        return super(SequenceStrict, self).get_id(clause)
-
-SequenceStrict()
+    @classmethod
+    def get_id(cls, clause):
+        Transaction().cursor.lock(cls._table)
+        return super(SequenceStrict, cls).get_id(clause)
diff -r f6ef988fda71 trytond/ir/session.py
--- a/trytond/ir/session.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/ir/session.py	Sun Sep 23 17:30:03 2012 +0200
@@ -12,36 +12,42 @@
 from ..backend import TableHandler
 from ..transaction import Transaction
 
+__all__ = [
+    'Session', 'SessionWizard',
+    ]
+
 
 class Session(ModelSQL):
     "Session"
-    _name = 'ir.session'
-    _description = __doc__
+    __name__ = 'ir.session'
     _rec_name = 'key'
 
     key = fields.Char('Key', required=True, select=True)
 
-    def __init__(self):
-        super(Session, self).__init__()
-        self._rpc = {}
+    @classmethod
+    def __setup__(cls):
+        super(Session, cls).__setup__()
+        cls.__rpc__ = {}
 
-    def init(self, module_name):
-        super(Session, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(Session, cls).__register__(module_name)
 
-        table = TableHandler(Transaction().cursor, self, module_name)
+        table = TableHandler(Transaction().cursor, cls, module_name)
         table.index_action('create_uid', 'add')
 
-    def default_key(self):
+    @staticmethod
+    def default_key():
         return uuid.uuid4().hex
 
-    def check(self, user, key):
+    @classmethod
+    def check(cls, user, key):
         "Check user key and delete old one"
         now = datetime.datetime.now()
         timeout = datetime.timedelta(seconds=int(CONFIG['session_timeout']))
-        session_ids = self.search([
+        sessions = cls.search([
                 ('create_uid', '=', user),
                 ])
-        sessions = self.browse(session_ids)
         find = False
         for session in sessions:
             timestamp = session.write_date or session.create_date
@@ -49,31 +55,29 @@
                 if session.key == key:
                     find = True
             else:
-                self.delete(session.id)
+                cls.delete([session])
         return find
 
-    def reset(self, session):
+    @classmethod
+    def reset(cls, session):
         "Reset session timestamp"
-        session_id = self.search([
+        sessions = cls.search([
                 ('key', '=', session),
                 ])
-        self.write(session_id, {})
-
-Session()
+        cls.write(sessions, {})
 
 
 class SessionWizard(ModelSQL):
     "Session Wizard"
-    _name = 'ir.session.wizard'
-    _description = __doc__
+    __name__ = 'ir.session.wizard'
 
     data = fields.Text('Data')
 
-    def __init__(self):
-        super(SessionWizard, self).__init__()
-        self._rpc = {}
+    @classmethod
+    def __setup__(cls):
+        super(SessionWizard, cls).__setup__()
+        cls.__rpc__ = {}
 
-    def default_data(self):
+    @staticmethod
+    def default_data():
         return json.dumps({})
-
-SessionWizard()
diff -r f6ef988fda71 trytond/ir/translation.py
--- a/trytond/ir/translation.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/ir/translation.py	Sun Sep 23 17:30:03 2012 +0200
@@ -17,7 +17,6 @@
 from functools import reduce
 from ..config import CONFIG
 from ..model import ModelView, ModelSQL, fields
-from ..model.cacheable import Cacheable
 from ..wizard import Wizard, StateView, StateTransition, StateAction, \
     Button
 from ..tools import file_open, reduce_ids
@@ -25,6 +24,15 @@
 from ..pyson import PYSONEncoder
 from ..transaction import Transaction
 from ..pool import Pool
+from ..cache import Cache
+
+__all__ = ['Translation',
+    'ReportTranslationSetStart', 'ReportTranslationSetSucceed',
+    'ReportTranslationSet',
+    'TranslationCleanStart', 'TranslationCleanSucceed', 'TranslationClean',
+    'TranslationUpdateStart', 'TranslationUpdate',
+    'TranslationExportStart', 'TranslationExportResult', 'TranslationExport',
+    ]
 
 TRANSLATION_TYPE = [
     ('field', 'Field'),
@@ -45,10 +53,10 @@
             key=lambda x: (x.msgctxt, x.msgid))
 
 
-class Translation(ModelSQL, ModelView, Cacheable):
+class Translation(ModelSQL, ModelView):
     "Translation"
-    _name = "ir.translation"
-    _description = __doc__
+    __name__ = "ir.translation"
+
     name = fields.Char('Field Name', required=True)
     res_id = fields.Integer('Resource ID', select=True)
     lang = fields.Selection('get_language', string='Language')
@@ -61,98 +69,103 @@
     fuzzy = fields.Boolean('Fuzzy')
     model = fields.Function(fields.Char('Model'), 'get_model',
             searcher='search_model')
+    _translation_cache = Cache('ir.translation')
 
-    def __init__(self):
-        super(Translation, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Translation, cls).__setup__()
+        cls._sql_constraints += [
             ('translation_md5_uniq',
                 'UNIQUE (name, res_id, lang, type, src_md5, module)',
                 'Translation must be unique'),
         ]
-        self._max_len = 10240
+        cls._max_len = 10240
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         # Migration from 1.8: new field src_md5
         src_md5_exist = table.column_exist('src_md5')
         if not src_md5_exist:
             table.add_raw_column('src_md5',
-                FIELDS[self.src_md5._type].sql_type(self.src_md5),
-                FIELDS[self.src_md5._type].sql_format, None,
-                self.src_md5.size, string=self.src_md5.string)
+                FIELDS[cls.src_md5._type].sql_type(cls.src_md5),
+                FIELDS[cls.src_md5._type].sql_format, None,
+                cls.src_md5.size, string=cls.src_md5.string)
         table.drop_constraint('translation_uniq')
         table.index_action(['lang', 'type', 'name', 'src'], 'remove')
 
-        super(Translation, self).init(module_name)
+        super(Translation, cls).__register__(module_name)
 
         # Migration from 1.8: fill new field src_md5
         if not src_md5_exist:
             offset = 0
             limit = cursor.IN_MAX
-            translation_ids = True
-            while translation_ids:
-                translation_ids = self.search([], offset=offset, limit=limit)
+            translations = True
+            while translations:
+                translations = cls.search([], offset=offset, limit=limit)
                 offset += limit
-                for translation in self.browse(translation_ids):
-                    src_md5 = self.get_src_md5(translation.src)
-                    self.write(translation.id, {
+                for translation in cls.browse(translations):
+                    src_md5 = cls.get_src_md5(translation.src)
+                    cls.write(translation.id, {
                         'src_md5': src_md5,
                     })
-            table = TableHandler(cursor, self, module_name)
+            table = TableHandler(cursor, cls, module_name)
             table.not_null_action('src_md5', action='add')
 
         # Migration from 2.2
-        cursor.execute("UPDATE " + self._table + " "
+        cursor.execute("UPDATE " + cls._table + " "
             "SET res_id = %s "
             "WHERE res_id = %s",
             (None, 0))
 
-        table = TableHandler(Transaction().cursor, self, module_name)
+        table = TableHandler(Transaction().cursor, cls, module_name)
         table.index_action(['lang', 'type', 'name'], 'add')
 
-    def default_fuzzy(self):
+    @staticmethod
+    def default_fuzzy():
         return False
 
-    def get_model(self, ids, name):
-        res = {}
-        for translation in self.browse(ids):
-            res[translation.id] = translation.name.split(',')[0]
-        return res
+    def get_model(self, name):
+        return self.name.split(',')[0]
 
-    def search_rec_name(self, name, clause):
+    @classmethod
+    def search_rec_name(cls, name, clause):
         clause = tuple(clause)
-        ids = self.search(['OR',
+        translations = cls.search(['OR',
                 ('src',) + clause[1:],
                 ('value',) + clause[1:],
                 ])
-        if ids:
-            return [('id', 'in', ids)]
-        return [(self._rec_name,) + clause[1:]]
+        if translations:
+            return [('id', 'in', [t.id for t in translations])]
+        return [(cls._rec_name,) + clause[1:]]
 
-    def search_model(self, name, clause):
+    @classmethod
+    def search_model(cls, name, clause):
         cursor = Transaction().cursor
         cursor.execute('SELECT id FROM "%s" '
             'WHERE split_part(name, \',\', 1) %s %%s' %
-            (self._table, clause[1]), (clause[2],))
+            (cls._table, clause[1]), (clause[2],))
         return [('id', 'in', [x[0] for x in cursor.fetchall()])]
 
-    def get_language(self):
+    @classmethod
+    def get_language(cls):
         pool = Pool()
-        lang_obj = pool.get('ir.lang')
-        lang_ids = lang_obj.search([])
-        langs = lang_obj.browse(lang_ids)
+        Lang = pool.get('ir.lang')
+        langs = Lang.search([])
         res = [(lang.code, lang.name) for lang in langs]
         return res
 
-    def get_src_md5(self, src):
+    @classmethod
+    def get_src_md5(cls, src):
         return md5((src or '').encode('utf-8')).hexdigest()
 
-    def get_ids(self, name, ttype, lang, ids):
+    @classmethod
+    def get_ids(cls, name, ttype, lang, ids):
         "Return translation for each id"
         pool = Pool()
-        model_fields_obj = pool.get('ir.model.field')
-        model_obj = pool.get('ir.model')
+        ModelFields = pool.get('ir.model.field')
+        Model = pool.get('ir.model')
 
         translations, to_fetch = {}, []
         name = unicode(name)
@@ -166,10 +179,10 @@
                         ttype = u'field'
                     else:
                         ttype = u'help'
-                    records = model_fields_obj.browse(ids)
+                    records = ModelFields.browse(ids)
                 else:
                     ttype = u'model'
-                    records = model_obj.browse(ids)
+                    records = Model.browse(ids)
 
             trans_args = []
             for record in records:
@@ -178,20 +191,20 @@
                 else:
                     name = record.model + ',' + field_name
                 trans_args.append((name, ttype, lang, None))
-            self.get_sources(trans_args)
+            cls.get_sources(trans_args)
 
             for record in records:
                 if ttype in ('field', 'help'):
                     name = record.model.model + ',' + record.name
                 else:
                     name = record.model + ',' + field_name
-                translations[record.id] = self.get_source(name, ttype, lang)
+                translations[record.id] = cls.get_source(name, ttype, lang)
             return translations
         # Don't use cache for fuzzy translation
         if not Transaction().context.get(
                 'fuzzy_translation', False):
             for obj_id in ids:
-                trans = self.get((lang, ttype, name, obj_id))
+                trans = cls._translation_cache.get((lang, ttype, name, obj_id))
                 if trans is not None:
                     translations[obj_id] = trans
                 else:
@@ -222,19 +235,21 @@
                     # Don't store fuzzy translation in cache
                     if not Transaction().context.get(
                             'fuzzy_translation', False):
-                        self.add((lang, ttype, name, res_id), value)
+                        cls._translation_cache.set((lang, ttype, name, res_id),
+                            value)
                     translations[res_id] = value
         for res_id in ids:
             if res_id not in translations:
-                self.add((lang, ttype, name, res_id), False)
+                cls._translation_cache.set((lang, ttype, name, res_id), False)
                 translations[res_id] = False
         return translations
 
-    def set_ids(self, name, ttype, lang, ids, value):
+    @classmethod
+    def set_ids(cls, name, ttype, lang, ids, value):
         "Set translation for each id"
         pool = Pool()
-        model_fields_obj = pool.get('ir.model.field')
-        model_obj = pool.get('ir.model')
+        ModelFields = pool.get('ir.model.field')
+        Model = pool.get('ir.model')
 
         model_name, field_name = name.split(',')
         if model_name in ('ir.model.field', 'ir.model'):
@@ -244,86 +259,87 @@
                 else:
                     ttype = 'help'
                 with Transaction().set_context(language='en_US'):
-                    records = model_fields_obj.browse(ids)
+                    records = ModelFields.browse(ids)
             else:
                 ttype = 'model'
                 with Transaction().set_context(language='en_US'):
-                    records = model_obj.browse(ids)
+                    records = Model.browse(ids)
             for record in records:
                 if ttype in ('field', 'help'):
                     name = record.model + ',' + record.name
-                ids2 = self.search([
+                translation2 = cls.search([
                     ('lang', '=', lang),
                     ('type', '=', ttype),
                     ('name', '=', name),
                     ])
                 with Transaction().set_user(0):
-                    if not ids2:
-                        self.create({
+                    if not translation2:
+                        cls.create({
                             'name': name,
                             'lang': lang,
                             'type': ttype,
-                            'src': record[field_name],
+                            'src': getattr(record, field_name),
                             'value': value,
                             'fuzzy': False,
                             })
                     else:
-                        self.write(ids, {
-                            'src': record[field_name],
+                        cls.write(translation2, {
+                            'src': getattr(record, field_name),
                             'value': value,
                             'fuzzy': False,
                             })
             return len(ids)
-        model_obj = pool.get(model_name)
+        Model = pool.get(model_name)
         with Transaction().set_context(language=CONFIG['language']):
-            records = model_obj.browse(ids)
+            records = Model.browse(ids)
         for record in records:
-            ids2 = self.search([
+            translation2 = cls.search([
                 ('lang', '=', lang),
                 ('type', '=', ttype),
                 ('name', '=', name),
                 ('res_id', '=', record.id),
                 ])
             with Transaction().set_user(0):
-                if not ids2:
-                    self.create({
+                if not translation2:
+                    cls.create({
                         'name': name,
                         'lang': lang,
                         'type': ttype,
                         'res_id': record.id,
                         'value': value,
-                        'src': record[field_name],
+                        'src': getattr(record, field_name),
                         'fuzzy': False,
                         })
                 else:
-                    self.write(ids2, {
+                    cls.write(translation2, {
                         'value': value,
-                        'src': record[field_name],
+                        'src': getattr(record, field_name),
                         'fuzzy': False,
                         })
                     if (lang == CONFIG['language']
                             and Transaction().context.get('fuzzy_translation',
                                 True)):
-                        other_lang_ids = self.search([
+                        other_langs = cls.search([
                                 ('lang', '!=', lang),
                                 ('type', '=', ttype),
                                 ('name', '=', name),
                                 ('res_id', '=', record.id),
                                 ])
-                        self.write(other_lang_ids, {
-                                'src': record[field_name],
+                        cls.write(other_langs, {
+                                'src': getattr(record, field_name),
                                 'fuzzy': True,
                                 })
         return len(ids)
 
-    def get_source(self, name, ttype, lang, source=None):
+    @classmethod
+    def get_source(cls, name, ttype, lang, source=None):
         "Return translation for source"
         name = unicode(name)
         ttype = unicode(ttype)
         lang = unicode(lang)
         if source is not None:
             source = unicode(source)
-        trans = self.get((lang, ttype, name, source))
+        trans = cls._translation_cache.get((lang, ttype, name, source))
         if trans is not None:
             return trans
 
@@ -353,13 +369,14 @@
                     (lang, ttype, str(name), False))
         res = cursor.fetchone()
         if res:
-            self.add((lang, ttype, name, source), res[0])
+            cls._translation_cache.set((lang, ttype, name, source), res[0])
             return res[0]
         else:
-            self.add((lang, ttype, name, source), False)
+            cls._translation_cache.set((lang, ttype, name, source), False)
             return None
 
-    def get_sources(self, args):
+    @classmethod
+    def get_sources(cls, args):
         '''
         Take a list of (name, ttype, lang, source).
         Add the translations to the cache.
@@ -371,7 +388,7 @@
         if len(args) > cursor.IN_MAX:
             for i in range(0, len(args), cursor.IN_MAX):
                 sub_args = args[i:i + cursor.IN_MAX]
-                res.update(self.get_sources(sub_args))
+                res.update(cls.get_sources(sub_args))
             return res
         for name, ttype, lang, source in args:
             name = unicode(name)
@@ -379,12 +396,12 @@
             lang = unicode(lang)
             if source is not None:
                 source = unicode(source)
-            trans = self.get((lang, ttype, name, source))
+            trans = cls._translation_cache.get((lang, ttype, name, source))
             if trans is not None:
                 res[(name, ttype, lang, source)] = trans
             else:
                 res[(name, ttype, lang, source)] = None
-                self.add((lang, ttype, name, source), None)
+                cls._translation_cache.set((lang, ttype, name, source), None)
                 if source is not None:
                     clause += [('(lang = %s ' \
                             'AND type = %s ' \
@@ -415,17 +432,20 @@
                     if (name, ttype, lang, source) not in args:
                         source = None
                     res[(name, ttype, lang, source)] = value
-                    self.add((lang, ttype, name, source), value)
+                    cls._translation_cache.set((lang, ttype, name, source),
+                        value)
         return res
 
-    def delete(self, ids):
-        self.clear()
-        self.fields_view_get.reset()
-        return super(Translation, self).delete(ids)
+    @classmethod
+    def delete(cls, translations):
+        cls._translation_cache.clear()
+        ModelView._fields_view_get_cache.clear()
+        return super(Translation, cls).delete(translations)
 
-    def create(self, vals):
-        self.clear()
-        self.fields_view_get.reset()
+    @classmethod
+    def create(cls, vals):
+        cls._translation_cache.clear()
+        ModelView._fields_view_get_cache.clear()
         cursor = Transaction().cursor
         if not vals.get('module'):
             if Transaction().context.get('module'):
@@ -458,38 +478,40 @@
                     vals = vals.copy()
                     vals['module'], vals['src'] = fetchone
         vals = vals.copy()
-        vals['src_md5'] = self.get_src_md5(vals.get('src'))
-        return super(Translation, self).create(vals)
+        vals['src_md5'] = cls.get_src_md5(vals.get('src'))
+        return super(Translation, cls).create(vals)
 
-    def write(self, ids, vals):
-        self.clear()
-        self.fields_view_get.reset()
+    @classmethod
+    def write(cls, translations, vals):
+        cls._translation_cache.clear()
+        ModelView._fields_view_get_cache.clear()
         if 'src' in vals:
             vals = vals.copy()
-            vals['src_md5'] = self.get_src_md5(vals.get('src'))
-        return super(Translation, self).write(ids, vals)
+            vals['src_md5'] = cls.get_src_md5(vals.get('src'))
+        return super(Translation, cls).write(translations, vals)
 
-    def translation_import(self, lang, module, po_path):
+    @classmethod
+    def translation_import(cls, lang, module, po_path):
         pool = Pool()
-        model_data_obj = pool.get('ir.model.data')
-        model_data_ids = model_data_obj.search([
-            ('module', '=', module),
-            ])
+        ModelData = pool.get('ir.model.data')
+        models_data = ModelData.search([
+                ('module', '=', module),
+                ])
         fs_id2model_data = {}
-        for model_data in model_data_obj.browse(model_data_ids):
+        for model_data in models_data:
             fs_id2model_data.setdefault(model_data.model, {})
             fs_id2model_data[model_data.model][model_data.fs_id] = model_data
 
-        translation_ids = []
+        translations = []
         pofile = polib.pofile(po_path)
 
         id2translation = {}
         key2ids = {}
-        module_translation_ids = self.search([
+        module_translations = cls.search([
             ('lang', '=', lang),
             ('module', '=', module),
             ])
-        for translation in self.browse(module_translation_ids):
+        for translation in module_translations:
             if translation.type in ('odt', 'view', 'wizard_button',
                     'selection', 'error'):
                 key = (translation.name, translation.res_id, translation.type,
@@ -534,7 +556,7 @@
             with contextlib.nested(Transaction().set_user(0),
                     Transaction().set_context(module=module)):
                 if not ids:
-                    translation_ids.append(self.create({
+                    translations.append(cls.create({
                         'name': name,
                         'res_id': res_id,
                         'lang': lang,
@@ -545,38 +567,39 @@
                         'module': module,
                         }))
                 else:
-                    ids2 = []
+                    translations2 = []
                     for translation_id in ids:
                         translation = id2translation[translation_id]
                         if translation.value != value \
                                 or translation.fuzzy != fuzzy:
-                            ids2.append(translation.id)
-                    if ids2 and not noupdate:
-                        self.write(ids2, {
+                            translations2.append(translation)
+                    if translations2 and not noupdate:
+                        cls.write(translations2, {
                             'value': value,
                             'fuzzy': fuzzy,
                             })
-                    translation_ids += ids
+                    translations += cls.browse(ids)
 
-        if translation_ids:
-            all_translation_ids = self.search([
+        if translations:
+            all_translations = cls.search([
                 ('module', '=', module),
                 ('lang', '=', lang),
                 ])
-            translation_ids_to_delete = [x for x in all_translation_ids
-                    if x not in translation_ids]
-            self.delete(translation_ids_to_delete)
-        return len(translation_ids)
+            translations_to_delete = [x for x in all_translations
+                    if x not in translations]
+            cls.delete(translations_to_delete)
+        return len(translations)
 
-    def translation_export(self, lang, module):
+    @classmethod
+    def translation_export(cls, lang, module):
         pool = Pool()
-        model_data_obj = pool.get('ir.model.data')
+        ModelData = pool.get('ir.model.data')
 
-        model_data_ids = model_data_obj.search([
-            ('module', '=', module),
-            ])
+        models_data = ModelData.search([
+                ('module', '=', module),
+                ])
         db_id2fs_id = {}
-        for model_data in model_data_obj.browse(model_data_ids):
+        for model_data in models_data:
             db_id2fs_id.setdefault(model_data.model, {})
             db_id2fs_id[model_data.model][model_data.db_id] = model_data.fs_id
 
@@ -586,14 +609,17 @@
             }
 
         with Transaction().set_context(language=CONFIG['language']):
-            translation_ids = self.search([
+            translations = cls.search([
                 ('lang', '=', lang),
                 ('module', '=', module),
                 ], order=[])
-        for translation in self.browse(translation_ids):
-            flags = [] if not translation['fuzzy'] else ['fuzzy']
-            trans_ctxt = '%(type)s:%(name)s:' % translation
-            res_id = translation['res_id']
+        for translation in translations:
+            flags = [] if not translation.fuzzy else ['fuzzy']
+            trans_ctxt = '%(type)s:%(name)s:' % {
+                'type': translation.type,
+                'name': translation.name,
+                }
+            res_id = translation.res_id
             if res_id:
                 model, _ = translation.name.split(',')
                 if model in db_id2fs_id:
@@ -609,28 +635,20 @@
         pofile.sort()
         return unicode(pofile).encode('utf-8')
 
-Translation()
-
 
 class ReportTranslationSetStart(ModelView):
     "Update Report Translation"
-    _name = 'ir.translation.set_report.start'
-    _description = __doc__
-
-ReportTranslationSetStart()
+    __name__ = 'ir.translation.set_report.start'
 
 
 class ReportTranslationSetSucceed(ModelView):
     "Update Report Translation"
-    _name = 'ir.translation.set_report.succeed'
-    _description = __doc__
-
-ReportTranslationSetSucceed()
+    __name__ = 'ir.translation.set_report.succeed'
 
 
 class ReportTranslationSet(Wizard):
     "Update report translation"
-    _name = "ir.translation.set_report"
+    __name__ = "ir.translation.set_report"
 
     start = StateView('ir.translation.set_report.start',
         'ir.translation_set_report_start_view_form', [
@@ -662,18 +680,17 @@
             strings.extend(self._translate_report(child))
         return strings
 
-    def transition_set_report(self, session):
+    def transition_set_report(self):
         pool = Pool()
-        report_obj = pool.get('ir.action.report')
-        translation_obj = pool.get('ir.translation')
+        Report = pool.get('ir.action.report')
+        Translation = pool.get('ir.translation')
 
         with Transaction().set_context(active_test=False):
-            report_ids = report_obj.search([])
+            reports = Report.search([])
 
-        if not report_ids:
+        if not reports:
             return {}
 
-        reports = report_obj.browse(report_ids)
         cursor = Transaction().cursor
         for report in reports:
             cursor.execute('SELECT id, name, src FROM ir_translation ' \
@@ -721,7 +738,7 @@
                 strings += self._translate_report(document.documentElement)
 
             for string in {}.fromkeys(strings).keys():
-                src_md5 = translation_obj.get_src_md5(string)
+                src_md5 = Translation.get_src_md5(string)
                 done = False
                 if string in trans_reports:
                     del trans_reports[string]
@@ -767,28 +784,20 @@
                                 tuple(strings))
         return 'succeed'
 
-ReportTranslationSet()
-
 
 class TranslationCleanStart(ModelView):
     'Clean translation'
-    _name = 'ir.translation.clean.start'
-    _description = __doc__
-
-TranslationCleanStart()
+    __name__ = 'ir.translation.clean.start'
 
 
 class TranslationCleanSucceed(ModelView):
     'Clean translation'
-    _name = 'ir.translation.clean.succeed'
-    _description = __doc__
-
-TranslationCleanSucceed()
+    __name__ = 'ir.translation.clean.succeed'
 
 
 class TranslationClean(Wizard):
     "Clean translation"
-    _name = 'ir.translation.clean'
+    __name__ = 'ir.translation.clean'
 
     start = StateView('ir.translation.clean.start',
         'ir.translation_clean_start_view_form', [
@@ -801,7 +810,8 @@
             Button('Ok', 'end', 'tryton-ok', default=True),
             ])
 
-    def _clean_field(self, translation):
+    @staticmethod
+    def _clean_field(translation):
         pool = Pool()
         try:
             model_name, field_name = translation.name.split(',', 1)
@@ -809,11 +819,12 @@
             return True
         if model_name not in pool.object_name_list():
             return True
-        model_obj = pool.get(model_name)
-        if field_name not in model_obj._columns:
+        Model = pool.get(model_name)
+        if field_name not in Model._fields:
             return True
 
-    def _clean_model(self, translation):
+    @staticmethod
+    def _clean_model(translation):
         pool = Pool()
         try:
             model_name, field_name = translation.name.split(',', 1)
@@ -822,26 +833,28 @@
         if model_name not in pool.object_name_list():
             return True
         if translation.res_id:
-            model_obj = pool.get(model_name)
-            if field_name not in model_obj._columns:
+            Model = pool.get(model_name)
+            if field_name not in Model._fields:
                 return True
-            field = model_obj._columns[field_name]
+            field = Model._fields[field_name]
             if (not hasattr(field, 'translate')
                     or not field.translate):
                 return True
         elif field_name not in ('name'):
             return True
 
-    def _clean_odt(self, translation):
+    @staticmethod
+    def _clean_odt(translation):
         pool = Pool()
-        report_obj = pool.get('ir.action.report')
+        Report = pool.get('ir.action.report')
         with Transaction().set_context(active_test=False):
-            if not report_obj.search([
+            if not Report.search([
                         ('report_name', '=', translation.name),
                         ]):
                 return True
 
-    def _clean_selection(self, translation):
+    @staticmethod
+    def _clean_selection(translation):
         pool = Pool()
         try:
             model_name, field_name = translation.name.split(',', 1)
@@ -849,10 +862,10 @@
             return True
         if model_name not in pool.object_name_list():
             return True
-        model_obj = pool.get(model_name)
-        if field_name not in model_obj._columns:
+        Model = pool.get(model_name)
+        if field_name not in Model._fields:
             return True
-        field = model_obj._columns[field_name]
+        field = Model._fields[field_name]
         if (not hasattr(field, 'selection')
                 or not field.selection
                 or not getattr(field, 'translate_selection', True)):
@@ -861,13 +874,15 @@
                 and translation.src not in dict(field.selection).values()):
             return True
 
-    def _clean_view(self, translation):
+    @staticmethod
+    def _clean_view(translation):
         pool = Pool()
         model_name = translation.name
         if model_name not in pool.object_name_list():
             return True
 
-    def _clean_wizard_button(self, translation):
+    @staticmethod
+    def _clean_wizard_button(translation):
         pool = Pool()
         try:
             wizard_name, state_name, button_name = \
@@ -877,17 +892,18 @@
         if (wizard_name not in
                 pool.object_name_list(type='wizard')):
             return True
-        wizard = pool.get(wizard_name, type='wizard')
-        if not wizard:
+        Wizard = pool.get(wizard_name, type='wizard')
+        if not Wizard:
             return True
-        state = wizard.states.get(state_name)
+        state = Wizard.states.get(state_name)
         if not state or not hasattr(state, 'buttons'):
             return True
         if button_name in [b.state for b in state.buttons]:
             return False
         return True
 
-    def _clean_help(self, translation):
+    @staticmethod
+    def _clean_help(translation):
         pool = Pool()
         try:
             model_name, field_name = translation.name.split(',', 1)
@@ -895,13 +911,14 @@
             return True
         if model_name not in pool.object_name_list():
             return True
-        model_obj = pool.get(model_name)
-        if field_name not in model_obj._columns:
+        Model = pool.get(model_name)
+        if field_name not in Model._fields:
             return True
-        field = model_obj._columns[field_name]
+        field = Model._fields[field_name]
         return not field.help
 
-    def _clean_error(self, translation):
+    @staticmethod
+    def _clean_error(translation):
         pool = Pool()
         model_name = translation.name
         if model_name in (
@@ -927,30 +944,30 @@
                 ):
             return False
         if model_name in pool.object_name_list():
-            model_obj = pool.get(model_name)
-            errors = model_obj._error_messages.values() + \
-                    model_obj._sql_error_messages.values()
-            for _, _, error in model_obj._sql_constraints:
+            Model = pool.get(model_name)
+            errors = Model._error_messages.values() + \
+                    Model._sql_error_messages.values()
+            for _, _, error in Model._sql_constraints:
                 errors.append(error)
             if translation.src not in errors:
                 return True
         elif model_name in pool.object_name_list(type='wizard'):
-            wizard_obj = pool.get(model_name, type='wizard')
-            errors = wizard_obj._error_messages.values()
+            Wizard = pool.get(model_name, type='wizard')
+            errors = Wizard._error_messages.values()
             if translation.src not in errors:
                 return True
         else:
             return True
 
-    def transition_clean(self, session):
+    def transition_clean(self):
         pool = Pool()
-        translation_obj = pool.get('ir.translation')
-        model_data_obj = pool.get('ir.model.data')
+        Translation = pool.get('ir.translation')
+        ModelData = pool.get('ir.model.data')
 
         to_delete = []
         keys = set()
-        translation_ids = translation_obj.search([])
-        for translation in translation_obj.browse(translation_ids):
+        translations = Translation.search([])
+        for translation in translations:
             if getattr(self, '_clean_%s' % translation.type)(translation):
                 to_delete.append(translation.id)
             elif translation.type in ('field', 'model', 'wizard_button',
@@ -962,46 +979,42 @@
                 else:
                     keys.add(key)
         # skip translation handled in ir.model.data
-        mdata_ids = model_data_obj.search([
+        models_data = ModelData.search([
             ('db_id', 'in', to_delete),
             ('model', '=', 'ir.translation'),
             ])
-        for mdata in model_data_obj.browse(mdata_ids):
+        for mdata in models_data:
             if mdata.db_id in to_delete:
                 to_delete.remove(mdata.db_id)
 
-        translation_obj.delete(to_delete)
+        Translation.delete(Translation.browse(to_delete))
         return 'succeed'
 
-TranslationClean()
-
 
 class TranslationUpdateStart(ModelView):
     "Update translation"
-    _name = 'ir.translation.update.start'
-    _description = __doc__
+    __name__ = 'ir.translation.update.start'
 
     language = fields.Many2One('ir.lang', 'Language', required=True,
         domain=[('translatable', '=', True)])
 
-    def default_language(self):
-        lang_obj = Pool().get('ir.lang')
+    @staticmethod
+    def default_language():
+        Lang = Pool().get('ir.lang')
         code = Transaction().context.get('language', False)
         try:
-            lang_id, = lang_obj.search([
+            lang, = Lang.search([
                     ('code', '=', code),
                     ('translatable', '=', True),
                     ], limit=1)
-            return lang_id
+            return lang.id
         except ValueError:
             return None
 
-TranslationUpdateStart()
-
 
 class TranslationUpdate(Wizard):
     "Update translation"
-    _name = "ir.translation.update"
+    __name__ = "ir.translation.update"
 
     start = StateView('ir.translation.update.start',
         'ir.translation_update_start_view_form', [
@@ -1010,14 +1023,15 @@
             ])
     update = StateAction('ir.act_translation_form')
 
-    def transition_update(self, session):
+    @staticmethod
+    def transition_update():
         return 'end'
 
-    def do_update(self, session, action):
+    def do_update(self, action):
         pool = Pool()
-        translation_obj = pool.get('ir.translation')
+        Translation = pool.get('ir.translation')
         cursor = Transaction().cursor
-        lang = session.start.language.code
+        lang = self.start.language.code
         cursor.execute('SELECT name, res_id, type, src, module ' \
                 'FROM ir_translation ' \
                 'WHERE lang=\'en_US\' ' \
@@ -1031,7 +1045,7 @@
                 (lang,))
         for row in cursor.dictfetchall():
             with Transaction().set_user(0):
-                translation_obj.create({
+                Translation.create({
                     'name': row['name'],
                     'res_id': row['res_id'],
                     'lang': lang,
@@ -1050,7 +1064,7 @@
                 (lang,))
         for row in cursor.dictfetchall():
             with Transaction().set_user(0):
-                translation_obj.create({
+                Translation.create({
                     'name': row['name'],
                     'res_id': row['res_id'],
                     'lang': lang,
@@ -1109,13 +1123,11 @@
         ])
         return action, {}
 
-TranslationUpdate()
-
 
 class TranslationExportStart(ModelView):
     "Export translation"
-    _name = 'ir.translation.export.start'
-    _description = __doc__
+    __name__ = 'ir.translation.export.start'
+
     language = fields.Many2One('ir.lang', 'Language', required=True,
         domain=[
             ('translatable', '=', True),
@@ -1126,33 +1138,30 @@
             ('state', 'in', ['installed', 'to upgrade', 'to remove']),
             ])
 
-    def default_language(self):
-        lang_obj = Pool().get('ir.lang')
+    @staticmethod
+    def default_language():
+        Lang = Pool().get('ir.lang')
         code = Transaction().context.get('language', False)
         try:
-            lang_id, = lang_obj.search([
+            lang, = Lang.search([
                     ('code', '=', code),
                     ('translatable', '=', True),
                     ], limit=1)
-            return lang_id
+            return lang.id
         except ValueError:
             return None
 
-TranslationExportStart()
-
 
 class TranslationExportResult(ModelView):
     "Export translation"
-    _description = __doc__
-    _name = 'ir.translation.export.result'
+    __name__ = 'ir.translation.export.result'
+
     file = fields.Binary('File', readonly=True)
 
-TranslationExportResult()
-
 
 class TranslationExport(Wizard):
     "Export translation"
-    _name = "ir.translation.export"
+    __name__ = "ir.translation.export"
 
     start = StateView('ir.translation.export.start',
         'ir.translation_export_start_view_form', [
@@ -1165,19 +1174,17 @@
             Button('Close', 'end', 'tryton-close'),
             ])
 
-    def transition_export(self, session):
+    def transition_export(self):
         pool = Pool()
-        translation_obj = pool.get('ir.translation')
-        file_data = translation_obj.translation_export(
-            session.start.language.code, session.start.module.name)
-        session.result.file = buffer(file_data)
+        Translation = pool.get('ir.translation')
+        file_data = Translation.translation_export(
+            self.start.language.code, self.start.module.name)
+        self.result.file = buffer(file_data)
         return 'result'
 
-    def default_result(self, session, fields):
-        file_ = session.result.file
-        session.result.file = False  # No need to store it in session
+    def default_result(self, fields):
+        file_ = self.result.file
+        self.result.file = False  # No need to store it in session
         return {
             'file': file_,
             }
-
-TranslationExport()
diff -r f6ef988fda71 trytond/ir/trigger.py
--- a/trytond/ir/trigger.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/ir/trigger.py	Sun Sep 23 17:30:03 2012 +0200
@@ -11,11 +11,14 @@
 from ..cache import Cache
 from ..pool import Pool
 
+__all__ = [
+    'Trigger', 'TriggerLog',
+    ]
+
 
 class Trigger(ModelSQL, ModelView):
     "Trigger"
-    _name = 'ir.trigger'
-    _description = __doc__
+    __name__ = 'ir.trigger'
     name = fields.Char('Name', required=True, translate=True)
     active = fields.Boolean('Active', select=True)
     model = fields.Many2One('ir.model', 'Model', required=True, select=True)
@@ -48,41 +51,46 @@
         '0 for no delay.')
     action_model = fields.Many2One('ir.model', 'Action Model', required=True)
     action_function = fields.Char('Action Function', required=True)
+    _get_triggers_cache = Cache('ir_trigger.get_triggers')
 
-    def __init__(self):
-        super(Trigger, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Trigger, cls).__setup__()
+        cls._sql_constraints += [
             ('on_exclusive',
                 'CHECK(NOT(on_time AND (on_create OR on_write OR on_delete)))',
                 '"On Time" and others are mutually exclusive!'),
         ]
-        self._constraints += [
+        cls._constraints += [
             ('check_condition', 'invalid_condition'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'invalid_condition': 'Condition must be a python expression!',
         })
-        self._order.insert(0, ('name', 'ASC'))
+        cls._order.insert(0, ('name', 'ASC'))
 
-    def check_condition(self, ids):
+    @classmethod
+    def check_condition(cls, triggers):
         '''
         Check condition
         '''
-        for trigger in self.browse(ids):
+        for trigger in triggers:
             try:
                 compile(trigger.condition, '', 'eval')
             except (SyntaxError, TypeError):
                 return False
         return True
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_limit_number(self):
+    @staticmethod
+    def default_limit_number():
         return 0
 
-    def on_change_on_time(self, values):
-        if values.get('on_time'):
+    def on_change_on_time(self):
+        if self.on_time:
             return {
                     'on_create': False,
                     'on_write': False,
@@ -90,35 +98,31 @@
                     }
         return {}
 
-    def on_change_on_create(self, values):
-        if values.get('on_create'):
+    def on_change_on_create(self):
+        if self.on_create:
             return {
                     'on_time': False,
                     }
         return {}
 
-    def on_change_on_write(self, values):
-        if values.get('on_write'):
+    def on_change_on_write(self):
+        if self.on_write:
             return {
                     'on_time': False,
                     }
         return {}
 
-    def on_change_on_delete(self, values):
-        if values.get('on_delete'):
+    def on_change_on_delete(self):
+        if self.on_delete:
             return {
                     'on_time': False,
                     }
         return {}
 
-    @Cache('ir_trigger.get_triggers')
-    def get_triggers(self, model_name, mode):
+    @classmethod
+    def get_triggers(cls, model_name, mode):
         """
-        Return trigger ids for a model and a mode
-
-        :param model_name: the name of the model
-        :param mode: the mode that can be 'create', 'write', 'delete' or 'time'
-        :return: a list of ir.trigger ids
+        Return triggers for a model and a mode
         """
         assert mode in ['create', 'write', 'delete', 'time'], \
                 'Invalid trigger mode'
@@ -126,19 +130,22 @@
         if Transaction().user == 0:
             return []  # XXX is it want we want?
 
-        trigger_ids = self.search([
-            ('model.model', '=', model_name),
-            ('on_%s' % mode, '=', True),
-            ])
-        return trigger_ids
+        key = (model_name, mode)
+        trigger_ids = cls._get_triggers_cache.get(key)
+        if trigger_ids is not None:
+            return cls.browse(trigger_ids)
 
-    def eval(self, trigger, record):
+        triggers = cls.search([
+                ('model.model', '=', model_name),
+                ('on_%s' % mode, '=', True),
+                ])
+        cls._get_triggers_cache.set(key, map(int, triggers))
+        return triggers
+
+    @staticmethod
+    def eval(trigger, record):
         """
         Evaluate the condition of trigger
-
-        :param trigger: a BrowseRecord of ir.trigger
-        :param record: a BrowseRecord of the tested model
-        :return: a boolean
         """
         env = {}
         env['current_date'] = datetime.datetime.today()
@@ -147,18 +154,16 @@
         env['self'] = record
         return bool(safe_eval(trigger.condition, env))
 
-    def trigger_action(self, ids, trigger_id):
+    @classmethod
+    def trigger_action(cls, records, trigger):
         """
-        Trigger the action define on trigger_id for the ids
-
-        :param ids: the list of record ids triggered
-        :param trigger_id: the trigger id
+        Trigger the action define on trigger for the records
         """
         pool = Pool()
-        trigger_log_obj = pool.get('ir.trigger.log')
-        trigger = self.browse(trigger_id)
-        model_obj = pool.get(trigger.action_model.model)
+        TriggerLog = pool.get('ir.trigger.log')
+        Model = pool.get(trigger.action_model.model)
         cursor = Transaction().cursor
+        ids = map(int, records)
 
         # Filter on limit_number
         if trigger.limit_number:
@@ -169,7 +174,7 @@
                 cursor.execute('SELECT "record_id", COUNT(1) FROM "%s" '
                         'WHERE %s AND "trigger" = %%s '
                         'GROUP BY "record_id"'
-                        % (trigger_log_obj._table, red_sql),
+                        % (TriggerLog._table, red_sql),
                         red_ids + [trigger.id])
                 number = dict(cursor.fetchall())
                 for record_id in sub_ids:
@@ -190,7 +195,7 @@
                         'FROM "%s" '
                         'WHERE %s AND "trigger" = %%s '
                         'GROUP BY "record_id"'
-                        % (trigger_log_obj._table, red_sql),
+                        % (TriggerLog._table, red_sql),
                         red_ids + [trigger.id])
                 delay = dict(cursor.fetchall())
                 for record_id in sub_ids:
@@ -214,66 +219,66 @@
                         new_ids.append(record_id)
             ids = new_ids
 
-        if ids:
-            getattr(model_obj, trigger.action_function)(ids, trigger_id)
+        records = Model.browse(ids)
+        if records:
+            getattr(Model, trigger.action_function)(records, trigger)
         if trigger.limit_number or trigger.minimum_delay:
-            for record_id in ids:
-                trigger_log_obj.create({
+            for record in records:
+                TriggerLog.create({
                     'trigger': trigger.id,
-                    'record_id': record_id,
+                    'record_id': record.id,
                     })
 
-    def trigger_time(self):
+    @classmethod
+    def trigger_time(cls):
         '''
         Trigger time actions
         '''
         pool = Pool()
-        trigger_ids = self.search([
-            ('on_time', '=', True),
-            ])
-        for trigger in self.browse(trigger_ids):
-            model_obj = pool.get(trigger.model.model)
-            triggered_ids = []
+        triggers = cls.search([
+                ('on_time', '=', True),
+                ])
+        for trigger in triggers:
+            Model = pool.get(trigger.model.model)
+            triggered = []
             # TODO add a domain
-            record_ids = model_obj.search([])
-            for record in model_obj.browse(record_ids):
-                if self.eval(trigger, record):
-                    triggered_ids.append(record.id)
-            if triggered_ids:
-                self.trigger_action(triggered_ids, trigger.id)
+            records = Model.search([])
+            for record in records:
+                if cls.eval(trigger, record):
+                    triggered.append(record)
+            if triggered:
+                cls.trigger_action(triggered, trigger)
 
-    def create(self, values):
-        res = super(Trigger, self).create(values)
+    @classmethod
+    def create(cls, values):
+        res = super(Trigger, cls).create(values)
         # Restart the cache on the get_triggers method of ir.trigger
-        self.get_triggers.reset()
+        cls._get_triggers_cache.clear()
         return res
 
-    def write(self, ids, values):
-        res = super(Trigger, self).write(ids, values)
+    @classmethod
+    def write(cls, ids, values):
+        res = super(Trigger, cls).write(ids, values)
         # Restart the cache on the get_triggers method of ir.trigger
-        self.get_triggers.reset()
+        cls._get_triggers_cache.clear()
         return res
 
-    def delete(self, ids):
-        res = super(Trigger, self).delete(ids)
+    @classmethod
+    def delete(cls, records):
+        super(Trigger, cls).delete(records)
         # Restart the cache on the get_triggers method of ir.trigger
-        self.get_triggers.reset()
-        return res
-
-Trigger()
+        cls._get_triggers_cache.clear()
 
 
 class TriggerLog(ModelSQL):
     'Trigger Log'
-    _name = 'ir.trigger.log'
-    _description = __doc__
+    __name__ = 'ir.trigger.log'
     trigger = fields.Many2One('ir.trigger', 'Trigger', required=True)
     record_id = fields.Integer('Record ID', required=True)
 
-    def init(self, module_name):
-        super(TriggerLog, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(TriggerLog, cls).__register__(module_name)
 
-        table = TableHandler(Transaction().cursor, self, module_name)
+        table = TableHandler(Transaction().cursor, cls, module_name)
         table.index_action(['trigger', 'record_id'], 'add')
-
-TriggerLog()
diff -r f6ef988fda71 trytond/ir/ui/__init__.py
--- a/trytond/ir/ui/__init__.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/ir/ui/__init__.py	Sun Sep 23 17:30:03 2012 +0200
@@ -1,5 +1,2 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
-from .menu import *
-from .view import *
-from .icon import *
diff -r f6ef988fda71 trytond/ir/ui/icon.py
--- a/trytond/ir/ui/icon.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/ir/ui/icon.py	Sun Sep 23 17:30:03 2012 +0200
@@ -6,12 +6,16 @@
 from trytond.model import ModelView, ModelSQL, fields
 from trytond.tools import file_open
 from trytond.transaction import Transaction
+from trytond.rpc import RPC
+
+__all__ = [
+    'Icon',
+    ]
 
 
 class Icon(ModelSQL, ModelView):
     'Icon'
-    _name = 'ir.ui.icon'
-    _description = __doc__
+    __name__ = 'ir.ui.icon'
 
     name = fields.Char('Name', required=True, select=True)
     module = fields.Char('Module', readonly=True, required=True)
@@ -19,33 +23,32 @@
     icon = fields.Function(fields.Char('Icon', depends=['path']), 'get_icon')
     sequence = fields.Integer('Sequence', required=True)
 
-    def __init__(self):
-        super(Icon, self).__init__()
-        self._order.insert(0, ('sequence', 'ASC'))
-        self._rpc.update({
-            'list_icons': False,
-        })
+    @classmethod
+    def __setup__(cls):
+        super(Icon, cls).__setup__()
+        cls._order.insert(0, ('sequence', 'ASC'))
+        cls.__rpc__.update({
+                'list_icons': RPC(),
+                })
 
-    def default_module(self):
+    @staticmethod
+    def default_module():
         return Transaction().context.get('module') or ''
 
-    def default_sequence(self):
+    @staticmethod
+    def default_sequence():
         return 10
 
-    def list_icons(self):
+    @classmethod
+    def list_icons(cls):
         icons = {}
-        for icon in self.browse(self.search([],
+        for icon in cls.browse(cls.search([],
                 order=[('sequence', 'ASC'), ('id', 'ASC')])):
             if icon.name not in icons:
                 icons[icon.name] = icon.id
         return sorted((icon_id, name) for name, icon_id in icons.iteritems())
 
-    def get_icon(self, ids, name):
-        result = {}
-        for icon in self.browse(ids):
-            path = os.path.join(icon.module, icon.path.replace('/', os.sep))
-            with file_open(path, subdir='modules') as fp:
-                result[icon.id] = fp.read()
-        return result
-
-Icon()
+    def get_icon(self, name):
+        path = os.path.join(self.module, self.path.replace('/', os.sep))
+        with file_open(path, subdir='modules') as fp:
+            return fp.read()
diff -r f6ef988fda71 trytond/ir/ui/menu.py
--- a/trytond/ir/ui/menu.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/ir/ui/menu.py	Sun Sep 23 17:30:03 2012 +0200
@@ -4,6 +4,10 @@
 from trytond.transaction import Transaction
 from trytond.pool import Pool
 
+__all__ = [
+    'UIMenu',
+    ]
+
 
 def one_in(i, j):
     """Check the presence of an element of setA in setB
@@ -64,8 +68,8 @@
 
 class UIMenu(ModelSQL, ModelView):
     "UI menu"
-    _name = 'ir.ui.menu'
-    _description = __doc__
+    __name__ = 'ir.ui.menu'
+
     name = fields.Char('Menu', required=True, translate=True)
     sequence = fields.Integer('Sequence', required=True)
     childs = fields.One2Many('ir.ui.menu', 'parent', 'Children')
@@ -86,56 +90,51 @@
                 ]), 'get_action', setter='set_action')
     active = fields.Boolean('Active')
 
-    def __init__(self):
-        super(UIMenu, self).__init__()
-        self._order.insert(0, ('sequence', 'ASC'))
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(UIMenu, cls).__setup__()
+        cls._order.insert(0, ('sequence', 'ASC'))
+        cls._constraints += [
             ('check_recursion', 'recursive_menu'),
             ('check_name', 'wrong_name'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'recursive_menu': 'You can not create recursive menu!',
             'wrong_name': 'You can not use "%s" in name field!' % SEPARATOR,
         })
 
-    def default_icon(self):
+    @staticmethod
+    def default_icon():
         return 'tryton-open'
 
-    def default_sequence(self):
+    @staticmethod
+    def default_sequence():
         return 10
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def list_icons(self):
+    @staticmethod
+    def list_icons():
         pool = Pool()
-        icon_obj = pool.get('ir.ui.icon')
+        Icon = pool.get('ir.ui.icon')
         return sorted(CLIENT_ICONS
-            + [(name, name) for _, name in icon_obj.list_icons()])
+            + [(name, name) for _, name in Icon.list_icons()])
 
-    def check_name(self, ids):
-        for menu in self.browse(ids):
-            if SEPARATOR in menu.name:
-                return False
+    def check_name(self):
+        if SEPARATOR in self.name:
+            return False
         return True
 
-    def get_rec_name(self, ids, name):
-        if not ids:
-            return {}
-        res = {}
+    def get_rec_name(self, name):
+        if self.parent:
+            return self.parent.get_rec_name(name) + SEPARATOR + self.name
+        else:
+            return self.name
 
-        def _name(menu):
-            if menu.id in res:
-                return res[menu.id]
-            elif menu.parent:
-                return _name(menu.parent) + SEPARATOR + menu.name
-            else:
-                return menu.name
-        for menu in self.browse(ids):
-            res[menu.id] = _name(menu)
-        return res
-
-    def search_rec_name(self, name, clause):
+    @classmethod
+    def search_rec_name(cls, name, clause):
         if isinstance(clause[2], basestring):
             values = clause[2].split(SEPARATOR)
             values.reverse()
@@ -144,85 +143,81 @@
             for name in values:
                 domain.append((field, clause[1], name))
                 field = 'parent.' + field
-            ids = self.search(domain, order=[])
+            ids = cls.search(domain, order=[])
             return [('id', 'in', ids)]
         #TODO Handle list
         return [('name',) + tuple(clause[1:])]
 
-    def search(self, domain, offset=0, limit=None, order=None, count=False,
+    @classmethod
+    def search(cls, domain, offset=0, limit=None, order=None, count=False,
             query_string=False):
-        res = super(UIMenu, self).search(domain, offset=offset, limit=limit,
+        menus = super(UIMenu, cls).search(domain, offset=offset, limit=limit,
                 order=order, count=False, query_string=query_string)
         if query_string:
-            return res
+            return menus
 
-        if res:
-            menus = self.browse(res)
+        if menus:
             parent_ids = [x.parent.id for x in menus if x.parent]
-            parent_ids = self.search([
-                ('id', 'in', parent_ids),
-                ])
-            res = [x.id for x in menus
-                    if (x.parent.id in parent_ids) or not x.parent]
+            parents = cls.search([
+                    ('id', 'in', parent_ids),
+                    ])
+            menus = [x for x in menus
+                if (x.parent and x.parent in parents) or not x.parent]
 
         if count:
-            return len(res)
-        return res
+            return len(menus)
+        return menus
 
-    def get_action(self, ids, name):
+    @classmethod
+    def get_action(cls, menus, name):
         pool = Pool()
-        action_keyword_obj = pool.get('ir.action.keyword')
-        res = {}
-        for menu_id in ids:
-            res[menu_id] = None
+        ActionKeyword = pool.get('ir.action.keyword')
+        actions = dict((m.id, None) for m in menus)
         with Transaction().set_context(active_test=False):
-            action_keyword_ids = action_keyword_obj.search([
-                ('keyword', '=', 'tree_open'),
-                ('model', 'in', [self._name + ',' + str(x) for x in ids]),
-                ])
-        for action_keyword in action_keyword_obj.browse(action_keyword_ids):
-            model_id = int(
-                action_keyword.model.split(',')[1].split(',')[0].strip('('))
-            action_obj = pool.get(action_keyword.action.type)
+            action_keywords = ActionKeyword.search([
+                    ('keyword', '=', 'tree_open'),
+                    ('model', 'in', [str(m) for m in menus]),
+                    ])
+        for action_keyword in action_keywords:
+            model = action_keyword.model
+            Action = pool.get(action_keyword.action.type)
             with Transaction().set_context(active_test=False):
-                action_id = action_obj.search([
-                    ('action', '=', action_keyword.action.id),
-                    ])
-            if action_id:
-                action_id = action_id[0]
+                factions = Action.search([
+                        ('action', '=', action_keyword.action.id),
+                        ], limit=1)
+            if factions:
+                action, = factions
             else:
-                action_id = 0
-            res[model_id] = action_keyword.action.type + ',' + str(action_id)
-        return res
+                action = '%s,0' % action_keyword.action.type
+            actions[model.id] = str(action)
+        return actions
 
-    def set_action(self, ids, name, value):
+    @classmethod
+    def set_action(cls, menus, name, value):
         if not value:
             return
         pool = Pool()
-        action_keyword_obj = pool.get('ir.action.keyword')
-        action_keyword_ids = []
+        ActionKeyword = pool.get('ir.action.keyword')
+        action_keywords = []
         cursor = Transaction().cursor
-        for i in range(0, len(ids), cursor.IN_MAX):
-            sub_ids = ids[i:i + cursor.IN_MAX]
-            action_keyword_ids += action_keyword_obj.search([
+        for i in range(0, len(menus), cursor.IN_MAX):
+            sub_menus = menus[i:i + cursor.IN_MAX]
+            action_keywords += ActionKeyword.search([
                 ('keyword', '=', 'tree_open'),
-                ('model', 'in', [self._name + ',' + str(menu_id)
-                    for menu_id in sub_ids]),
+                ('model', 'in', [str(menu) for menu in sub_menus]),
                 ])
-        if action_keyword_ids:
+        if action_keywords:
             with Transaction().set_context(_timestamp=False):
-                action_keyword_obj.delete(action_keyword_ids)
+                ActionKeyword.delete(action_keywords)
         action_type, action_id = value.split(',')
         if not int(action_id):
             return
-        action_obj = pool.get(action_type)
-        action = action_obj.browse(int(action_id))
-        for menu_id in ids:
+        Action = pool.get(action_type)
+        action = Action(int(action_id))
+        for menu in menus:
             with Transaction().set_context(_timestamp=False):
-                action_keyword_obj.create({
+                ActionKeyword.create({
                     'keyword': 'tree_open',
-                    'model': self._name + ',' + str(menu_id),
+                    'model': str(menu),
                     'action': action.action.id,
                     })
-
-UIMenu()
diff -r f6ef988fda71 trytond/ir/ui/view.py
--- a/trytond/ir/ui/view.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/ir/ui/view.py	Sun Sep 23 17:30:03 2012 +0200
@@ -9,15 +9,19 @@
 from trytond.pyson import CONTEXT, Eval
 from trytond.tools import safe_eval
 from trytond.transaction import Transaction
-from trytond.wizard import Wizard, StateView, StateAction, StateTransition, \
-    Button
+from trytond.wizard import Wizard, StateView, StateAction, Button
 from trytond.pool import Pool
+from trytond.rpc import RPC
+
+__all__ = [
+    'View', 'ShowViewStart', 'ShowView', 'ViewShortcut', 'ShowView',
+    'OpenShortcut', 'ViewTreeWidth', 'ViewTreeExpandedState',
+    ]
 
 
 class View(ModelSQL, ModelView):
     "View"
-    _name = 'ir.ui.view'
-    _description = __doc__
+    __name__ = 'ir.ui.view'
     _rec_name = 'model'
     model = fields.Char('Model', select=True, states={
             'required': Eval('type').in_([None, 'tree', 'form', 'graph']),
@@ -41,22 +45,24 @@
             'invisible': ~Eval('inherit'),
             }, depends=['inherit'])
 
-    def __init__(self):
-        super(View, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(View, cls).__setup__()
+        cls._constraints += [
             ('check_xml', 'invalid_xml'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'invalid_xml': 'Invalid XML for View!',
         })
-        self._order.insert(0, ('priority', 'ASC'))
-        self._buttons.update({
+        cls._order.insert(0, ('priority', 'ASC'))
+        cls._buttons.update({
                 'show': {},
                 })
 
-    def init(self, module_name):
-        super(View, self).init(module_name)
-        table = TableHandler(Transaction().cursor, self, module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(View, cls).__register__(module_name)
+        table = TableHandler(Transaction().cursor, cls, module_name)
 
         # Migration from 1.0 arch no more required
         table.not_null_action('arch', action='remove')
@@ -64,25 +70,29 @@
         # Migration from 2.4 model no more required
         table.not_null_action('model', action='remove')
 
-    def default_arch(self):
+    @staticmethod
+    def default_arch():
         return '<?xml version="1.0"?>'
 
-    def default_priority(self):
+    @staticmethod
+    def default_priority():
         return 16
 
-    def default_module(self):
+    @staticmethod
+    def default_module():
         return Transaction().context.get('module') or ''
 
+    @classmethod
     @ModelView.button_action('ir.act_view_show')
-    def show(self, ids):
+    def show(cls, views):
         pass
 
-    def check_xml(self, ids):
+    @classmethod
+    def check_xml(cls, views):
         "Check XML"
         pool = Pool()
-        translation_obj = pool.get('ir.translation')
+        Translation = pool.get('ir.translation')
         cursor = Transaction().cursor
-        views = self.browse(ids)
         for view in views:
             cursor.execute('SELECT id, name, src FROM ir_translation ' \
                     'WHERE lang = %s ' \
@@ -144,20 +154,20 @@
             if not encode(root_element):
                 return False
 
-            strings = self._translate_view(root_element)
+            strings = cls._translate_view(root_element)
             with Transaction().set_user(0):
-                view_ids = self.search([
+                views2 = cls.search([
                     ('model', '=', view.model),
                     ('id', '!=', view.id),
                     ('module', '=', view.module),
                     ])
-                for view2 in self.browse(view_ids):
+                for view2 in views2:
                     xml2 = view2.arch.strip()
                     if not xml2:
                         continue
                     tree2 = etree.fromstring(xml2)
                     root2_element = tree2.getroottree().getroot()
-                    strings += self._translate_view(root2_element)
+                    strings += cls._translate_view(root2_element)
             if not strings:
                 continue
             for string in set(strings):
@@ -165,7 +175,7 @@
                 if string in trans_views:
                     del trans_views[string]
                     continue
-                string_md5 = translation_obj.get_src_md5(string)
+                string_md5 = Translation.get_src_md5(string)
                 for string_trans in trans_views:
                     if string_trans in strings:
                         continue
@@ -203,40 +213,27 @@
                         (view.model, 'view', view.module) + tuple(strings))
         return True
 
-    def delete(self, ids):
-        res = super(View, self).delete(ids)
+    @classmethod
+    def delete(cls, views):
+        super(View, cls).delete(views)
         # Restart the cache
-        pool = Pool()
-        for _, model in pool.iterobject():
-            try:
-                model.fields_view_get.reset()
-            except Exception:
-                pass
-        return res
+        ModelView._fields_view_get_cache.clear()
 
-    def create(self, vals):
-        res = super(View, self).create(vals)
+    @classmethod
+    def create(cls, vals):
+        view = super(View, cls).create(vals)
         # Restart the cache
-        pool = Pool()
-        for _, model in pool.iterobject():
-            try:
-                model.fields_view_get.reset()
-            except Exception:
-                pass
-        return res
+        ModelView._fields_view_get_cache.clear()
+        return view
 
-    def write(self, ids, vals):
-        res = super(View, self).write(ids, vals)
+    @classmethod
+    def write(cls, views, vals):
+        super(View, cls).write(views, vals)
         # Restart the cache
-        pool = Pool()
-        for _, model in pool.iterobject():
-            try:
-                model.fields_view_get.reset()
-            except Exception:
-                pass
-        return res
+        ModelView._fields_view_get_cache.clear()
 
-    def _translate_view(self, element):
+    @classmethod
+    def _translate_view(cls, element):
         strings = []
         for attr in ('string', 'sum', 'confirm', 'help'):
             if element.get(attr):
@@ -244,22 +241,18 @@
                 if string:
                     strings.append(string)
         for child in element:
-            strings.extend(self._translate_view(child))
+            strings.extend(cls._translate_view(child))
         return strings
 
-View()
-
 
 class ShowViewStart(ModelView):
     'Show view'
-    _name = 'ir.ui.view.show.start'
-
-ShowViewStart()
+    __name__ = 'ir.ui.view.show.start'
 
 
 class ShowView(Wizard):
     'Show view'
-    _name = 'ir.ui.view.show'
+    __name__ = 'ir.ui.view.show'
 
     class ShowStateView(StateView):
 
@@ -268,28 +261,25 @@
 
         def get_view(self):
             pool = Pool()
-            view_obj = pool.get('ir.ui.view')
-            view_id = Transaction().context.get('active_id')
-            view = view_obj.browse(view_id)
-            model_obj = pool.get(view.model)
+            View = pool.get('ir.ui.view')
+            view = View(Transaction().context.get('active_id'))
+            Model = pool.get(view.model)
             if view.type != 'form':
-                return model_obj.fields_view_get(view_type='form')
-            return model_obj.fields_view_get(view_id=view_id)
+                return Model.fields_view_get(view_type='form')
+            return Model.fields_view_get(view_id=view.id)
 
-        def get_defaults(self, wizard, session, state_name, fields):
+        def get_defaults(self, wizard, state_name, fields):
             return {}
 
     start = ShowStateView('ir.ui.view.show.start', [
             Button('Close', 'end', 'tryton-close', default=True),
             ])
 
-ShowView()
-
 
 class ViewShortcut(ModelSQL, ModelView):
     "View shortcut"
-    _name = 'ir.ui.view_sc'
-    _description = __doc__
+    __name__ = 'ir.ui.view_sc'
+
     name = fields.Char('Shortcut Name', required=True)
     res_id = fields.Integer('Resource Ref.', required=True)
     sequence = fields.Integer('Sequence', required=True)
@@ -297,34 +287,37 @@
        ondelete='CASCADE')
     resource = fields.Char('Resource Name', required=True)
 
-    def __init__(self):
-        super(ViewShortcut, self).__init__()
-        self._rpc.update({'get_sc': False})
-        self._order.insert(0, ('sequence', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(ViewShortcut, cls).__setup__()
+        cls.__rpc__.update({
+                'get_sc': RPC(),
+                })
+        cls._order.insert(0, ('sequence', 'ASC'))
 
-    def get_sc(self, user_id, model='ir.ui.menu'):
+    @classmethod
+    def get_sc(cls, user_id, model='ir.ui.menu'):
         "Provide user's shortcuts"
         result = []
-        ids = self.search([
+        shortcuts = cls.search([
                 ('user_id', '=', user_id),
                 ('resource', '=', model),
                 ])
-        for shorcut in self.browse(ids):
+        for shorcut in shortcuts:
             result.append({
                     'res_id': shorcut.res_id,
                     'name': shorcut.name,
                     })
         return result
 
-    def default_resource(self):
+    @staticmethod
+    def default_resource():
         return 'ir.ui.menu'
 
-ViewShortcut()
-
 
 class OpenShortcut(Wizard):
     'Open a shortcut'
-    _name = 'ir.ui.view_sc.open'
+    __name__ = 'ir.ui.view_sc.open'
 
     start_state = 'open_'
 
@@ -337,41 +330,40 @@
 
     open_ = OpenStateAction()
 
-    def transition_open_(self, session):
+    @staticmethod
+    def transition_open_():
         return 'end'
 
-    def do_open_(self, session, action):
+    @staticmethod
+    def do_open_(action):
         pool = Pool()
-        view_sc_obj = pool.get('ir.ui.view_sc')
-        action_keyword_obj = pool.get('ir.action.keyword')
-        action_obj = pool.get('ir.action')
+        ViewSC = pool.get('ir.ui.view_sc')
+        ActionKeyword = pool.get('ir.action.keyword')
+        Action = pool.get('ir.action')
 
-        view_sc = view_sc_obj.browse(Transaction().context.get('active_id'))
+        view_sc = ViewSC(Transaction().context.get('active_id'))
         models = (
                 '%s,%d' % (view_sc.resource, view_sc.res_id),
                 '%s,0' % (view_sc.resource),
                 )
-        action_keyword_ids = None
+        action_keywords = None
         for model in models:
-            action_keyword_ids = action_keyword_obj.search([
-                ('keyword', '=', 'tree_open'),
-                ('model', '=', model),
-                ])
-            if action_keyword_ids:
+            action_keywords = ActionKeyword.search([
+                    ('keyword', '=', 'tree_open'),
+                    ('model', '=', model),
+                    ])
+            if action_keywords:
                 break
-        if not action_keyword_ids:
+        if not action_keywords:
             return {}, {}
-        action_keyword = action_keyword_obj.browse(action_keyword_ids[0])
-        return action_obj.get_action_values(action_keyword.action.type,
-            action_keyword.action.id), {}
-
-OpenShortcut()
+        action_keyword = action_keywords[0]
+        return Action.get_action_values(action_keyword.action.type,
+            [action_keyword.action.id])[0], {}
 
 
 class ViewTreeWidth(ModelSQL, ModelView):
     "View Tree Width"
-    _name = 'ir.ui.view_tree_width'
-    _description = __doc__
+    __name__ = 'ir.ui.view_tree_width'
     _rec_name = 'model'
     model = fields.Char('Model', required=True, select=True)
     field = fields.Char('Field', required=True, select=True)
@@ -379,85 +371,54 @@
             ondelete='CASCADE', select=True)
     width = fields.Integer('Width', required=True)
 
-    def __init__(self):
-        super(ViewTreeWidth, self).__init__()
-        self._rpc.update({
-            'set_width': True,
-        })
+    @classmethod
+    def __setup__(cls):
+        super(ViewTreeWidth, cls).__setup__()
+        cls.__rpc__.update({
+                'set_width': RPC(readonly=False),
+                })
 
-    def delete(self, ids):
-        pool = Pool()
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        views = self.browse(ids)
-        for view in views:
-            # Restart the cache
-            try:
-                pool.get(view.model).fields_view_get.reset()
-            except Exception:
-                pass
-        res = super(ViewTreeWidth, self).delete(ids)
+    @classmethod
+    def delete(cls, records):
+        ModelView._fields_view_get_cache.clear()
+        super(ViewTreeWidth, cls).delete(records)
+
+    @classmethod
+    def create(cls, vals):
+        res = super(ViewTreeWidth, cls).create(vals)
+        ModelView._fields_view_get_cache.clear()
         return res
 
-    def create(self, vals):
-        pool = Pool()
-        res = super(ViewTreeWidth, self).create(vals)
-        if 'model' in vals:
-            model = vals['model']
-            # Restart the cache
-            try:
-                pool.get(model).fields_view_get.reset()
-            except Exception:
-                pass
-        return res
+    @classmethod
+    def write(cls, records, vals):
+        super(ViewTreeWidth, cls).write(records, vals)
+        ModelView._fields_view_get_cache.clear()
 
-    def write(self, ids, vals):
-        pool = Pool()
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        views = self.browse(ids)
-        for view in views:
-            # Restart the cache
-            try:
-                pool.get(view.model).fields_view_get.reset()
-            except Exception:
-                pass
-        res = super(ViewTreeWidth, self).write(ids, vals)
-        views = self.browse(ids)
-        for view in views:
-            # Restart the cache
-            try:
-                pool.get(view.model).fields_view_get.reset()
-            except Exception:
-                pass
-        return res
-
-    def set_width(self, model, fields):
+    @classmethod
+    def set_width(cls, model, fields):
         '''
         Set width for the current user on the model.
         fields is a dictionary with key: field name and value: width.
         '''
-        ids = self.search([
+        records = cls.search([
             ('user', '=', Transaction().user),
             ('model', '=', model),
             ('field', 'in', fields.keys()),
             ])
-        self.delete(ids)
+        cls.delete(records)
 
         for field in fields.keys():
-            self.create({
+            cls.create({
                 'model': model,
                 'field': field,
                 'user': Transaction().user,
                 'width': fields[field],
                 })
 
-ViewTreeWidth()
-
 
 class ViewTreeExpandedState(ModelSQL, ModelView):
-    _name = 'ir.ui.view_tree_expanded_state'
-
+    'View Tree Expanded State'
+    __name__ = 'ir.ui.view_tree_expanded_state'
     _rec_name = 'model'
     model = fields.Char('Model', required=True)
     domain = fields.Char('Domain', required=True)
@@ -466,34 +427,38 @@
     child_name = fields.Char('Child Name')
     nodes = fields.Text('Expanded Nodes')
 
-    def __init__(self):
-        super(ViewTreeExpandedState, self).__init__()
-        self._rpc.update({
-                'set_expanded': True,
-                'get_expanded': True,
+    @classmethod
+    def __setup__(cls):
+        super(ViewTreeExpandedState, cls).__setup__()
+        cls.__rpc__.update({
+                'set_expanded': RPC(readonly=False),
+                'get_expanded': RPC(),
                 })
 
-    def init(self, module_name):
-        super(ViewTreeExpandedState, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(ViewTreeExpandedState, cls).__register__(module_name)
 
         cursor = Transaction().cursor
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.index_action(['model', 'domain', 'user', 'child_name'], 'add')
 
-    def default_nodes(self):
+    @staticmethod
+    def default_nodes():
         return '[]'
 
-    def set_expanded(self, model, domain, child_name, nodes):
+    @classmethod
+    def set_expanded(cls, model, domain, child_name, nodes):
         current_user = Transaction().user
         with Transaction().set_user(0):
-            ids = self.search([
+            records = cls.search([
                     ('user', '=', current_user),
                     ('model', '=', model),
                     ('domain', '=', domain),
                     ('child_name', '=', child_name),
                     ])
-            self.delete(ids)
-            self.create({
+            cls.delete(records)
+            cls.create({
                     'user': current_user,
                     'model': model,
                     'domain': domain,
@@ -501,11 +466,12 @@
                     'nodes': nodes,
                     })
 
-    def get_expanded(self, model, domain, child_name):
+    @classmethod
+    def get_expanded(cls, model, domain, child_name):
         current_user = Transaction().user
         with Transaction().set_user(0):
             try:
-                expanded_info, = self.search([
+                expanded_info, = cls.search([
                         ('user', '=', current_user),
                         ('model', '=', model),
                         ('domain', '=', domain),
@@ -514,7 +480,4 @@
                     limit=1)
             except ValueError:
                 return '[]'
-            return self.browse(expanded_info).nodes
-
-
-ViewTreeExpandedState()
+            return cls(expanded_info).nodes
diff -r f6ef988fda71 trytond/model/__init__.py
--- a/trytond/model/__init__.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/model/__init__.py	Sun Sep 23 17:30:03 2012 +0200
@@ -1,8 +1,11 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
-from .model import *
-from .modelview import *
-from .modelstorage import *
-from .modelsingleton import *
-from .modelsql import *
+from .model import Model
+from .modelview import ModelView
+from .modelstorage import ModelStorage
+from .modelsingleton import ModelSingleton
+from .modelsql import ModelSQL
 from .workflow import Workflow
+
+__all__ = ['Model', 'ModelView', 'ModelStorage', 'ModelSingleton', 'ModelSQL',
+    'Workflow']
diff -r f6ef988fda71 trytond/model/browse.py
--- a/trytond/model/browse.py	Fri Sep 21 16:04:02 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,335 +0,0 @@
-#This file is part of Tryton.  The COPYRIGHT file at the top level of
-#this repository contains the full copyright notices and license terms.
-import contextlib
-from itertools import islice, ifilter, ifilterfalse, chain
-from trytond.config import CONFIG
-from trytond.model import fields
-from trytond.transaction import Transaction
-from trytond.pool import Pool
-from trytond.cache import LRUDict
-from trytond.const import MODEL_CACHE_SIZE, RECORD_CACHE_SIZE, \
-        BROWSE_FIELD_TRESHOLD
-
-
-class BrowseRecordList(list):
-    '''
-    A list of BrowseRecord
-    '''
-    #TODO: execute an object method on BrowseRecordList
-
-    def __init__(self, lst):
-        super(BrowseRecordList, self).__init__(lst)
-
-    def get_eval(self):
-        return [record.id for record in self]
-
-
-class BrowseRecordNull(object):
-    '''
-    An object that represents an empty record.
-    '''
-
-    def __init__(self):
-        self.id = None
-
-    def __getitem__(self, name):
-        return None
-
-    def __int__(self):
-        return None
-
-    def __str__(self):
-        return None
-
-    def __nonzero__(self):
-        return False
-
-
-class BrowseRecord(object):
-    '''
-    An object that represents record defined by a ORM object.
-    '''
-
-    def __init__(self, record_id, model, ids, local_cache):
-        assert isinstance(ids, list)
-        if ids:
-            assert record_id in ids
-        self._cursor = Transaction().cursor
-        self._user = Transaction().user
-        self.id = record_id
-        self._model = model
-        self._model_name = self._model._name
-        self._context = Transaction().context
-        self._pool = Pool()
-        self._ids = ids
-
-        cache = self._cursor.get_cache(self._context)
-        if model._name not in cache:
-            cache[model._name] = LRUDict(RECORD_CACHE_SIZE)
-        self._data = cache[model._name]
-        self._cache = cache
-        assert isinstance(local_cache, LRUDict)
-        self._local_data = local_cache
-
-    def __getitem__(self, name):
-        # Access to LRUDict must be atomic
-        result = self._local_data.get(self.id, {}).get(name)
-        if (self.id in self._local_data
-                and name in self._local_data[self.id]):
-            return result
-        result = self._data.get(self.id, {}).get(name)
-        if self.id in self._data and name in self._data[self.id]:
-            return result
-
-        # build the list of fields we will fetch
-        # fetch the definition of the field which was asked for
-        if name in self._model._columns:
-            col = self._model._columns[name]
-        elif name in self._model._inherit_fields:
-            col = self._model._inherit_fields[name][2]
-        elif hasattr(self._model, name):
-            return getattr(self._model, name)
-        else:
-            raise Exception('Error', 'Programming error: field "%s" ' \
-                    'does not exist in model "%s"!' \
-                    % (name, self._model._name))
-
-        ffields = {}
-        if col.loading == 'eager':
-            field_access_obj = self._pool.get('ir.model.field.access')
-            fread_accesses = {}
-            for inherit_name in self._model._inherits:
-                inherit_obj = self._pool.get(inherit_name)
-                fread_accesses.update(field_access_obj.check(inherit_name,
-                    inherit_obj._columns.keys(), 'read', access=True))
-            fread_accesses.update(field_access_obj.check(self._model._name,
-                self._model._columns.keys(), 'read', access=True))
-            to_remove = set(x for x, y in fread_accesses.iteritems()
-                    if not y and x != name)
-
-            threshold = BROWSE_FIELD_TRESHOLD
-            inherit_threshold = threshold - len(self._model._columns)
-
-            def not_cached(item):
-                fname, field = item
-                return (fname not in self._data.get(self.id, {})
-                    and fname not in self._local_data.get(self.id, {}))
-
-            def to_load(item):
-                fname, field = item
-                return (field.loading == 'eager'
-                    and fname not in to_remove)
-
-            def overrided(item):
-                fname, field = item
-                return fname in self._model._columns
-
-            if inherit_threshold > 0:
-                ifields = ((fname, field)
-                    for fname, (_, _, field) in
-                    self._model._inherit_fields.iteritems())
-                ifields = ifilterfalse(overrided,
-                    ifilter(to_load,
-                        ifilter(not_cached, ifields)))
-                ifields = islice(ifields, 0, inherit_threshold)
-                ffields.update(ifields)
-                threshold -= inherit_threshold
-
-            ifields = ifilter(to_load,
-                ifilter(not_cached,
-                    self._model._columns.iteritems()))
-            ifields = islice(ifields, 0, threshold)
-            ffields.update(ifields)
-
-        ffields[name] = col
-
-        # add datetime_field
-        for field in ffields.values():
-            if hasattr(field, 'datetime_field') and field.datetime_field:
-                if field.datetime_field in self._model._columns:
-                    datetime_field = self._model._columns[field.datetime_field]
-                else:
-                    datetime_field = self._model._inherit_fields[
-                            field.datetime_field][2]
-                ffields[field.datetime_field] = datetime_field
-
-        def filter_(id_):
-            if (id_ in self._local_data
-                    and name in self._local_data[id_]):
-                return False
-            if id_ in self._data and name in self._data[id_]:
-                return False
-            return True
-        index = self._ids.index(self.id)
-        ids = chain(islice(self._ids, index, None),
-            islice(self._ids, 0, max(index - 1, 0)))
-        ids = list(islice(ifilter(filter_, ids), self._cursor.IN_MAX))
-        model2ids = {}
-        model2cache = {}
-        # read the data
-        with contextlib.nested(Transaction().set_cursor(self._cursor),
-                Transaction().set_user(self._user),
-                Transaction().set_context(self._context)):
-            # create browse records for 'remote' models
-            for data in self._model.read(ids, ffields.keys()):
-                for i, j in ffields.iteritems():
-                    model = None
-                    if (hasattr(j, 'model_name') and
-                            j.model_name in
-                            self._pool.object_name_list()):
-                        model = self._pool.get(j.model_name)
-                    elif hasattr(j, 'get_target'):
-                        model = j.get_target()
-                    if model and j._type in ('many2one', 'one2one'):
-                        if (not data[i]
-                                and not (isinstance(data[i], (int, long))
-                                    and not isinstance(data[i],
-                                        type(False)))):
-                            data[i] = BrowseRecordNull()
-                        else:
-                            _datetime = None
-                            if (hasattr(j, 'datetime_field')
-                                    and j.datetime_field):
-                                _datetime = data[j.datetime_field]
-                            with Transaction().set_context(
-                                    _datetime=_datetime):
-                                ids = model2ids.setdefault(model, [])
-                                ids.append(data[i])
-                                local_cache = model2cache.setdefault(model,
-                                    LRUDict(RECORD_CACHE_SIZE))
-                                data[i] = BrowseRecord(data[i], model,
-                                    ids, local_cache)
-                    elif (model and j._type in ('one2many', 'many2many')):
-                        _datetime = None
-                        if hasattr(j, 'datetime_field') and j.datetime_field:
-                            _datetime = data[j.datetime_field]
-                        with Transaction().set_context(
-                                _datetime=_datetime):
-                            ids = model2ids.setdefault(model, [])
-                            ids.extend(data[i])
-                            local_cache = model2cache.setdefault(model,
-                                LRUDict(RECORD_CACHE_SIZE))
-                            data[i] = BrowseRecordList(
-                                BrowseRecord(x, model, ids, local_cache)
-                                for x in data[i])
-                    if (isinstance(j, fields.Function)
-                            or isinstance(data[i], (BrowseRecord,
-                                    BrowseRecordList))):
-                        if data['id'] == self.id and i == name:
-                            result = data[i]
-                        self._local_data.setdefault(data['id'], {})[i] = \
-                            data[i]
-                        del data[i]
-                self._data.setdefault(data['id'], {}).update(data)
-                if data['id'] == self.id and name in data:
-                    result = data[name]
-        return result
-
-    def __getattr__(self, name):
-        # TODO raise an AttributeError exception
-        return self[name]
-
-    def __contains__(self, name):
-        return (name in self._model._columns) \
-                or (name in self._model._inherit_fields) \
-                or hasattr(self._model, name)
-
-    def __hasattr__(self, name):
-        return name in self
-
-    def __int__(self):
-        return self.id
-
-    def __str__(self):
-        return "BrowseRecord(%s, %d)" % (self._model_name, self.id)
-
-    def __eq__(self, other):
-        if not isinstance(other, BrowseRecord):
-            return False
-        return (self._model_name, self.id) == (other._model_name, other.id)
-
-    def __ne__(self, other):
-        if not isinstance(other, BrowseRecord):
-            return True
-        return (self._model_name, self.id) != (other._model_name, other.id)
-
-    # we need to define __unicode__ even though we've already defined __str__
-    # because we have overridden __getattr__
-    def __unicode__(self):
-        return unicode(str(self))
-
-    def __hash__(self):
-        return hash((self._model_name, self.id))
-
-    def __nonzero__(self):
-        return True
-
-    __repr__ = __str__
-
-    def setLang(self, lang):
-        self._context = self._context.copy()
-        prev_lang = self._context.get('language') or CONFIG['language']
-        self._context['language'] = lang
-        for cache in (self._cache, {self._model_name: self._local_data}):
-            language_cache = cache.setdefault('_language_cache', {})
-            for model in cache:
-                if model == '_language_cache':
-                    continue
-                for record_id in cache[model]:
-                    language_cache.setdefault(prev_lang,
-                        LRUDict(MODEL_CACHE_SIZE)).setdefault(model,
-                            LRUDict(RECORD_CACHE_SIZE))[record_id] = \
-                            cache[model][record_id]
-                    if lang in language_cache \
-                            and model in language_cache[lang] \
-                            and record_id in language_cache[lang][model]:
-                        cache[model][record_id] = \
-                                language_cache[lang][model][record_id]
-                    else:
-                        cache[model][record_id] = {}
-
-    def get_eval(self, name):
-        res = self[name]
-        if isinstance(res, BrowseRecord):
-            res = res.id
-        if isinstance(res, BrowseRecordList):
-            res = res.get_eval()
-        if isinstance(res, BrowseRecordNull):
-            res = False
-        return res
-
-
-class EvalEnvironment(dict):
-
-    def __init__(self, record, model):
-        super(EvalEnvironment, self).__init__()
-        self._record = record
-        self._model = model
-        self._pool = Pool()
-
-    def __getitem__(self, item):
-        if item.startswith('_parent_'):
-            field = item[8:]
-            if field in self._model._columns:
-                model_name = self._model._columns[field].model_name
-            else:
-                model_name = self._model._inherit_fields[field][2].model_name
-            model = self._pool.get(model_name)
-            return EvalEnvironment(self._record[field], model)
-        if item in self._model._columns \
-                or item in self._model._inherit_fields:
-            return self._record.get_eval(item)
-        return super(EvalEnvironment, self).__getitem__(item)
-
-    def __getattr__(self, item):
-        return self.__getitem__(item)
-
-    def get(self, item, default=None):
-        try:
-            return self.__getitem__(item)
-        except Exception:
-            pass
-        return super(EvalEnvironment, self).get(item, default)
-
-    def __nonzero__(self):
-        return bool(self._record)
diff -r f6ef988fda71 trytond/model/cacheable.py
--- a/trytond/model/cacheable.py	Fri Sep 21 16:04:02 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-#This file is part of Tryton.  The COPYRIGHT file at the top level of
-#this repository contains the full copyright notices and license terms.
-from threading import Lock
-import time
-from trytond.transaction import Transaction
-from trytond.cache import Cache
-
-
-class Cacheable(object):
-    _name = None
-
-    def __init__(self):
-        super(Cacheable, self).__init__()
-        self._cache = {}
-        self._max_len = 1024
-        self._timeout = 3600
-        self._lock = Lock()
-        self._timestamp = None
-        Cache._cache_instance.append(self)
-
-    def add(self, key, value):
-        dbname = Transaction().cursor.dbname
-        self._lock.acquire()
-        try:
-            self._cache.setdefault(dbname, {})
-
-            lower = None
-            if len(self._cache[dbname]) > self._max_len:
-                mintime = time.time() - self._timeout
-                for key2 in self._cache[dbname].keys():
-                    last_time = self._cache[dbname][key2][1]
-                    if mintime > last_time:
-                        del self._cache[dbname][key2]
-                    else:
-                        if not lower or lower[1] > last_time:
-                            lower = (key2, last_time)
-            if len(self._cache[dbname]) > self._max_len and lower:
-                del self._cache[dbname][lower[0]]
-
-            self._cache[dbname][key] = (value, time.time())
-        finally:
-            self._lock.release()
-
-    def invalidate(self, key):
-        dbname = Transaction().cursor.dbname
-        self._lock.acquire()
-        try:
-            del self._cache[dbname][key]
-        finally:
-            self._lock.release()
-
-    def get(self, key):
-        dbname = Transaction().cursor.dbname
-        try:
-            return self._cache[dbname][key][0]
-        except KeyError:
-            return None
-
-    def clear(self):
-        dbname = Transaction().cursor.dbname
-        self._lock.acquire()
-        try:
-            self._cache.setdefault(dbname, {})
-            self._cache[dbname].clear()
-            Cache.reset(dbname, self._name)
-        finally:
-            self._lock.release()
diff -r f6ef988fda71 trytond/model/fields/field.py
--- a/trytond/model/fields/field.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/model/fields/field.py	Sun Sep 23 17:30:03 2012 +0200
@@ -121,6 +121,7 @@
         assert loading in ('lazy', 'eager'), \
             'loading must be "lazy" or "eager"'
         self.loading = loading
+        self.name = None
 
     def _get_domain(self):
         return self.__domain
@@ -174,3 +175,15 @@
         self.__context = value
 
     context = property(_get_context, _set_context)
+
+    def __get__(self, inst, cls):
+        if inst is None:
+            return self
+        assert self.name is not None
+        return inst.__getattr__(self.name)
+
+    def __set__(self, inst, value):
+        assert self.name is not None
+        if inst._values is None:
+            inst._values = {}
+        inst._values[self.name] = value
diff -r f6ef988fda71 trytond/model/fields/function.py
--- a/trytond/model/fields/function.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/model/fields/function.py	Sun Sep 23 17:30:03 2012 +0200
@@ -39,6 +39,13 @@
         return Function(copy.copy(self._field), self.getter,
                 setter=self.setter, searcher=self.searcher)
 
+    def __deepcopy__(self, memo):
+        return Function(copy.deepcopy(self._field, memo),
+            copy.deepcopy(self.getter, memo),
+            setter=copy.deepcopy(self.setter, memo),
+            searcher=copy.deepcopy(self.searcher, memo),
+            loading=copy.deepcopy(self.loading, memo))
+
     def __getattr__(self, name):
         return getattr(self._field, name)
 
@@ -46,60 +53,52 @@
         return self._field[name]
 
     def __setattr__(self, name, value):
-        if name in ('_field', '_type', 'getter', 'setter', 'searcher'):
-            return object.__setattr__(self, name, value)
-        return setattr(self._field, name, value)
+        if name in ('_field', '_type', 'getter', 'setter', 'searcher', 'name'):
+            object.__setattr__(self, name, value)
+            if name != 'name':
+                return
+        setattr(self._field, name, value)
 
     def search(self, model, name, clause):
         '''
         Call the searcher.
-
-        :param model: The model.
-        :param name: The name of the field.
-        :param clause: The search domain clause. See ModelStorage.search
-        :return: a list of domain clause.
+        Return a list of clauses.
         '''
         if not self.searcher:
             model.raise_user_error('search_function_missing', name)
         return getattr(model, self.searcher)(name, tuple(clause))
 
-    def get(self, ids, model, name, values=None):
+    def get(self, ids, Model, name, values=None):
         '''
         Call the getter.
         If the function has ``names`` in the function definition then
         it will call it with a list of name.
-
-        :param ids: A list of ids.
-        :param model: The model.
-        :param name: The name of the field or a list of name field.
-        :param values:
-        :return: a dictionary with ids as key and values as value or
-            a dictionary with name as key and a dictionary as value if
-            name is a list of field name.
         '''
+        def call(name):
+            method = getattr(Model, self.getter)
+            records = Model.browse(ids)
+            if not hasattr(method, 'im_self') or method.im_self:
+                return method(records, name)
+            else:
+                return dict((r.id, method(r, name)) for r in records)
         if isinstance(name, list):
             names = name
             # Test is the function works with a list of names
-            if 'names' in inspect.getargspec(getattr(model, self.getter))[0]:
-                return getattr(model, self.getter)(ids, names)
-            res = {}
-            for name in names:
-                res[name] = getattr(model, self.getter)(ids, name)
-            return res
+            if 'names' in inspect.getargspec(getattr(Model, self.getter))[0]:
+                return call(names)
+            return dict((name, call(name)) for name in names)
         else:
             # Test is the function works with a list of names
-            if 'names' in inspect.getargspec(getattr(model, self.getter))[0]:
+            if 'names' in inspect.getargspec(getattr(Model, self.getter))[0]:
                 name = [name]
-            return getattr(model, self.getter)(ids, name)
+            return call(name)
 
-    def set(self, ids, model, name, value):
+    def set(self, ids, Model, name, value):
         '''
         Call the setter.
-
-        :param ids: A list of ids.
-        :param model: The model.
-        :param name: The name of the field.
-        :param value: The value to set.
         '''
         if self.setter:
-            getattr(model, self.setter)(ids, name, value)
+            getattr(Model, self.setter)(Model.browse(ids), name, value)
+
+    def __set__(self, inst, value):
+        self._field.__set__(inst, value)
diff -r f6ef988fda71 trytond/model/fields/many2many.py
--- a/trytond/model/fields/many2many.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/model/fields/many2many.py	Sun Sep 23 17:30:03 2012 +0200
@@ -60,12 +60,6 @@
     def get(self, ids, model, name, values=None):
         '''
         Return target records ordered.
-
-        :param ids: a list of ids
-        :param model: a string with the name of the model
-        :param name: a string with the name of the field
-        :param values: a dictionary with the read values
-        :return: a dictionary with ids as key and values as value
         '''
         if values is None:
             values = {}
@@ -80,41 +74,34 @@
         else:
             order = self.order
 
-        relation_obj = Pool().get(self.relation_name)
-        if self.origin in relation_obj._columns:
-            origin_field = relation_obj._columns[self.origin]
+        Relation = Pool().get(self.relation_name)
+        if self.origin in Relation._fields:
+            origin_field = Relation._fields[self.origin]
         else:
-            origin_field = relation_obj._inherit_fields[self.origin][2]
+            origin_field = Relation._inherit_fields[self.origin][2]
 
-        relation_ids = []
+        relations = []
         for i in range(0, len(ids), Transaction().cursor.IN_MAX):
             sub_ids = ids[i:i + Transaction().cursor.IN_MAX]
             if origin_field._type == 'reference':
-                references = ['%s,%s' % (model._name, x) for x in sub_ids]
+                references = ['%s,%s' % (model.__name__, x) for x in sub_ids]
                 clause = [(self.origin, 'in', references)]
             else:
                 clause = [(self.origin, 'in', sub_ids)]
             clause += [(self.target + '.id', '!=', None)]
-            relation_ids.append(relation_obj.search(clause, order=order))
-        relation_ids = list(chain(*relation_ids))
+            relations.append(Relation.search(clause, order=order))
+        relations = list(chain(*relations))
 
-        for relation in relation_obj.browse(relation_ids):
-            if origin_field._type == 'reference':
-                _, origin_id = relation[self.origin].split(',')
-                origin_id = int(origin_id)
-            else:
-                origin_id = relation[self.origin].id
-            res[origin_id].append(relation[self.target].id)
-        return res
+        for relation in relations:
+            origin_id = getattr(relation, self.origin).id
+            res[origin_id].append(getattr(relation, self.target).id)
+        return dict((key, tuple(value)) for key, value in res.iteritems())
 
     def set(self, ids, model, name, values):
         '''
         Set the values.
 
-        :param ids: A list of ids
-        :param model: A string with the name of the model
-        :param name: A string with the name of the field
-        :param values: A list of tuples:
+        values: A list of tuples:
             (``create``, ``{<field name>: value}``),
             (``write``, ``<ids>``, ``{<field name>: value}``),
             (``delete``, ``<ids>``),
@@ -127,44 +114,44 @@
         pool = Pool()
         if not values:
             return
-        relation_obj = pool.get(self.relation_name)
-        target_obj = self.get_target()
+        Relation = pool.get(self.relation_name)
+        Target = self.get_target()
 
-        if self.origin in relation_obj._columns:
-            origin_field = relation_obj._columns[self.origin]
+        if self.origin in Relation._fields:
+            origin_field = Relation._fields[self.origin]
         else:
-            origin_field = relation_obj._inherit_fields[self.origin][2]
+            origin_field = Relation._inherit_fields[self.origin][2]
 
         def search_clause(ids):
             if origin_field._type == 'reference':
-                references = ['%s,%s' % (model._name, x) for x in ids]
+                references = ['%s,%s' % (model.__name__, x) for x in ids]
                 return (self.origin, 'in', references)
             else:
                 return (self.origin, 'in', ids)
 
         def field_value(record_id):
             if origin_field._type == 'reference':
-                return '%s,%s' % (model._name, record_id)
+                return '%s,%s' % (model.__name__, record_id)
             else:
                 return record_id
 
         for act in values:
             if act[0] == 'create':
                 for record_id in ids:
-                    relation_obj.create({
+                    Relation.create({
                             self.origin: field_value(record_id),
-                            self.target: target_obj.create(act[1]),
+                            self.target: Target.create(act[1]).id,
                             })
             elif act[0] == 'write':
-                target_obj.write(act[1], act[2])
+                Target.write(Target.browse(act[1]), act[2])
             elif act[0] == 'delete':
-                target_obj.delete(act[1])
+                Target.delete(Target.browse(act[1]))
             elif act[0] == 'delete_all':
-                relations = relation_obj.browse(relation_obj.search([
-                            search_clause(ids),
-                            ]))
-                target_obj.delete([r[self.target].id for r in relations
-                        if r[self.target].id])
+                relations = Relation.search([
+                        search_clause(ids),
+                        ])
+                Target.delete([getattr(r, self.target) for r in relations
+                        if getattr(r, self.target)])
             elif act[0] == 'unlink':
                 if isinstance(act[1], (int, long)):
                     target_ids = [act[1]]
@@ -172,58 +159,55 @@
                     target_ids = list(act[1])
                 if not target_ids:
                     continue
-                relation_ids = []
+                relations = []
                 for i in range(0, len(target_ids),
                         Transaction().cursor.IN_MAX):
                     sub_ids = target_ids[i:i + Transaction().cursor.IN_MAX]
-                    relation_ids += relation_obj.search([
+                    relations += Relation.search([
                             search_clause(ids),
                             (self.target, 'in', sub_ids),
                             ])
-                relation_obj.delete(relation_ids)
+                Relation.delete(relations)
             elif act[0] == 'add':
-                if isinstance(act[1], (int, long)):
-                    target_ids = [act[1]]
-                else:
-                    target_ids = list(act[1])
+                target_ids = list(act[1])
                 if not target_ids:
                     continue
                 existing_ids = []
                 for i in range(0, len(target_ids),
                         Transaction().cursor.IN_MAX):
                     sub_ids = target_ids[i:i + Transaction().cursor.IN_MAX]
-                    relation_ids = relation_obj.search([
+                    relations = Relation.search([
                             search_clause(ids),
                             (self.target, 'in', sub_ids),
                             ])
-                    for relation in relation_obj.browse(relation_ids):
-                        existing_ids.append(relation[self.target].id)
+                    for relation in relations:
+                        existing_ids.append(getattr(relation, self.target).id)
                 for new_id in (x for x in target_ids if x not in existing_ids):
                     for record_id in ids:
-                        relation_obj.create({
+                        Relation.create({
                                 self.origin: field_value(record_id),
                                 self.target: new_id,
                                 })
             elif act[0] == 'unlink_all':
-                target_ids = relation_obj.search([
+                targets = Relation.search([
                         search_clause(ids),
                         (self.target + '.id', '!=', None),
                         ])
-                relation_obj.delete(target_ids)
+                Relation.delete(targets)
             elif act[0] == 'set':
                 if not act[1]:
                     target_ids = []
                 else:
                     target_ids = list(act[1])
-                target_ids2 = relation_obj.search([
+                targets2 = Relation.search([
                         search_clause(ids),
                         (self.target + '.id', '!=', None),
                         ])
-                relation_obj.delete(target_ids2)
+                Relation.delete(targets2)
 
                 for new_id in target_ids:
                     for record_id in ids:
-                        relation_obj.create({
+                        Relation.create({
                                 self.origin: field_value(record_id),
                                 self.target: new_id,
                                 })
@@ -231,18 +215,24 @@
                 raise Exception('Bad arguments')
 
     def get_target(self):
-        '''
-        Return the target model.
+        'Return the target model'
+        Relation = Pool().get(self.relation_name)
+        if not self.target:
+            return Relation
+        if self.target in Relation._fields:
+            return Relation._fields[self.target].get_target()
+        else:
+            return Relation._inherit_fields[self.target][2].get_target()
 
-        :return: A Model
-        '''
-        relation_obj = Pool().get(self.relation_name)
-        if not self.target:
-            return relation_obj
-        if self.target in relation_obj._columns:
-            target_obj = Pool().get(
-                    relation_obj._columns[self.target].model_name)
-        else:
-            target_obj = Pool().get(
-                    relation_obj._inherit_fields[self.target][2].model_name)
-        return target_obj
+    def __set__(self, inst, value):
+        Target = self.get_target()
+
+        def instance(data):
+            if isinstance(data, Target):
+                return data
+            elif isinstance(data, dict):
+                return Target(**data)
+            else:
+                return Target(data)
+        value = [instance(x) for x in (value or [])]
+        super(Many2Many, self).__set__(inst, value)
diff -r f6ef988fda71 trytond/model/fields/many2one.py
--- a/trytond/model/fields/many2one.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/model/fields/many2one.py	Sun Sep 23 17:30:03 2012 +0200
@@ -1,7 +1,9 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
+from types import NoneType
 
 from trytond.model.fields.field import Field
+from trytond.pool import Pool
 
 
 class Many2One(Field):
@@ -45,6 +47,7 @@
         self.left = left
         self.right = right
         self.datetime_field = datetime_field
+    __init__.__doc__ += Field.__init__.__doc__
 
     def __get_required(self):
         return self.__required
@@ -56,4 +59,15 @@
 
     required = property(__get_required, __set_required)
 
-    __init__.__doc__ += Field.__init__.__doc__
+    def get_target(self):
+        'Return the target Model'
+        return Pool().get(self.model_name)
+
+    def __set__(self, inst, value):
+        Target = self.get_target()
+        if isinstance(value, dict):
+            value = Target(**value)
+        elif isinstance(value, (int, long)):
+            value = Target(value)
+        assert isinstance(value, (Target, NoneType))
+        super(Many2One, self).__set__(inst, value)
diff -r f6ef988fda71 trytond/model/fields/one2many.py
--- a/trytond/model/fields/one2many.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/model/fields/one2many.py	Sun Sep 23 17:30:03 2012 +0200
@@ -76,19 +76,13 @@
     def get(self, ids, model, name, values=None):
         '''
         Return target records ordered.
-
-        :param ids: a list of ids
-        :param model: the model
-        :param name: a string with the name of the field
-        :param values: a dictionary with the read values
-        :return: a dictionary with ids as key and values as value
         '''
         pool = Pool()
-        relation_obj = pool.get(self.model_name)
-        if self.field in relation_obj._columns:
-            field = relation_obj._columns[self.field]
+        Relation = pool.get(self.model_name)
+        if self.field in Relation._fields:
+            field = Relation._fields[self.field]
         else:
-            field = relation_obj._inherit_fields[self.field][2]
+            field = Relation._inherit_fields[self.field][2]
         res = {}
         for i in ids:
             res[i] = []
@@ -96,11 +90,11 @@
         for i in range(0, len(ids), Transaction().cursor.IN_MAX):
             sub_ids = ids[i:i + Transaction().cursor.IN_MAX]
             if field._type == 'reference':
-                references = ['%s,%s' % (model._name, x) for x in sub_ids]
+                references = ['%s,%s' % (model.__name__, x) for x in sub_ids]
                 clause = [(self.field, 'in', references)]
             else:
                 clause = [(self.field, 'in', sub_ids)]
-            ids2.append(relation_obj.search(clause, order=self.order))
+            ids2.append(map(int, Relation.search(clause, order=self.order)))
 
         cache = Transaction().cursor.get_cache(Transaction().context)
         cache.setdefault(self.model_name, {})
@@ -118,7 +112,7 @@
                 ids3.append(i)
 
         if ids3:
-            for i in relation_obj.read(ids3, [self.field]):
+            for i in Relation.read(ids3, [self.field]):
                 if field._type == 'reference':
                     _, id_ = i[self.field].split(',')
                     id_ = int(id_)
@@ -128,18 +122,14 @@
 
         index_of_ids2 = dict((i, index)
             for index, i in enumerate(chain(*ids2)))
-        for val in res.values():
-            val.sort(key=lambda x: index_of_ids2[x])
+        for id_, val in res.iteritems():
+            res[id_] = tuple(sorted(val, key=lambda x: index_of_ids2[x]))
         return res
 
     def set(self, ids, model, name, values):
         '''
         Set the values.
-
-        :param ids: A list of ids
-        :param model: the model
-        :param name: A string with the name of the field
-        :param values: A list of tuples:
+        values: A list of tuples:
             (``create``, ``{<field name>: value}``),
             (``write``, ``<ids>``, ``{<field name>: value}``),
             (``delete``, ``<ids>``),
@@ -149,25 +139,24 @@
             (``unlink_all``),
             (``set``, ``<ids>``)
         '''
-        pool = Pool()
         if not values:
             return
-        target_obj = pool.get(self.model_name)
-        if self.field in target_obj._columns:
-            field = target_obj._columns[self.field]
+        Target = self.get_target()
+        if self.field in Target._fields:
+            field = Target._fields[self.field]
         else:
-            field = target_obj._inherit_fields[self.field][2]
+            field = Target._inherit_fields[self.field][2]
 
         def search_clause(ids):
             if field._type == 'reference':
-                references = ['%s,%s' % (model._name, x) for x in ids]
+                references = ['%s,%s' % (model.__name__, x) for x in ids]
                 return (self.field, 'in', references)
             else:
                 return (self.field, 'in', ids)
 
         def field_value(record_id):
             if field._type == 'reference':
-                return '%s,%s' % (model._name, record_id)
+                return '%s,%s' % (model.__name__, record_id)
             else:
                 return record_id
 
@@ -175,63 +164,75 @@
             if act[0] == 'create':
                 for record_id in ids:
                     act[1][self.field] = field_value(record_id)
-                    target_obj.create(act[1])
+                    Target.create(act[1])
             elif act[0] == 'write':
-                target_obj.write(act[1], act[2])
+                Target.write(Target.browse(act[1]), act[2])
             elif act[0] == 'delete':
-                target_obj.delete(act[1])
+                Target.delete(Target.browse(act[1]))
             elif act[0] == 'delete_all':
-                target_obj.delete(target_obj.search([
-                            search_clause(ids),
-                            ]))
+                targets = Target.search([
+                        search_clause(ids),
+                        ])
+                Target.delete(targets)
             elif act[0] == 'unlink':
-                if isinstance(act[1], (int, long)):
-                    target_ids = [act[1]]
-                else:
-                    target_ids = list(act[1])
+                target_ids = map(int, act[1])
                 if not target_ids:
                     continue
-                target_ids = target_obj.search([
+                targets = Target.search([
                         search_clause(ids),
                         ('id', 'in', target_ids),
                         ])
-                target_obj.write(target_ids, {
+                Target.write(targets, {
                         self.field: None,
                         })
             elif act[0] == 'add':
-                if isinstance(act[1], (int, long)):
-                    target_ids = [act[1]]
-                else:
-                    target_ids = list(act[1])
+                target_ids = map(int, act[1])
                 if not target_ids:
                     continue
                 for record_id in ids:
-                    target_obj.write(target_ids, {
+                    Target.write(Target.browse(target_ids), {
                             self.field: field_value(record_id),
                             })
             elif act[0] == 'unlink_all':
-                target_ids = target_obj.search([
+                targets = Target.search([
                         search_clause(ids),
                         ])
-                target_obj.write(target_ids, {
+                Target.write(targets, {
                         self.field: None,
                         })
             elif act[0] == 'set':
                 if not act[1]:
-                    target_ids = [0]
+                    target_ids = [-1]
                 else:
-                    target_ids = list(act[1])
+                    target_ids = map(int, act[1])
                 for record_id in ids:
-                    target_ids2 = target_obj.search([
+                    targets = Target.search([
                             search_clause([record_id]),
                             ('id', 'not in', target_ids),
                             ])
-                    target_obj.write(target_ids2, {
+                    Target.write(targets, {
                             self.field: None,
                             })
                     if act[1]:
-                        target_obj.write(target_ids, {
+                        Target.write(Target.browse(target_ids), {
                                 self.field: field_value(record_id),
                                 })
             else:
                 raise Exception('Bad arguments')
+
+    def get_target(self):
+        'Return the target Model'
+        return Pool().get(self.model_name)
+
+    def __set__(self, inst, value):
+        Target = self.get_target()
+
+        def instance(data):
+            if isinstance(data, Target):
+                return data
+            elif isinstance(data, dict):
+                return Target(**data)
+            else:
+                return Target(data)
+        value = [instance(x) for x in (value or [])]
+        super(One2Many, self).__set__(inst, value)
diff -r f6ef988fda71 trytond/model/fields/one2one.py
--- a/trytond/model/fields/one2one.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/model/fields/one2one.py	Sun Sep 23 17:30:03 2012 +0200
@@ -1,6 +1,8 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
+from types import NoneType
 
+from trytond.model.fields.field import Field
 from trytond.model.fields.many2many import Many2Many
 from trytond.pool import Pool
 
@@ -36,14 +38,23 @@
         :param value: The id to link
         '''
         pool = Pool()
-        relation_obj = pool.get(self.relation_name)
-        relation_ids = relation_obj.search([
-            (self.origin, 'in', ids),
-            ])
-        relation_obj.delete(relation_ids)
+        Relation = pool.get(self.relation_name)
+        relations = Relation.search([
+                (self.origin, 'in', ids),
+                ])
+        Relation.delete(relations)
         if value:
             for record_id in ids:
-                relation_obj.create({
-                    self.origin: record_id,
-                    self.target: value,
-                    })
+                Relation.create({
+                        self.origin: record_id,
+                        self.target: value,
+                        })
+
+    def __set__(self, inst, value):
+        Target = self.get_target()
+        if isinstance(value, dict):
+            value = Target(*value)
+        elif isinstance(value, (int, long)):
+            value = Target(value)
+        assert isinstance(value, (Target, NoneType))
+        Field.__set__(self, inst, value)
diff -r f6ef988fda71 trytond/model/fields/property.py
--- a/trytond/model/fields/property.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/model/fields/property.py	Sun Sep 23 17:30:03 2012 +0200
@@ -25,6 +25,9 @@
     def __copy__(self):
         return Property(copy.copy(self._field))
 
+    def __deepcopy__(self, memo):
+        return Property(copy.deepcopy(self._field))
+
     def get(self, ids, model, name, values=None):
         '''
         Retreive the property.
@@ -36,9 +39,8 @@
         :return: a dictionary with ids as key and values as value
         '''
         pool = Pool()
-        property_obj = pool.get('ir.property')
-        res = property_obj.get(name, model._name, ids)
-        return res
+        Property = pool.get('ir.property')
+        return Property.get(name, model.__name__, ids)
 
     def set(self, ids, model, name, value):
         '''
@@ -50,13 +52,13 @@
         :param value: The value to set.
         '''
         pool = Pool()
-        property_obj = pool.get('ir.property')
+        Property = pool.get('ir.property')
         if value is not None:
             prop_value = '%s,%s' % (getattr(self, 'model_name', ''),
                 str(value))
         else:
             prop_value = None
-        return property_obj.set(name, model._name, ids, prop_value)
+        Property.set(name, model.__name__, ids, prop_value)
 
     def search(self, model, name, clause):
         '''
@@ -65,21 +67,21 @@
         :return: New list of domain.
         '''
         pool = Pool()
-        rule_obj = pool.get('ir.rule')
-        property_obj = pool.get('ir.property')
-        model_obj = pool.get('ir.model')
-        field_obj = pool.get('ir.model.field')
+        Rule = pool.get('ir.rule')
+        Property = pool.get('ir.property')
+        Model = pool.get('ir.model')
+        Field = pool.get('ir.model.field')
         cursor = Transaction().cursor
 
         field_class = backend.FIELDS[self._type]
         if not field_class:
             return []
-        sql_type = field_class.sql_type(model._columns[name])
+        sql_type = field_class.sql_type(model._fields[name])
         if not sql_type:
             return []
         sql_type = sql_type[0]
 
-        property_query, property_val = rule_obj.domain_get('ir.property')
+        property_query, property_val = Rule.domain_get('ir.property')
 
         property_clause = ''
         if property_query:
@@ -88,27 +90,27 @@
         #Fetch res ids that comply with the domain
         cursor.execute(
             'SELECT CAST('
-                'SPLIT_PART("' + property_obj._table + '".res,\',\',2) '
+                'SPLIT_PART("' + Property._table + '".res,\',\',2) '
                     'AS INTEGER), '
-                '"' + property_obj._table + '".id '
-            'FROM "' + property_obj._table + '" '
-                'JOIN "' + field_obj._table + '" ON '
-                    '("' + field_obj._table + '"' +
-                        '.id = "' + property_obj._table + '".field) '
-                'JOIN "' + model_obj._table + '" ON '
-                    '("' + model_obj._table +
-                        '".id = "' + field_obj._table + '".model) '
+                '"' + Property._table + '".id '
+            'FROM "' + Property._table + '" '
+                'JOIN "' + Field._table + '" ON '
+                    '("' + Field._table + '"' +
+                        '.id = "' + Property._table + '".field) '
+                'JOIN "' + Model._table + '" ON '
+                    '("' + Model._table +
+                        '".id = "' + Field._table + '".model) '
             'WHERE '
                 'CASE WHEN "' +
-                    model_obj._table + '".model = %s '
-                    'AND "' + field_obj._table + '".name = %s '
+                    Model._table + '".model = %s '
+                    'AND "' + Field._table + '".name = %s '
                     + property_clause +
                 ' THEN ' +
                     self.get_condition(sql_type, clause) +
                 ' ELSE '
                     '%s '
                 'END',
-            [model._name, name] + property_val +
+            [model.__name__, name] + property_val +
             self.get_condition_args(clause) + [False])
 
         props = cursor.fetchall()
@@ -134,7 +136,7 @@
         #Fetch the res ids that doesn't use the default value
         cursor.execute(
             "SELECT cast(split_part(res,',',2) as integer) "
-            'FROM "' + property_obj._table + '"'
+            'FROM "' + Property._table + '"'
             'WHERE ' + property_query + ' AND res is not null',
             property_val)
 
diff -r f6ef988fda71 trytond/model/fields/reference.py
--- a/trytond/model/fields/reference.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/model/fields/reference.py	Sun Sep 23 17:30:03 2012 +0200
@@ -1,6 +1,8 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
 import contextlib
+from types import NoneType
+
 from trytond.model.fields.field import Field
 from trytond.transaction import Transaction
 from trytond.pool import Pool
@@ -32,13 +34,7 @@
 
     def get(self, ids, model, name, values=None):
         '''
-        Replace removed reference id by False.
-
-        :param ids: a list of ids
-        :param model: a string with the name of the model
-        :param name: a string with the name of the field
-        :param values: a dictionary with the read values
-        :return: a dictionary with ids as key and values as value
+        Replace removed reference id by None.
         '''
         pool = Pool()
         if values is None:
@@ -71,15 +67,28 @@
         with contextlib.nested(Transaction().set_context(active_test=False),
                 Transaction().set_user(0)):
             for ref_model, (ref_ids, ids) in ref_to_check.iteritems():
-                if ref_model not in pool.object_name_list():
-                    res.update(dict((i, False) for i in ids))
+                try:
+                    pool.get(ref_model)
+                except KeyError:
+                    res.update(dict((i, None) for i in ids))
                     continue
-                ref_obj = pool.get(ref_model)
-                ref_ids = ref_obj.search([
+                Ref = pool.get(ref_model)
+                refs = Ref.search([
                     ('id', 'in', list(ref_ids)),
                     ], order=[])
-                refs = [ref_model + ',' + str(ref_id) for ref_id in ref_ids]
+                refs = map(str, refs)
                 for i in ids:
                     if res[i] not in refs:
-                        res[i] = False
+                        res[i] = None
         return res
+
+    def __set__(self, inst, value):
+        from ..model import Model
+        if not isinstance(value, (Model, NoneType)):
+            if isinstance(value, basestring):
+                target, id_ = value.split(',')
+            else:
+                target, id_ = value
+            Target = Pool().get(target)
+            value = Target(id_)
+        super(Reference, self).__set__(inst, value)
diff -r f6ef988fda71 trytond/model/model.py
--- a/trytond/model/model.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/model/model.py	Sun Sep 23 17:30:03 2012 +0200
@@ -3,154 +3,152 @@
 
 import copy
 import collections
+import warnings
+
 from trytond.model import fields
 from trytond.error import WarningErrorMixin
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 from trytond.pyson import PYSONEncoder
 from trytond.transaction import Transaction
 from trytond.url import URLMixin
+from trytond.rpc import RPC
+
+__all__ = ['Model']
+
+
+class ModelMeta(PoolMeta):
+
+    def __getattr__(self, name):
+        pool = Pool()
+        for model_name, _ in self._inherits.iteritems():
+            Model = pool.get(model_name)
+            if isinstance(getattr(Model, name, None),
+                    collections.Callable):
+                meth = getattr(Model, name)
+                if not hasattr(meth, 'im_self') or meth.im_self:
+                    # Return classmethod bounded to inherited class
+                    return meth
+                else:
+                    for cls in Model.__mro__:
+                        if name in cls.__dict__:
+                            # Return a method bounded to current class
+                            return cls.__dict__[name].__get__(None, self)
+        raise AttributeError("'%s' Model has no attribute '%s'"
+            % (self.__name__, name))
 
 
 class Model(WarningErrorMixin, URLMixin):
     """
     Define a model in Tryton.
     """
-    _name = None
+    __metaclass__ = ModelMeta
     _inherits = {}
-    _description = ''
-    __columns = None
-    __defaults = None
     _rec_name = 'name'
 
     id = fields.Integer('ID', readonly=True)
 
-    def _reset_columns(self):
-        self.__columns = None
-        self.__defaults = None
+    @classmethod
+    def __setup__(cls):
+        cls.__rpc__ = {
+            'default_get': RPC(),
+            'fields_get': RPC(),
+            'on_change_with': RPC(instantiate=0),
+            }
+        cls._error_messages = {}
 
-    def _getcolumns(self):
-        if self.__columns:
-            return self.__columns
-        res = {}
-        for attr in dir(self):
+        # Copy fields
+        for attr in dir(cls):
             if attr.startswith('_'):
                 continue
-            if isinstance(getattr(self, attr), fields.Field):
-                res[attr] = getattr(self, attr)
-        self.__columns = res
-        return res
+            if isinstance(getattr(cls, attr), fields.Field):
+                setattr(cls, attr, copy.deepcopy(getattr(cls, attr)))
 
-    #replace by _fields
-    _columns = property(fget=_getcolumns)
+    @classmethod
+    def __post_setup__(cls):
+        pool = Pool()
 
-    def _reset_defaults(self):
-        self.__defaults = None
+        # Set _fields
+        cls._fields = {}
+        for attr in dir(cls):
+            if attr.startswith('_'):
+                continue
+            if isinstance(getattr(cls, attr), fields.Field):
+                cls._fields[attr] = getattr(cls, attr)
 
-    def _getdefaults(self):
-        if self.__defaults:
-            return self.__defaults
-        res = {}
-        fields_names = self._columns.keys()
-        fields_names += self._inherit_fields.keys()
+        # Set _inherit_fields
+        cls._inherit_fields = {}
+        for model_name in cls._inherits:
+            Model = pool.get(model_name)
+            cls._inherit_fields.update(Model._inherit_fields)
+            for field_name in Model._fields.keys():
+                cls._inherit_fields[field_name] = (model_name,
+                    cls._inherits[model_name],
+                    Model._fields[field_name])
+            for field_name in Model._inherit_fields.keys():
+                cls._inherit_fields[field_name] = (model_name,
+                    cls._inherits[model_name],
+                    Model._inherit_fields[field_name][2])
+        # Update models that uses this one in _inherits
+        for _, Model in pool.iterobject():
+            if cls.__name__ in Model._inherits:
+                Model.__post_setup__()
+
+        # Set _defaults
+        cls._defaults = {}
+        fields_names = cls._fields.keys()
+        fields_names += cls._inherit_fields.keys()
         for field_name in fields_names:
-            default_method = getattr(self, 'default_%s' % field_name, False)
+            default_method = getattr(cls, 'default_%s' % field_name, False)
+            if not default_method and field_name in cls._inherit_fields:
+                icls = pool.get(cls._inherit_fields[field_name][0])
+                default_method = getattr(icls, 'default_%s' % field_name,
+                    False)
             if isinstance(default_method, collections.Callable):
-                res[field_name] = default_method
-        self.__defaults = res
-        return res
+                cls._defaults[field_name] = default_method
 
-    _defaults = property(fget=_getdefaults)
+        for k in cls._defaults:
+            assert (k in cls._fields) or (k in cls._inherit_fields), \
+                'Default function defined in %s but field %s does not exist!' \
+                % (cls.__name__, k,)
 
-    def __new__(cls):
-        Pool.register(cls, type='model')
-
-    def __init__(self):
-        super(Model, self).__init__()
-        self._rpc = {
-            'default_get': False,
-            'fields_get': False,
-            'on_change_with': False,
-        }
-        self._inherit_fields = []
-        self._error_messages = {}
-        # reinit the cache on _columns and _defaults
-        self.__columns = None
-        self.__defaults = None
-
-        if not self._description:
-            self._description = self._name
-
-        self._inherits_reload()
-        for k in self._defaults:
-            assert (k in self._columns) or (k in self._inherit_fields), \
-            'Default function defined in %s but field %s does not exist!' % \
-                (self._name, k,)
-        self._update_rpc()
-
-    def _update_rpc(self):
-        for field_name in self._columns.keys() + self._inherit_fields.keys():
-            if field_name in self._columns:
-                field = self._columns[field_name]
+        # Update __rpc__
+        for field_name in cls._fields.keys() + cls._inherit_fields.keys():
+            if field_name in cls._fields:
+                field = cls._fields[field_name]
             else:
-                field = self._inherit_fields[field_name][2]
+                field = cls._inherit_fields[field_name][2]
             if isinstance(field, (fields.Selection, fields.Reference)) \
                     and not isinstance(field.selection, (list, tuple)) \
-                    and field.selection not in self._rpc:
-                self._rpc[field.selection] = False
+                    and field.selection not in cls.__rpc__:
+                cls.__rpc__[field.selection] = RPC()
 
             for attribute in ('on_change', 'on_change_with', 'autocomplete'):
                 function_name = '%s_%s' % (attribute, field_name)
-                if (getattr(field, attribute, False)
-                         and isinstance(getattr(self, function_name, False),
-                             collections.Callable)):
-                    self._rpc.setdefault(function_name, False)
+                if getattr(field, attribute, False):
+                    cls.__rpc__.setdefault(function_name, RPC(instantiate=0))
 
-    def __getattr__(self, name):
-        pool = Pool()
-        # Search if a function exists in inherits parents
-        for model_name, field_name in self._inherits.iteritems():
-            model_obj = pool.get(model_name)
-            if (hasattr(model_obj, name)
-                    and isinstance(getattr(model_obj, name),
-                        collections.Callable)):
-                return getattr(model_obj, name)
-        raise AttributeError(name)
+        # Set name to fields
+        for name, field in cls._fields.iteritems():
+            if field.name is None:
+                field.name = name
+            else:
+                assert field.name == name, (
+                    'Duplicate fields on %s: %s, %s'
+                    % (cls, field.name, name))
 
-    def _inherits_reload(self):
-        """
-        Reconstruct _inherit_fields
-        """
-        res = {}
-        pool = Pool()
-        for model in self._inherits:
-            res.update(pool.get(model)._inherit_fields)
-            for field_name in pool.get(model)._columns.keys():
-                res[field_name] = (model, self._inherits[model],
-                        pool.get(model)._columns[field_name])
-            for field_name in pool.get(model)._inherit_fields.keys():
-                res[field_name] = (model, self._inherits[model],
-                        pool.get(model)._inherit_fields[field_name][2])
-        self._inherit_fields = res
-        self._reset_columns()
-        self._update_rpc()
-        # Update objects that uses this one to update their _inherits fields
-        for obj_name in pool.object_name_list():
-            obj = pool.get(obj_name)
-            if self._name in obj._inherits:
-                obj._inherits_reload()
-
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         """
         Add model in ir.model and ir.model.field.
-
-        :param module_name: the module name
         """
-        translation_obj = Pool().get('ir.translation')
+        pool = Pool()
+        Translation = pool.get('ir.translation')
+        Property = pool.get('ir.property')
 
         cursor = Transaction().cursor
         # Add model in ir_model
         cursor.execute("SELECT id FROM ir_model WHERE model = %s",
-                (self._name,))
+                (cls.__name__,))
         model_id = None
         if cursor.rowcount == -1 or cursor.rowcount is None:
             data = cursor.fetchone()
@@ -159,28 +157,31 @@
         elif cursor.rowcount != 0:
             model_id, = cursor.fetchone()
         if not model_id:
-            cursor.execute("INSERT INTO ir_model " \
-                    "(model, name, info, module) VALUES (%s, %s, %s, %s)",
-                    (self._name, self._description, self.__doc__,
-                        module_name))
+            cursor.execute("INSERT INTO ir_model "
+                "(model, name, info, module) VALUES (%s, %s, %s, %s)",
+                (cls.__name__, cls.__doc__.splitlines()[0], cls.__doc__,
+                    module_name))
+            Property._models_get_cache.clear()
             cursor.execute("SELECT id FROM ir_model WHERE model = %s",
-                    (self._name,))
+                    (cls.__name__,))
             (model_id,) = cursor.fetchone()
-        else:
-            cursor.execute('UPDATE ir_model ' \
-                    'SET name = %s, ' \
-                        'info = %s ' \
-                    'WHERE id = %s',
-                    (self._description, self.__doc__, model_id))
+        elif cls.__doc__:
+            cursor.execute('UPDATE ir_model '
+                'SET name = %s, '
+                    'info = %s '
+                'WHERE id = %s',
+                (cls.__doc__.splitlines()[0], cls.__doc__, model_id))
 
         # Update translation of model
-        for name, src in [(self._name + ',name', self._description)]:
-            cursor.execute('SELECT id FROM ir_translation '
-                'WHERE lang = %s '
-                    'AND type = %s '
-                    'AND name = %s '
-                    'AND (res_id IS NULL OR res_id = %s)',
-                ('en_US', 'model', name, 0))
+        if cls.__doc__:
+            name = cls.__name__ + ',name'
+            src = cls.__doc__.splitlines()[0]
+            cursor.execute('SELECT id FROM ir_translation ' \
+                    'WHERE lang = %s ' \
+                        'AND type = %s ' \
+                        'AND name = %s ' \
+                        'AND (res_id IS NULL OR res_id = %s)',
+                    ('en_US', 'model', name, 0))
             trans_id = None
             if cursor.rowcount == -1 or cursor.rowcount is None:
                 data = cursor.fetchone()
@@ -188,7 +189,7 @@
                     trans_id, = data
             elif cursor.rowcount != 0:
                 trans_id, = cursor.fetchone()
-            src_md5 = translation_obj.get_src_md5(src)
+            src_md5 = Translation.get_src_md5(src)
             if trans_id is None:
                 cursor.execute('INSERT INTO ir_translation '
                     '(name, lang, type, src, src_md5, value, module, fuzzy) '
@@ -209,21 +210,21 @@
                 'FROM ir_model_field AS f, ir_model AS m ' \
                 'WHERE f.model = m.id ' \
                     'AND m.model = %s ',
-                        (self._name,))
+                        (cls.__name__,))
         model_fields = {}
         for field in cursor.dictfetchall():
             model_fields[field['name']] = field
 
         # Prefetch field translations
-        if self._columns:
+        if cls._fields:
             cursor.execute('SELECT id, name, src, type FROM ir_translation ' \
                     'WHERE lang = %s ' \
                         'AND type IN (%s, %s, %s) ' \
                         'AND name IN ' \
-                            '(' + ','.join(('%s',) * len(self._columns)) + ')',
+                            '(' + ','.join(('%s',) * len(cls._fields)) + ')',
                             ('en_US', 'field', 'help', 'selection') + \
-                                    tuple([self._name + ',' + x \
-                                        for x in self._columns]))
+                                    tuple([cls.__name__ + ',' + x \
+                                        for x in cls._fields]))
         trans_fields = {}
         trans_help = {}
         trans_selection = {}
@@ -236,8 +237,8 @@
                 trans_selection.setdefault(trans['name'], {})
                 trans_selection[trans['name']][trans['src']] = trans
 
-        for field_name in self._columns:
-            field = self._columns[field_name]
+        for field_name in cls._fields:
+            field = cls._fields[field_name]
             relation = ''
             if hasattr(field, 'model_name'):
                 relation = field.model_name
@@ -262,8 +263,8 @@
                         'WHERE id = %s ',
                         (field.string, field._type, relation,
                             field.help, model_fields[field_name]['id']))
-            trans_name = self._name + ',' + field_name
-            string_md5 = translation_obj.get_src_md5(field.string)
+            trans_name = cls.__name__ + ',' + field_name
+            string_md5 = Translation.get_src_md5(field.string)
             if trans_name not in trans_fields:
                 cursor.execute('INSERT INTO ir_translation '
                     '(name, lang, type, src, src_md5, value, module, fuzzy) '
@@ -275,7 +276,7 @@
                     'SET src = %s, src_md5 = %s '
                     'WHERE id = %s ',
                     (field.string, string_md5, trans_fields[trans_name]['id']))
-            help_md5 = translation_obj.get_src_md5(field.help)
+            help_md5 = Translation.get_src_md5(field.help)
             if trans_name not in trans_help:
                 if field.help:
                     cursor.execute('INSERT INTO ir_translation '
@@ -297,7 +298,7 @@
                 for (_, val) in field.selection:
                     if trans_name not in trans_selection \
                             or val not in trans_selection[trans_name]:
-                        val_md5 = translation_obj.get_src_md5(val)
+                        val_md5 = Translation.get_src_md5(val)
                         cursor.execute('INSERT INTO ir_translation '
                             '(name, lang, type, src, src_md5, value, module, '
                                 'fuzzy) '
@@ -307,7 +308,7 @@
         # Clean ir_model_field from field that are no more existing.
         for field_name in model_fields:
             if model_fields[field_name]['module'] == module_name \
-                    and field_name not in self._columns:
+                    and field_name not in cls._fields:
                 #XXX This delete field even when it is defined later
                 # in the module
                 cursor.execute('DELETE FROM ir_model_field '\
@@ -319,145 +320,135 @@
                 'WHERE lang = %s ' \
                     'AND type = %s ' \
                     'AND name = %s',
-                ('en_US', 'error', self._name))
+                ('en_US', 'error', cls.__name__))
         trans_error = {}
         for trans in cursor.dictfetchall():
             trans_error[trans['src']] = trans
 
-        errors = self._get_error_messages()
+        errors = cls._get_error_messages()
         for error in set(errors):
             if error not in trans_error:
-                error_md5 = translation_obj.get_src_md5(error)
+                error_md5 = Translation.get_src_md5(error)
                 cursor.execute('INSERT INTO ir_translation '
                     '(name, lang, type, src, src_md5, value, module, fuzzy) '
                     'VALUES (%s, %s, %s, %s, %s, %s, %s, %s)',
-                    (self._name, 'en_US', 'error', error, error_md5, '',
+                    (cls.__name__, 'en_US', 'error', error, error_md5, '',
                         module_name, False))
 
-    def _get_error_messages(self):
-        return self._error_messages.values()
+    @classmethod
+    def _get_error_messages(cls):
+        return cls._error_messages.values()
 
-    def default_get(self, fields_names, with_rec_name=True):
+    @classmethod
+    def default_get(cls, fields_names, with_rec_name=True):
         '''
         Return a dict with the default values for each field in fields_names.
-
-        :param fields_names: a list of fields names
-        :param with_rec_name: a boolean to add rec_name value
-        :return: a dictionary with field name as key
-            and default value as value
+        If with_rec_name is True, rec_name will be added.
         '''
         pool = Pool()
-        property_obj = pool.get('ir.property')
+        Property = pool.get('ir.property')
         value = {}
 
         # get the default values defined in the object
         for field_name in fields_names:
-            if field_name in self._defaults:
-                value[field_name] = self._defaults[field_name]()
-            if field_name in self._columns:
-                field = self._columns[field_name]
+            if field_name in cls._defaults:
+                value[field_name] = cls._defaults[field_name]()
+            if field_name in cls._fields:
+                field = cls._fields[field_name]
             else:
-                field = self._inherit_fields[field_name][2]
+                field = cls._inherit_fields[field_name][2]
             if (field._type == 'boolean'
                     and not field_name in value):
                 value[field_name] = False
             if isinstance(field, fields.Property):
-                value[field_name] = property_obj.get(field_name, self._name)
+                value[field_name] = Property.get(field_name, cls.__name__)
             if (with_rec_name
                     and field._type in ('many2one',)
                     and value.get(field_name)):
-                obj = pool.get(field.model_name)
-                if 'rec_name' in obj._columns:
-                    value[field_name + '.rec_name'] = obj.browse(
+                Target = pool.get(field.model_name)
+                if 'rec_name' in Target._fields:
+                    value[field_name + '.rec_name'] = Target(
                         value[field_name]).rec_name
 
-        value = self._default_on_change(value)
+        value = cls._default_on_change(value)
         if not with_rec_name:
             for field in value.keys():
                 if field.endswith('.rec_name'):
                     del value[field]
         return value
 
-    def _default_on_change(self, value):
+    @classmethod
+    def _default_on_change(cls, value):
         """
         Call on_change function for the default value
         and return new default value
-
-        :param value: a dictionnary with the default value
-        :return: a new dictionnary of default value
         """
         pool = Pool()
         res = value.copy()
         val = {}
-        for i in self._inherits.keys():
+        for i in cls._inherits.keys():
             val.update(pool.get(i)._default_on_change(value))
         for field in value.keys():
-            if field in self._columns:
-                if self._columns[field].on_change:
-                    args = {}
-                    for arg in self._columns[field].on_change:
-                        args[arg] = value.get(arg, None)
-                        if arg in self._columns \
-                                and self._columns[arg]._type == 'many2one':
-                            if isinstance(args[arg], (list, tuple)):
-                                args[arg] = args[arg][0]
-                    val.update(getattr(self, 'on_change_' + field)(args))
-                if self._columns[field]._type in ('one2many',):
-                    obj = pool.get(self._columns[field].model_name)
+            if field in cls._fields:
+                if cls._fields[field].on_change:
+                    inst = cls()
+                    for fname in cls._fields[field].on_change:
+                        setattr(inst, fname, value.get(fname))
+                    val.update(getattr(inst, 'on_change_' + field)())
+                if cls._fields[field]._type in ('one2many',):
+                    Target = pool.get(cls._fields[field].model_name)
                     for val2 in res[field]:
-                        val2.update(obj._default_on_change(val2))
+                        val2.update(Target._default_on_change(val2))
         res.update(val)
         return res
 
-    def fields_get(self, fields_names=None):
+    @classmethod
+    def fields_get(cls, fields_names=None):
         """
         Return the definition of each field on the model.
-
-        :param fields_names: a list of field names or None for all fields
-        :return: a dictionary with field name as key and definition as value
         """
         res = {}
         pool = Pool()
-        translation_obj = pool.get('ir.translation')
-        field_access_obj = pool.get('ir.model.field.access')
+        Translation = pool.get('ir.translation')
+        FieldAccess = pool.get('ir.model.field.access')
 
-        for parent in self._inherits:
+        for parent in cls._inherits:
             res.update(pool.get(parent).fields_get(fields_names))
 
         #Add translation to cache
         language = Transaction().language
         trans_args = []
-        for field in (x for x in self._columns.keys()
+        for field in (x for x in cls._fields.keys()
                 if ((not fields_names) or x in fields_names)):
-            trans_args.append((self._name + ',' + field, 'field', language,
+            trans_args.append((cls.__name__ + ',' + field, 'field', language,
                 None))
-            trans_args.append((self._name + ',' + field, 'help', language,
+            trans_args.append((cls.__name__ + ',' + field, 'help', language,
                 None))
-            if hasattr(self._columns[field], 'selection'):
-                if isinstance(self._columns[field].selection, (tuple, list)) \
-                        and ((hasattr(self._columns[field],
+            if hasattr(cls._fields[field], 'selection'):
+                if isinstance(cls._fields[field].selection, (tuple, list)) \
+                        and ((hasattr(cls._fields[field],
                             'translate_selection') \
-                            and self._columns[field].translate_selection) \
-                            or not hasattr(self._columns[field],
+                            and cls._fields[field].translate_selection) \
+                            or not hasattr(cls._fields[field],
                                 'translate_selection')):
-                    sel = self._columns[field].selection
+                    sel = cls._fields[field].selection
                     for (key, val) in sel:
-                        trans_args.append((self._name + ',' + field,
+                        trans_args.append((cls.__name__ + ',' + field,
                             'selection', language, val))
-        translation_obj.get_sources(trans_args)
+        Translation.get_sources(trans_args)
 
         encoder = PYSONEncoder()
 
-        fwrite_accesses = field_access_obj.check(self._name, fields_names or
-                self._columns.keys(), 'write', access=True)
-        fcreate_accesses = field_access_obj.check(self._name, fields_names or
-            self._columns.keys(), 'create', access=True)
-        fdelete_accesses = field_access_obj.check(self._name, fields_names or
-            self._columns.keys(), 'delete', access=True)
-        for field in (x for x in self._columns.keys()
+        fwrite_accesses = FieldAccess.check(cls.__name__, fields_names or
+            cls._fields.keys(), 'write', access=True)
+        fcreate_accesses = FieldAccess.check(cls.__name__, fields_names or
+            cls._fields.keys(), 'create', access=True)
+        fdelete_accesses = FieldAccess.check(cls.__name__, fields_names or
+            cls._fields.keys(), 'delete', access=True)
+        for field in (x for x in cls._fields.keys()
                 if ((not fields_names) or x in fields_names)):
             res[field] = {
-                'type': self._columns[field]._type,
+                'type': cls._fields[field]._type,
                 'name': field,
                 }
             for arg in (
@@ -478,8 +469,8 @@
                     'loading',
                     'filename',
                     ):
-                if getattr(self._columns[field], arg, None) is not None:
-                    res[field][arg] = copy.copy(getattr(self._columns[field],
+                if getattr(cls._fields[field], arg, None) is not None:
+                    res[field][arg] = copy.copy(getattr(cls._fields[field],
                         arg))
             if not fwrite_accesses.get(field, True):
                 res[field]['readonly'] = True
@@ -487,78 +478,78 @@
                         'readonly' in res[field]['states']:
                     del res[field]['states']['readonly']
             for arg in ('digits', 'invisible'):
-                if hasattr(self._columns[field], arg) \
-                        and getattr(self._columns[field], arg):
-                    res[field][arg] = copy.copy(getattr(self._columns[field],
+                if hasattr(cls._fields[field], arg) \
+                        and getattr(cls._fields[field], arg):
+                    res[field][arg] = copy.copy(getattr(cls._fields[field],
                         arg))
-            if isinstance(self._columns[field],
+            if isinstance(cls._fields[field],
                     (fields.Function, fields.One2Many)) \
-                    and not self._columns[field].order_field:
+                    and not cls._fields[field].order_field:
                 res[field]['sortable'] = False
-            if ((isinstance(self._columns[field], fields.Function)
-                    and not self._columns[field].searcher)
-                    or self._columns[field]._type in ('binary', 'sha')):
+            if ((isinstance(cls._fields[field], fields.Function)
+                    and not cls._fields[field].searcher)
+                    or cls._fields[field]._type in ('binary', 'sha')):
                 res[field]['searchable'] = False
             else:
                 res[field]['searchable'] = True
 
             if Transaction().context.get('language'):
                 # translate the field label
-                res_trans = translation_obj.get_source(
-                        self._name + ',' + field, 'field',
-                        Transaction().context['language'])
+                res_trans = Translation.get_source(
+                    cls.__name__ + ',' + field, 'field',
+                    Transaction().context['language'])
                 if res_trans:
                     res[field]['string'] = res_trans
-                help_trans = translation_obj.get_source(
-                        self._name + ',' + field, 'help',
-                        Transaction().context['language'])
+                help_trans = Translation.get_source(
+                    cls.__name__ + ',' + field, 'help',
+                    Transaction().context['language'])
                 if help_trans:
                     res[field]['help'] = help_trans
 
-            if hasattr(self._columns[field], 'selection'):
-                if isinstance(self._columns[field].selection, (tuple, list)):
-                    sel = copy.copy(self._columns[field].selection)
+            if hasattr(cls._fields[field], 'selection'):
+                if isinstance(cls._fields[field].selection, (tuple, list)):
+                    sel = copy.copy(cls._fields[field].selection)
                     if Transaction().context.get('language') and \
-                            ((hasattr(self._columns[field],
+                            ((hasattr(cls._fields[field],
                                 'translate_selection') \
-                                and self._columns[field].translate_selection) \
-                                or not hasattr(self._columns[field],
+                                and cls._fields[field].translate_selection) \
+                                or not hasattr(cls._fields[field],
                                     'translate_selection')):
                         # translate each selection option
                         sel2 = []
                         for (key, val) in sel:
-                            val2 = translation_obj.get_source(
-                                    self._name + ',' + field, 'selection',
-                                    language, val)
+                            val2 = Translation.get_source(
+                                cls.__name__ + ',' + field, 'selection',
+                                language, val)
                             sel2.append((key, val2 or val))
                         sel = sel2
                     res[field]['selection'] = sel
                 else:
                     # call the 'dynamic selection' function
                     res[field]['selection'] = copy.copy(
-                            self._columns[field].selection)
+                            cls._fields[field].selection)
             if res[field]['type'] in (
                     'one2many',
                     'many2many',
                     'many2one',
                     'one2one',
                     ):
-                if hasattr(self._columns[field], 'model_name'):
-                    relation = copy.copy(self._columns[field].model_name)
+                if hasattr(cls._fields[field], 'model_name'):
+                    relation = copy.copy(cls._fields[field].model_name)
                 else:
                     relation = copy.copy(
-                        self._columns[field].get_target()._name)
+                        cls._fields[field].get_target().__name__)
                 res[field]['relation'] = relation
-                res[field]['domain'] = copy.copy(self._columns[field].domain)
-                res[field]['context'] = copy.copy(self._columns[field].context)
+                res[field]['domain'] = copy.copy(cls._fields[field].domain)
+                res[field]['context'] = copy.copy(cls._fields[field].context)
                 res[field]['create'] = fcreate_accesses.get(field, True)
                 res[field]['delete'] = fdelete_accesses.get(field, True)
             if res[field]['type'] == 'one2many' \
-                    and hasattr(self._columns[field], 'field'):
+                    and hasattr(cls._fields[field], 'field'):
                 res[field]['relation_field'] = copy.copy(
-                        self._columns[field].field)
+                        cls._fields[field].field)
             if res[field]['type'] in ('datetime', 'time'):
-                res[field]['format'] = copy.copy(self._columns[field].format)
+                res[field]['format'] = copy.copy(cls._fields[field].format)
 
             # convert attributes into pyson
             for attr in ('states', 'domain', 'context', 'digits', 'size',
@@ -573,9 +564,128 @@
                     del res[i]
         return res
 
-    def on_change_with(self, fieldnames, values):
+    def on_change_with(self, fieldnames):
         changes = {}
         for fieldname in fieldnames:
-            changes[fieldname] = getattr(self,
-                'on_change_with_%s' % fieldname)(values)
+            method_name = 'on_change_with_%s' % fieldname
+            changes[fieldname] = getattr(self, method_name)()
         return changes
+
+    def __init__(self, id=None, **kwargs):
+        super(Model, self).__init__()
+        if id is not None:
+            id = int(id)
+        self.__dict__['id'] = id
+        self._values = None
+        parent_values = {}
+        for name, value in kwargs.iteritems():
+            if not name.startswith('_parent_'):
+                setattr(self, name, value)
+            else:
+                parent_values[name] = value
+        for name, value in parent_values.iteritems():
+            parent_name, field = name.split('.', 1)
+            parent_name = parent_name[8:]  # Strip '_parent_'
+            parent = getattr(self, parent_name, None)
+            if parent is not None:
+                setattr(parent, field, value)
+            else:
+                setattr(self, parent_name, {field: value})
+
+    def __getattr__(self, name):
+        if name == 'id':
+            return self.__dict__['id']
+        if (name not in self._fields
+                and name not in self._inherit_fields):
+            # Search for method on inherits parents
+            pool = Pool()
+            for model_name, _ in self._inherits.iteritems():
+                iModel = pool.get(model_name)
+                if isinstance(getattr(iModel, name, None),
+                        collections.Callable):
+                    for cls in iModel.__mro__:
+                        if name in cls.__dict__:
+                            # Return a method bounded to current class
+                            return cls.__dict__[name].__get__(self,
+                                self.__class__)
+        elif self._values and name in self._values:
+            return self._values.get(name)
+        raise AttributeError("'%s' Model has no attribute '%s': %s"
+            % (self.__name__, name, self._values))
+
+    def __setattr__(self, name, value):
+        if name == 'id':
+            self.__dict__['id'] = value
+            return
+        if (name not in self._fields
+                and name in self._inherit_fields):
+            field = self._inherit_fields[name][2]
+            field.__set__(self, value)
+            return
+        super(Model, self).__setattr__(name, value)
+
+    def __getitem__(self, name):
+        warnings.warn('Use __getattr__ instead of __getitem__',
+            DeprecationWarning, stacklevel=2)
+        return getattr(self, name)
+
+    def __contains__(self, name):
+        return (name in self._fields
+            or name in self._inherit_fields)
+
+    def __int__(self):
+        return int(self.id)
+
+    def __str__(self):
+        return '%s,%s' % (self.__name__, self.id)
+
+    def __unicode__(self):
+        return u'%s,%s' % (self.__name__, self.id)
+
+    def __repr__(self):
+        if self.id < 0:
+            return "Pool().get('%s')(**%s)" % (self.__name__,
+                repr(self._default_values))
+        else:
+            return "Pool().get('%s')(%s)" % (self.__name__, self.id)
+
+    def __eq__(self, other):
+        if not isinstance(other, Model):
+            return NotImplemented
+        if self.id is None or other.id is None:
+            return False
+        return (self.__name__, self.id) == (other.__name__, other.id)
+
+    def __ne__(self, other):
+        if not isinstance(other, Model):
+            return NotImplemented
+        if self.id is None or other.id is None:
+            return True
+        return (self.__name__, self.id) != (other.__name__, other.id)
+
+    def __hash__(self):
+        return hash((self.__name__, self.id))
+
+    def __nonzero__(self):
+        return True
+
+    @property
+    def _default_values(self):
+        if self.id >= 0:
+            return self.id
+        values = {}
+        if self._values:
+            for fname, value in self._values.iteritems():
+                if fname in self._fields:
+                    field = self._fields[fname]
+                else:
+                    field = self._inherit_fields[fname][2]
+                if isinstance(field, fields.Reference):
+                    if value is not None:
+                        value = str(value)
+                elif isinstance(value, Model):
+                    value = value._default_values
+                elif isinstance(value, list):
+                    value = [r._default_values for r in value]
+                values[fname] = value
+        return values
diff -r f6ef988fda71 trytond/model/modelsingleton.py
--- a/trytond/model/modelsingleton.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/model/modelsingleton.py	Sun Sep 23 17:30:03 2012 +0200
@@ -8,98 +8,87 @@
     Define a singleton model in Tryton.
     """
 
-    def get_singleton_id(self):
+    @classmethod
+    def get_singleton(cls):
         '''
-        Return the id of the unique record if there is one.
+        Return the instance of the unique record if there is one.
         '''
-        singleton_ids = super(ModelSingleton, self).search([], limit=1)
-        if singleton_ids:
-            return singleton_ids[0]
-        return False
+        singletons = super(ModelSingleton, cls).search([], limit=1)
+        if singletons:
+            return singletons[0]
 
-    def create(self, values):
-        singleton_id = self.get_singleton_id()
-        if singleton_id:
-            self.write(singleton_id, values)
+    @classmethod
+    def create(cls, values):
+        singleton = cls.get_singleton()
+        if singleton:
+            cls.write([singleton], values)
         else:
-            singleton_id = super(ModelSingleton, self).create(values)
-        return singleton_id
+            singleton = super(ModelSingleton, cls).create(values)
+        return singleton
 
-    def read(self, ids, fields_names=None):
-        singleton_id = self.get_singleton_id()
-        if not singleton_id:
-            res = self.default_get(fields_names, with_rec_name=False)
+    @classmethod
+    def read(cls, ids, fields_names=None):
+        singleton = cls.get_singleton()
+        if not singleton:
+            res = cls.default_get(fields_names, with_rec_name=False)
             if not fields_names:
-                fields_names = (set(self._columns.keys()
-                    + self._inherit_fields.keys()))
+                fields_names = (set(cls._fields.keys()
+                    + cls._inherit_fields.keys()))
             for field_name in fields_names:
                 if field_name not in res:
                     res[field_name] = False
-            if not isinstance(ids, (int, long)):
-                res['id'] = ids[0]
-                res = [res]
-            else:
-                res['id'] = ids
-            return res
-        if isinstance(ids, (int, long)):
-            ids2 = singleton_id
-        else:
-            ids2 = [singleton_id]
-        res = super(ModelSingleton, self).read(ids2, fields_names=fields_names)
-        if isinstance(ids, (int, long)):
-            res['id'] = ids
-        else:
-            res[0]['id'] = ids[0]
+            res['id'] = ids[0]
+            return [res]
+        res = super(ModelSingleton, cls).read([singleton.id],
+            fields_names=fields_names)
+        res[0]['id'] = ids[0]
         return res
 
-    def write(self, ids, values):
-        singleton_id = self.get_singleton_id()
-        if not singleton_id:
-            return self.create(values)
-        if isinstance(ids, (int, long)):
-            ids = singleton_id
-        else:
-            ids = [singleton_id]
-        return super(ModelSingleton, self).write(ids, values)
+    @classmethod
+    def write(cls, records, values):
+        singleton = cls.get_singleton()
+        if not singleton:
+            return cls.create(values)
+        return super(ModelSingleton, cls).write([singleton], values)
 
-    def delete(self, ids):
-        singleton_id = self.get_singleton_id()
-        if not singleton_id:
-            return True
-        if isinstance(ids, (int, long)):
-            ids = singleton_id
-        else:
-            ids = [singleton_id]
-        return super(ModelSingleton, self).delete(ids)
+    @classmethod
+    def delete(cls, records):
+        singleton = cls.get_singleton()
+        if not singleton:
+            return
+        return super(ModelSingleton, cls).delete([singleton])
 
-    def copy(self, ids, default=None):
+    @classmethod
+    def copy(cls, records, default=None):
         if default:
-            self.write(ids, default)
-        return ids
+            cls.write(records, default)
+        return records
 
-    def search(self, domain, offset=0, limit=None, order=None, count=False):
-        res = super(ModelSingleton, self).search(domain, offset=offset,
+    @classmethod
+    def search(cls, domain, offset=0, limit=None, order=None, count=False):
+        res = super(ModelSingleton, cls).search(domain, offset=offset,
                 limit=limit, order=order, count=count)
         if not res:
             if count:
                 return 1
-            return [1]
+            return [cls(1)]
         return res
 
-    def default_get(self, fields_names, with_rec_name=True):
+    @classmethod
+    def default_get(cls, fields_names, with_rec_name=True):
         if '_timestamp' in fields_names:
             fields_names = list(fields_names)
             fields_names.remove('_timestamp')
-        res = super(ModelSingleton, self).default_get(fields_names,
+        default = super(ModelSingleton, cls).default_get(fields_names,
                 with_rec_name=with_rec_name)
-        singleton_id = self.get_singleton_id()
-        if singleton_id:
+        singleton = cls.get_singleton()
+        if singleton:
             if with_rec_name:
                 fields_names = fields_names[:]
                 for field in fields_names[:]:
-                    if self._columns[field]._type in ('many2one',):
+                    if cls._fields[field]._type in ('many2one',):
                         fields_names.append(field + '.rec_name')
-            res = self.read(singleton_id, fields_names=fields_names)
-            for field in (x for x in res.keys() if x not in fields_names):
-                del res[field]
-        return res
+            default, = cls.read([singleton.id], fields_names=fields_names)
+            for field in (x for x in default.keys() if x not in fields_names):
+                del default[field]
+        return default
diff -r f6ef988fda71 trytond/model/modelsql.py
--- a/trytond/model/modelsql.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/model/modelsql.py	Sun Sep 23 17:30:03 2012 +0200
@@ -30,68 +30,70 @@
     _order_name = None  # Use to force order field when sorting on Many2One
     _history = False
 
-    def __init__(self):
-        super(ModelSQL, self).__init__()
-        self._sql_constraints = []
-        self._order = [('id', 'ASC')]
-        self._sql_error_messages = {}
+    @classmethod
+    def __setup__(cls):
+        super(ModelSQL, cls).__setup__()
+        cls._sql_constraints = []
+        cls._order = [('id', 'ASC')]
+        cls._sql_error_messages = {}
 
-        if not self._table:
-            self._table = self._name.replace('.', '_')
+        if not cls._table:
+            cls._table = cls.__name__.replace('.', '_')
 
-        assert self._table[-9:] != '__history', \
-                'Model _table %s cannot end with "__history"' % self._table
+        assert cls._table[-9:] != '__history', \
+                'Model _table %s cannot end with "__history"' % cls._table
 
-    def init(self, module_name):
-        super(ModelSQL, self).init(module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(ModelSQL, cls).__register__(module_name)
 
-        if self.table_query():
+        if cls.table_query():
             return
 
         pool = Pool()
 
         # create/update table in the database
-        table = TableHandler(Transaction().cursor, self, module_name)
-        if self._history:
-            history_table = TableHandler(Transaction().cursor, self,
+        table = TableHandler(Transaction().cursor, cls, module_name)
+        if cls._history:
+            history_table = TableHandler(Transaction().cursor, cls,
                     module_name, history=True)
         timestamp_field = FIELDS['timestamp']
         integer_field = FIELDS['integer']
         logs = (
             ('create_date', timestamp_field.sql_type(None),
                 timestamp_field.sql_format, lambda *a: datetime.datetime.now(),
-                self.create_date.string),
+                cls.create_date.string),
             ('write_date', timestamp_field.sql_type(None),
-                timestamp_field.sql_format, None, self.write_date.string),
+                timestamp_field.sql_format, None, cls.write_date.string),
             ('create_uid', (integer_field.sql_type(None)[0],
              'INTEGER REFERENCES res_user ON DELETE SET NULL',),
-             integer_field.sql_format, lambda *a: 0, self.create_uid.string),
+             integer_field.sql_format, lambda *a: 0, cls.create_uid.string),
             ('write_uid', (integer_field.sql_type(None)[0],
              'INTEGER REFERENCES res_user ON DELETE SET NULL'),
-             integer_field.sql_format, None, self.write_uid.string),
+             integer_field.sql_format, None, cls.write_uid.string),
             )
         for log in logs:
             table.add_raw_column(log[0], log[1], log[2],
                     default_fun=log[3], migrate=False, string=log[4])
-        if self._history:
+        if cls._history:
             history_logs = (
                     ('create_date', timestamp_field.sql_type(None),
-                        timestamp_field.sql_format, self.create_date.string),
+                        timestamp_field.sql_format, cls.create_date.string),
                     ('write_date', timestamp_field.sql_type(None),
-                        timestamp_field.sql_format, self.write_date.string),
+                        timestamp_field.sql_format, cls.write_date.string),
                     ('create_uid', (integer_field.sql_type(None)[0],
                      'INTEGER REFERENCES res_user ON DELETE SET NULL',),
-                     integer_field.sql_format, self.create_uid.string),
+                     integer_field.sql_format, cls.create_uid.string),
                     ('write_uid', (integer_field.sql_type(None)[0],
                      'INTEGER REFERENCES res_user ON DELETE SET NULL'),
-                     integer_field.sql_format, self.write_uid.string),
+                     integer_field.sql_format, cls.write_uid.string),
                     )
             for log in history_logs:
                 history_table.add_raw_column(log[0], log[1], log[2],
                         migrate=False, string=log[3])
             history_table.index_action('id', action='add')
 
-        for field_name, field in self._columns.iteritems():
+        for field_name, field in cls._fields.iteritems():
             default_fun = None
             if field_name in (
                     'id',
@@ -104,8 +106,8 @@
                 continue
 
             if not hasattr(field, 'set'):
-                if field_name in self._defaults:
-                    default_fun = self._defaults[field_name]
+                if field_name in cls._defaults:
+                    default_fun = cls._defaults[field_name]
 
                     def unpack_wrapper(fun):
                         def unpack_result(*a):
@@ -115,7 +117,7 @@
                                 result = fun(*a)
                             except Exception:
                                 return None
-                            clean_results = self._clean_defaults(
+                            clean_results = cls._clean_defaults(
                                 {field_name: result})
                             return clean_results[field_name]
                         return unpack_result
@@ -129,7 +131,7 @@
                         FIELDS[field._type].sql_type(field),
                         FIELDS[field._type].sql_format, default_fun,
                         field_size, string=field.string)
-                if self._history:
+                if cls._history:
                     history_table.add_raw_column(field_name,
                             FIELDS[field._type].sql_type(field), None,
                             string=field.string)
@@ -159,69 +161,71 @@
                 fields.Many2Many)):
                 raise Exception('Unknow field type !')
 
-        for field_name, field in self._columns.iteritems():
+        for field_name, field in cls._fields.iteritems():
             if isinstance(field, fields.Many2One) \
-                    and field.model_name == self._name \
+                    and field.model_name == cls.__name__ \
                     and field.left and field.right:
                 with Transaction().set_user(0):
-                    self._rebuild_tree(field_name, False, 0)
+                    cls._rebuild_tree(field_name, False, 0)
 
-        for ident, constraint, _ in self._sql_constraints:
+        for ident, constraint, _ in cls._sql_constraints:
             table.add_constraint(ident, constraint)
 
-        if self._history:
-            self._update_history_table()
+        if cls._history:
+            cls._update_history_table()
             cursor = Transaction().cursor
-            cursor.execute('SELECT id FROM "' + self._table + '"')
+            cursor.execute('SELECT id FROM "' + cls._table + '"')
             if cursor.fetchone():
-                cursor.execute('SELECT id FROM "' + self._table + '__history"')
+                cursor.execute('SELECT id FROM "' + cls._table + '__history"')
                 if not cursor.fetchone():
-                    columns = ['"' + str(x) + '"' for x in self._columns
-                            if not hasattr(self._columns[x], 'set')]
+                    columns = ['"' + str(x) + '"' for x in cls._fields
+                            if not hasattr(cls._fields[x], 'set')]
                     cursor.execute('INSERT ' \
-                            'INTO "' + self._table + '__history" '\
+                            'INTO "' + cls._table + '__history" '\
                             '(' + ','.join(columns) + ') ' \
                             'SELECT ' + ','.join(columns) + \
-                            ' FROM "' + self._table + '"')
-                    cursor.execute('UPDATE "' + self._table + '__history" ' \
+                            ' FROM "' + cls._table + '"')
+                    cursor.execute('UPDATE "' + cls._table + '__history" ' \
                             'SET write_date = NULL')
 
-    def _update_history_table(self):
-        if self._history:
-            table = TableHandler(Transaction().cursor, self)
-            history_table = TableHandler(Transaction().cursor, self,
+    @classmethod
+    def _update_history_table(cls):
+        if cls._history:
+            table = TableHandler(Transaction().cursor, cls)
+            history_table = TableHandler(Transaction().cursor, cls,
                     history=True)
             for column_name in table._columns:
                 string = ''
-                if column_name in self._columns:
-                    string = self._columns[column_name].string
+                if column_name in cls._fields:
+                    string = cls._fields[column_name].string
                 history_table.add_raw_column(column_name,
-                        (table._columns[column_name]['typname'],
-                            table._columns[column_name]['typname']),
-                            None, string=string)
+                    (table._columns[column_name]['typname'],
+                        table._columns[column_name]['typname']),
+                    None, string=string)
 
-    def _get_error_messages(self):
-        res = super(ModelSQL, self)._get_error_messages()
-        res += self._sql_error_messages.values()
-        for _, _, error in self._sql_constraints:
+    @classmethod
+    def _get_error_messages(cls):
+        res = super(ModelSQL, cls)._get_error_messages()
+        res += cls._sql_error_messages.values()
+        for _, _, error in cls._sql_constraints:
             res.append(error)
         return res
 
-    def table_query(self):
+    @staticmethod
+    def table_query():
         '''
         Return None if the model is a real table in the database
         or return a tuple with the SQL query and the arguments.
-
-        :return: None or a tuple with a SQL query and arguments
         '''
         return None
 
-    def create(self, values):
-        super(ModelSQL, self).create(values)
+    @classmethod
+    def create(cls, values):
+        super(ModelSQL, cls).create(values)
         cursor = Transaction().cursor
         pool = Pool()
 
-        if self.table_query():
+        if cls.table_query():
             return False
 
         values = values.copy()
@@ -235,22 +239,22 @@
         # Get default values
         default = []
         avoid_table = []
-        for (i, j) in self._inherits.items():
+        for (i, j) in cls._inherits.items():
             if j in values:
                 avoid_table.append(i)
-        for i in self._columns.keys():
+        for i in cls._fields.keys():
             if not i in values \
                     and i not in ('create_uid', 'create_date',
                             'write_uid', 'write_date'):
                 default.append(i)
-        for i in self._inherit_fields.keys():
+        for i in cls._inherit_fields.keys():
             if ((not i in values)
-                    and (not self._inherit_fields[i][0] in avoid_table)
-                    and i in self._defaults):
+                    and (not cls._inherit_fields[i][0] in avoid_table)
+                    and i in cls._defaults):
                 default.append(i)
 
         if len(default):
-            defaults = self.default_get(default, with_rec_name=False)
+            defaults = cls.default_get(default, with_rec_name=False)
             for field in defaults.keys():
                 if '.' in field:
                     del defaults[field]
@@ -259,86 +263,78 @@
                     del defaults[field]
                 if field in values:
                     del defaults[field]
-            values.update(self._clean_defaults(defaults))
+            values.update(cls._clean_defaults(defaults))
 
         (upd0, upd1, upd2) = ('', '', [])
         upd_todo = []
 
         # Create inherits
         tocreate = {}
-        for i in self._inherits:
-            if self._inherits[i] not in values:
+        for i in cls._inherits:
+            if cls._inherits[i] not in values:
                 tocreate[i] = {}
 
         for i in values.keys():
-            if i in self._inherit_fields:
-                (inherits, _, _) = self._inherit_fields[i]
-                if i in self._columns:
+            if i in cls._inherit_fields:
+                (inherits, _, _) = cls._inherit_fields[i]
+                if i in cls._fields:
                     continue
                 if inherits in tocreate:
                     tocreate[inherits][i] = values[i]
                 del values[i]
 
         for inherits in tocreate:
-            inherits_obj = pool.get(inherits)
+            Inherits = pool.get(inherits)
             with Transaction().set_user(0, set_context=True):
-                inherits_id = inherits_obj.create(tocreate[inherits])
-            values[self._inherits[inherits]] = inherits_id
+                inherits_record = Inherits.create(tocreate[inherits])
+            values[cls._inherits[inherits]] = inherits_record.id
 
         # Insert record
-        for field in values:
-            column = self._columns[field]
-            if not hasattr(self._columns[field], 'set'):
-                upd0 = upd0 + ',"' + field + '"'
+        for fname, value in values.iteritems():
+            field = cls._fields[fname]
+            if not hasattr(field, 'set'):
+                upd0 = upd0 + ',"' + fname + '"'
                 upd1 = upd1 + ', %s'
-                upd2.append(FIELDS[self._columns[field]._type].sql_format(
-                    values[field]))
+                upd2.append(FIELDS[field._type].sql_format(value))
             else:
-                upd_todo.append(field)
-            if field in self._columns \
-                    and hasattr(self._columns[field], 'selection') \
-                    and self._columns[field].selection \
-                    and values[field]:
-                if self._columns[field]._type == 'reference':
-                    if isinstance(values[field], basestring):
-                        val = values[field].split(',')[0]
+                upd_todo.append(fname)
+            if (hasattr(field, 'selection')
+                    and field.selection
+                    and value):
+                if field._type == 'reference':
+                    if isinstance(value, basestring):
+                        value = value.split(',')[0]
                     else:
-                        val, _ = values[field]
+                        value, _ = value
+                if isinstance(field.selection, (tuple, list)):
+                    test = dict(field.selection)
                 else:
-                    val = values[field]
-                if isinstance(self._columns[field].selection, (tuple, list)):
-                    if val not in dict(self._columns[field].selection):
-                        raise Exception('ValidateError',
-                        'The value "%s" for the field "%s" ' \
-                                'is not in the selection' % \
-                                (val, field))
-                else:
-                    if val not in dict(getattr(self,
-                        self._columns[field].selection)()):
-                        raise Exception('ValidateError',
-                        'The value "%s" for the field "%s" ' \
-                                'is not in the selection' % \
-                                (val, field))
+                    test = dict(getattr(cls, field.selection)())
+                if value not in test:
+                    raise Exception('ValidateError',
+                        'The value "%s" for the field "%s" '
+                        'is not in the selection'
+                        % (value, fname))
         upd0 += ', create_uid, create_date'
         upd1 += ', %s, %s'
         upd2.append(Transaction().user)
         upd2.append(datetime.datetime.now())
         try:
             if cursor.has_returning():
-                cursor.execute('INSERT INTO "' + self._table + '" '
+                cursor.execute('INSERT INTO "' + cls._table + '" '
                     '(' + upd0[1:] + ') '
                     'VALUES (' + upd1[1:] + ') RETURNING id',
                     tuple(upd2))
                 id_new, = cursor.fetchone()
             else:
-                id_new = cursor.nextid(self._table)
+                id_new = cursor.nextid(cls._table)
                 if id_new:
-                    cursor.execute('INSERT INTO "' + self._table + '" '
+                    cursor.execute('INSERT INTO "' + cls._table + '" '
                         '(id' + upd0 + ') '
                         'VALUES (' + str(id_new) + upd1 + ')',
                         tuple(upd2))
                 else:
-                    cursor.execute('INSERT INTO "' + self._table + '" '
+                    cursor.execute('INSERT INTO "' + cls._table + '" '
                         '(' + upd0[1:] + ') '
                         'VALUES (' + upd1[1:] + ')',
                         tuple(upd2))
@@ -346,101 +342,99 @@
         except DatabaseIntegrityError, exception:
             with contextlib.nested(Transaction().new_cursor(),
                     Transaction().set_user(0)):
-                for field_name in self._columns:
-                    field = self._columns[field_name]
+                for field_name in cls._fields:
+                    field = cls._fields[field_name]
                     # Check required fields
                     if field.required and \
                             not hasattr(field, 'set') and \
                             field_name not in ('create_uid', 'create_date'):
                         if not values.get(field_name):
-                            self.raise_user_error('required_field',
-                                    error_args=self._get_error_args(
+                            cls.raise_user_error('required_field',
+                                    error_args=cls._get_error_args(
                                         field_name))
                     if isinstance(field, fields.Many2One) \
                             and values.get(field_name):
-                        model_obj = pool.get(field.model_name)
+                        Model = pool.get(field.model_name)
                         create_records = Transaction().create_records.get(
                                 field.model_name, set())
                         delete_records = Transaction().delete_records.get(
                                 field.model_name, set())
-                        if not ((model_obj.search([
-                            ('id', '=', values[field_name]),
-                            ], order=[])
-                            or values[field_name] in create_records)
-                            and values[field_name] not in delete_records):
-                            self.raise_user_error('foreign_model_missing',
-                                    error_args=self._get_error_args(
+                        if not ((Model.search([
+                                            ('id', '=', values[field_name]),
+                                            ], order=[])
+                                    or values[field_name] in create_records)
+                                and values[field_name] not in delete_records):
+                            cls.raise_user_error('foreign_model_missing',
+                                    error_args=cls._get_error_args(
                                         field_name))
-                for name, _, error in self._sql_constraints:
+                for name, _, error in cls._sql_constraints:
                     if name in exception[0]:
-                        self.raise_user_error(error)
-                for name, error in self._sql_error_messages:
+                        cls.raise_user_error(error)
+                for name, error in cls._sql_error_messages:
                     if name in exception[0]:
-                        self.raise_user_error(error)
+                        cls.raise_user_error(error)
             raise
 
-        domain1, domain2 = pool.get('ir.rule').domain_get(self._name,
+        domain1, domain2 = pool.get('ir.rule').domain_get(cls.__name__,
                 mode='create')
         if domain1:
-            cursor.execute('SELECT id FROM "' + self._table + '" ' \
+            cursor.execute('SELECT id FROM "' + cls._table + '" ' \
                     'WHERE id = %s AND (' + domain1 + ')',
                     [id_new] + domain2)
             if not cursor.fetchone():
-                self.raise_user_error('access_error', self._description)
+                cls.raise_user_error('access_error', cls.__name__)
 
-        Transaction().create_records.setdefault(self._name, set()).add(id_new)
+        Transaction().create_records.setdefault(
+            cls.__name__, set()).add(id_new)
 
         for field in values:
-            if getattr(self._columns[field], 'translate', False):
+            if getattr(cls._fields[field], 'translate', False):
                 pool.get('ir.translation').set_ids(
-                        self._name + ',' + field, 'model',
+                        cls.__name__ + ',' + field, 'model',
                         Transaction().language, [id_new], values[field])
 
         for field in upd_todo:
-            self._columns[field].set([id_new], self, field, values[field])
+            cls._fields[field].set([id_new], cls, field, values[field])
 
-        if self._history:
-            cursor.execute('INSERT INTO "' + self._table + '__history" '
+        if cls._history:
+            cursor.execute('INSERT INTO "' + cls._table + '__history" '
                 '(id' + upd0 + ') '
                 'SELECT id' + upd0 + ' '
-                'FROM "' + self._table + '" '
+                'FROM "' + cls._table + '" '
                 'WHERE id = %s', (id_new,))
 
-        self._validate([id_new])
+        record = cls(id_new)
+        cls._validate([record])
 
         # Check for Modified Preorder Tree Traversal
-        for k in self._columns:
-            field = self._columns[k]
+        for k in cls._fields:
+            field = cls._fields[k]
             if isinstance(field, fields.Many2One) \
-                    and field.model_name == self._name \
+                    and field.model_name == cls.__name__ \
                     and field.left and field.right:
-                self._update_tree(id_new, k, field.left, field.right)
+                cls._update_tree(id_new, k, field.left, field.right)
 
-        self.trigger_create(id_new)
+        cls.trigger_create(record)
 
-        return id_new
+        return record
 
-    def read(self, ids, fields_names=None):
+    @classmethod
+    def read(cls, ids, fields_names=None):
         pool = Pool()
-        rule_obj = pool.get('ir.rule')
-        translation_obj = pool.get('ir.translation')
-        super(ModelSQL, self).read(ids, fields_names=fields_names)
+        Rule = pool.get('ir.rule')
+        Translation = pool.get('ir.translation')
+        super(ModelSQL, cls).read(ids, fields_names=fields_names)
         cursor = Transaction().cursor
 
         if not fields_names:
-            fields_names = list(set(self._columns.keys() \
-                    + self._inherit_fields.keys()))
-
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
+            fields_names = list(set(cls._fields.keys() \
+                    + cls._inherit_fields.keys()))
 
         if not ids:
             return []
 
         # construct a clause for the rules :
-        domain1, domain2 = rule_obj.domain_get(self._name, mode='read')
+        domain1, domain2 = Rule.domain_get(cls.__name__, mode='read')
 
         fields_related = {}
         datetime_fields = []
@@ -451,27 +445,27 @@
                 field, field_related = field_name.split('.', 1)
                 fields_related.setdefault(field, [])
                 fields_related[field].append(field_related)
-            elif field_name in self._columns:
-                field = self._columns[field_name]
+            elif field_name in cls._fields:
+                field = cls._fields[field_name]
             else:
-                field = self._inherit_fields[field_name][2]
+                field = cls._inherit_fields[field_name][2]
             if hasattr(field, 'datetime_field') and field.datetime_field:
                 datetime_fields.append(field.datetime_field)
 
         # all inherited fields + all non inherited fields
         fields_pre = [x for x in \
                 fields_names + fields_related.keys() + datetime_fields \
-                if (x in self._columns and \
-                not hasattr(self._columns[x], 'set')) or \
+                if (x in cls._fields and \
+                not hasattr(cls._fields[x], 'set')) or \
                 (x == '_timestamp')] + \
-                self._inherits.values()
+                cls._inherits.values()
 
         res = []
         table_query = ''
         table_args = []
 
-        if self.table_query():
-            table_query, table_args = self.table_query()
+        if cls.table_query():
+            table_query, table_args = cls.table_query()
             table_query = '(' + table_query + ') AS '
 
         in_max = cursor.IN_MAX
@@ -479,33 +473,33 @@
         history_clause = ''
         history_limit = ''
         history_args = []
-        if (self._history
+        if (cls._history
                 and Transaction().context.get('_datetime')
                 and not table_query):
             in_max = 1
-            table_query = '"' + self._table + '__history" AS '
+            table_query = '"' + cls._table + '__history" AS '
             history_clause = ' AND (COALESCE(write_date, create_date) <= %s)'
             history_order = ' ORDER BY COALESCE(write_date, create_date) DESC'
             history_limit = cursor.limit_clause('', 1)
             history_args = [Transaction().context['_datetime']]
         if len(fields_pre):
-            fields_pre2 = ['"%s"."%s" AS "%s"' % (self._table, x, x)
+            fields_pre2 = ['"%s"."%s" AS "%s"' % (cls._table, x, x)
                     for x in fields_pre if x != '_timestamp']
             if '_timestamp' in fields_pre:
-                if not self.table_query():
+                if not cls.table_query():
                     fields_pre2 += ['CAST(EXTRACT(EPOCH FROM '
                             '(COALESCE("%s".write_date, "%s".create_date))) '
                             'AS VARCHAR) AS _timestamp' %
-                            (self._table, self._table)]
+                            (cls._table, cls._table)]
             if 'id' not in fields_pre:
-                fields_pre2 += ['"%s".id AS id' % self._table]
+                fields_pre2 += ['"%s".id AS id' % cls._table]
 
             for i in range(0, len(ids), in_max):
                 sub_ids = ids[i:i + in_max]
                 red_sql, red_ids = reduce_ids('id', sub_ids)
                 if domain1:
                     cursor.execute('SELECT ' + ','.join(fields_pre2)
-                        + ' FROM ' + table_query + '\"' + self._table + '\" '
+                        + ' FROM ' + table_query + '\"' + cls._table + '\" '
                         'WHERE ' + red_sql + history_clause +
                             ' AND (' + domain1 + ') '
                         + history_order
@@ -513,7 +507,7 @@
                         table_args + red_ids + history_args + domain2)
                 else:
                     cursor.execute('SELECT ' + ','.join(fields_pre2)
-                        + ' FROM ' + table_query + '\"' + self._table + '\" '
+                        + ' FROM ' + table_query + '\"' + cls._table + '\" '
                         'WHERE ' + red_sql + history_clause
                         + history_order
                         + history_limit,
@@ -522,7 +516,7 @@
                 if not len(dictfetchall) == len({}.fromkeys(sub_ids)):
                     if domain1:
                         cursor.execute('SELECT id '
-                            'FROM ' + table_query + '\"' + self._table + '\" '
+                            'FROM ' + table_query + '\"' + cls._table + '\" '
                             'WHERE ' + red_sql + history_clause
                             + history_order
                             + history_limit,
@@ -531,9 +525,8 @@
                         if rowcount == -1 or rowcount is None:
                             rowcount = len(cursor.fetchall())
                         if rowcount == len({}.fromkeys(sub_ids)):
-                            self.raise_user_error('access_error',
-                                    self._description)
-                    self.raise_user_error('read_error', self._description)
+                            cls.raise_user_error('access_error', cls.__name__)
+                    cls.raise_user_error('read_error', cls.__name__)
                 res.extend(dictfetchall)
         else:
             res = [{'id': x} for x in ids]
@@ -541,19 +534,19 @@
         for field in fields_pre:
             if field == '_timestamp':
                 continue
-            if getattr(self._columns[field], 'translate', False):
+            if getattr(cls._fields[field], 'translate', False):
                 ids = [x['id'] for x in res]
-                res_trans = translation_obj.get_ids(self._name + ',' + field,
+                res_trans = Translation.get_ids(cls.__name__ + ',' + field,
                     'model', Transaction().language, ids)
                 for i in res:
                     i[field] = res_trans.get(i['id'], False) or i[field]
 
-        for table in self._inherits:
-            field = self._inherits[table]
+        for table in cls._inherits:
+            field = cls._inherits[table]
             inherits_fields = list(
-                    set(self._inherit_fields.keys()).intersection(
+                    set(cls._inherit_fields.keys()).intersection(
                     set(fields_names + fields_related.keys() + datetime_fields)
-                    ).difference(set(self._columns.keys())))
+                    ).difference(set(cls._fields.keys())))
             if not inherits_fields:
                 for record in res:
                     if field not in fields_names + fields_related.keys() + \
@@ -585,27 +578,27 @@
         # all non inherited fields for which there is a get attribute
         fields_post = [x for x in \
                 fields_names + fields_related.keys() + datetime_fields \
-                if x in self._columns \
-                and hasattr(self._columns[x], 'get')]
+                if x in cls._fields \
+                and hasattr(cls._fields[x], 'get')]
         func_fields = {}
         for field in fields_post:
-            if isinstance(self._columns[field], fields.Function):
-                key = (self._columns[field].getter,
-                        getattr(self._columns[field], 'datetime_field', None))
+            if isinstance(cls._fields[field], fields.Function):
+                key = (cls._fields[field].getter,
+                        getattr(cls._fields[field], 'datetime_field', None))
                 func_fields.setdefault(key, [])
                 func_fields[key].append(field)
                 continue
-            if hasattr(self._columns[field], 'datetime_field') \
-                    and self._columns[field].datetime_field:
+            if hasattr(cls._fields[field], 'datetime_field') \
+                    and cls._fields[field].datetime_field:
                 for record in res:
                     with Transaction().set_context(_datetime=record[
-                                self._columns[field].datetime_field]):
-                        res2 = self._columns[field].get([record['id']], self,
+                                cls._fields[field].datetime_field]):
+                        res2 = cls._fields[field].get([record['id']], cls,
                             field, values=[record])
                     record[field] = res2[record['id']]
                 continue
             # get the value of that field for all records/ids
-            res2 = self._columns[field].get(ids, self, field, values=res)
+            res2 = cls._fields[field].get(ids, cls, field, values=res)
             for record in res:
                 record[field] = res2[record['id']]
         for key in func_fields:
@@ -616,12 +609,12 @@
                 for record in res:
                     with Transaction().set_context(
                             _datetime=record[datetime_field]):
-                        res2 = self._columns[field].get([record['id']], self,
+                        res2 = cls._fields[field].get([record['id']], cls,
                                 field_list, values=[record])
                     for field in res2:
                         record[field] = res2[field][record['id']]
                 continue
-            res2 = self._columns[field].get(ids, self, field_list, values=res)
+            res2 = cls._fields[field].get(ids, cls, field_list, values=res)
             for field in res2:
                 for record in res:
                     record[field] = res2[field][record['id']]
@@ -631,24 +624,24 @@
         for field in fields_related.keys() + datetime_fields:
             if field not in fields_names:
                 to_del.add(field)
-            if field not in self._columns:
+            if field not in cls._fields:
                 continue
             if field not in fields_related.keys():
                 continue
             fields_related2values.setdefault(field, {})
-            if self._columns[field]._type in ('many2one', 'one2one'):
-                if hasattr(self._columns[field], 'model_name'):
-                    obj = pool.get(self._columns[field].model_name)
+            if cls._fields[field]._type in ('many2one', 'one2one'):
+                if hasattr(cls._fields[field], 'model_name'):
+                    obj = pool.get(cls._fields[field].model_name)
                 else:
-                    obj = self._columns[field].get_target()
-                if hasattr(self._columns[field], 'datetime_field') \
-                        and self._columns[field].datetime_field:
+                    obj = cls._fields[field].get_target()
+                if hasattr(cls._fields[field], 'datetime_field') \
+                        and cls._fields[field].datetime_field:
                     for record in res:
                         if not record[field]:
                             continue
                         with Transaction().set_context(_datetime=record[
-                                    self._columns[field].datetime_field]):
-                            record2 = obj.read(record[field],
+                                    cls._fields[field].datetime_field]):
+                            record2, = obj.read([record[field]],
                                     fields_related[field])
                         record_id = record2['id']
                         del record2['id']
@@ -664,7 +657,7 @@
                         for record2 in res:
                             fields_related2values[
                                 field][record_id][record2['id']] = record
-            elif self._columns[field]._type == 'reference':
+            elif cls._fields[field]._type == 'reference':
                 for record in res:
                     if not record[field]:
                         continue
@@ -675,17 +668,17 @@
                     if record_id < 0:
                         continue
                     obj = pool.get(model_name)
-                    record2 = obj.read(record_id, fields_related[field])
+                    record2, = obj.read([record_id], fields_related[field])
                     del record2['id']
                     fields_related2values[field][record_id] = record2
 
         if to_del or fields_related.keys() or datetime_fields:
             for record in res:
                 for field in fields_related.keys():
-                    if field not in self._columns:
+                    if field not in cls._fields:
                         continue
                     for related in fields_related[field]:
-                        if self._columns[field]._type in (
+                        if cls._fields[field]._type in (
                                 'many2one', 'one2one'):
                             if record[field]:
                                 record[field + '.' + related] = (
@@ -693,7 +686,7 @@
                                         record[field]][record['id']][related])
                             else:
                                 record[field + '.' + related] = False
-                        elif self._columns[field]._type == 'reference':
+                        elif cls._fields[field]._type == 'reference':
                             if record[field]:
                                 model_name, record_id = record[field].split(
                                         ',', 1)
@@ -708,44 +701,40 @@
                 for field in to_del:
                     del record[field]
 
-        if int_id:
-            return res[0]
         return res
 
-    def write(self, ids, values):
+    @classmethod
+    def write(cls, records, values):
         cursor = Transaction().cursor
         pool = Pool()
-        translation_obj = pool.get('ir.translation')
+        Translation = pool.get('ir.translation')
+        ids = map(int, records)
 
         # Call before cursor cache cleaning
-        trigger_eligibles = self.trigger_write_get_eligibles(
-                isinstance(ids, (int, long)) and [ids] or ids)
+        trigger_eligibles = cls.trigger_write_get_eligibles(records)
 
-        super(ModelSQL, self).write(ids, values)
+        super(ModelSQL, cls).write(records, values)
 
-        if not ids:
+        if not records:
             return True
 
-        if self.table_query():
+        if cls.table_query():
             return True
 
         values = values.copy()
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        else:
-            # _update_tree works if only one record has changed
-            update_tree = False
-            for k in self._columns:
-                field = self._columns[k]
-                if isinstance(field, fields.Many2One) \
-                        and field.model_name == self._name \
-                        and field.left and field.right:
-                    update_tree = True
-            if update_tree:
-                for object_id in ids:
-                    self.write(object_id, values)
-                return True
+        # _update_tree works if only one record has changed
+        update_tree = False
+        for k in cls._fields:
+            field = cls._fields[k]
+            if isinstance(field, fields.Many2One) \
+                    and field.model_name == cls.__name__ \
+                    and field.left and field.right:
+                update_tree = True
+        if update_tree and len(records) > 1:
+            for record in records:
+                cls.write([record], values)
+            return True
 
         if Transaction().timestamp:
             for i in range(0, len(ids), cursor.IN_MAX):
@@ -753,25 +742,26 @@
                 clause = ('(id = %s AND '
                         'CAST(EXTRACT(EPOCH FROM '
                         'COALESCE(write_date, create_date)) AS ' + \
-                        FIELDS['numeric'].sql_type(self.create_date)[1] + \
+                        FIELDS['numeric'].sql_type(cls.create_date)[1] + \
                         ') > %s)')
                 args = []
                 for i in sub_ids:
-                    if Transaction().timestamp.get(self._name + ',' + str(i)):
+                    if Transaction().timestamp.get(
+                            cls.__name__ + ',' + str(i)):
                         args.append(i)
                         args.append(Decimal(Transaction().timestamp[
-                            self._name + ',' + str(i)]))
+                            cls.__name__ + ',' + str(i)]))
                 if args:
                     cursor.execute("SELECT id " \
-                            'FROM "' + self._table + '" ' \
+                            'FROM "' + cls._table + '" ' \
                             'WHERE ' + ' OR '.join(
                                 (clause,) * (len(args) // 2)), args)
                     if cursor.fetchone():
                         raise ConcurrencyException(
                             'Records were modified in the meanwhile')
             for i in ids:
-                if Transaction().timestamp.get(self._name + ',' + str(i)):
-                    del Transaction().timestamp[self._name + ',' + str(i)]
+                if Transaction().timestamp.get(cls.__name__ + ',' + str(i)):
+                    del Transaction().timestamp[cls.__name__ + ',' + str(i)]
 
         # Clean values
         for key in ('create_uid', 'create_date', 'write_uid', 'write_date',
@@ -785,38 +775,38 @@
         updend = []
         direct = []
         for field in values:
-            if field in self._columns:
-                if not hasattr(self._columns[field], 'set'):
-                    if ((not getattr(self._columns[field], 'translate', False))
+            if field in cls._fields:
+                if not hasattr(cls._fields[field], 'set'):
+                    if ((not getattr(cls._fields[field], 'translate', False))
                             or (Transaction().language == CONFIG['language'])):
                         upd0.append(('"' + field + '"', '%s'))
-                        upd1.append(FIELDS[self._columns[field]._type]\
+                        upd1.append(FIELDS[cls._fields[field]._type]\
                                 .sql_format(values[field]))
                     direct.append(field)
                 else:
                     upd_todo.append(field)
             else:
                 updend.append(field)
-            if field in self._columns \
-                    and hasattr(self._columns[field], 'selection') \
-                    and self._columns[field].selection \
+            if field in cls._fields \
+                    and hasattr(cls._fields[field], 'selection') \
+                    and cls._fields[field].selection \
                     and values[field]:
-                if self._columns[field]._type == 'reference':
+                if cls._fields[field]._type == 'reference':
                     if isinstance(values[field], basestring):
                         val = values[field].split(',')[0]
                     else:
                         val, _ = values[field]
                 else:
                     val = values[field]
-                if isinstance(self._columns[field].selection, (tuple, list)):
-                    if val not in dict(self._columns[field].selection):
+                if isinstance(cls._fields[field].selection, (tuple, list)):
+                    if val not in dict(cls._fields[field].selection):
                         raise Exception('ValidateError',
                         'The value "%s" for the field "%s" ' \
                                 'is not in the selection' % \
                                 (val, field))
                 else:
-                    if val not in dict(getattr(self,
-                        self._columns[field].selection)()):
+                    if val not in dict(getattr(cls,
+                        cls._fields[field].selection)()):
                         raise Exception('ValidateError',
                         'The value "%s" for the field "%s" ' \
                                 'is not in the selection' % \
@@ -827,7 +817,7 @@
         upd1.append(Transaction().user)
         upd1.append(datetime.datetime.now())
 
-        domain1, domain2 = pool.get('ir.rule').domain_get(self._name,
+        domain1, domain2 = pool.get('ir.rule').domain_get(cls.__name__,
                 mode='write')
         if domain1:
             domain1 = ' AND (' + domain1 + ') '
@@ -835,37 +825,36 @@
             sub_ids = ids[i:i + cursor.IN_MAX]
             red_sql, red_ids = reduce_ids('id', sub_ids)
             if domain1:
-                cursor.execute('SELECT id FROM "' + self._table + '" ' \
+                cursor.execute('SELECT id FROM "' + cls._table + '" ' \
                         'WHERE ' + red_sql + ' ' + domain1,
                         red_ids + domain2)
             else:
-                cursor.execute('SELECT id FROM "' + self._table + '" ' \
+                cursor.execute('SELECT id FROM "' + cls._table + '" ' \
                         'WHERE ' + red_sql, red_ids)
             rowcount = cursor.rowcount
             if rowcount == -1 or rowcount is None:
                 rowcount = len(cursor.fetchall())
             if not rowcount == len({}.fromkeys(sub_ids)):
                 if domain1:
-                    cursor.execute('SELECT id FROM "' + self._table + '" ' \
+                    cursor.execute('SELECT id FROM "' + cls._table + '" ' \
                             'WHERE ' + red_sql, red_ids)
                     rowcount = cursor.rowcount
                     if rowcount == -1 or rowcount is None:
                         rowcount = len(cursor.fetchall())
                     if rowcount == len({}.fromkeys(sub_ids)):
-                        self.raise_user_error('access_error',
-                                self._description)
-                self.raise_user_error('write_error', self._description)
+                        cls.raise_user_error('access_error', cls.__name__)
+                cls.raise_user_error('write_error', cls.__name__)
             try:
-                cursor.execute('UPDATE "' + self._table + '" '
+                cursor.execute('UPDATE "' + cls._table + '" '
                     'SET ' + ','.join([x[0] + ' = ' + x[1] for x in upd0])
                     + ' WHERE ' + red_sql, upd1 + red_ids)
             except DatabaseIntegrityError, exception:
                 with contextlib.nested(Transaction().new_cursor(),
                         Transaction().set_user(0)):
                     for field_name in values:
-                        if field_name not in self._columns:
+                        if field_name not in cls._fields:
                             continue
-                        field = self._columns[field_name]
+                        field = cls._fields[field_name]
                         # Check required fields
                         if field.required and \
                                 not hasattr(field, 'set') and \
@@ -874,17 +863,17 @@
                                 field_name not in ('create_uid',
                                     'create_date'):
                             if not values[field_name]:
-                                self.raise_user_error('required_field',
-                                        error_args=self._get_error_args(
+                                cls.raise_user_error('required_field',
+                                        error_args=cls._get_error_args(
                                             field_name))
                         if isinstance(field, fields.Many2One) \
                                 and values[field_name]:
-                            model_obj = pool.get(field.model_name)
+                            Model = pool.get(field.model_name)
                             create_records = Transaction().create_records.get(
                                     field.model_name, set())
                             delete_records = Transaction().delete_records.get(
                                     field.model_name, set())
-                            if not ((model_obj.search([
+                            if not ((Model.search([
                                                 ('id', '=',
                                                     values[field_name]),
                                                 ], order=[])
@@ -892,92 +881,93 @@
                                             in create_records))
                                     and (values[field_name]
                                         not in delete_records)):
-                                self.raise_user_error('foreign_model_missing',
-                                    error_args=self._get_error_args(
+                                cls.raise_user_error('foreign_model_missing',
+                                    error_args=cls._get_error_args(
                                         field_name))
-                    for name, _, error in self._sql_constraints:
+                    for name, _, error in cls._sql_constraints:
                         if name in exception[0]:
-                            self.raise_user_error(error)
-                    for name, error in self._sql_error_messages:
+                            cls.raise_user_error(error)
+                    for name, error in cls._sql_error_messages:
                         if name in exception[0]:
-                            self.raise_user_error(error)
+                            cls.raise_user_error(error)
                 raise
 
         for field in direct:
-            if getattr(self._columns[field], 'translate', False):
-                translation_obj.set_ids(
-                        self._name + ',' + field, 'model',
+            if getattr(cls._fields[field], 'translate', False):
+                Translation.set_ids(
+                        cls.__name__ + ',' + field, 'model',
                         Transaction().language, ids, values[field])
 
         # call the 'set' method of fields
         for field in upd_todo:
-            self._columns[field].set(ids, self, field, values[field])
+            cls._fields[field].set(ids, cls, field, values[field])
 
-        if self._history:
-            columns = ['"' + str(x) + '"' for x in self._columns
-                    if not hasattr(self._columns[x], 'set')]
+        if cls._history:
+            columns = ['"' + str(x) + '"' for x in cls._fields
+                    if not hasattr(cls._fields[x], 'set')]
             for obj_id in ids:
-                cursor.execute('INSERT INTO "' + self._table + '__history" ' \
+                cursor.execute('INSERT INTO "' + cls._table + '__history" ' \
                         '(' + ','.join(columns) + ') ' \
                         'SELECT ' + ','.join(columns) + ' ' + \
-                        'FROM "' + self._table + '" ' \
+                        'FROM "' + cls._table + '" ' \
                         'WHERE id = %s', (obj_id,))
 
-        for table in self._inherits:
-            col = self._inherits[table]
-            nids = []
+        for iname in cls._inherits:
+            col = cls._inherits[iname]
+            iids = []
             for i in range(0, len(ids), cursor.IN_MAX):
                 sub_ids = ids[i:i + cursor.IN_MAX]
                 red_sql, red_ids = reduce_ids('id', sub_ids)
                 cursor.execute('SELECT DISTINCT "' + col + '" ' \
-                        'FROM "' + self._table + '" WHERE ' + red_sql,
+                        'FROM "' + cls._table + '" WHERE ' + red_sql,
                         red_ids)
-                nids.extend([x[0] for x in cursor.fetchall()])
+                iids.extend([x[0] for x in cursor.fetchall()])
 
             values2 = {}
             for val in updend:
-                if self._inherit_fields[val][0] == table:
+                if cls._inherit_fields[val][0] == iname:
                     values2[val] = values[val]
             with Transaction().set_user(0, set_context=True):
-                pool.get(table).write(nids, values2)
+                IModel = pool.get(iname)
+                IModel.write(IModel.browse(iids), values2)
 
-        self._validate(ids)
+        cls._validate(records)
 
         # Check for Modified Preorder Tree Traversal
-        for k in self._columns:
-            field = self._columns[k]
+        for k in cls._fields:
+            field = cls._fields[k]
             if isinstance(field, fields.Many2One) \
-                    and field.model_name == self._name \
+                    and field.model_name == cls.__name__ \
                     and field.left and field.right:
                 if field.left in values or field.right in values:
                     raise Exception('ValidateError',
                             'You can not update fields: "%s", "%s"' %
                             (field.left, field.right))
                 if len(ids) == 1:
-                    self._update_tree(ids[0], k, field.left, field.right)
+                    cls._update_tree(ids[0], k, field.left, field.right)
                 else:
                     with Transaction().set_user(0):
-                        self._rebuild_tree(k, False, 0)
+                        cls._rebuild_tree(k, False, 0)
 
-        self.trigger_write(trigger_eligibles)
+        cls.trigger_write(trigger_eligibles)
 
         return True
 
-    def delete(self, ids):
+    @classmethod
+    def delete(cls, records):
         cursor = Transaction().cursor
         pool = Pool()
+        ids = map(int, records)
+
         if not ids:
             return True
 
-        if self.table_query():
+        if cls.table_query():
             return True
 
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-
-        if Transaction().delete and Transaction().delete.get(self._name):
+        if Transaction().delete and Transaction().delete.get(cls.__name__):
             ids = ids[:]
-            for del_id in Transaction().delete[self._name]:
+            for del_id in Transaction().delete[cls.__name__]:
                 for i in range(ids.count(del_id)):
                     ids.remove(del_id)
 
@@ -987,34 +977,35 @@
                 clause = ('(id = %s AND '
                         'CAST(EXTRACT(EPOCH FROM '
                         'COALESCE(write_date, create_date)) AS ' + \
-                        FIELDS['numeric'].sql_type(self.create_date)[1] + \
+                        FIELDS['numeric'].sql_type(cls.create_date)[1] + \
                         ') > %s)')
                 args = []
                 for i in sub_ids:
-                    if Transaction().timestamp.get(self._name + ',' + str(i)):
+                    if Transaction().timestamp.get(
+                            cls.__name__ + ',' + str(i)):
                         args.append(i)
                         args.append(Transaction().timestamp[
-                            self._name + ',' + str(i)])
+                            cls.__name__ + ',' + str(i)])
                 if args:
                     cursor.execute("SELECT id "
-                        'FROM "' + self._table + '" '
+                        'FROM "' + cls._table + '" '
                         'WHERE ' + ' OR '.join(
                             (clause,) * (len(args) / 2)), args)
                     if cursor.fetchone():
                         raise ConcurrencyException(
                             'Records were modified in the meanwhile')
             for i in ids:
-                if Transaction().timestamp.get(self._name + ',' + str(i)):
-                    del Transaction().timestamp[self._name + ',' + str(i)]
+                if Transaction().timestamp.get(cls.__name__ + ',' + str(i)):
+                    del Transaction().timestamp[cls.__name__ + ',' + str(i)]
 
         tree_ids = {}
-        for k in self._columns:
-            field = self._columns[k]
+        for k in cls._fields:
+            field = cls._fields[k]
             if isinstance(field, fields.Many2One) \
-                    and field.model_name == self._name \
+                    and field.model_name == cls.__name__ \
                     and field.left and field.right:
                 red_sql, red_ids = reduce_ids('"' + k + '"', ids)
-                cursor.execute('SELECT id FROM "' + self._table + '" '
+                cursor.execute('SELECT id FROM "' + cls._table + '" '
                     'WHERE ' + red_sql, red_ids)
                 tree_ids[k] = [x[0] for x in cursor.fetchall()]
 
@@ -1025,11 +1016,11 @@
             if hasattr(model, 'table_query') \
                     and model.table_query():
                 continue
-            if not isinstance(model, ModelStorage):
+            if not issubclass(model, ModelStorage):
                 continue
-            for field_name, field in model._columns.iteritems():
+            for field_name, field in model._fields.iteritems():
                 if isinstance(field, fields.Many2One) \
-                        and field.model_name == self._name:
+                        and field.model_name == cls.__name__:
                     if field.ondelete == 'CASCADE':
                         foreign_keys_todelete.append((model, field_name))
                     elif field.ondelete == 'SET NULL':
@@ -1040,9 +1031,9 @@
                     else:
                         foreign_keys_tocheck.append((model, field_name))
 
-        Transaction().delete.setdefault(self._name, set()).update(ids)
+        Transaction().delete.setdefault(cls.__name__, set()).update(ids)
 
-        domain1, domain2 = pool.get('ir.rule').domain_get(self._name,
+        domain1, domain2 = pool.get('ir.rule').domain_get(cls.__name__,
                 mode='delete')
         if domain1:
             domain1 = ' AND (' + domain1 + ') '
@@ -1051,110 +1042,110 @@
             sub_ids = ids[i:i + cursor.IN_MAX]
             red_sql, red_ids = reduce_ids('id', sub_ids)
             if domain1:
-                cursor.execute('SELECT id FROM "' + self._table + '" '
+                cursor.execute('SELECT id FROM "' + cls._table + '" '
                     'WHERE ' + red_sql + ' ' + domain1,
                     red_ids + domain2)
                 rowcount = cursor.rowcount
                 if rowcount == -1 or rowcount is None:
                     rowcount = len(cursor.fetchall())
                 if not rowcount == len({}.fromkeys(sub_ids)):
-                    self.raise_user_error('access_error', self._description)
+                    cls.raise_user_error('access_error', cls._description)
 
-        self.trigger_delete(ids)
+        cls.trigger_delete(records)
 
         for i in range(0, len(ids), cursor.IN_MAX):
             sub_ids = ids[i:i + cursor.IN_MAX]
+            sub_records = records[i:i + cursor.IN_MAX]
             red_sql, red_ids = reduce_ids('id', sub_ids)
 
-            Transaction().delete_records.setdefault(self._name,
+            Transaction().delete_records.setdefault(cls.__name__,
                     set()).update(sub_ids)
 
-            for model, field_name in foreign_keys_toupdate:
-                if not hasattr(model, 'search') \
-                        or not hasattr(model, 'write'):
+            for Model, field_name in foreign_keys_toupdate:
+                if (not hasattr(Model, 'search')
+                        or not hasattr(Model, 'write')):
                     continue
                 red_sql2, red_ids2 = reduce_ids('"' + field_name + '"',
                     sub_ids)
-                cursor.execute('SELECT id FROM "' + model._table + '" '
+                cursor.execute('SELECT id FROM "' + Model._table + '" '
                     'WHERE ' + red_sql2, red_ids2)
-                model_ids = [x[0] for x in cursor.fetchall()]
-                if model_ids:
-                    model.write(model_ids, {
-                        field_name: False,
-                        })
+                models = Model.browse([x[0] for x in cursor.fetchall()])
+                if models:
+                    Model.write(models, {
+                            field_name: False,
+                            })
 
-            for model, field_name in foreign_keys_todelete:
-                if not hasattr(model, 'search') \
-                        or not hasattr(model, 'delete'):
+            for Model, field_name in foreign_keys_todelete:
+                if (not hasattr(Model, 'search')
+                        or not hasattr(Model, 'delete')):
                     continue
                 red_sql2, red_ids2 = reduce_ids('"' + field_name + '"',
                     sub_ids)
-                cursor.execute('SELECT id FROM "' + model._table + '" '
+                cursor.execute('SELECT id FROM "' + Model._table + '" '
                     'WHERE ' + red_sql2, red_ids2)
-                model_ids = [x[0] for x in cursor.fetchall()]
-                if model_ids:
-                    model.delete(model_ids)
+                models = Model.browse([x[0] for x in cursor.fetchall()])
+                if models:
+                    Model.delete(models)
 
-            for model, field_name in foreign_keys_tocheck:
+            for Model, field_name in foreign_keys_tocheck:
                 with Transaction().set_user(0):
-                    if model.search([
-                        (field_name, 'in', sub_ids),
-                        ], order=[]):
+                    if Model.search([
+                                (field_name, 'in', sub_ids),
+                                ], order=[]):
                         error_args = []
-                        error_args.append(self._get_error_args('id')[1])
+                        error_args.append(cls._get_error_args('id')[1])
                         error_args.extend(list(
-                            model._get_error_args(field_name)))
-                        self.raise_user_error('foreign_model_exist',
-                                error_args=tuple(error_args))
+                                Model._get_error_args(field_name)))
+                        cls.raise_user_error('foreign_model_exist',
+                            error_args=tuple(error_args))
 
-            super(ModelSQL, self).delete(sub_ids)
+            super(ModelSQL, cls).delete(sub_records)
 
             try:
-                cursor.execute('DELETE FROM "' + self._table + '" '
+                cursor.execute('DELETE FROM "' + cls._table + '" '
                     'WHERE ' + red_sql, red_ids)
             except DatabaseIntegrityError, exception:
                 with Transaction().new_cursor():
-                    for name, _, error in self._sql_constraints:
+                    for name, _, error in cls._sql_constraints:
                         if name in exception[0]:
-                            self.raise_user_error(error)
-                    for name, error in self._sql_error_messages:
+                            cls.raise_user_error(error)
+                    for name, error in cls._sql_error_messages:
                         if name in exception[0]:
-                            self.raise_user_error(error)
+                            cls.raise_user_error(error)
                 raise
 
-        if self._history:
+        if cls._history:
             for obj_id in ids:
-                cursor.execute('INSERT INTO "' + self._table + '__history" ' \
+                cursor.execute('INSERT INTO "' + cls._table + '__history" ' \
                         '(id, write_uid, write_date) VALUES (%s, %s, %s)',
                         (obj_id, Transaction().user, datetime.datetime.now()))
 
         for k in tree_ids.keys():
-            field = self._columns[k]
+            field = cls._fields[k]
             if len(tree_ids[k]) == 1:
-                self._update_tree(tree_ids[k][0], k, field.left, field.right)
+                cls._update_tree(tree_ids[k][0], k, field.left, field.right)
             else:
                 with Transaction().set_user(0):
-                    self._rebuild_tree(k, False, 0)
+                    cls._rebuild_tree(k, False, 0)
 
-        return True
-
-    def search(self, domain, offset=0, limit=None, order=None, count=False,
+    @classmethod
+    def search(cls, domain, offset=0, limit=None, order=None, count=False,
             query_string=False):
         pool = Pool()
-        rule_obj = pool.get('ir.rule')
+        Rule = pool.get('ir.rule')
         cursor = Transaction().cursor
 
         # Get domain clauses
-        qu1, qu2, tables, tables_args = self.search_domain(domain)
+        qu1, qu2, tables, tables_args = cls.search_domain(domain)
 
         # Get order by
         order_by = []
         if order is None or order is False:
-            order = self._order
+            order = cls._order
         for field, otype in order:
             if otype.upper() not in ('DESC', 'ASC'):
                 raise Exception('Error', 'Wrong order type (%s)!' % otype)
-            order_by2, tables2, tables2_args = self._order_calc(field, otype)
+            order_by2, tables2, tables2_args = cls._order_calc(field, otype)
             order_by += order_by2
             for table in tables2:
                 if table not in tables:
@@ -1170,7 +1161,7 @@
             raise Exception('Error', 'Wrong offset type (%s)!' % type(offset))
 
         # construct a clause for the rules :
-        domain1, domain2 = rule_obj.domain_get(self._name, mode='read')
+        domain1, domain2 = Rule.domain_get(cls.__name__, mode='read')
         if domain1:
             if qu1:
                 qu1 += ' AND ' + domain1
@@ -1180,29 +1171,29 @@
 
         if count:
             cursor.execute(cursor.limit_clause(
-                'SELECT COUNT("%s".id) FROM ' % self._table +
+                'SELECT COUNT("%s".id) FROM ' % cls._table +
                     ' '.join(tables) + (qu1 and ' WHERE ' + qu1 or ''),
                     limit, offset), tables_args + qu2)
             res = cursor.fetchall()
             return res[0][0]
         # execute the "main" query to fetch the ids we were searching for
-        select_fields = ['"' + self._table + '".id AS id']
-        if self._history and Transaction().context.get('_datetime') \
+        select_fields = ['"' + cls._table + '".id AS id']
+        if cls._history and Transaction().context.get('_datetime') \
                 and not query_string:
-            select_fields += ['COALESCE("' + self._table + '".write_date, "'
-                + self._table + '".create_date) AS _datetime']
+            select_fields += ['COALESCE("' + cls._table + '".write_date, "'
+                + cls._table + '".create_date) AS _datetime']
         if not query_string:
             select_fields += [
-                '"' + self._table + '"."' + name + '" AS "' + name + '"'
-                for name, field in self._columns.iteritems()
+                '"' + cls._table + '"."' + name + '" AS "' + name + '"'
+                for name, field in cls._fields.iteritems()
                 if not hasattr(field, 'get')
                 and name != 'id'
                 and not getattr(field, 'translate', False)
                 and field.loading == 'eager']
-            if not self.table_query():
+            if not cls.table_query():
                 select_fields += ['CAST(EXTRACT(EPOCH FROM '
-                        '(COALESCE("' + self._table + '".write_date, '
-                        '"' + self._table + '".create_date))) AS VARCHAR'
+                        '(COALESCE("' + cls._table + '".write_date, '
+                        '"' + cls._table + '".create_date))) AS VARCHAR'
                         ') AS _timestamp']
         query_str = cursor.limit_clause(
                 'SELECT ' + ','.join(select_fields) + ' FROM ' + \
@@ -1212,13 +1203,13 @@
             return (query_str, tables_args + qu2)
         cursor.execute(query_str, tables_args + qu2)
 
-        datas = cursor.dictfetchmany(cursor.IN_MAX)
+        rows = cursor.dictfetchmany(cursor.IN_MAX)
         cache = cursor.get_cache()
-        cache.setdefault(self._name, LRUDict(RECORD_CACHE_SIZE))
-        delete_records = Transaction().delete_records.setdefault(self._name,
+        cache.setdefault(cls.__name__, LRUDict(RECORD_CACHE_SIZE))
+        delete_records = Transaction().delete_records.setdefault(cls.__name__,
                 set())
         keys = None
-        for data in islice(datas, 0, cache.size_limit):
+        for data in islice(rows, 0, cache.size_limit):
             if data['id'] in delete_records:
                 continue
             if not keys:
@@ -1227,17 +1218,17 @@
                     if k in ('_timestamp', '_datetime'):
                         keys.remove(k)
                         continue
-                    field = self._columns[k]
+                    field = cls._fields[k]
                     if field._type not in ('many2one',):
                         keys.remove(k)
                         continue
             for k in keys:
                 del data[k]
-            cache[self._name].setdefault(data['id'], {}).update(data)
+            cache[cls.__name__].setdefault(data['id'], {}).update(data)
 
-        if len(datas) >= cursor.IN_MAX:
+        if len(rows) >= cursor.IN_MAX:
             select_fields2 = [select_fields[0]]
-            if (self._history
+            if (cls._history
                     and Transaction().context.get('_datetime')
                     and not query_string):
                 select_fields2 += [select_fields[1]]
@@ -1247,14 +1238,14 @@
                         'SELECT ' + ','.join(select_fields2) + ' FROM ' + \
                         ' '.join(tables) + (qu1 and ' WHERE ' + qu1 or '') + \
                         (order_by and ' ORDER BY ' + order_by or ''),
-                        limit, offset) + ') AS "' + self._table + '"',
+                        limit, offset) + ') AS "' + cls._table + '"',
                     tables_args + qu2)
-            datas = cursor.dictfetchall()
+            rows = cursor.dictfetchall()
 
-        if self._history and Transaction().context.get('_datetime'):
+        if cls._history and Transaction().context.get('_datetime'):
             res = []
             ids_date = {}
-            for data in datas:
+            for data in rows:
                 if data['id'] in ids_date:
                     if data['_datetime'] <= ids_date[data['id']]:
                         continue
@@ -1269,50 +1260,45 @@
                 cursor.execute(('SELECT id, write_date '
                         'FROM "%s__history" WHERE %s'
                         'AND (write_date IS NOT NULL AND create_date IS NULL)'
-                        ) % (self._table, reduced_sql),
+                        ) % (cls._table, reduced_sql),
                     reduced_ids)
                 for deleted_id, delete_date in cursor.fetchall():
                     if ids_date[deleted_id] < delete_date:
                         to_delete.add(deleted_id)
-            return filter(lambda x: x not in to_delete, res)
+            return cls.browse(filter(lambda x: x not in to_delete, res))
 
-        return [x['id'] for x in datas]
+        return cls.browse([x['id'] for x in rows])
 
-    def search_domain(self, domain, active_test=True):
+    @classmethod
+    def search_domain(cls, domain, active_test=True):
         '''
-        Return SQL clause and arguments for the domain
-
-        :param domain: a domain like in search
-        :param active_test: a boolean to add 'active' test
-        :return: a tuple with
-            - a SQL clause string
-            - a list of arguments for the SQL clause
-            - a list of tables used in the SQL clause
-            - a list of arguments for the tables
+        Return SQL clause, tables to use and arguments for the domain
+        Set active_test to add it.
         '''
-        domain = self._search_domain_active(domain, active_test=active_test)
+        domain = cls._search_domain_active(domain, active_test=active_test)
 
         table_query = ''
         tables_args = []
-        if self.table_query():
-            table_query, tables_args = self.table_query()
+        if cls.table_query():
+            table_query, tables_args = cls.table_query()
             table_query = '(' + table_query + ') AS '
 
-        if self._history and Transaction().context.get('_datetime'):
-            table_query = '"' + self._table + '__history" AS '
+        if cls._history and Transaction().context.get('_datetime'):
+            table_query = '"' + cls._table + '__history" AS '
 
-        tables = [table_query + '"' + self._table + '"']
+        tables = [table_query + '"' + cls._table + '"']
 
-        qu1, qu2 = self.__search_domain_oper(domain, tables, tables_args)
-        if self._history and Transaction().context.get('_datetime'):
+        qu1, qu2 = cls.__search_domain_oper(domain, tables, tables_args)
+        if cls._history and Transaction().context.get('_datetime'):
             if qu1:
                 qu1 += ' AND'
-            qu1 += ' (COALESCE("' + self._table + '".write_date, "' + \
-                    self._table + '".create_date) <= %s)'
+            qu1 += ' (COALESCE("' + cls._table + '".write_date, "' + \
+                    cls._table + '".create_date) <= %s)'
             qu2 += [Transaction().context['_datetime']]
         return qu1, qu2, tables, tables_args
 
-    def __search_domain_oper(self, domain, tables, tables_args):
+    @classmethod
+    def __search_domain_oper(cls, domain, tables, tables_args):
         operator = 'AND'
         if len(domain) and isinstance(domain[0], basestring):
             if domain[0] not in ('AND', 'OR'):
@@ -1331,14 +1317,14 @@
             elif isinstance(arg, list):
                 list_args.append(arg)
 
-        qu1, qu2 = self.__search_domain_calc(tuple_args, tables, tables_args)
+        qu1, qu2 = cls.__search_domain_calc(tuple_args, tables, tables_args)
         if len(qu1):
             qu1 = (' ' + operator + ' ').join(qu1)
         else:
             qu1 = ''
 
         for domain2 in list_args:
-            qu1b, qu2b = self.__search_domain_oper(domain2, tables,
+            qu1b, qu2b = cls.__search_domain_oper(domain2, tables,
                     tables_args)
             if not qu1b:
                 qu1b = '%s'
@@ -1352,30 +1338,31 @@
             qu1 = '(' + qu1 + ')'
         return qu1, qu2
 
-    def __search_domain_calc(self, domain, tables, tables_args):
+    @classmethod
+    def __search_domain_calc(cls, domain, tables, tables_args):
         pool = Pool()
         domain = domain[:]
         cursor = Transaction().cursor
 
         def column_get(model, field_name):
-            field = model._columns.get(field_name)
+            field = model._fields.get(field_name)
             if not field:
                 if not field_name in model._inherit_fields:
                     raise Exception('ValidateError', 'Field "%s" doesn\'t ' \
-                            'exist on "%s"' % (field_name, model._name))
+                            'exist on "%s"' % (field_name, model.__name__))
                 parent_model = pool.get(model._inherit_fields[field_name][0])
                 field = column_get(parent_model, field_name)
             return field
 
         def get_model_for_field(model, field):
-            if field in model._columns:
+            if field in model._fields:
                 return model
             else:
                 parent_model = pool.get(model._inherit_fields[field][0])
                 return get_model_for_field(parent_model, field)
 
         def get_inherits_join(model, field):
-            if field in model._columns:
+            if field in model._fields:
                 return []
             itable = pool.get(model._inherit_fields[field][0])
             table_query = ''
@@ -1386,7 +1373,7 @@
             table_join = 'LEFT JOIN ' + table_query + \
                     '"' + itable._table + '" ON ' \
                     '"%s".id = "%s"."%s"' % (itable._table, model._table,
-                            model._inherits[itable._name])
+                            model._inherits[itable.__name__])
             inherits = [(table_join, table_arg)]
             inherits.extend(get_inherits_join(itable, field))
             return inherits
@@ -1399,88 +1386,91 @@
         joins = []
         while i < len(domain):
             fargs = domain[i][0].split('.', 1)
-            if fargs[0] in self._inherit_fields:
-                inherit_joins = get_inherits_join(self, fargs[0])
+            if fargs[0] in cls._inherit_fields:
+                inherit_joins = get_inherits_join(cls, fargs[0])
                 for (table_join, table_arg) in inherit_joins:
                     if table_join not in tables:
                         tables.append(table_join)
                         tables_args.extend(table_arg)
 
-            field = column_get(self, fargs[0])
-            table = get_model_for_field(self, fargs[0])
+            field = column_get(cls, fargs[0])
+            table = get_model_for_field(cls, fargs[0])
             if len(fargs) > 1:
                 if field._type == 'many2one':
-                    target_obj = pool.get(field.model_name)
+                    Target = pool.get(field.model_name)
                     m2o_search = [(fargs[1], domain[i][1], domain[i][2])]
-                    if ('active' in target_obj._columns
-                            or 'active' in target_obj._inherit_fields):
+                    if ('active' in Target._fields
+                            or 'active' in Target._inherit_fields):
                         m2o_search += [('active', 'in', (True, False))]
                     if hasattr(field, 'search'):
                         domain.extend([(fargs[0], 'in',
-                                    target_obj.search(m2o_search, order=[]))])
+                                    map(int, Target.search(m2o_search,
+                                            order=[])))])
                         domain.pop(i)
                     else:
                         domain[i] = (fargs[0], 'inselect',
-                            target_obj.search(m2o_search, order=[],
+                            Target.search(m2o_search, order=[],
                                 query_string=True), table)
                         i += 1
                     continue
                 elif field._type in ('one2one', 'many2many', 'one2many'):
                     if hasattr(field, 'model_name'):
-                        target_obj = pool.get(field.model_name)
+                        Target = pool.get(field.model_name)
                     else:
-                        target_obj = field.get_target()
+                        Target = field.get_target()
                     if hasattr(field, 'relation_name'):
-                        relation_obj = pool.get(field.relation_name)
+                        Relation = pool.get(field.relation_name)
                         origin, target = field.origin, field.target
                     else:
-                        relation_obj = target_obj
+                        Relation = Target
                         origin, target = field.field, 'id'
-                    if origin in relation_obj._columns:
-                        rev_field = relation_obj._columns[origin]
+                    if origin in Relation._fields:
+                        rev_field = Relation._fields[origin]
                     else:
-                        rev_field = relation_obj._inherit_fields[origin][2]
+                        rev_field = Relation._inherit_fields[origin][2]
                     if rev_field._type == 'reference':
                         sql_type = FIELDS[
-                            self.id._type].sql_type(self.id)[0]
+                            cls.id._type].sql_type(cls.id)[0]
                         origin = ('CAST(SPLIT_PART("%s", \',\', 2) AS %s)'
                             % (origin, sql_type))
                     else:
                         origin = '"%s"' % origin
                     if hasattr(field, 'search'):
-                        domain.extend([(fargs[0], 'in', target_obj.search([
-                            (fargs[1], domain[i][1], domain[i][2]),
-                            ], order=[]))])
+                        domain.extend([(fargs[0], 'in',
+                                    map(int, Target.search([
+                                                (fargs[1], domain[i][1],
+                                                    domain[i][2]),
+                                                ], order=[])))])
                         domain.pop(i)
                     else:
-                        query1, query2 = target_obj.search([
+                        query1, query2 = Target.search([
                             (fargs[1], domain[i][1], domain[i][2]),
                             ], order=[], query_string=True)
                         query1 = ('SELECT %s FROM "%s" WHERE "%s" IN (%s)' %
-                                (origin, relation_obj._table, target, query1))
+                                (origin, Relation._table, target, query1))
                         domain[i] = ('id', 'inselect', (query1, query2))
                         i += 1
                     continue
                 else:
                     raise Exception('ValidateError', 'Clause on field "%s" ' \
                             'doesn\'t work on "%s"' %
-                            (domain[i][0], self._name))
+                            (domain[i][0], cls.__name__))
             if hasattr(field, 'search'):
                 clause = domain.pop(i)
                 domain.extend(field.search(table, clause[0], clause))
             elif field._type == 'one2many':
-                field_obj = pool.get(field.model_name)
+                Field = pool.get(field.model_name)
                 table_query = ''
                 table_args = []
-                if field_obj.table_query():
-                    table_query, table_args = field_obj.table_query()
+                if Field.table_query():
+                    table_query, table_args = Field.table_query()
                     table_query = '(' + table_query + ') AS '
-                if field.field in field_obj._columns:
-                    rev_field = field_obj._columns[field.field]
+                if field.field in Field._fields:
+                    rev_field = Field._fields[field.field]
                 else:
-                    rev_field = field_obj._inherit_fields[field.field][2]
+                    rev_field = Field._inherit_fields[field.field][2]
                 if rev_field._type == 'reference':
-                    sql_type = FIELDS[self.id._type].sql_type(self.id)[0]
+                    sql_type = FIELDS[cls.id._type].sql_type(cls.id)[0]
                     select = ('CAST(SPLIT_PART("%s", \',\', 2) AS %s)'
                         % (field.field, sql_type))
                 else:
@@ -1489,7 +1479,7 @@
                 if isinstance(domain[i][2], bool) or domain[i][2] is None:
                     query1 = 'SELECT ' + select + ' ' \
                             'FROM ' + table_query + \
-                                '"' + field_obj._table + '" ' \
+                                '"' + Field._table + '" ' \
                             'WHERE "' + field.field + '" IS NOT NULL'
                     query2 = table_args
                     clause = 'inselect'
@@ -1501,12 +1491,12 @@
                         target_field = 'rec_name'
                     else:
                         target_field = 'id'
-                    query1, query2 = field_obj.search([
+                    query1, query2 = Field.search([
                             (target_field, domain[i][1], domain[i][2]),
                             ], order=[], query_string=True)
                     query1 = ('SELECT ' + select + ' '
                         'FROM ' + table_query +
-                            '"' + field_obj._table + '" '
+                            '"' + Field._table + '" '
                         'WHERE id IN (' + query1 + ')')
                     query2 = table_args + query2
                     domain[i] = ('id', 'inselect', (query1, query2))
@@ -1514,14 +1504,14 @@
             elif field._type in ('many2many', 'one2one'):
                 # XXX must find a solution for long id list
                 if hasattr(field, 'model_name'):
-                    target_obj = pool.get(field.model_name)
+                    Target = pool.get(field.model_name)
                 else:
-                    target_obj = field.get_target()
+                    Target = field.get_target()
                 if domain[i][1] in ('child_of', 'not child_of'):
                     if isinstance(domain[i][2], basestring):
-                        ids2 = [x[0] for x in target_obj.search([
-                            ('rec_name', 'ilike', domain[i][2]),
-                            ], order=[])]
+                        ids2 = map(int, Target.search([
+                                    ('rec_name', 'ilike', domain[i][2]),
+                                    ], order=[]))
                     elif isinstance(domain[i][2], (int, long)):
                         ids2 = [domain[i][2]]
                     else:
@@ -1530,25 +1520,25 @@
                     def _rec_get(ids, table, parent):
                         if not ids:
                             return []
-                        ids2 = table.search([
-                            (parent, 'in', ids),
-                            (parent, '!=', False),
-                            ], order=[])
+                        ids2 = map(int, table.search([
+                                    (parent, 'in', ids),
+                                    (parent, '!=', None),
+                                    ], order=[]))
                         return ids + _rec_get(ids2, table, parent)
 
-                    if target_obj._name != table._name:
+                    if Target.__name__ != table.__name__:
                         if len(domain[i]) != 4:
                             raise Exception('Error', 'Programming error: '
                                 'child_of on field "%s" is not allowed!'
                                 % domain[i][0])
-                        ids2 = target_obj.search([
-                            (domain[i][3], 'child_of', ids2),
-                            ], order=[])
-                        relation_obj = pool.get(field.relation_name)
+                        ids2 = map(int, Target.search([
+                                    (domain[i][3], 'child_of', ids2),
+                                    ], order=[]))
+                        Relation = pool.get(field.relation_name)
                         red_sql, red_ids = reduce_ids('"' + field.target + '"',
                                 ids2)
                         query1 = 'SELECT "' + field.origin + '" ' \
-                                'FROM "' + relation_obj._table + '" ' \
+                                'FROM "' + Relation._table + '" ' \
                                 'WHERE ' + red_sql + ' ' \
                                     'AND "' + field.origin + '" IS NOT NULL'
                         query2 = red_ids
@@ -1564,19 +1554,19 @@
                             domain[i] = ('id', 'not in', ids2 + _rec_get(ids2,
                                 table, domain[i][0]))
                 else:
-                    relation_obj = pool.get(field.relation_name)
+                    Relation = pool.get(field.relation_name)
                     table_query = ''
                     table_args = []
-                    if relation_obj.table_query():
-                        table_query, table_args = relation_obj.table_query()
+                    if Relation.table_query():
+                        table_query, table_args = Relation.table_query()
                         table_query = '(' + table_query + ') AS '
-                    if field.origin in relation_obj._columns:
-                        origin_field = relation_obj._columns[field.origin]
+                    if field.origin in Relation._fields:
+                        origin_field = Relation._fields[field.origin]
                     else:
                         origin_field = \
-                            relation_obj._inherit_fields[field.origin][2]
+                            Relation._inherit_fields[field.origin][2]
                     if origin_field._type == 'reference':
-                        sql_type = FIELDS[self.id._type].sql_type(self.id)[0]
+                        sql_type = FIELDS[cls.id._type].sql_type(cls.id)[0]
                         select = ('CAST(SPLIT_PART("%s", \',\', 2) AS %s)'
                             % (field.origin, sql_type))
                     else:
@@ -1584,7 +1574,7 @@
                     if isinstance(domain[i][2], bool) or domain[i][2] is None:
                         query1 = 'SELECT ' + select + ' ' \
                                 'FROM ' + table_query + ' '\
-                                    '"' + relation_obj._table + '" ' \
+                                    '"' + Relation._table + '" ' \
                                 'WHERE "' + field.origin + '" IS NOT NULL'
                         query2 = table_args
                         clause = 'inselect'
@@ -1596,14 +1586,13 @@
                             target_field = 'rec_name'
                         else:
                             target_field = 'id'
-                        relation_obj = pool.get(field.relation_name)
 
-                        query1, query2 = target_obj.search([
+                        query1, query2 = Target.search([
                                     (target_field, domain[i][1], domain[i][2]),
                                     ], order=[], query_string=True)
                         query1 = ('SELECT ' + select + ' '
                             'FROM ' + table_query +
-                                '"' + relation_obj._table + '" '
+                                '"' + Relation._table + '" '
                             'WHERE "' + field.target + '" IN (' + query1 + ')')
                         query2 = table_args + query2
                         domain[i] = ('id', 'inselect', (query1, query2))
@@ -1613,10 +1602,10 @@
                 # XXX must find a solution for long id list
                 if domain[i][1] in ('child_of', 'not child_of'):
                     if isinstance(domain[i][2], basestring):
-                        field_obj = pool.get(field.model_name)
-                        ids2 = [x[0] for x in field_obj.search([
-                            ('rec_name', 'like', domain[i][2]),
-                            ], order=[])]
+                        Field = pool.get(field.model_name)
+                        ids2 = map(int, Field.search([
+                                    ('rec_name', 'like', domain[i][2]),
+                                    ], order=[]))
                     elif isinstance(domain[i][2], (int, long)):
                         ids2 = [domain[i][2]]
                     else:
@@ -1625,20 +1614,20 @@
                     def _rec_get(ids, table, parent):
                         if not ids:
                             return []
-                        ids2 = table.search([
-                            (parent, 'in', ids),
-                            (parent, '!=', False),
-                            ])
+                        ids2 = map(int, table.search([
+                                    (parent, 'in', ids),
+                                    (parent, '!=', None),
+                                    ]))
                         return ids + _rec_get(ids2, table, parent)
 
-                    if field.model_name != table._name:
+                    if field.model_name != table.__name__:
                         if len(domain[i]) != 4:
                             raise Exception('Error', 'Programming error: '
                                 'child_of on field "%s" is not allowed!'
                                 % domain[i][0])
-                        ids2 = pool.get(field.model_name).search([
-                            (domain[i][3], 'child_of', ids2),
-                            ], order=[])
+                        ids2 = map(int, pool.get(field.model_name).search([
+                                    (domain[i][3], 'child_of', ids2),
+                                    ], order=[]))
                         if domain[i][1] == 'child_of':
                             domain[i] = (domain[i][0], 'in', ids2, table)
                         else:
@@ -1648,7 +1637,7 @@
                             red_sql, red_ids = reduce_ids('id', ids2)
                             cursor.execute('SELECT "' + field.left + '", ' \
                                         '"' + field.right + '" ' + \
-                                    'FROM "' + self._table + '" ' + \
+                                    'FROM "' + cls._table + '" ' + \
                                     'WHERE ' + red_sql, red_ids)
                             clause = '(1=0) '
                             for left, right in cursor.fetchall():
@@ -1658,7 +1647,7 @@
                                         'AND "' + field.right + '" <= ' + \
                                         str(right) + ')'
 
-                            query = 'SELECT id FROM "' + self._table + '" ' + \
+                            query = 'SELECT id FROM "' + cls._table + '" ' + \
                                     'WHERE ' + clause
                             if domain[i][1] == 'child_of':
                                 domain[i] = ('id', 'inselect', (query, []))
@@ -1673,19 +1662,19 @@
                                     ids2, table, domain[i][0]), table)
                 else:
                     if isinstance(domain[i][2], basestring):
-                        field_obj = pool.get(field.model_name)
+                        Field = pool.get(field.model_name)
                         m2o_search = [('rec_name', domain[i][1], domain[i][2])]
-                        if ('active' in field_obj._columns
-                                or 'active' in field_obj._inherit_fields):
+                        if ('active' in Field._fields
+                                or 'active' in Field._inherit_fields):
                             m2o_search += [('active', 'in', (True, False))]
-                        res_ids = field_obj.search(m2o_search, order=[])
+                        res_ids = map(int, Field.search(m2o_search, order=[]))
                         domain[i] = (domain[i][0], 'in', res_ids, table)
                     else:
                         domain[i] += (table,)
                 i += 1
             else:
                 if getattr(field, 'translate', False):
-                    if self._name == 'ir.model':
+                    if cls.__name__ == 'ir.model':
                         table_join = 'LEFT JOIN "ir_translation" ' \
                                 'ON (ir_translation.name = ' \
                                         'ir_model.model||\',%s\' ' \
@@ -1694,7 +1683,7 @@
                                     'AND ir_translation.type = \'model\' ' \
                                     'AND ir_translation.fuzzy = %%s)' % \
                                 domain[i][0]
-                    elif self._name == 'ir.model.field':
+                    elif cls.__name__ == 'ir.model.field':
                         if domain[i][0] == 'field_description':
                             ttype = 'field'
                         else:
@@ -1716,7 +1705,7 @@
                                     'AND ir_translation.lang = %%s ' \
                                     'AND ir_translation.type = \'model\' ' \
                                     'AND ir_translation.fuzzy = %%s)' % \
-                                (table._table, table._name, domain[i][0])
+                                (table._table, table.__name__, domain[i][0])
                     table_join_args = [Transaction().language, False]
 
                     table_query = ''
@@ -1725,10 +1714,10 @@
                         table_query, table_args = table.table_query()
                         table_query = '(' + table_query + ') AS '
 
-                    translation_obj = pool.get('ir.translation')
+                    Translation = pool.get('ir.translation')
 
                     qu1, qu2, tables, table_args = \
-                            translation_obj.search_domain([
+                            Translation.search_domain([
                                 ('value', domain[i][1], domain[i][2]),
                                 ])
                     qu1 = qu1.replace('"ir_translation"."value"',
@@ -1747,8 +1736,8 @@
 
         qu1, qu2 = [], []
         for arg in domain:
-            table = get_model_for_field(self, arg[0])
-            if table == self and len(arg) > 3:
+            table = get_model_for_field(cls, arg[0])
+            if table == cls and len(arg) > 3:
                 table = arg[3]
             column = column_get(table, arg[0])
             if arg[1] in ('inselect', 'notinselect'):
@@ -1887,7 +1876,8 @@
 
         return qu1, qu2
 
-    def _order_calc(self, field, otype):
+    @classmethod
+    def _order_calc(cls, field, otype):
         pool = Pool()
         order_by = []
         tables = []
@@ -1896,25 +1886,25 @@
         table_name = None
         link_field = None
 
-        if field in self._columns:
-            table_name = self._table
+        if field in cls._fields:
+            table_name = cls._table
 
-            if not hasattr(self._columns[field], 'set'):
+            if not hasattr(cls._fields[field], 'set'):
                 field_name = field
 
-            if self._columns[field].order_field:
-                field_name = self._columns[field].order_field
+            if cls._fields[field].order_field:
+                field_name = cls._fields[field].order_field
 
-            if isinstance(self._columns[field], fields.Many2One):
-                obj = pool.get(self._columns[field].model_name)
+            if isinstance(cls._fields[field], fields.Many2One):
+                obj = pool.get(cls._fields[field].model_name)
                 table_name = obj._table
                 link_field = field
                 field_name = None
 
-                if obj._rec_name in obj._columns:
+                if obj._rec_name in obj._fields:
                     field_name = obj._rec_name
 
-                if obj._order_name in obj._columns:
+                if obj._order_name in obj._fields:
                     field_name = obj._order_name
 
                 if field_name:
@@ -1923,7 +1913,7 @@
                     table_join = 'LEFT JOIN "' + table_name + '" AS ' \
                             '"' + table_name + '.' + link_field + '" ON ' \
                             '"%s.%s".id = "%s"."%s"' % (table_name, link_field,
-                                    self._table, link_field)
+                                    cls._table, link_field)
                     for i in range(len(order_by)):
                         if table_name in order_by[i]:
                             order_by[i] = order_by[i].replace(table_name,
@@ -1951,14 +1941,14 @@
 
                 if obj2 and field_name:
                     table_name2 = obj2._table
-                    link_field2 = obj._inherits[obj2._name]
+                    link_field2 = obj._inherits[obj2.__name__]
                     order_by, tables, tables_args = \
                             obj2._order_calc(field_name, otype)
 
                     table_join = 'LEFT JOIN "' + table_name + '" AS ' \
                             '"' + table_name + '.' + link_field + '" ON ' \
                             '"%s.%s".id = "%s"."%s"' % \
-                            (table_name, link_field, self._table, link_field)
+                            (table_name, link_field, cls._table, link_field)
                     for i in range(len(order_by)):
                         if table_name in order_by[i]:
                             order_by[i] = order_by[i].replace(table_name,
@@ -1993,11 +1983,11 @@
                         tables_args[table_join2] = []
                     return order_by, tables, tables_args
 
-            if field_name in self._columns \
-                    and getattr(self._columns[field_name], 'translate', False):
+            if field_name in cls._fields \
+                    and getattr(cls._fields[field_name], 'translate', False):
                 translation_table = 'ir_translation_%s_%s' % \
                         (table_name, field_name)
-                if self._name == 'ir.model':
+                if cls.__name__ == 'ir.model':
                     table_join = 'LEFT JOIN "ir_translation" ' \
                             'AS "%s" ON ' \
                             '("%s".name = "ir_model".model||\',%s\' ' \
@@ -2008,7 +1998,7 @@
                             (translation_table, translation_table, field_name,
                                     translation_table, translation_table,
                                     translation_table, translation_table)
-                elif self._name == 'ir.model.field':
+                elif cls.__name__ == 'ir.model.field':
                     if field_name == 'field_description':
                         ttype = 'field'
                     else:
@@ -2037,9 +2027,9 @@
                                 'AND "%s".type = \'model\' ' \
                                 'AND "%s".fuzzy = %%s)' % \
                             (translation_table, translation_table, table_name,
-                                    translation_table, self._name, field_name,
-                                    translation_table, translation_table,
-                                    translation_table)
+                                translation_table, cls.__name__, field_name,
+                                translation_table, translation_table,
+                                translation_table)
                 if table_join not in tables:
                     tables.append(table_join)
                     tables_args[table_join] = [Transaction().language, False]
@@ -2048,14 +2038,14 @@
                         + '"' + table_name + '".' + field_name + ') ' + otype)
                 return order_by, tables, tables_args
 
-            if field_name in self._columns \
-                    and self._columns[field_name]._type == 'selection' \
-                    and self._columns[field_name].order_field is None:
-                selections = self.fields_get([field_name]
+            if field_name in cls._fields \
+                    and cls._fields[field_name]._type == 'selection' \
+                    and cls._fields[field_name].order_field is None:
+                selections = cls.fields_get([field_name]
                         )[field_name]['selection']
                 if not isinstance(selections, (tuple, list)):
-                    selections = getattr(self,
-                            self._columns[field_name].selection)()
+                    selections = getattr(cls,
+                            cls._fields[field_name].selection)()
                 order = 'CASE ' + table_name + '.' + field_name
                 for selection in selections:
                     order += ' WHEN \'%s\' THEN \'%s\'' % selection
@@ -2074,22 +2064,23 @@
                         % (table_name, field_name, otype))
                 return order_by, tables, tables_args
 
-        if field in self._inherit_fields.keys():
-            obj = pool.get(self._inherit_fields[field][0])
+        if field in cls._inherit_fields.keys():
+            obj = pool.get(cls._inherit_fields[field][0])
             table_name = obj._table
-            link_field = self._inherits[obj._name]
+            link_field = cls._inherits[obj.__name__]
             order_by, tables, tables_args = obj._order_calc(field, otype)
             table_join = 'LEFT JOIN "' + table_name + '" ON ' \
                     '"%s".id = "%s"."%s"' % \
-                    (table_name, self._table, link_field)
+                    (table_name, cls._table, link_field)
             if table_join not in tables:
                 tables.insert(0, table_join)
             return order_by, tables, tables_args
 
         raise Exception('Error', 'Wrong field name (%s) for %s in order!' %
-            (field, self._name))
+            (field, cls.__name__))
 
-    def _rebuild_tree(self, parent, parent_id, left):
+    @classmethod
+    def _rebuild_tree(cls, parent, parent_id, left):
         '''
         Rebuild left, right value for the tree.
         '''
@@ -2097,23 +2088,24 @@
         right = left + 1
 
         with Transaction().set_user(0):
-            child_ids = self.search([
-                (parent, '=', parent_id),
-                ])
+            childs = cls.search([
+                    (parent, '=', parent_id),
+                    ])
 
-        for child_id in child_ids:
-            right = self._rebuild_tree(parent, child_id, right)
+        for child in childs:
+            right = cls._rebuild_tree(parent, child.id, right)
 
-        field = self._columns[parent]
+        field = cls._fields[parent]
 
         if parent_id:
-            cursor.execute('UPDATE "' + self._table + '" ' \
+            cursor.execute('UPDATE "' + cls._table + '" ' \
                     'SET "' + field.left + '" = %s, ' \
                         '"' + field.right + '" = %s ' \
                     'WHERE id = %s', (left, right, parent_id))
         return right + 1
 
-    def _update_tree(self, object_id, field_name, left, right):
+    @classmethod
+    def _update_tree(cls, object_id, field_name, left, right):
         '''
         Update left, right values for the tree.
         Remarks:
@@ -2123,14 +2115,14 @@
         '''
         cursor = Transaction().cursor
         cursor.execute('SELECT "' + left + '", "' + right + '" ' \
-                'FROM "' + self._table + '" ' \
+                'FROM "' + cls._table + '" ' \
                 'WHERE id = %s', (object_id,))
         fetchone = cursor.fetchone()
         if not fetchone:
             return
         old_left, old_right = fetchone
         if old_left == old_right:
-            cursor.execute('UPDATE "' + self._table + '" ' \
+            cursor.execute('UPDATE "' + cls._table + '" ' \
                     'SET "' + right + '" = "' + right + '" + 1 ' \
                     'WHERE id = %s', (object_id,))
             old_right += 1
@@ -2138,42 +2130,42 @@
         parent_right = 1
 
         cursor.execute('SELECT "' + field_name + '" ' \
-                'FROM "' + self._table + '" ' \
+                'FROM "' + cls._table + '" ' \
                 'WHERE id = %s', (object_id,))
         parent_id = cursor.fetchone()[0] or False
 
         if parent_id:
             cursor.execute('SELECT "' + right + '" ' \
-                    'FROM "' + self._table + '" ' \
+                    'FROM "' + cls._table + '" ' \
                     'WHERE id = %s', (parent_id,))
             parent_right = cursor.fetchone()[0]
         else:
             cursor.execute('SELECT MAX("' + right + '") ' \
-                    'FROM "' + self._table + '" ' \
+                    'FROM "' + cls._table + '" ' \
                     'WHERE "' + field_name + '" IS NULL')
             fetchone = cursor.fetchone()
             if fetchone:
                 parent_right = fetchone[0] + 1
 
-        cursor.execute('SELECT id FROM "' + self._table + '" ' \
+        cursor.execute('SELECT id FROM "' + cls._table + '" ' \
                 'WHERE "' + left + '" >= %s AND "' + right + '" <= %s',
                 (old_left, old_right))
         child_ids = [x[0] for x in cursor.fetchall()]
 
         if len(child_ids) > cursor.IN_MAX:
             with Transaction().set_user(0):
-                return self._rebuild_tree(field_name, False, 0)
+                return cls._rebuild_tree(field_name, False, 0)
 
         red_child_sql, red_child_ids = reduce_ids('id', child_ids)
         # ids for left update
-        cursor.execute('SELECT id FROM "' + self._table + '" ' \
+        cursor.execute('SELECT id FROM "' + cls._table + '" ' \
                 'WHERE "' + left + '" >= %s ' \
                     'AND NOT ' + red_child_sql,
                     [parent_right] + red_child_ids)
         left_ids = [x[0] for x in cursor.fetchall()]
 
         # ids for right update
-        cursor.execute('SELECT id FROM "' + self._table + '" ' \
+        cursor.execute('SELECT id FROM "' + cls._table + '" ' \
                 'WHERE "' + right + '" >= %s ' \
                     'AND NOT ' + red_child_sql,
                     [parent_right] + red_child_ids)
@@ -2183,7 +2175,7 @@
             for i in range(0, len(left_ids), cursor.IN_MAX):
                 sub_ids = left_ids[i:i + cursor.IN_MAX]
                 red_sub_sql, red_sub_ids = reduce_ids('id', sub_ids)
-                cursor.execute('UPDATE "' + self._table + '" ' \
+                cursor.execute('UPDATE "' + cls._table + '" ' \
                         'SET "' + left + '" = "' + left + '" + ' \
                             + str(old_right - old_left + 1) + ' ' \
                         'WHERE ' + red_sub_sql, red_sub_ids)
@@ -2191,12 +2183,12 @@
             for i in range(0, len(right_ids), cursor.IN_MAX):
                 sub_ids = right_ids[i:i + cursor.IN_MAX]
                 red_sub_sql, red_sub_ids = reduce_ids('id', sub_ids)
-                cursor.execute('UPDATE "' + self._table + '" ' \
+                cursor.execute('UPDATE "' + cls._table + '" ' \
                         'SET "' + right + '" = "' + right + '" + ' \
                             + str(old_right - old_left + 1) + ' ' \
                         'WHERE ' + red_sub_sql, red_sub_ids)
 
-        cursor.execute('UPDATE "' + self._table + '" ' \
+        cursor.execute('UPDATE "' + cls._table + '" ' \
                 'SET "' + left + '" = "' + left + '" + ' \
                         + str(parent_right - old_left) + ', ' \
                     '"' + right + '" = "' + right + '" + ' \
@@ -2206,21 +2198,21 @@
         # Use root user to by-pass rules
         with contextlib.nested(Transaction().set_user(0),
                 Transaction().set_context(active_test=False)):
-            brother_ids = self.search([
-                (field_name, '=', parent_id),
-                ])
+            brother_ids = map(int, cls.search([
+                        (field_name, '=', parent_id),
+                        ]))
         if brother_ids[-1] != object_id:
             next_id = brother_ids[brother_ids.index(object_id) + 1]
             cursor.execute('SELECT "' + left + '" ' \
-                    'FROM "' + self._table + '" ' \
+                    'FROM "' + cls._table + '" ' \
                     'WHERE id = %s', (next_id,))
             next_left = cursor.fetchone()[0]
             cursor.execute('SELECT "' + left + '" '\
-                    'FROM "' + self._table + '" ' \
+                    'FROM "' + cls._table + '" ' \
                     'WHERE id = %s', (object_id,))
             current_left = cursor.fetchone()[0]
 
-            cursor.execute('UPDATE "' + self._table + '" ' \
+            cursor.execute('UPDATE "' + cls._table + '" ' \
                     'SET "' + left + '" = "' + left + '" + ' \
                             + str(old_right - old_left + 1) + ', ' \
                         '"' + right + '" = "' + right + '" + ' \
@@ -2228,20 +2220,22 @@
                     'WHERE "' + left + '" >= %s AND "' + right + '" <= %s',
                     (next_left, current_left))
 
-            cursor.execute('UPDATE "' + self._table + '" ' \
+            cursor.execute('UPDATE "' + cls._table + '" ' \
                     'SET "' + left + '" = "' + left + '" - ' \
                             + str(current_left - next_left) + ', ' \
                         '"' + right + '" = "' + right + '" - ' \
                             + str(current_left - next_left) + ' ' \
                     'WHERE ' + red_child_sql, red_child_ids)
 
-    def _validate(self, ids):
-        super(ModelSQL, self)._validate(ids)
+    @classmethod
+    def _validate(cls, records):
+        super(ModelSQL, cls)._validate(records)
         cursor = Transaction().cursor
         if cursor.has_constraint():
             return
         # Works only for a single transaction
-        for _, sql, error in self._sql_constraints:
+        ids = map(int, records)
+        for _, sql, error in cls._sql_constraints:
             match = _RE_UNIQUE.match(sql)
             if match:
                 sql = match.group(1)
@@ -2254,18 +2248,18 @@
                     red_sql, red_ids = reduce_ids('id', sub_ids)
 
                     cursor.execute('SELECT id,' + sql + ' ' \
-                            'FROM "' + self._table + '" ' \
+                            'FROM "' + cls._table + '" ' \
                             'WHERE ' + red_sql, red_ids)
 
                     fetchall = cursor.fetchall()
                     cursor.execute('SELECT id ' \
-                            'FROM "' + self._table + '" ' \
+                            'FROM "' + cls._table + '" ' \
                             'WHERE ' + \
                                 ' OR '.join((sql_clause,) * len(fetchall)),
                             reduce(lambda x, y: x + list(y), fetchall, []))
 
                     if cursor.fetchone():
-                        self.raise_user_error(error)
+                        cls.raise_user_error(error)
                 continue
             match = _RE_CHECK.match(sql)
             if match:
@@ -2274,9 +2268,9 @@
                     sub_ids = ids[i:i + cursor.IN_MAX]
                     red_sql, red_ids = reduce_ids('id', sub_ids)
                     cursor.execute('SELECT id ' \
-                            'FROM "' + self._table + '" ' \
+                            'FROM "' + cls._table + '" ' \
                             'WHERE NOT (' + sql + ') ' \
                                 'AND ' + red_sql, red_ids)
                     if cursor.fetchone():
-                        self.raise_user_error(error)
+                        cls.raise_user_error(error)
                     continue
diff -r f6ef988fda71 trytond/model/modelstorage.py
--- a/trytond/model/modelstorage.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/model/modelstorage.py	Sun Sep 23 17:30:03 2012 +0200
@@ -3,31 +3,34 @@
 
 import datetime
 import time
-from decimal import Decimal
 import logging
-from itertools import chain
+import contextlib
+import traceback
+import sys
+import csv
 try:
     import cStringIO as StringIO
 except ImportError:
     import StringIO
-import csv
+
+from decimal import Decimal
+from itertools import islice, ifilter, ifilterfalse, chain
 from functools import reduce
-import traceback
-import sys
+
 from trytond.model import Model
 from trytond.model import fields
-from trytond.model.browse import BrowseRecordList, BrowseRecord, \
-        BrowseRecordNull
-from trytond.model.browse import EvalEnvironment
-from trytond.tools import safe_eval, reduce_domain, datetime_strftime
+from trytond.tools import safe_eval, reduce_domain
 from trytond.pyson import PYSONEncoder, PYSONDecoder, PYSON
-from trytond.const import OPERATORS, RECORD_CACHE_SIZE
+from trytond.const import OPERATORS, RECORD_CACHE_SIZE, BROWSE_FIELD_TRESHOLD
 from trytond.transaction import Transaction
 from trytond.pool import Pool
 from trytond.cache import LRUDict
 from trytond.config import CONFIG
+from trytond.rpc import RPC
 from .modelview import ModelView
 
+__all__ = ['ModelStorage']
+
 
 class ModelStorage(Model):
     """
@@ -41,237 +44,211 @@
     rec_name = fields.Function(fields.Char('Name'), 'get_rec_name',
             searcher='search_rec_name')
 
-    def __init__(self):
-        super(ModelStorage, self).__init__()
-        if isinstance(self, ModelView):
-            self._rpc.update({
-                'create': True,
-                'read': False,
-                'write': True,
-                'delete': True,
-                'copy': True,
-                'search': False,
-                'search_count': False,
-                'search_read': False,
-                'export_data': False,
-                'import_data': True,
-            })
-        self._constraints = []
+    @classmethod
+    def __setup__(cls):
+        super(ModelStorage, cls).__setup__()
+        if issubclass(cls, ModelView):
+            cls.__rpc__.update({
+                    'create': RPC(readonly=False, result=int),
+                    'read': RPC(),
+                    'write': RPC(readonly=False, instantiate=0),
+                    'delete': RPC(readonly=False, instantiate=0),
+                    'copy': RPC(readonly=False, instantiate=0,
+                        result=lambda r: map(int, r)),
+                    'search': RPC(result=lambda r: map(int, r)),
+                    'search_count': RPC(),
+                    'search_read': RPC(),
+                    'export_data': RPC(instantiate=0),
+                    'import_data': RPC(readonly=False),
+                    })
+        cls._constraints = []
 
-    def default_create_uid(self):
+    @staticmethod
+    def default_create_uid():
         "Default value for uid field."
         return int(Transaction().user)
 
-    def default_create_date(self):
+    @staticmethod
+    def default_create_date():
         "Default value for create_date field."
         return datetime.datetime.today()
 
-    def __fields_by_model(self, values):
+    @classmethod
+    def __fields_by_model(cls, values):
         "Return a dictionary with list of fields from values grouped by model"
         fields_by_model = {}
         for field in values:
-            if field in self._columns:
-                model = self._name
+            if field in cls._fields:
+                model = cls.__name__
             else:
-                model = self._inherit_fields[field][0]
+                model = cls._inherit_fields[field][0]
             fields_by_model.setdefault(model, []).append(field)
         return fields_by_model
 
-    def create(self, values):
+    @classmethod
+    def create(cls, values):
         '''
-        Create records.
-
-        :param values: a dictionary with fields names as key
-                and created values as value
-        :return: the id of the created record
+        Create record and return an instance.
         '''
         pool = Pool()
-        model_access_obj = pool.get('ir.model.access')
-        model_field_access_obj = pool.get('ir.model.field.access')
+        ModelAccess = pool.get('ir.model.access')
+        ModelFieldAccess = pool.get('ir.model.field.access')
 
-        model_access_obj.check(self._name, 'create')
-        for model, fields in self.__fields_by_model(values).iteritems():
-            model_field_access_obj.check(model, fields, 'write')
-        return False
+        ModelAccess.check(cls.__name__, 'create')
+        for model, fields in cls.__fields_by_model(values).iteritems():
+            ModelFieldAccess.check(model, fields, 'write')
 
-    def trigger_create(self, id):
+        # Increase transaction counter
+        Transaction().counter += 1
+
+    @classmethod
+    def trigger_create(cls, record):
         '''
         Trigger create actions
+        '''
+        Trigger = Pool().get('ir.trigger')
+        triggers = Trigger.get_triggers(cls.__name__, 'create')
+        if not triggers:
+            return
+        for trigger in triggers:
+            if Trigger.eval(trigger, record):
+                Trigger.trigger_action([record], trigger)
 
-        :param id: the created id
+    @classmethod
+    def read(cls, ids, fields_names=None):
         '''
-        trigger_obj = Pool().get('ir.trigger')
-        trigger_ids = trigger_obj.get_triggers(self._name, 'create')
-        if not trigger_ids:
-            return
-        record = self.browse(id)
-        triggers = trigger_obj.browse(trigger_ids)
-        for trigger in triggers:
-            if trigger_obj.eval(trigger, record):
-                trigger_obj.trigger_action([id], trigger.id)
-
-    def read(self, ids, fields_names=None):
-        '''
-        Read records.
-
-        :param ids: a list of ids or an id
-        :param fields_names: fields names to read if None read all fields
-        :return: a list of dictionnary or a dictionnary if ids is an id
-            the dictionnaries will have fields names as key
-            and fields value as value. The list will not be in the same order.
+        Read fields_names of record ids.
+        If fields_names is None, it read all fields.
+        The order is not guaranteed.
         '''
         pool = Pool()
-        model_access_obj = pool.get('ir.model.access')
-        model_field_access_obj = pool.get('ir.model.field.access')
+        ModelAccess = pool.get('ir.model.access')
+        ModelFieldAccess = pool.get('ir.model.field.access')
 
-        model_access_obj.check(self._name, 'read')
-        model_field_access_obj.check(self._name,
-                fields_names or self._columns.keys(), 'read')
+        ModelAccess.check(cls.__name__, 'read')
+        ModelFieldAccess.check(cls.__name__,
+                fields_names or cls._fields.keys(), 'read')
         if isinstance(ids, (int, long)):
             return {}
         return []
 
-    def write(self, ids, values):
+    @classmethod
+    def write(cls, records, values):
         '''
         Write values on records.
-
-        :param ids: a list of ids or an id
-        :param values: a dictionary with fields names as key
-                and written values as value
-        :return: True if succeed
         '''
         pool = Pool()
-        model_access_obj = pool.get('ir.model.access')
-        model_field_access_obj = pool.get('ir.model.field.access')
+        ModelAccess = pool.get('ir.model.access')
+        ModelFieldAccess = pool.get('ir.model.field.access')
 
-        model_access_obj.check(self._name, 'write')
-        for model, fields in self.__fields_by_model(values).iteritems():
-            model_field_access_obj.check(model, fields, 'write')
-        if not self.check_xml_record(ids, values):
-            self.raise_user_error('write_xml_record',
+        ModelAccess.check(cls.__name__, 'write')
+        for model, fields in cls.__fields_by_model(values).iteritems():
+            ModelFieldAccess.check(model, fields, 'write')
+        if not cls.check_xml_record(records, values):
+            cls.raise_user_error('write_xml_record',
                     error_description='xml_record_desc')
 
+        # Increase transaction counter
+        Transaction().counter += 1
+
+        # Clean local cache
+        for record in records:
+            local_cache = record._local_cache.get(record.id)
+            if local_cache:
+                local_cache.clear()
+
+        # Clean cursor cache
+        for cache in Transaction().cursor.cache.itervalues():
+            if cls.__name__ in cache:
+                for record in records:
+                    if record.id in cache[cls.__name__]:
+                        cache[cls.__name__][record.id].clear()
+
+    @classmethod
+    def trigger_write_get_eligibles(cls, records):
+        '''
+        Return eligible records for write actions by triggers
+        '''
+        Trigger = Pool().get('ir.trigger')
+        triggers = Trigger.get_triggers(cls.__name__, 'write')
+        if not triggers:
+            return {}
+        eligibles = {}
+        for trigger in triggers:
+            eligibles[trigger] = []
+            for record in records:
+                if not Trigger.eval(trigger, record):
+                    eligibles[trigger].append(record)
+        return eligibles
+
+    @classmethod
+    def trigger_write(cls, eligibles):
+        '''
+        Trigger write actions.
+        eligibles is a dictionary of the lists of eligible records by triggers
+        '''
+        Trigger = Pool().get('ir.trigger')
+        for trigger, records in eligibles.iteritems():
+            triggered = []
+            for record in records:
+                if Trigger.eval(trigger, record):
+                    triggered.append(record)
+            if triggered:
+                Trigger.trigger_action(triggered, trigger)
+
+    @classmethod
+    def delete(cls, records):
+        '''
+        Delete records.
+        '''
+        ModelAccess = Pool().get('ir.model.access')
+
+        ModelAccess.check(cls.__name__, 'delete')
+        if not cls.check_xml_record(records, None):
+            cls.raise_user_error('delete_xml_record',
+                    error_description='xml_record_desc')
+
+        # Increase transaction counter
+        Transaction().counter += 1
+
         # Clean cursor cache
         for cache in Transaction().cursor.cache.values():
             for cache in (cache, cache.get('_language_cache', {}).values()):
-                if self._name in cache:
-                    if isinstance(ids, (int, long)):
-                        ids = [ids]
-                    for i in ids:
-                        if i in cache[self._name]:
-                            cache[self._name][i] = {}
-        return False
+                if cls.__name__ in cache:
+                    for record in records:
+                        if record.id in cache[cls.__name__]:
+                            del cache[cls.__name__][record.id]
 
-    def trigger_write_get_eligibles(self, ids):
-        '''
-        Return eligible ids for write actions by triggers
-
-        :param ids: a list of ids
-        :return: a dictionary of the lists of eligible ids by triggers
-        '''
-        trigger_obj = Pool().get('ir.trigger')
-        trigger_ids = trigger_obj.get_triggers(self._name, 'write')
-        if not trigger_ids:
-            return {}
-        records = self.browse(ids)
-        triggers = trigger_obj.browse(trigger_ids)
-        eligibles = {}
-        for trigger in triggers:
-            eligibles[trigger.id] = []
-            for record in records:
-                if not trigger_obj.eval(trigger, record):
-                    eligibles[trigger.id].append(record.id)
-        return eligibles
-
-    def trigger_write(self, eligibles):
-        '''
-        Trigger write actions
-
-        :param eligibles: a dictionary of the lists of eligible ids by triggers
-        '''
-        trigger_obj = Pool().get('ir.trigger')
-        trigger_ids = eligibles.keys()
-        if not trigger_ids:
-            return
-        records = self.browse(list(chain(*eligibles.values())))
-        id2record = dict((x.id, x) for x in records)
-        triggers = trigger_obj.browse(trigger_ids)
-        for trigger in triggers:
-            triggered_ids = []
-            for record_id in eligibles[trigger.id]:
-                record = id2record[record_id]
-                if trigger_obj.eval(trigger, record):
-                    triggered_ids.append(record.id)
-            if triggered_ids:
-                trigger_obj.trigger_action(triggered_ids, trigger.id)
-
-    def delete(self, ids):
-        '''
-        Delete records.
-
-        :param ids: a list of ids or an id
-        :return: True if succeed
-        '''
-        model_access_obj = Pool().get('ir.model.access')
-
-        model_access_obj.check(self._name, 'delete')
-        if not self.check_xml_record(ids, None):
-            self.raise_user_error('delete_xml_record',
-                    error_description='xml_record_desc')
-
-        # Clean cursor cache
-        for cache in Transaction().cursor.cache.values():
-            for cache in (cache, cache.get('_language_cache', {}).values()):
-                if self._name in cache:
-                    if isinstance(ids, (int, long)):
-                        ids = [ids]
-                    for i in ids:
-                        if i in cache[self._name]:
-                            del cache[self._name][i]
-        return False
-
-    def trigger_delete(self, ids):
+    @classmethod
+    def trigger_delete(cls, records):
         '''
         Trigger delete actions
+        '''
+        Trigger = Pool().get('ir.trigger')
+        triggers = Trigger.get_triggers(cls.__name__, 'delete')
+        if not triggers:
+            return
+        for trigger in triggers:
+            triggered = []
+            for record in records:
+                if Trigger.eval(trigger, record):
+                    triggered.append(record)
+            if triggered:
+                Trigger.trigger_action(triggered, trigger)
 
-        :param ids: the deleted ids
+    @classmethod
+    def copy(cls, records, default=None):
         '''
-        trigger_obj = Pool().get('ir.trigger')
-        trigger_ids = trigger_obj.get_triggers(self._name, 'delete')
-        if not trigger_ids:
-            return
-        records = self.browse(ids)
-        triggers = trigger_obj.browse(trigger_ids)
-        for trigger in triggers:
-            triggered_ids = []
-            for record in records:
-                if trigger_obj.eval(trigger, record):
-                    triggered_ids.append(record.id)
-            if triggered_ids:
-                trigger_obj.trigger_action(triggered_ids, trigger.id)
-
-    def copy(self, ids, default=None):
-        '''
-        Duplicate the record(s) in ids.
-
-        :param ids: a list of ids or an id
-        :param default: a dictionary with field name as keys and
-            new value for the field as value
-        :return: a list of new ids or the new id
+        Duplicate the records and return a list of new records.
         '''
         pool = Pool()
-        lang_obj = pool.get('ir.lang')
+        Lang = pool.get('ir.lang')
         if default is None:
             default = {}
 
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
-
         if 'state' not in default:
-            if 'state' in self._defaults:
-                default['state'] = self._defaults['state']()
+            if 'state' in cls._defaults:
+                default['state'] = cls._defaults['state']()
 
         def convert_data(field_defs, data):
             data = data.copy()
@@ -309,145 +286,94 @@
             return data, data_o2m
 
         new_ids = {}
-        fields_names = self._columns.keys()
-        datas = self.read(ids, fields_names=fields_names)
-        field_defs = self.fields_get(fields_names=fields_names)
+        fields_names = cls._fields.keys()
+        ids = map(int, records)
+        datas = cls.read(ids, fields_names=fields_names)
+        field_defs = cls.fields_get(fields_names=fields_names)
         for data in datas:
             data_id = data['id']
             data, data_o2m = convert_data(field_defs, data)
-            new_ids[data_id] = self.create(data)
+            new_record = cls.create(data)
+            if new_record:
+                new_ids[data_id] = new_record.id
+            else:
+                continue
             for field_name in data_o2m:
-                relation_model = pool.get(
+                Relation = pool.get(
                         field_defs[field_name]['relation'])
                 relation_field = field_defs[field_name]['relation_field']
                 if relation_field:
-                    relation_model.copy(data_o2m[field_name],
-                            default={relation_field: new_ids[data_id]})
+                    Relation.copy(Relation.browse(data_o2m[field_name]),
+                        default={relation_field: new_ids[data_id]})
 
         fields_translate = {}
         for field_name, field in field_defs.iteritems():
-            if field_name in self._columns and \
-                    getattr(self._columns[field_name], 'translate', False):
+            if field_name in cls._fields and \
+                    getattr(cls._fields[field_name], 'translate', False):
                 fields_translate[field_name] = field
 
         if fields_translate:
-            lang_ids = lang_obj.search([
+            lang_ids = Lang.search([
                 ('translatable', '=', True),
                 ])
             if lang_ids:
-                lang_ids += lang_obj.search([
+                lang_ids += Lang.search([
                     ('code', '=', CONFIG['language']),
                     ])
-                langs = lang_obj.browse(lang_ids)
+                langs = Lang.browse(lang_ids)
                 for lang in langs:
                     with Transaction().set_context(language=lang.code):
-                        datas = self.read(ids,
+                        datas = cls.read(ids,
                                 fields_names=fields_translate.keys() + ['id'])
                         for data in datas:
                             data_id = data['id']
                             data, _ = convert_data(fields_translate, data)
-                            self.write(new_ids[data_id], data)
-        if int_id:
-            return new_ids.values()[0]
-        return new_ids.values()
+                            cls.write([cls(new_ids[data_id])], data)
+        return cls.browse(new_ids.values())
 
-    def search(self, domain, offset=0, limit=None, order=None, count=False):
+    @classmethod
+    def search(cls, domain, offset=0, limit=None, order=None, count=False):
         '''
-        Return a list of ids that match the domain.
-
-        :param domain: a list of tuples or lists
-            lists are constructed like this:
-            ``['operator', args, args, ...]``
-            operator can be 'AND' or 'OR', if it is missing the default
-            value will be 'AND'
-            tuples are constructed like this:
-            ``('field name', 'operator', value)``
-            field name: is a field name from the model or a relational field
-            by using '.' as separator.
-            operator must be in OPERATORS
-        :param offset: an integer to specify the offset of the result
-        :param limit: an integer to specify the number of result
-        :param order: a list of tuples that are constructed like this:
-            ``('field name', 'DESC|ASC')``
-            allowing to specify the order of result
-        :param count: a boolean to return only the length of the result
-        :return: a list of ids or an integer
+        Return a list of records that match the domain.
         '''
         if count:
             return 0
         return []
 
-    def search_count(self, domain):
+    @classmethod
+    def search_count(cls, domain):
         '''
-        Return the number of records that match the domain. (See search)
-
-        :param domain: a domain like in search
-        :return: an integer
+        Return the number of records that match the domain.
         '''
-        res = self.search(domain, count=True)
+        res = cls.search(domain, count=True)
         if isinstance(res, list):
             return len(res)
         return res
 
-    def search_read(self, domain, offset=0, limit=None, order=None,
+    @classmethod
+    def search_read(cls, domain, offset=0, limit=None, order=None,
             fields_names=None):
         '''
         Call search and read functions at once.
         Useful for the client to reduce the number of calls.
-
-        :param domain: a domain like in search
-        :param offset: an integer to specify the offset for the result
-        :param limit: an integer to specify the number of records
-        :param order: a list of tuples that are constructed like this:
-            ('field name', 'DESC|ASC')
-            allowing to to specify the order of result
-        :param fields_names: fields names to read if None read all fields
-        :return: a list of dictionaries or a dictionary if limit is 1
-            the dictionaries will have field names as key
-            and field values as value
         '''
-        ids = self.search(domain, offset=offset, limit=limit, order=order)
+        records = cls.search(domain, offset=offset, limit=limit, order=order)
 
         if not fields_names:
-            fields_names = list(set(self._columns.keys() \
-                    + self._inherit_fields.keys()))
+            fields_names = list(set(cls._fields.keys() \
+                    + cls._inherit_fields.keys()))
         if 'id' not in fields_names:
             fields_names.append('id')
+        return cls.read(map(int, records), fields_names)
 
-        res = []
-        for model in self.browse(ids):
-            record = {}
-            for fields_name in set(fields_names):
-                fields_split = fields_name.split('.')
-                while fields_split:
-                    field_name = fields_split.pop(0)
-                    if fields_name not in record:
-                        record[fields_name] = model[field_name]
-                    else:
-                        if isinstance(record[fields_name], BrowseRecordNull):
-                            continue
-                        record[fields_name] = record[fields_name][field_name]
-                if isinstance(record[fields_name], BrowseRecordNull):
-                    record[fields_name] = False
-                elif isinstance(record[fields_name], BrowseRecord):
-                    record[fields_name] = record[fields_name].id
-                elif isinstance(record[fields_name], BrowseRecordList):
-                    record[fields_name] = [x.id for x in record[fields_name]]
-            res.append(record)
-
-        if limit == 1:
-            if not ids:
-                return []
-            return res[0]
-        return res
-
-    def _search_domain_active(self, domain, active_test=True):
+    @classmethod
+    def _search_domain_active(cls, domain, active_test=True):
         # reduce_domain return a new instance so we can safety modify domain
         domain = reduce_domain(domain)
         # if the object has a field named 'active', filter out all inactive
         # records unless they were explicitely asked for
-        if not (('active' in self._columns
-            or 'active' in self._inherit_fields.keys())
+        if not (('active' in cls._fields
+            or 'active' in cls._inherit_fields.keys())
             and (active_test
                 and Transaction().context.get('active_test', True))):
             return domain
@@ -476,55 +402,39 @@
             return domain
         return process(domain)
 
-    def get_rec_name(self, ids, name):
+    def get_rec_name(self, name):
         '''
-        Return a dictionary with id as key and rec_name as value.
+        Return the rec_name of the instance.
         It is used by the Function field rec_name.
-
-        :param ids: a list of ids
-        :param name: the name of the Function field
-        :return: a dictionary
         '''
-        if not ids:
-            return {}
-        res = {}
         rec_name = self._rec_name
-        if (rec_name not in self._columns
+        if (rec_name not in self._fields
                 and rec_name not in self._inherit_fields):
             rec_name = 'id'
-        for record in self.browse(ids):
-            res[record.id] = unicode(record[rec_name])
-        return res
+        return unicode(getattr(self, rec_name))
 
-    def search_rec_name(self, name, clause):
+    @classmethod
+    def search_rec_name(cls, name, clause):
         '''
         Return a list of arguments for search on rec_name.
-
-        :param name: the name of the Function field
-        :param clause: a domain clause
-        :return: a list of domain clause
         '''
-        rec_name = self._rec_name
-        if (rec_name not in self._columns
-                and rec_name not in self._inherit_fields):
+        rec_name = cls._rec_name
+        if (rec_name not in cls._fields
+                and rec_name not in cls._inherit_fields):
             return []
         return [(rec_name,) + clause[1:]]
 
-    def browse(self, ids):
+    @classmethod
+    def browse(cls, ids):
         '''
-        Return a BrowseRecordList for the ids
-            or BrowseRecord if ids is a integer.
+        Return a list of instance for the ids
+        '''
+        ids = map(int, ids)
+        local_cache = LRUDict(RECORD_CACHE_SIZE)
+        return [cls(int(x), _ids=ids, _local_cache=local_cache) for x in ids]
 
-        :param ids: a list of ids or an id
-        :return: a BrowseRecordList or a BrowseRecord
-        '''
-        local_cache = LRUDict(RECORD_CACHE_SIZE)
-        if isinstance(ids, (int, long)):
-            return BrowseRecord(ids, self, [ids], local_cache)
-        return BrowseRecordList((BrowseRecord(x, self, ids, local_cache)
-            for x in ids))
-
-    def __export_row(self, record, fields_names):
+    @staticmethod
+    def __export_row(record, fields_names):
         pool = Pool()
         lines = []
         data = ['' for x in range(len(fields_names))]
@@ -536,21 +446,21 @@
             value = record
             i = 0
             while i < len(fields_tree):
-                if not isinstance(value, BrowseRecord):
+                if not isinstance(value, ModelStorage):
                     break
                 field_name = fields_tree[i]
-                model_obj = pool.get(value._model_name)
-                if field_name in model_obj._columns:
-                    field = model_obj._columns[field_name]
-                elif field_name in model_obj._inherit_fields:
-                    field = model_obj._inherit_fields[field_name][2]
+                eModel = pool.get(value.__name__)
+                if field_name in eModel._fields:
+                    field = eModel._fields[field_name]
+                elif field_name in eModel._inherit_fields:
+                    field = eModel._inherit_fields[field_name][2]
                 else:
                     raise Exception('Field %s not available on object "%s"'
-                        % (field_name, model_obj._name))
+                        % (field_name, eModel.__name__))
                 if field.states and 'invisible' in field.states:
                     pyson_invisible = PYSONEncoder().encode(
                             field.states['invisible'])
-                    env = EvalEnvironment(value, model_obj)
+                    env = EvalEnvironment(value, eModel)
                     env.update(Transaction().context)
                     env['current_date'] = datetime.datetime.today()
                     env['time'] = time
@@ -560,8 +470,8 @@
                     if invisible:
                         value = ''
                         break
-                value = value[field_name]
-                if isinstance(value, (BrowseRecordList, list)):
+                value = getattr(value, field_name)
+                if isinstance(value, (list, tuple)):
                     first = True
                     child_fields_names = [(x[:i + 1] == fields_tree[:i + 1] and
                         x[i + 1:]) or [] for x in fields_names]
@@ -569,7 +479,7 @@
                         break
                     done.append(child_fields_names)
                     for child_record in value:
-                        child_lines = self.__export_row(child_record,
+                        child_lines = ModelStorage.__export_row(child_record,
                                 child_fields_names)
                         if first:
                             for child_fpos in xrange(len(fields_names)):
@@ -585,33 +495,30 @@
             if i == len(fields_tree):
                 if value is None:
                     value = ''
+                elif isinstance(value, Model):
+                    value = str(value)
                 data[fpos] = value
         return [data] + lines
 
-    def export_data(self, ids, fields_names):
+    @classmethod
+    def export_data(cls, records, fields_names):
         '''
-        Return list of list of values for each id in ids.
+        Return list of list of values for each record.
         The list of values follows fields_names.
         Relational fields are defined with '/' at any depth.
-
-        :param ids: a list of ids
-        :param fields_names: a list of field names
-        :return: a list of list of values for each id in ids
         '''
         fields_names = [x.split('/') for x in fields_names]
-        datas = []
-        for record in self.browse(ids):
-            datas += self.__export_row(record, fields_names)
-        return datas
+        data = []
+        for record in records:
+            data += cls.__export_row(record, fields_names)
+        return data
 
-    def import_data(self, fields_names, datas):
+    @classmethod
+    def import_data(cls, fields_names, data):
         '''
-        Create records for all values in datas.
+        Create records for all values in data.
         The field names of values must be defined in fields_names.
-
-        :param fields_names: a list of fields names
-        :param datas: the data to import
-        :return: a tuple with
+        It returns a tuple with
             - the number of records imported
             - the last values if failed
             - the exception if failed
@@ -619,17 +526,17 @@
         '''
         pool = Pool()
 
-        def process_lines(self, datas, prefix, fields_def, position=0):
+        def process_lines(data, prefix, fields_def, position=0):
 
             def warn(msgname, *args):
-                msg = self.raise_user_error(msgname, args,
+                msg = cls.raise_user_error(msgname, args,
                         raise_exception=False)
                 warnings.warn(msg)
 
             def get_selection(selection, value):
                 res = None
                 if not isinstance(selection, (tuple, list)):
-                    selection = getattr(self, selection)()
+                    selection = getattr(cls, selection)()
                 for key, _ in selection:
                     if str(key) == value:
                         res = key
@@ -641,8 +548,8 @@
             def get_many2one(relation, value):
                 if not value:
                     return None
-                relation_obj = pool.get(relation)
-                res = relation_obj.search([
+                Relation = pool.get(relation)
+                res = Relation.search([
                     ('rec_name', '=', value),
                     ], limit=2)
                 if len(res) < 1:
@@ -659,10 +566,10 @@
                 if not value:
                     return None
                 res = []
-                relation_obj = pool.get(relation)
+                Relation = pool.get(relation)
                 for word in csv.reader(StringIO.StringIO(value), delimiter=',',
                         quoting=csv.QUOTE_NONE, escapechar='\\').next():
-                    res2 = relation_obj.search([
+                    res2 = Relation.search([
                         ('rec_name', '=', word),
                         ], limit=2)
                     if len(res2) < 1:
@@ -686,8 +593,8 @@
                 except Exception:
                     warn('reference_syntax_error', value, '/'.join(field))
                     return None
-                relation_obj = pool.get(relation)
-                res = relation_obj.search([
+                Relation = pool.get(relation)
+                res = Relation.search([
                     ('rec_name', '=', value),
                     ], limit=2)
                 if len(res) < 1:
@@ -724,7 +631,7 @@
                     except Exception:
                         warn('xml_id_syntax_error', word, '/'.join(field))
                         continue
-                    db_id = ir_model_data_obj.get_id(module, xml_id)
+                    db_id = ModelData.get_id(module, xml_id)
                     res_ids.append(db_id)
                 if ftype == 'many2many' and res_ids:
                     return [('set', res_ids)]
@@ -732,7 +639,7 @@
                     return '%s,%s' % (relation, str(res_ids[0]))
                 return res_ids and res_ids[0] or False
 
-            line = datas[position]
+            line = data[position]
             row = {}
             translate = {}
             todo = set()
@@ -795,7 +702,7 @@
             for field in todo:
                 newfd = pool.get(fields_def[field]['relation']
                         ).fields_get()
-                res = process_lines(self, datas, prefix + [field], newfd,
+                res = process_lines(data, prefix + [field], newfd,
                         position)
                 (newrow, max2, _) = res
                 nbrmax = max(nbrmax, max2)
@@ -803,17 +710,17 @@
                 row[field] = (reduce(lambda x, y: x or y, newrow.values()) and
                          [('create', newrow)]) or []
                 i = max2
-                while (position + i) < len(datas):
+                while (position + i) < len(data):
                     test = True
                     for j, field2 in enumerate(fields_names):
                         if len(field2) <= (prefix_len + 1) \
-                               and datas[position + i][j]:
+                               and data[position + i][j]:
                             test = False
                             break
                     if not test:
                         break
                     (newrow, max2, _) = \
-                            process_lines(self, datas, prefix + [field], newfd,
+                            process_lines(data, prefix + [field], newfd,
                                     position + i)
                     if reduce(lambda x, y: x or y, newrow.values()):
                         row[field].append(('create', newrow))
@@ -821,10 +728,10 @@
                     nbrmax = max(nbrmax, i)
             if prefix_len == 0:
                 for i in xrange(max(nbrmax, 1)):
-                    datas.pop(0)
+                    data.pop(0)
             return (row, nbrmax, translate)
 
-        ir_model_data_obj = pool.get('ir.model.data')
+        ModelData = pool.get('ir.model.data')
 
         # logger for collecting warnings for the client
         warnings = logging.Logger("import")
@@ -832,27 +739,26 @@
         warnings.addHandler(logging.StreamHandler(warning_stream))
 
         len_fields_names = len(fields_names)
-        for data in datas:
-            assert len(data) == len_fields_names
+        assert all(len(x) == len_fields_names for x in data)
         fields_names = [x.split('/') for x in fields_names]
-        fields_def = self.fields_get()
+        fields_def = cls.fields_get()
         done = 0
 
         warning = ''
-        while len(datas):
+        while len(data):
             res = {}
             try:
                 (res, _, translate) = \
-                        process_lines(self, datas, [], fields_def)
+                        process_lines(data, [], fields_def)
                 warning = warning_stream.getvalue()
                 if warning:
                     # XXX should raise Exception
                     Transaction().cursor.rollback()
                     return (-1, res, warning, '')
-                new_id = self.create(res)
+                new_id = cls.create(res)
                 for lang in translate:
                     with Transaction().set_context(language=lang):
-                        self.write(new_id, translate[lang])
+                        cls.write(new_id, translate[lang])
             except Exception, exp:
                 logger = logging.getLogger('import')
                 logger.error(exp)
@@ -864,7 +770,8 @@
             done += 1
         return (done, 0, 0, 0)
 
-    def check_xml_record(self, ids, values):
+    @classmethod
+    def check_xml_record(cls, records, values):
         """
         Check if a list of records and their corresponding fields are
         originating from xml data. This is used by write and delete
@@ -874,28 +781,21 @@
         xml. Values is the dictionary of written values. If values is
         equal to None, no field by field check is performed, False is
         returned as soon as one of the record comes from the xml.
-
-        :param ids: a list of ids or an id
-        :param values: a dictionary with field names as key and
-            written values as value
-        :return: True or False
         """
-        model_data_obj = Pool().get('ir.model.data')
+        ModelData = Pool().get('ir.model.data')
         # Allow root user to update/delete
         if Transaction().user == 0:
             return True
-        if isinstance(ids, (int, long)):
-            ids = [ids]
         with Transaction().set_user(0):
-            model_data_ids = model_data_obj.search([
-                ('model', '=', self._name),
-                ('db_id', 'in', ids),
+            model_data_ids = ModelData.search([
+                ('model', '=', cls.__name__),
+                ('db_id', 'in', map(int, records)),
                 ])
             if not model_data_ids:
                 return True
             if values == None:
                 return False
-            for line in model_data_obj.browse(model_data_ids):
+            for line in ModelData.browse(model_data_ids):
                 if not line.values:
                     continue
                 xml_values = safe_eval(line.values, {
@@ -907,84 +807,89 @@
                         return False
         return True
 
-    def check_recursion(self, ids, parent='parent'):
+    @classmethod
+    def check_recursion(cls, records, parent='parent'):
         '''
         Function that checks if there is no recursion in the tree
         composed with parent as parent field name.
-
-        :param ids: a list of ids
-        :param parent: the parent field name
-        :return: True or False
         '''
-        if parent in self._columns:
-            parent_type = self._columns[parent]._type
-        elif parent in self._inherit_fields:
-            parent_type = self._inherit_fields[parent][2]._type
+        if parent in cls._fields:
+            parent_type = cls._fields[parent]._type
+        elif parent in cls._inherit_fields:
+            parent_type = cls._inherit_fields[parent][2]._type
         else:
             raise Exception('Field %s not available on object "%s"' % \
-                    (parent, self._name))
+                    (parent, cls.__name__))
 
         if parent_type not in ('many2one', 'many2many'):
             raise Exception(
                     'Unsupported field type "%s" for field "%s" on "%s"' % \
-                    (parent_type, parent, self._name))
+                    (parent_type, parent, cls.__name__))
 
-        records = self.browse(ids)
         visited = set()
 
         for record in records:
             walked = set()
-            walker = record[parent]
+            walker = getattr(record, parent)
             while walker:
                 if parent_type == 'many2many':
                     for walk in walker:
                         walked.add(walk.id)
                         if walk.id == record.id:
                             return False
-                    walker = list(chain(*(walk[parent] for walk in walker
-                            if walk.id not in visited)))
+                    walker = list(chain(*(getattr(walk, parent)
+                                for walk in walker if walk.id not in visited)))
                 else:
                     walked.add(walker.id)
                     if walker.id == record.id:
                         return False
-                    walker = walker[parent] not in visited and walker[parent]
+                    walker = (getattr(walker, parent) not in visited
+                        and getattr(walker, parent))
             visited.update(walked)
 
         return True
 
-    def _get_error_args(self, field_name):
+    @classmethod
+    def _get_error_args(cls, field_name):
         pool = Pool()
-        model_field_obj = pool.get('ir.model.field')
-        error_args = (field_name, self._name)
-        if model_field_obj:
-            model_field_ids = model_field_obj.search([
+        ModelField = pool.get('ir.model.field')
+        error_args = (field_name, cls.__name__)
+        if ModelField:
+            model_fields = ModelField.search([
                         ('name', '=', field_name),
-                        ('model.model', '=', self._name),
+                        ('model.model', '=', cls.__name__),
                         ], limit=1)
-            if model_field_ids:
-                model_field = model_field_obj.browse(model_field_ids[0])
+            if model_fields:
+                model_field, = model_fields
                 error_args = (model_field.field_description,
                         model_field.model.name)
         return error_args
 
-    def _validate(self, ids):
+    @classmethod
+    def _validate(cls, records):
         pool = Pool()
         if (Transaction().user == 0
                 and Transaction().context.get('user')):
             with Transaction().set_user(Transaction().context.get('user')):
-                return self._validate(ids)
+                return cls._validate(cls.browse(records))
 
-        for field in self._constraints:
-            if not getattr(self, field[0])(ids):
-                self.raise_user_error(field[1])
+        def call(name):
+            method = getattr(cls, name)
+            if not hasattr(method, 'im_self') or method.im_self:
+                return method(records)
+            else:
+                return all(method(r) for r in records)
+        for field in cls._constraints:
+            if not call(field[0]):
+                cls.raise_user_error(field[1])
 
         if not 'res.user' in pool.object_name_list() \
                 or Transaction().user == 0:
             ctx_pref = {
             }
         else:
-            user_obj = pool.get('res.user')
-            ctx_pref = user_obj.get_preferences(context_only=True)
+            User = pool.get('res.user')
+            ctx_pref = User.get_preferences(context_only=True)
 
         def is_pyson(test):
             if isinstance(test, PYSON):
@@ -999,8 +904,7 @@
             return False
 
         with Transaction().set_context(ctx_pref):
-            records = self.browse(ids)
-            for field_name, field in self._columns.iteritems():
+            for field_name, field in cls._fields.iteritems():
                 if isinstance(field, fields.Function) and \
                         not field.setter:
                     continue
@@ -1009,13 +913,13 @@
                         ('many2one', 'many2many', 'one2many', 'one2one')
                     and field.domain):
                     if field._type in ('many2one', 'one2many'):
-                        relation_obj = pool.get(field.model_name)
+                        Relation = pool.get(field.model_name)
                     else:
-                        relation_obj = field.get_target()
+                        Relation = field.get_target()
                     if is_pyson(field.domain):
                         pyson_domain = PYSONEncoder().encode(field.domain)
                         for record in records:
-                            env = EvalEnvironment(record, self)
+                            env = EvalEnvironment(record, cls)
                             env.update(Transaction().context)
                             env['current_date'] = datetime.datetime.today()
                             env['time'] = time
@@ -1023,56 +927,60 @@
                             env['active_id'] = record.id
                             domain = PYSONDecoder(env).decode(pyson_domain)
                             relation_ids = []
-                            if record[field_name]:
+                            if getattr(record, field_name):
                                 if field._type in ('many2one',):
-                                    relation_ids.append(record[field_name].id)
+                                    relation_ids.append(
+                                        getattr(record, field_name).id)
                                 else:
                                     relation_ids.extend(
-                                            [x.id for x in record[field_name]])
-                            if relation_ids and not relation_obj.search([
+                                        [x.id for x in getattr(record,
+                                                field_name)])
+                            if relation_ids and not Relation.search([
                                         'AND',
                                         [('id', 'in', relation_ids)],
                                         domain,
                                         ]):
-                                self.raise_user_error(
+                                cls.raise_user_error(
                                         'domain_validation_record',
-                                        error_args=self._get_error_args(
+                                        error_args=cls._get_error_args(
                                             field_name))
                     else:
                         relation_ids = []
                         for record in records:
-                            if record[field_name]:
+                            if getattr(record, field_name):
                                 if field._type in ('many2one',):
-                                    relation_ids.append(record[field_name].id)
+                                    relation_ids.append(
+                                        getattr(record, field_name).id)
                                 else:
                                     relation_ids.extend(
-                                            [x.id for x in record[field_name]])
+                                        [x.id for x in getattr(record,
+                                                field_name)])
                         if relation_ids:
-                            find_ids = relation_obj.search([
+                            finds = Relation.search([
                                 'AND',
                                 [('id', 'in', relation_ids)],
                                 field.domain,
                                 ])
+                            find_ids = map(int, finds)
                             if not set(relation_ids) == set(find_ids):
-                                self.raise_user_error(
+                                cls.raise_user_error(
                                         'domain_validation_record',
-                                        error_args=self._get_error_args(
+                                        error_args=cls._get_error_args(
                                             field_name))
 
                 def required_test(value, field_name):
-                    if (isinstance(value,
-                                (BrowseRecordNull, type(None), type(False),
-                                    BrowseRecordList, basestring))
+                    if (isinstance(value, (type(None), type(False), list,
+                                    tuple, basestring))
                             and not value):
-                        self.raise_user_error('required_validation_record',
-                            error_args=self._get_error_args(field_name))
+                        cls.raise_user_error('required_validation_record',
+                            error_args=cls._get_error_args(field_name))
                 # validate states required
                 if field.states and 'required' in field.states:
                     if is_pyson(field.states['required']):
                         pyson_required = PYSONEncoder().encode(
                                 field.states['required'])
                         for record in records:
-                            env = EvalEnvironment(record, self)
+                            env = EvalEnvironment(record, cls)
                             env.update(Transaction().context)
                             env['current_date'] = datetime.datetime.today()
                             env['time'] = time
@@ -1080,21 +988,23 @@
                             env['active_id'] = record.id
                             required = PYSONDecoder(env).decode(pyson_required)
                             if required:
-                                required_test(record[field_name], field_name)
+                                required_test(getattr(record, field_name),
+                                    field_name)
                     else:
                         if field.states['required']:
                             for record in records:
-                                required_test(record[field_name], field_name)
+                                required_test(getattr(record, field_name),
+                                    field_name)
                 # validate required
                 if field.required:
                     for record in records:
-                        required_test(record[field_name], field_name)
+                        required_test(getattr(record, field_name), field_name)
                 # validate size
                 if hasattr(field, 'size') and field.size is not None:
                     for record in records:
                         if isinstance(field.size, PYSON):
                             pyson_size = PYSONEncoder().encode(field.size)
-                            env = EvalEnvironment(record, self)
+                            env = EvalEnvironment(record, cls)
                             env.update(Transaction().context)
                             env['current_date'] = datetime.datetime.today()
                             env['time'] = time
@@ -1103,15 +1013,16 @@
                             field_size = PYSONDecoder(env).decode(pyson_size)
                         else:
                             field_size = field.size
-                        if (len(record[field_name] or '') > field_size >= 0):
-                            self.raise_user_error(
+                        if (len(getattr(record, field_name) or '')
+                                > field_size >= 0):
+                            cls.raise_user_error(
                                 'size_validation_record',
-                                error_args=self._get_error_args(field_name))
+                                error_args=cls._get_error_args(field_name))
 
                 def digits_test(value, digits, field_name):
                     def raise_user_error():
-                        self.raise_user_error('digits_validation_record',
-                            error_args=self._get_error_args(field_name))
+                        cls.raise_user_error('digits_validation_record',
+                            error_args=cls._get_error_args(field_name))
                     if value is None:
                         return
                     if isinstance(value, Decimal):
@@ -1126,18 +1037,19 @@
                     if is_pyson(field.digits):
                         pyson_digits = PYSONEncoder().encode(field.digits)
                         for record in records:
-                            env = EvalEnvironment(record, self)
+                            env = EvalEnvironment(record, cls)
                             env.update(Transaction().context)
                             env['current_date'] = datetime.datetime.today()
                             env['time'] = time
                             env['context'] = Transaction().context
                             env['active_id'] = record.id
                             digits = PYSONDecoder(env).decode(pyson_digits)
-                            digits_test(record[field_name], digits, field_name)
+                            digits_test(getattr(record, field_name), digits,
+                                field_name)
                     else:
                         for record in records:
-                            digits_test(record[field_name], field.digits,
-                                field_name)
+                            digits_test(getattr(record, field_name),
+                                field.digits, field_name)
 
                 def format_test(value, format, field_name):
                     if not value:
@@ -1146,8 +1058,8 @@
                         value = value.time()
                     if value != datetime.datetime.strptime(
                             value.strftime(format), format).time():
-                        self.raise_user_error('time_format_validation_record',
-                            error_args=self._get_error_args(field_name))
+                        cls.raise_user_error('time_format_validation_record',
+                            error_args=cls._get_error_args(field_name))
 
                 # validate time format
                 if (field._type in ('datetime', 'time')
@@ -1155,25 +1067,27 @@
                     if is_pyson(field.format):
                         pyson_format = PYSONDecoder().encode(field.format)
                         for record in records:
-                            env = EvalEnvironment(record, self)
+                            env = EvalEnvironment(record, cls)
                             env.update(Transaction().context)
                             env['current_date'] = datetime.datetime.today()
                             env['time'] = time
                             env['context'] = Transaction().context
                             env['active_id'] = record.id
                             format = PYSONDecoder(env).decode(pyson_format)
-                            format_test(record[field_name], format, field_name)
+                            format_test(getattr(record, field_name), format,
+                                field_name)
                     else:
                         for record in records:
-                            format_test(record[field_name], field.format,
-                                field_name)
+                            format_test(getattr(record, field_name),
+                                field.format, field_name)
 
-    def _clean_defaults(self, defaults):
+    @classmethod
+    def _clean_defaults(cls, defaults):
         pool = Pool()
         vals = {}
         for field in defaults.keys():
-            fld_def = (field in self._columns) and self._columns[field] \
-                    or self._inherit_fields[field][2]
+            fld_def = (field in cls._fields) and cls._fields[field] \
+                    or cls._inherit_fields[field][2]
             if fld_def._type in ('many2one', 'one2one'):
                 if isinstance(defaults[field], (list, tuple)):
                     vals[field] = defaults[field][0]
@@ -1192,3 +1106,287 @@
             else:
                 vals[field] = defaults[field]
         return vals
+
+    def __init__(self, id=None, **kwargs):
+        _ids = kwargs.pop('_ids', None)
+        _local_cache = kwargs.pop('_local_cache', None)
+        super(ModelStorage, self).__init__(id, **kwargs)
+        self._cursor = Transaction().cursor
+        self._user = Transaction().user
+        self._context = Transaction().context
+        if _ids is not None:
+            self._ids = _ids
+            assert id in _ids
+        else:
+            self._ids = [self.id]
+
+        self._cursor_cache = self._cursor.get_cache(self._context)
+
+        if _local_cache is not None:
+            self._local_cache = _local_cache
+        else:
+            self._local_cache = LRUDict(RECORD_CACHE_SIZE)
+        self._local_counter = Transaction().counter
+
+    @property
+    def _cache(self):
+        cache = self._cursor_cache
+        if self.__name__ not in cache:
+            cache[self.__name__] = LRUDict(RECORD_CACHE_SIZE)
+        return cache[self.__name__]
+
+    def __getattr__(self, name):
+        try:
+            return super(ModelStorage, self).__getattr__(name)
+        except AttributeError:
+            if self.id < 0:
+                raise
+
+        counter = Transaction().counter
+        if self._local_counter != counter:
+            self._local_cache.clear()
+            self._local_counter = counter
+        try:
+            return self._local_cache[self.id][name]
+        except KeyError:
+            pass
+        try:
+            return self._cache[self.id][name]
+        except KeyError:
+            pass
+
+        # fetch the definition of the field
+        if name in self._fields:
+            field = self._fields[name]
+        elif name in self._inherit_fields:
+            field = self._inherit_fields[name][2]
+        else:
+            raise AttributeError("'%s' Model has no attribute '%s'"
+                % (self.__name__, name))
+
+        # build the list of fields we will fetch
+        ffields = {
+            name: field,
+            }
+        if field.loading == 'eager':
+            FieldAccess = Pool().get('ir.model.field.access')
+            fread_accesses = {}
+            for inherit_name in self._inherits:
+                Inherit = Pool().get(inherit_name)
+                fread_accesses.update(FieldAccess.check(inherit_name,
+                    Inherit._fields.keys(), 'read', access=True))
+            fread_accesses.update(FieldAccess.check(self.__name__,
+                self._fields.keys(), 'read', access=True))
+            to_remove = set(x for x, y in fread_accesses.iteritems()
+                    if not y and x != name)
+
+            threshold = BROWSE_FIELD_TRESHOLD
+            inherit_threshold = threshold - len(self._fields)
+
+            def not_cached(item):
+                fname, field = item
+                return (fname not in self._cache.get(self.id, {})
+                    and fname not in self._local_cache.get(self.id, {}))
+
+            def to_load(item):
+                fname, field = item
+                return (field.loading == 'eager'
+                    and fname not in to_remove)
+
+            def overrided(item):
+                fname, field = item
+                return fname in self._fields
+
+            if inherit_threshold > 0:
+                ifields = ((fname, field)
+                    for fname, (_, _, field) in
+                    self._inherit_fields.iteritems())
+                ifields = ifilterfalse(overrided,
+                    ifilter(to_load,
+                        ifilter(not_cached, ifields)))
+                ifields = islice(ifields, 0, inherit_threshold)
+                ffields.update(ifields)
+                threshold -= inherit_threshold
+
+            ifields = ifilter(to_load,
+                ifilter(not_cached,
+                    self._fields.iteritems()))
+            ifields = islice(ifields, 0, threshold)
+            ffields.update(ifields)
+
+        # add datetime_field
+        for field in ffields.values():
+            if hasattr(field, 'datetime_field') and field.datetime_field:
+                if field.datetime_field in self._fields:
+                    datetime_field = self._fields[field.datetime_field]
+                else:
+                    datetime_field = self._inherit_fields[
+                            field.datetime_field][2]
+                ffields[field.datetime_field] = datetime_field
+
+        def filter_(id_):
+            try:
+                if name in self._local_cache[id_]:
+                    return False
+                if name in self._cache[id_]:
+                    return False
+            except KeyError:
+                pass
+            return True
+        index = self._ids.index(self.id)
+        ids = chain(islice(self._ids, index, None),
+            islice(self._ids, 0, max(index - 1, 0)))
+        ids = list(islice(ifilter(filter_, ids), self._cursor.IN_MAX))
+
+        def instantiate(field, value, data):
+            if field._type in ('many2one', 'one2one', 'reference'):
+                if value is None or value is False:
+                    return None
+            elif field._type in ('one2many', 'many2many'):
+                if not value:
+                    return ()
+            try:
+                if field._type == 'reference':
+                    model_name, record_id = value.split(',')
+                    Model = Pool().get(model_name)
+                    try:
+                        value = int(record_id)
+                    except ValueError:
+                        return value
+                else:
+                    Model = field.get_target()
+            except KeyError:
+                return value
+            if getattr(field, 'datetime_field', None):
+                _datetime = data.get(field.datetime_field)
+            else:
+                _datetime = None
+            with Transaction().set_context(_datetime=_datetime):
+                local_cache = model2cache.setdefault(Model,
+                    LRUDict(RECORD_CACHE_SIZE))
+                ids = model2ids.setdefault(Model, [])
+                if field._type in ('many2one', 'one2one', 'reference'):
+                    ids.append(value)
+                    return Model(value, _ids=ids, _local_cache=local_cache)
+                elif field._type in ('one2many', 'many2many'):
+                    ids.extend(value)
+                    return tuple(Model(id, _ids=ids, _local_cache=local_cache)
+                        for id in value)
+
+        model2ids = {}
+        model2cache = {}
+        # Read the data
+        with contextlib.nested(Transaction().set_cursor(self._cursor),
+                Transaction().set_user(self._user),
+                Transaction().set_context(self._context)):
+            # create browse records for 'remote' models
+            for data in self.read(ids, ffields.keys()):
+                for fname, field in ffields.iteritems():
+                    if field._type in ('many2one', 'one2one', 'one2many',
+                            'many2many', 'reference'):
+                        data[fname] = instantiate(field, data[fname], data)
+                    elif not isinstance(field, fields.Function):
+                        continue
+                    if data['id'] == self.id and fname == name:
+                        value = data[fname]
+                    if data['id'] not in self._local_cache:
+                        self._local_cache[data['id']] = {}
+                    self._local_cache[data['id']][fname] = data[fname]
+                    del data[fname]
+                if data['id'] not in self._cache:
+                    self._cache[data['id']] = {}
+                self._cache[data['id']].update(data)
+                if data['id'] == self.id and name in data:
+                    value = data[name]
+        return value
+
+    @property
+    def _save_values(self):
+        values = {}
+        if not self._values:
+            return values
+        for fname, value in self._values.iteritems():
+            if fname in self._fields:
+                field = self._fields[fname]
+            else:
+                field = self._inherit_fields[fname][2]
+            if field._type in ('many2one', 'one2one', 'reference'):
+                if value:
+                    if value.id < 0 and field._type != 'reference':
+                        value.save()
+                    if field._type == 'reference':
+                        value = str(value)
+                    else:
+                        value = value.id
+            if field._type in ('one2many', 'many2many'):
+                targets = value
+                value = [
+                    ('set', []),
+                    ]
+                for target in targets:
+                    if target.id < 0:
+                        value.append(('create', target._save_values))
+                    elif target._save_values:
+                        value.append(
+                            ('write', [target.id], target._save_values))
+                    else:
+                        value[0][1].append(target.id)
+            values[fname] = value
+        return values
+
+    def save(self):
+        if self._save_values or self.id < 0:
+            with contextlib.nested(Transaction().set_cursor(self._cursor),
+                    Transaction().set_user(self._user),
+                    Transaction().set_context(self._context)):
+                if self.id < 0:
+                    self._ids.remove(self.id)
+                    self.id = self.create(self._save_values).id
+                    self._ids.append(self.id)
+                else:
+                    self.write([self], self._save_values)
+        self._values = None
+
+
+class EvalEnvironment(dict):
+
+    def __init__(self, record, Model):
+        super(EvalEnvironment, self).__init__()
+        self._record = record
+        self._model = Model
+
+    def __getitem__(self, item):
+        if item.startswith('_parent_'):
+            field = item[8:]
+            if field in self._model._fields:
+                model_name = self._model._fields[field].model_name
+            else:
+                model_name = self._model._inherit_fields[field][2].model_name
+            ParentModel = Pool().get(model_name)
+            return EvalEnvironment(getattr(self._record, field), ParentModel)
+        if (item in self._model._fields
+                or item in self._model._inherit_fields):
+            value = getattr(self._record, item)
+            if isinstance(value, Model):
+                return value.id
+            elif isinstance(value, list):
+                return [r.id for r in value]
+            else:
+                return value
+        return super(EvalEnvironment, self).__getitem__(item)
+
+    def __getattr__(self, item):
+        try:
+            return self.__getitem__(item)
+        except KeyError, exception:
+            raise AttributeError(*exception.args)
+
+    def get(self, item, default=None):
+        try:
+            return self.__getitem__(item)
+        except Exception:
+            pass
+        return super(EvalEnvironment, self).get(item, default)
+
+    def __nonzero__(self):
+        return bool(self._record)
diff -r f6ef988fda71 trytond/model/modelview.py
--- a/trytond/model/modelview.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/model/modelview.py	Sun Sep 23 17:30:03 2012 +0200
@@ -3,12 +3,15 @@
 from lxml import etree
 from functools import wraps
 from trytond.model import Model
-from trytond.tools import safe_eval
+from trytond.tools import safe_eval, ClassProperty
 from trytond.pyson import PYSONEncoder, CONTEXT
 from trytond.transaction import Transaction
 from trytond.cache import Cache
 from trytond.pool import Pool
 from trytond.exceptions import UserError
+from trytond.rpc import RPC
+
+__all__ = ['ModelView']
 
 
 def _find(tree, element):
@@ -74,12 +77,16 @@
     Define a model with views in Tryton.
     """
     __modules_list = None  # Cache for the modules list sorted by dependency
+    _fields_view_get_cache = Cache('modelview.fields_view_get')
+    _view_toolbar_get_cache = Cache('modelview.view_toolbar_get')
 
     @staticmethod
     def _reset_modules_list():
         ModelView.__modules_list = None
 
-    def _get_modules_list(self):
+    @ClassProperty
+    @classmethod
+    def _modules_list(cls):
         from trytond.modules import create_graph, get_module_list
         if ModelView.__modules_list:
             return ModelView.__modules_list
@@ -87,27 +94,28 @@
         ModelView.__modules_list = [x.name for x in graph] + [None]
         return ModelView.__modules_list
 
-    _modules_list = property(fget=_get_modules_list)
+    @classmethod
+    def __setup__(cls):
+        super(ModelView, cls).__setup__()
+        cls.__rpc__['fields_view_get'] = RPC()
+        cls.__rpc__['view_toolbar_get'] = RPC()
+        cls._buttons = {}
 
-    def __init__(self):
-        super(ModelView, self).__init__()
-        self._rpc['fields_view_get'] = False
-        self._rpc['view_toolbar_get'] = False
-        self._buttons = {}
-
-    @Cache('modelview.fields_view_get')
-    def fields_view_get(self, view_id=None, view_type='form'):
+    @classmethod
+    def fields_view_get(cls, view_id=None, view_type='form'):
         '''
         Return a view definition.
-
-        :param view_id: the id of the view, if None the first one will be used
-        :param view_type: the type of the view if view_id is None
-        :return: a dictionary with keys:
+        If view_id is None the first one will be used of view_type.
+        The definition is a dictionary with keys:
            - model: the model name
            - arch: the xml description of the view
            - fields: a dictionary with the definition of each field in the view
         '''
-        result = {'model': self._name}
+        key = (cls.__name__, view_id, view_type)
+        result = cls._fields_view_get_cache.get(key)
+        if result:
+            return result
+        result = {'model': cls.__name__}
         pool = Pool()
 
         test = True
@@ -117,7 +125,7 @@
         cursor = Transaction().cursor
         while test:
             if view_id:
-                where = (model and (" and model='%s'" % (self._name,))) or ''
+                where = (model and (" and model='%s'" % (cls.__name__,))) or ''
                 cursor.execute('SELECT arch, field_childs, id, type, ' \
                             'inherit, model ' \
                         'FROM ir_ui_view WHERE id = %s ' + where, (view_id,))
@@ -127,7 +135,7 @@
                         'FROM ir_ui_view ' \
                         'WHERE model = %s AND type = %s ' \
                         'ORDER BY inherit DESC, priority ASC, id ASC',
-                        (self._name, view_type))
+                        (cls.__name__, view_type))
             sql_res = cursor.fetchone()
             if not sql_res:
                 break
@@ -145,9 +153,9 @@
             result['field_childs'] = sql_res[1] or False
 
             # Check if view is not from an inherited model
-            if sql_res[5] != self._name:
-                inherit_obj = pool.get(sql_res[5])
-                result['arch'] = inherit_obj.fields_view_get(
+            if sql_res[5] != cls.__name__:
+                Inherit = pool.get(sql_res[5])
+                result['arch'] = Inherit.fields_view_get(
                         result['view_id'])['arch']
                 view_id = inherit_view_id
 
@@ -156,13 +164,13 @@
                     'WHERE (inherit = %s AND model = %s) OR ' \
                         ' (id = %s AND inherit IS NOT NULL) '
                     'ORDER BY priority ASC, id ASC',
-                    (view_id, self._name, view_id))
+                    (view_id, cls.__name__, view_id))
             sql_inherit = cursor.fetchall()
             raise_p = False
             while True:
                 try:
                     sql_inherit.sort(key=lambda x:
-                        self._modules_list.index(x[2] or None))
+                        cls._modules_list.index(x[2] or None))
                     break
                 except ValueError:
                     if raise_p:
@@ -182,9 +190,9 @@
         # otherwise, build some kind of default view
         else:
             if view_type == 'form':
-                res = self.fields_get()
+                res = cls.fields_get()
                 xml = '''<?xml version="1.0" encoding="utf-8"?>''' \
-                '''<form string="%s" col="4">''' % (self._description,)
+                '''<form string="%s" col="4">''' % (cls.__doc__,)
                 for i in res:
                     if i in ('create_uid', 'create_date',
                             'write_uid', 'write_date', 'id', 'rec_name'):
@@ -199,11 +207,11 @@
                 xml += "</form>"
             elif view_type == 'tree':
                 field = 'id'
-                if self._rec_name in self._columns:
-                    field = self._rec_name
+                if cls._rec_name in cls._fields:
+                    field = cls._rec_name
                 xml = '''<?xml version="1.0" encoding="utf-8"?>''' \
                 '''<tree string="%s"><field name="%s"/></tree>''' \
-                % (self._description, field)
+                % (cls.__doc__, field)
             else:
                 xml = ''
             result['type'] = view_type
@@ -214,34 +222,41 @@
         # Update arch and compute fields from arch
         parser = etree.XMLParser(remove_blank_text=True)
         tree = etree.fromstring(result['arch'], parser)
-        xarch, xfields = self._view_look_dom_arch(tree, result['type'],
+        xarch, xfields = cls._view_look_dom_arch(tree, result['type'],
                 result['field_childs'])
         result['arch'] = xarch
         result['fields'] = xfields
 
+        cls._fields_view_get_cache.set(key, result)
         return result
 
-    @Cache('modelview.view_toolbar_get')
-    def view_toolbar_get(self):
+    @classmethod
+    def view_toolbar_get(cls):
         """
         Returns the model specific actions.
-
-        :return: a dictionary with keys:
+        A dictionary with keys:
             - print: a list of available reports
             - action: a list of available actions
             - relate: a list of available relations
         """
-        action_obj = Pool().get('ir.action.keyword')
-        prints = action_obj.get_keyword('form_print', (self._name, -1))
-        actions = action_obj.get_keyword('form_action', (self._name, -1))
-        relates = action_obj.get_keyword('form_relate', (self._name, -1))
-        return {
+        Action = Pool().get('ir.action.keyword')
+        key = (cls.__name__, repr(Transaction().context))
+        result = cls._view_toolbar_get_cache.get(key)
+        if result:
+            return result
+        prints = Action.get_keyword('form_print', (cls.__name__, -1))
+        actions = Action.get_keyword('form_action', (cls.__name__, -1))
+        relates = Action.get_keyword('form_relate', (cls.__name__, -1))
+        result = {
             'print': prints,
             'action': actions,
             'relate': relates,
-        }
+            }
+        cls._view_toolbar_get_cache.set(key, result)
+        return result
 
-    def view_header_get(self, value, view_type='form'):
+    @classmethod
+    def view_header_get(cls, value, view_type='form'):
         """
         Overload this method if you need a window title.
         which depends on the context
@@ -252,30 +267,31 @@
         """
         return value
 
-    def _view_look_dom_arch(self, tree, type, field_children=None):
+    @classmethod
+    def _view_look_dom_arch(cls, tree, type, field_children=None):
         pool = Pool()
-        model_access_obj = pool.get('ir.model.access')
-        field_access_obj = pool.get('ir.model.field.access')
+        ModelAccess = pool.get('ir.model.access')
+        FieldAccess = pool.get('ir.model.field.access')
 
         fields_width = {}
         tree_root = tree.getroottree().getroot()
 
         # Find field without read access
-        fread_accesses = field_access_obj.check(self._name,
-                self._columns.keys(), 'read', access=True)
+        fread_accesses = FieldAccess.check(cls.__name__,
+                cls._fields.keys(), 'read', access=True)
         fields_to_remove = list(x for x, y in fread_accesses.iteritems()
                 if not y)
 
         def check_relation(model, field):
             if field._type in ('one2many', 'many2one'):
-                if not model_access_obj.check(field.model_name, mode='read',
+                if not ModelAccess.check(field.model_name, mode='read',
                         raise_exception=False):
                     return False
             if field._type in ('many2many', 'one2one'):
-                if not model_access_obj.check(field.target, mode='read',
+                if not ModelAccess.check(field.target, mode='read',
                         raise_exception=False):
                     return False
-                elif not model_access_obj.check(field.relation_name,
+                elif not ModelAccess.check(field.relation_name,
                         mode='read', raise_exception=False):
                     return False
             if field._type == 'reference':
@@ -283,35 +299,35 @@
                 if isinstance(selection, basestring):
                     selection = getattr(model, field.selection)()
                 for model_name, _ in selection:
-                    if not model_access_obj.check(model_name, mode='read',
+                    if not ModelAccess.check(model_name, mode='read',
                             raise_exception=False):
                         return False
             return True
 
         # Find relation field without read access
-        for name, field in self._columns.iteritems():
-            if not check_relation(self, field):
+        for name, field in cls._fields.iteritems():
+            if not check_relation(cls, field):
                 fields_to_remove.append(name)
 
-        for name, field in self._columns.iteritems():
+        for name, field in cls._fields.iteritems():
             for field_to_remove in fields_to_remove:
                 if field_to_remove in field.depends:
                     fields_to_remove.append(name)
 
         # Find field inherited without read access
-        for inherit_name in self._inherits:
-            inherit_obj = pool.get(inherit_name)
-            fread_accesses = field_access_obj.check(inherit_obj._name,
-                    inherit_obj._columns.keys(), 'read', access=True)
+        for inherit_name in cls._inherits:
+            Inherit = pool.get(inherit_name)
+            fread_accesses = FieldAccess.check(Inherit.__name__,
+                    Inherit._fields.keys(), 'read', access=True)
             fields_to_remove += list(x for x, y in fread_accesses.iteritems()
-                    if not y and x not in self._columns.keys())
+                    if not y and x not in cls._fields.keys())
 
             # Find relation field without read access
-            for name, field in inherit_obj._columns.iteritems():
-                if not check_relation(inherit_obj, field):
+            for name, field in Inherit._fields.iteritems():
+                if not check_relation(Inherit, field):
                     fields_to_remove.append(name)
 
-            for name, field in inherit_obj._columns.iteritems():
+            for name, field in Inherit._fields.iteritems():
                 for field_to_remove in fields_to_remove:
                     if field_to_remove in field.depends:
                         fields_to_remove.append(name)
@@ -329,55 +345,56 @@
                     parent.remove(element)
 
         if type == 'tree':
-            viewtreewidth_obj = pool.get('ir.ui.view_tree_width')
-            viewtreewidth_ids = viewtreewidth_obj.search([
-                ('model', '=', self._name),
+            ViewTreeWidth = pool.get('ir.ui.view_tree_width')
+            viewtreewidth_ids = ViewTreeWidth.search([
+                ('model', '=', cls.__name__),
                 ('user', '=', Transaction().user),
                 ])
-            for viewtreewidth in viewtreewidth_obj.browse(viewtreewidth_ids):
+            for viewtreewidth in ViewTreeWidth.browse(viewtreewidth_ids):
                 if viewtreewidth.width > 0:
                     fields_width[viewtreewidth.field] = viewtreewidth.width
 
-        fields_def = self.__view_look_dom(tree_root, type,
+        fields_def = cls.__view_look_dom(tree_root, type,
                 fields_width=fields_width)
 
         if field_children:
             fields_def.setdefault(field_children, {'name': field_children})
             model, field = None, None
-            if field_children in self._columns:
-                model = self
-                field = self._columns[field_children]
-            elif field_children in self._inherit_fields:
-                model_name, model, field = self._inherit_fields[field_children]
-            if model and field and field.model_name == model._name:
+            if field_children in cls._fields:
+                model = cls
+                field = cls._fields[field_children]
+            elif field_children in cls._inherit_fields:
+                model_name, model, field = cls._inherit_fields[field_children]
+            if model and field and field.model_name == model.__name__:
                 fields_def.setdefault(field.field, {'name': field.field})
 
         for field_name in fields_def.keys():
-            if field_name in self._columns:
-                field = self._columns[field_name]
-            elif field_name in self._inherit_fields:
-                field = self._inherit_fields[field_name][2]
+            if field_name in cls._fields:
+                field = cls._fields[field_name]
+            elif field_name in cls._inherit_fields:
+                field = cls._inherit_fields[field_name][2]
             else:
                 continue
             for depend in field.depends:
                 fields_def.setdefault(depend, {'name': depend})
 
-        if ('active' in self._columns) or ('active' in self._inherit_fields):
+        if ('active' in cls._fields) or ('active' in cls._inherit_fields):
             fields_def.setdefault('active', {'name': 'active'})
 
         arch = etree.tostring(tree, encoding='utf-8', pretty_print=False)
-        fields2 = self.fields_get(fields_def.keys())
+        fields2 = cls.fields_get(fields_def.keys())
         for field in fields_def:
             if field in fields2:
                 fields2[field].update(fields_def[field])
         return arch, fields2
 
-    def __view_look_dom(self, element, type, fields_width=None):
+    @classmethod
+    def __view_look_dom(cls, element, type, fields_width=None):
         pool = Pool()
-        translation_obj = pool.get('ir.translation')
-        model_data_obj = pool.get('ir.model.data')
-        button_obj = pool.get('ir.model.button')
-        user_obj = pool.get('res.user')
+        Translation = pool.get('ir.translation')
+        ModelData = pool.get('ir.model.data')
+        Button = pool.get('ir.model.button')
+        User = pool.get('res.user')
 
         if fields_width is None:
             fields_width = {}
@@ -389,15 +406,15 @@
                 if element.get(attr):
                     attrs = {}
                     try:
-                        if element.get(attr) in self._columns:
-                            field = self._columns[element.get(attr)]
+                        if element.get(attr) in cls._fields:
+                            field = cls._fields[element.get(attr)]
                         else:
-                            field = self._inherit_fields[element.get(
+                            field = cls._inherit_fields[element.get(
                                 attr)][2]
                         if hasattr(field, 'model_name'):
                             relation = field.model_name
                         else:
-                            relation = field.get_target()._name
+                            relation = field.get_target().__name__
                     except Exception:
                         relation = False
                     if relation and element.tag == 'field':
@@ -411,9 +428,9 @@
                                 try:
                                     view_ids.append(int(view_id))
                                 except ValueError:
-                                    view_ids.append(model_data_obj.get_id(
+                                    view_ids.append(ModelData.get_id(
                                             *view_id.split('.')))
-                        relation_obj = pool.get(relation)
+                        Relation = pool.get(relation)
                         if (not len(element)
                                 and type == 'form'
                                 and field._type in ('one2many', 'many2many')):
@@ -421,14 +438,14 @@
                             # loop
                             if view_ids:
                                 for view_id in view_ids:
-                                    view = relation_obj.fields_view_get(
+                                    view = Relation.fields_view_get(
                                         view_id=view_id)
                                     views[view['type']] = view
                                     break
                             else:
                                 for view_type in mode:
                                     views[view_type] = \
-                                        relation_obj.fields_view_get(
+                                        Relation.fields_view_get(
                                             view_type=view_type)
                                     break
                         element.attrib['mode'] = ','.join(mode)
@@ -451,12 +468,12 @@
 
         if element.tag == 'button':
             button_name = element.attrib['name']
-            if button_name in self._buttons:
-                states = self._buttons[button_name]
+            if button_name in cls._buttons:
+                states = cls._buttons[button_name]
             else:
                 states = {}
-            groups = set(user_obj.get_groups())
-            button_groups = button_obj.get_groups(self._name, button_name)
+            groups = set(User.get_groups())
+            button_groups = Button.get_groups(cls.__name__, button_name)
             if button_groups and not groups & button_groups:
                 states = states.copy()
                 states['readonly'] = True
@@ -466,14 +483,14 @@
         if Transaction().language != 'en_US':
             for attr in ('string', 'sum', 'confirm', 'help'):
                 if element.get(attr):
-                    trans = translation_obj.get_source(self._name, 'view',
+                    trans = Translation.get_source(cls.__name__, 'view',
                             Transaction().language, element.get(attr))
                     if trans:
                         element.set(attr, trans)
 
         # Set header string
         if element.tag in ('form', 'tree', 'graph'):
-            element.set('string', self.view_header_get(
+            element.set('string', cls.view_header_get(
                 element.get('string') or '', view_type=element.tag))
 
         if element.tag == 'tree' and element.get('sequence'):
@@ -481,26 +498,26 @@
 
         if childs:
             for field in element:
-                fields_attrs.update(self.__view_look_dom(field, type,
+                fields_attrs.update(cls.__view_look_dom(field, type,
                     fields_width=fields_width))
         return fields_attrs
 
     @staticmethod
     def button(func):
         @wraps(func)
-        def wrapper(self, *args, **kwargs):
+        def wrapper(cls, *args, **kwargs):
             pool = Pool()
-            button_obj = pool.get('ir.model.button')
-            user_obj = pool.get('res.user')
+            Button = pool.get('ir.model.button')
+            User = pool.get('res.user')
 
             if Transaction().user != 0:
-                groups = set(user_obj.get_groups())
-                button_groups = button_obj.get_groups(self._name,
+                groups = set(User.get_groups())
+                button_groups = Button.get_groups(cls.__name__,
                     func.__name__)
                 if button_groups and not groups & button_groups:
                     raise UserError('Calling button %s on %s is not allowed!'
-                        % (func.__name__, self._name))
-            return func(self, *args, **kwargs)
+                        % (func.__name__, cls.__name__))
+            return func(cls, *args, **kwargs)
         return wrapper
 
     @staticmethod
@@ -511,14 +528,14 @@
             @wraps(func)
             def wrapper(*args, **kwargs):
                 pool = Pool()
-                model_data_obj = pool.get('ir.model.data')
-                action_obj = pool.get('ir.action')
+                ModelData = pool.get('ir.model.data')
+                Action = pool.get('ir.action')
 
                 func(*args, **kwargs)
 
                 module, fs_id = action.split('.')
-                action_id = action_obj.get_action_id(
-                    model_data_obj.get_id(module, fs_id))
+                action_id = Action.get_action_id(
+                    ModelData.get_id(module, fs_id))
                 return action_id
             return wrapper
         return decorator
diff -r f6ef988fda71 trytond/model/workflow.py
--- a/trytond/model/workflow.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/model/workflow.py	Sun Sep 23 17:30:03 2012 +0200
@@ -9,36 +9,35 @@
     """
     _transition_state = 'state'
 
-    def __init__(self):
-        super(Workflow, self).__init__()
-        self._transitions = set()
+    @classmethod
+    def __setup__(cls):
+        super(Workflow, cls).__setup__()
+        cls._transitions = set()
 
     @staticmethod
     def transition(state):
         def check_transition(func):
             @wraps(func)
-            def wrapper(self, ids, *args, **kwargs):
-                records = self.browse(ids)
+            def wrapper(cls, records, *args, **kwargs):
                 filtered = []
                 to_update = {}
 
                 for record in records:
-                    current_state = getattr(record, self._transition_state)
+                    current_state = getattr(record, cls._transition_state)
                     transition = (current_state, state)
-                    if transition in self._transitions:
-                        filtered.append(record.id)
+                    if transition in cls._transitions:
+                        filtered.append(record)
                         if current_state != state:
-                            to_update[record.id] = current_state
+                            to_update[record] = current_state
 
-                result = func(self, filtered, *args, **kwargs)
+                result = func(cls, filtered, *args, **kwargs)
                 if to_update:
-                    records = self.browse(to_update.keys())
-                    for record in records:
-                        current_state = getattr(record, self._transition_state)
-                        if current_state != to_update[record.id]:
-                            del to_update[record.id]
-                    self.write(to_update.keys(), {
-                            self._transition_state: state,
+                    for record in to_update.keys():
+                        current_state = getattr(record, cls._transition_state)
+                        if current_state != to_update[record]:
+                            del to_update[record]
+                    cls.write(to_update.keys(), {
+                            cls._transition_state: state,
                             })
                 return result
             return wrapper
diff -r f6ef988fda71 trytond/modules/__init__.py
--- a/trytond/modules/__init__.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/modules/__init__.py	Sun Sep 23 17:30:03 2012 +0200
@@ -215,7 +215,7 @@
             continue
         logger.info(module)
         sys.stdout.flush()
-        objects = pool.instanciate(module)
+        classes = pool.setup(module)
         package_state = module2state.get(module, 'uninstalled')
         if (is_module_to_install(module)
                 or package_state in ('to install', 'to upgrade')):
@@ -223,13 +223,13 @@
                 package_state = 'to install'
             for child in package.childs:
                 module2state[child.name] = package_state
-            for type in objects.keys():
-                for obj in objects[type]:
-                    logger.info('%s:init %s' % (module, obj._name))
-                    obj.init(module)
-            for model in objects['model']:
+            for type in classes.keys():
+                for cls in classes[type]:
+                    logger.info('%s:register %s' % (module, cls.__name__))
+                    cls.__register__(module)
+            for model in classes['model']:
                 if hasattr(model, '_history'):
-                    models_to_update_history.add(model._name)
+                    models_to_update_history.add(model.__name__)
 
             #Instanciate a new parser for the package:
             tryton_parser = convert.TrytondXmlHandler(pool=pool, module=module)
@@ -251,8 +251,8 @@
                     continue
                 logger.info('%s:loading %s' % (module,
                         filename[len(package.info['directory']) + 1:]))
-                translation_obj = pool.get('ir.translation')
-                translation_obj.translation_import(lang2, module, filename)
+                Translation = pool.get('ir.translation')
+                Translation.translation_import(lang2, module, filename)
 
             cursor.execute('SELECT id FROM ir_module_module '
                 'WHERE name = %s', (package.name,))
@@ -271,23 +271,23 @@
 
     # Create missing reports
     from trytond.report import Report
-    report_obj = pool.get('ir.action.report')
-    report_ids = report_obj.search([
-        ('module', '=', module),
-        ])
+    ActionReport = pool.get('ir.action.report')
+    reports = ActionReport.search([
+            ('module', '=', module),
+            ])
     report_names = pool.object_name_list(type='report')
-    for report in report_obj.browse(report_ids):
+    for report in reports:
         report_name = report.report_name
         if report_name not in report_names:
             report = object.__new__(Report)
-            report._name = report_name
+            report.__name__ = report_name
             pool.add(report, type='report')
             report.__init__()
 
     for model_name in models_to_update_history:
         model = pool.get(model_name)
         if model._history:
-            logger.info('history:update %s' % model._name)
+            logger.info('history:update %s' % model.__name__)
             model._update_history_table()
 
     # Vacuum :
@@ -320,9 +320,13 @@
     Import modules to register the classes in the Pool
     '''
     import trytond.ir
+    trytond.ir.register()
     import trytond.res
+    trytond.res.register()
     import trytond.webdav
+    trytond.webdav.register()
     import trytond.test
+    trytond.test.register()
     logger = logging.getLogger('modules')
 
     for package in create_graph(get_module_list())[0]:
@@ -338,7 +342,10 @@
                     [MODULES_PATH])
             try:
                 imp.load_module('trytond.modules.' + module, mod_file,
-                        pathname, description)
+                        pathname, description).register()
+            except AttributeError:
+                # Some modules register nothing in the Pool
+                pass
             finally:
                 if mod_file is not None:
                     mod_file.close()
@@ -361,7 +368,7 @@
                     [mod_path])
             try:
                 imp.load_module('trytond.modules.' + module, mod_file,
-                        pathname, description)
+                        pathname, description).register()
             finally:
                 if mod_file is not None:
                     mod_file.close()
@@ -428,8 +435,8 @@
                 cursor.commit()
                 res = False
 
-            module_obj = pool.get('ir.module.module')
-            module_obj.update_list()
+            Module = pool.get('ir.module.module')
+            Module.update_list()
         cursor.commit()
     Cache.resets(database_name)
     return res
diff -r f6ef988fda71 trytond/pool.py
--- a/trytond/pool.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/pool.py	Sun Sep 23 17:30:03 2012 +0200
@@ -6,6 +6,17 @@
 from trytond.transaction import Transaction
 import __builtin__
 
+__all__ = ['Pool', 'PoolMeta']
+
+
+class PoolMeta(type):
+
+    def __new__(cls, name, bases, dct):
+        new = type.__new__(cls, name, bases, dct)
+        if '__name__' in dct:
+            new.__name__ = dct['__name__']
+        return new
+
 
 class Pool(object):
 
@@ -41,19 +52,17 @@
         self.database_name = database_name
 
     @staticmethod
-    def register(klass, type='model'):
+    def register(*classes, **kwargs):
         '''
-        Register a class
-
-        :param klass: the class
-        :param type: the type
+        Register a list of classes
         '''
-        module = None
-        for module in klass.__module__.split('.'):
-            if module != 'trytond' and module != 'modules':
-                break
-        if module:
-            Pool.classes[type].setdefault(module, []).append(klass)
+        module = kwargs['module']
+        type_ = kwargs['type_']
+        assert type_ in ('model', 'report', 'wizard')
+        for cls in classes:
+            mpool = Pool.classes[type_].setdefault(module, [])
+            assert cls not in mpool, cls
+            mpool.append(cls)
 
     @classmethod
     def start(cls):
@@ -61,6 +70,8 @@
         Start/restart the Pool
         '''
         with cls._lock:
+            for classes in Pool.classes.itervalues():
+                classes.clear()
             register_classes()
             cls._started = True
 
@@ -140,22 +151,17 @@
                 from trytond.report import Report
                 # Keyword argument 'type' conflicts with builtin function
                 cls = __builtin__.type(str(name), (Report,), {})
-                obj = object.__new__(cls)
-                obj._name = name
-                self.add(obj, type)
-                obj.__init__()
-                return obj
+                cls.__setup__()
+                self.add(cls, type)
+                return cls
             raise
 
-    def add(self, obj, type='model'):
+    def add(self, cls, type='model'):
         '''
-        Add an object to the pool
-
-        :param obj: the object
-        :param type: the type
+        Add a classe to the pool
         '''
         with self._locks[self.database_name]:
-            self._pool[self.database_name][type][obj._name] = obj
+            self._pool[self.database_name][type][cls.__name__] = cls
 
     def object_name_list(self, type='model'):
         '''
@@ -180,23 +186,29 @@
         '''
         return self._pool[self.database_name][type].iteritems()
 
-    def instanciate(self, module):
+    def setup(self, module):
         '''
-        Instanciate objects for a module
-
-        :param: the module name
-        :return: a dictionary with each type as key
-            and a list of object as value
+        Setup classes for module and return a list of classes for each type in
+        a dictionary.
         '''
-        res = {}
-        for _type in self.classes.keys():
-            res[_type] = []
-            for cls in self.classes[_type].get(module, []):
-                if cls._name in self._pool[self.database_name][_type].keys():
-                    parent_cls = self.get(cls._name, type=_type).__class__
-                    cls = type(cls._name, (cls, parent_cls), {})
-                obj = object.__new__(cls)
-                self.add(obj, type=_type)
-                obj.__init__()
-                res[_type].append(obj)
-        return res
+        classes = {}
+        for type_ in self.classes.keys():
+            classes[type_] = []
+            for cls in self.classes[type_].get(module, []):
+                try:
+                    previous_cls = self.get(cls.__name__, type=type_)
+                    cls = type(cls.__name__, (cls, previous_cls), {})
+                except KeyError:
+                    pass
+                try:
+                    cls.__setup__()
+                except AttributeError:
+                    if issubclass(cls.__class__, PoolMeta):
+                        continue
+                    raise
+                self.add(cls, type=type_)
+                classes[type_].append(cls)
+            for cls in classes[type_]:
+                if hasattr(cls, '__post_setup__'):
+                    cls.__post_setup__()
+        return classes
diff -r f6ef988fda71 trytond/protocols/dispatcher.py
--- a/trytond/protocols/dispatcher.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/protocols/dispatcher.py	Sun Sep 23 17:30:03 2012 +0200
@@ -20,10 +20,11 @@
 from trytond.cache import Cache
 from trytond.exceptions import UserError, UserWarning, NotLogged, \
     ConcurrencyException
+from trytond.rpc import RPC
 
 
 def dispatch(host, port, protocol, database_name, user, session, object_type,
-        object_name, method, *args, **kargs):
+        object_name, method, *args, **kwargs):
 
     if object_type == 'common':
         if method == 'login':
@@ -68,7 +69,7 @@
             ]
         elif method == 'db_exist':
             try:
-                database = Database(*args, **kargs).connect()
+                database = Database(*args, **kwargs).connect()
                 cursor = database.cursor()
                 cursor.close(close=True)
                 return True
@@ -88,13 +89,13 @@
                 res = []
             return res
         elif method == 'create':
-            return create(*args, **kargs)
+            return create(*args, **kwargs)
         elif method == 'restore':
-            return restore(*args, **kargs)
+            return restore(*args, **kwargs)
         elif method == 'drop':
-            return drop(*args, **kargs)
+            return drop(*args, **kwargs)
         elif method == 'dump':
-            return dump(*args, **kargs)
+            return dump(*args, **kwargs)
         return
     elif object_type == 'system':
         database = Database(database_name).connect()
@@ -131,34 +132,36 @@
             pool.init()
     obj = pool.get(object_name, type=object_type)
 
-    if method in obj._rpc:
-        readonly = not obj._rpc[method]
+    if method in obj.__rpc__:
+        rpc = obj.__rpc__[method]
     elif method in getattr(obj, '_buttons', {}):
-        readonly = False
+        rpc = RPC(readonly=False, instantiate=0)
     else:
         raise UserError('Calling method %s on %s %s is not allowed!'
             % (method, object_type, object_name))
 
     for count in range(int(CONFIG['retry']), -1, -1):
         with Transaction().start(database_name, user,
-                readonly=readonly) as transaction:
+                readonly=rpc.readonly) as transaction:
             try:
-
-                args_without_context = list(args)
-                if 'context' in kargs:
-                    context = kargs.pop('context')
+                args, kwargs, transaction.context, transaction.timestamp = \
+                    rpc.convert(obj, *args, **kwargs)
+                meth = getattr(obj, method)
+                if not hasattr(meth, 'im_self') or meth.im_self:
+                    result = rpc.result(meth(*args, **kwargs))
                 else:
-                    context = args_without_context.pop()
-                if '_timestamp' in context:
-                    transaction.timestamp = context['_timestamp']
-                    del context['_timestamp']
-                transaction.context = context
-                res = getattr(obj, method)(*args_without_context, **kargs)
-                if not readonly:
+                    assert rpc.instantiate == 0
+                    inst = args.pop(0)
+                    if hasattr(inst, method):
+                        result = rpc.result(meth(inst, *args, **kwargs))
+                    else:
+                        result = [rpc.result(meth(i, *args, **kwargs))
+                            for i in inst]
+                if not rpc.readonly:
                     transaction.cursor.commit()
             except DatabaseOperationalError, exception:
                 transaction.cursor.rollback()
-                if count and not readonly:
+                if count and not rpc.readonly:
                     continue
                 raise
             except Exception, exception:
@@ -176,16 +179,16 @@
         if not (object_name == 'res.request' and method == 'request_get'):
             with Transaction().start(database_name, 0) as transaction:
                 pool = Pool(database_name)
-                session_obj = pool.get('ir.session')
+                Session = pool.get('ir.session')
                 try:
-                    session_obj.reset(session)
+                    Session.reset(session)
                 except DatabaseOperationalError:
                     # Silently fail when reseting session
                     transaction.cursor.rollback()
                 else:
                     transaction.cursor.commit()
         Cache.resets(database_name)
-        return res
+        return result
 
 
 def create(database_name, password, lang, admin_password):
@@ -237,9 +240,9 @@
             cursor.execute('UPDATE res_user ' \
                     'SET password = %s ' \
                     'WHERE login = \'admin\'', (admin_password,))
-            module_obj = pool.get('ir.module.module')
-            if module_obj:
-                module_obj.update_list()
+            Module = pool.get('ir.module.module')
+            if Module:
+                Module.update_list()
             cursor.commit()
             res = True
     except Exception:
diff -r f6ef988fda71 trytond/protocols/webdav.py
--- a/trytond/protocols/webdav.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/protocols/webdav.py	Sun Sep 23 17:30:03 2012 +0200
@@ -174,12 +174,12 @@
             return res
         pool = Pool(Transaction().cursor.database_name)
         try:
-            collection_obj = pool.get('webdav.collection')
+            Collection = pool.get('webdav.collection')
             scheme, netloc, path, params, query, fragment = \
                 urlparse.urlparse(uri)
             if path[-1:] != '/':
                 path += '/'
-            for child in collection_obj.get_childs(dburi, filter=filter,
+            for child in Collection.get_childs(dburi, filter=filter,
                     cache=CACHE):
                 res.append(urlparse.urlunparse((scheme, netloc,
                             path + child.encode('utf-8'), params, query,
@@ -230,9 +230,9 @@
             res += '</html>'
             return res
         pool = Pool(Transaction().cursor.database_name)
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
         try:
-            res = collection_obj.get_data(dburi, cache=CACHE)
+            res = Collection.get_data(dburi, cache=CACHE)
         except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden), exception:
             self._log_exception(exception)
             raise
@@ -261,9 +261,9 @@
         if not dbname or not dburi:
             raise DAV_Forbidden
         pool = Pool(Transaction().cursor.database_name)
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
         try:
-            res = collection_obj.put(dburi, data, content_type, cache=CACHE)
+            res = Collection.put(dburi, data, content_type, cache=CACHE)
             Transaction().cursor.commit()
         except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden), exception:
             self._log_exception(exception)
@@ -284,9 +284,9 @@
         if not dbname or not dburi:
             raise DAV_Forbidden
         pool = Pool(Transaction().cursor.database_name)
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
         try:
-            res = collection_obj.mkcol(dburi, cache=CACHE)
+            res = Collection.mkcol(dburi, cache=CACHE)
             Transaction().cursor.commit()
         except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden), exception:
             self._log_exception(exception)
@@ -303,9 +303,9 @@
         if not dbname or not dburi:
             return COLLECTION
         pool = Pool(Transaction().cursor.database_name)
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
         try:
-            res = collection_obj.get_resourcetype(dburi, cache=CACHE)
+            res = Collection.get_resourcetype(dburi, cache=CACHE)
         except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden), exception:
             self._log_exception(exception)
             raise
@@ -320,8 +320,8 @@
             return uri.split('/')[-1]
         pool = Pool(Transaction().cursor.database_name)
         try:
-            collection_obj = pool.get('webdav.collection')
-            res = collection_obj.get_displayname(dburi, cache=CACHE)
+            Collection = pool.get('webdav.collection')
+            res = Collection.get_displayname(dburi, cache=CACHE)
         except KeyError:
             raise DAV_NotFound
         except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden), exception:
@@ -337,9 +337,9 @@
         if not dbname or not dburi:
             return '0'
         pool = Pool(Transaction().cursor.database_name)
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
         try:
-            res = collection_obj.get_contentlength(dburi, cache=CACHE)
+            res = Collection.get_contentlength(dburi, cache=CACHE)
         except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden), exception:
             self._log_exception(exception)
             raise
@@ -353,9 +353,9 @@
         if not dbname or self.is_collection(uri):
             return "text/html"
         pool = Pool(Transaction().cursor.database_name)
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
         try:
-            res = collection_obj.get_contenttype(dburi, cache=CACHE)
+            res = Collection.get_contenttype(dburi, cache=CACHE)
         except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden), exception:
             self._log_exception(exception)
             raise
@@ -372,9 +372,9 @@
         if not dbname or not dburi:
             return time.time()
         pool = Pool(Transaction().cursor.database_name)
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
         try:
-            res = collection_obj.get_creationdate(dburi, cache=CACHE)
+            res = Collection.get_creationdate(dburi, cache=CACHE)
         except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden), exception:
             self._log_exception(exception)
             raise
@@ -388,9 +388,9 @@
         if not dbname or not dburi:
             return time.time()
         pool = Pool(Transaction().cursor.database_name)
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
         try:
-            res = collection_obj.get_lastmodified(dburi, cache=CACHE)
+            res = Collection.get_lastmodified(dburi, cache=CACHE)
         except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden), exception:
             self._log_exception(exception)
             raise
@@ -404,9 +404,9 @@
         if not dbname or not dburi:
             return 403
         pool = Pool(Transaction().cursor.database_name)
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
         try:
-            res = collection_obj.rmcol(dburi, cache=CACHE)
+            res = Collection.rmcol(dburi, cache=CACHE)
             Transaction().cursor.commit()
         except Exception, exception:
             self._log_exception(exception)
@@ -419,9 +419,9 @@
         if not dbname or not dburi:
             return 403
         pool = Pool(Transaction().cursor.database_name)
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
         try:
-            res = collection_obj.rm(dburi, cache=CACHE)
+            res = Collection.rm(dburi, cache=CACHE)
             Transaction().cursor.commit()
         except Exception, exception:
             self._log_exception(exception)
@@ -434,9 +434,9 @@
         if not dbname or not dburi:
             return 1
         pool = Pool(Transaction().cursor.database_name)
-        collection_obj = pool.get('webdav.collection')
+        Collection = pool.get('webdav.collection')
         try:
-            res = collection_obj.exists(dburi, cache=CACHE)
+            res = Collection.exists(dburi, cache=CACHE)
         except (DAV_Error, DAV_NotFound, DAV_Secret, DAV_Forbidden), exception:
             self._log_exception(exception)
             raise
@@ -485,8 +485,8 @@
         else:
             pool = Pool(Transaction().cursor.database_name)
             try:
-                collection_obj = pool.get('webdav.collection')
-                privileges = collection_obj.current_user_privilege_set(dburi,
+                Collection = pool.get('webdav.collection')
+                privileges = Collection.current_user_privilege_set(dburi,
                         cache=CACHE)
             except KeyError:
                 pass
@@ -579,8 +579,8 @@
                     pool = Pool(dbname)
                     if not dbname in database_list:
                         pool.init()
-                    share_obj = pool.get('webdav.share')
-                    user = share_obj.get_login(key, command, path)
+                    Share = pool.get('webdav.share')
+                    user = Share.get_login(key, command, path)
                     transaction.cursor.commit()
             if not user:
                 return None
diff -r f6ef988fda71 trytond/pyson.py
--- a/trytond/pyson.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/pyson.py	Sun Sep 23 17:30:03 2012 +0200
@@ -547,8 +547,8 @@
 
     def pyson(self):
         from trytond.pool import Pool
-        model_data_obj = Pool().get('ir.model.data')
-        return model_data_obj.get_id(self._module, self._fs_id)
+        ModelData = Pool().get('ir.model.data')
+        return ModelData.get_id(self._module, self._fs_id)
 
     def types(self):
         return set([int])
diff -r f6ef988fda71 trytond/report/report.py
--- a/trytond/report/report.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/report/report.py	Sun Sep 23 17:30:03 2012 +0200
@@ -22,9 +22,10 @@
 from genshi.filters import Translator
 import lxml.etree
 from trytond.config import CONFIG
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 from trytond.transaction import Transaction
 from trytond.url import URLMixin
+from trytond.rpc import RPC
 
 MIMETYPES = {
     'odt': 'application/vnd.oasis.opendocument.text',
@@ -55,9 +56,10 @@
 
 class ReportFactory:
 
-    def __call__(self, objects, **kwargs):
+    def __call__(self, records, **kwargs):
         data = {}
-        data['objects'] = objects
+        data['objects'] = records  # XXX To remove
+        data['records'] = records
         data.update(kwargs)
         return data
 
@@ -73,7 +75,7 @@
     def __call__(self, text):
         if self.language not in self.cache:
             self.cache[self.language] = {}
-            translation_ids = self.translation.search([
+            translations = self.translation.search([
                 ('lang', '=', self.language),
                 ('type', '=', 'odt'),
                 ('name', '=', self.report_name),
@@ -82,7 +84,7 @@
                 ('fuzzy', '=', False),
                 ('res_id', '=', None),
                 ])
-            for translation in self.translation.browse(translation_ids):
+            for translation in translations:
                 self.cache[self.language][translation.src] = translation.value
         return self.cache[self.language].get(text, text)
 
@@ -91,78 +93,92 @@
 
 
 class Report(URLMixin):
-    _name = ""
+    __metaclass__ = PoolMeta
 
-    def __new__(cls):
-        Pool.register(cls, type='report')
+    @classmethod
+    def __setup__(cls):
+        cls.__rpc__ = {
+            'execute': RPC(),
+            }
 
-    def __init__(self):
-        self._rpc = {
-            'execute': False,
-        }
-
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         pass
 
-    def execute(self, ids, datas):
+    @classmethod
+    def execute(cls, ids, data):
         '''
-        Execute the report.
-
-        :param ids: a list of record ids on which execute report
-        :param datas: a dictionary with datas that will be set in
-            local context of the report
-        :return: a tuple with:
+        Execute the report on record ids.
+        The dictionary with data that will be set in local context of the
+        report.
+        It returns a tuple with:
             report type,
             data,
             a boolean to direct print,
             the report name
         '''
         pool = Pool()
-        action_report_obj = pool.get('ir.action.report')
-        action_report_ids = action_report_obj.search([
-            ('report_name', '=', self._name)
-            ])
-        if not action_report_ids:
-            raise Exception('Error', 'Report (%s) not find!' % self._name)
-        action_report = action_report_obj.browse(action_report_ids[0])
-        objects = None
+        ActionReport = pool.get('ir.action.report')
+        action_reports = ActionReport.search([
+                ('report_name', '=', cls.__name__)
+                ])
+        if not action_reports:
+            raise Exception('Error', 'Report (%s) not find!' % cls.__name__)
+        action_report = action_reports[0]
+        records = None
         if action_report.model:
-            objects = self._get_objects(ids, action_report.model, datas)
-        type, data = self.parse(action_report, objects, datas, {})
+            records = cls._get_records(ids, action_report.model, data)
+        type, data = cls.parse(action_report, records, data, {})
         return (type, buffer(data), action_report.direct_print,
-                action_report.name)
+            action_report.name)
 
-    def _get_objects(self, ids, model, datas):
+    @classmethod
+    def _get_records(cls, ids, model, data):
         pool = Pool()
-        model_obj = pool.get(model)
-        return model_obj.browse(ids)
+        Model = pool.get(model)
 
-    def parse(self, report, objects, datas, localcontext):
+        class TranslateModel:
+            _languages = {}
+
+            def __init__(self, id):
+                self.id = id
+                self._language = Transaction().language
+
+            def setLang(self, language):
+                self._language = language
+
+            def __getattr__(self, name):
+                if self._language not in TranslateModel._languages:
+                    with Transaction().set_context(language=self._language):
+                        records = Model.browse(ids)
+                    id2record = dict((r.id, r) for r in records)
+                    TranslateModel._languages[self._language] = id2record
+                else:
+                    id2record = TranslateModel._languages[self._language]
+                record = id2record[self.id]
+                return getattr(record, name)
+        return [TranslateModel(id) for id in ids]
+
+    @classmethod
+    def parse(cls, report, records, data, localcontext):
         '''
-        Parse the report.
-
-        :param report: a BrowseRecord of the ir.action.report
-        :param objects: a BrowseRecordList of the records on which parse report
-        :param datas: a dictionary with datas that will be set in local context
-            of the report
-        :param localcontext: the context used to parse the report
-        :return: a tuple with:
-            report type
-            report
+        Parse the report and return a tuple with report type and report.
         '''
         pool = Pool()
-        localcontext['datas'] = datas
-        localcontext['user'] = pool.get('res.user'
-                ).browse(Transaction().user)
+        User = pool.get('res.user')
+        Translation = pool.get('ir.translation')
+
+        localcontext['data'] = data
+        localcontext['user'] = User(Transaction().user)
         localcontext['formatLang'] = lambda *args, **kargs: \
-                self.format_lang(*args, **kargs)
+            cls.format_lang(*args, **kargs)
         localcontext['StringIO'] = StringIO.StringIO
         localcontext['time'] = time
         localcontext['datetime'] = datetime
         localcontext['context'] = Transaction().context
 
-        translate = TranslateFactory(self._name, Transaction().language,
-                pool.get('ir.translation'))
+        translate = TranslateFactory(cls.__name__, Transaction().language,
+            Translation)
         localcontext['setLang'] = lambda language: translate.set_language(
             language)
 
@@ -257,9 +273,10 @@
             localcontext.iteritems()))
         #Test compatibility with old relatorio version <= 0.3.0
         if len(inspect.getargspec(rel_report.__call__)[0]) == 2:
-            data = rel_report(objects, **localcontext).render().getvalue()
+            data = rel_report(records, **localcontext).render().getvalue()
         else:
-            localcontext['objects'] = objects
+            localcontext['objects'] = records  # XXX to remove
+            localcontext['records'] = records
             data = rel_report(**localcontext).render()
             if hasattr(data, 'getvalue'):
                 data = data.getvalue()
@@ -267,11 +284,12 @@
         os.remove(path)
         output_format = report.extension or report.template_extension
         if output_format not in MIMETYPES:
-            data = self.unoconv(data, report.template_extension, output_format)
+            data = cls.unoconv(data, report.template_extension, output_format)
         oext = FORMAT2EXT.get(output_format, output_format)
         return (oext, data)
 
-    def unoconv(self, data, input_format, output_format):
+    @classmethod
+    def unoconv(cls, data, input_format, output_format):
         '''
         Call unoconv to convert the OpenDocument
         '''
@@ -291,17 +309,18 @@
         finally:
             os.remove(path)
 
-    def format_lang(self, value, lang, digits=2, grouping=True, monetary=False,
+    @classmethod
+    def format_lang(cls, value, lang, digits=2, grouping=True, monetary=False,
             date=False, currency=None, symbol=True):
         pool = Pool()
-        lang_obj = pool.get('ir.lang')
+        Lang = pool.get('ir.lang')
 
         if date:
             if lang:
                 locale_format = lang.date
                 code = lang.code
             else:
-                locale_format = lang_obj.default_date()
+                locale_format = Lang.default_date()
                 code = CONFIG['language']
             if not isinstance(value, time.struct_time):
                 # assume string, parse it
@@ -318,9 +337,9 @@
                     string_pattern)[:6])
             else:
                 date = datetime.datetime(*(value.timetuple()[:6]))
-            return lang_obj.strftime(date, code, locale_format)
+            return Lang.strftime(date, code, locale_format)
         if currency:
-            return lang_obj.currency(lang, value, currency, grouping=grouping,
-                    symbol=symbol)
-        return lang_obj.format(lang, '%.' + str(digits) + 'f', value,
-                grouping=grouping, monetary=monetary)
+            return Lang.currency(lang, value, currency, grouping=grouping,
+                symbol=symbol)
+        return Lang.format(lang, '%.' + str(digits) + 'f', value,
+            grouping=grouping, monetary=monetary)
diff -r f6ef988fda71 trytond/res/__init__.py
--- a/trytond/res/__init__.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/res/__init__.py	Sun Sep 23 17:30:03 2012 +0200
@@ -1,6 +1,38 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
+from ..pool import Pool
 from .group import *
 from .user import *
 from .request import *
 from .ir import *
+
+
+def register():
+    Pool.register(
+        Group,
+        User,
+        Group2,
+        UserAction,
+        UserGroup,
+        Warning_,
+        UserConfigStart,
+        Request,
+        RequestLink,
+        RequestHistory,
+        RequestReference,
+        UIMenuGroup,
+        ActionGroup,
+        ModelFieldGroup,
+        ModelButtonGroup,
+        RuleGroupGroup,
+        RuleGroupUser,
+        Lang,
+        SequenceType,
+        SequenceTypeGroup,
+        Sequence,
+        SequenceStrict,
+        ModuleConfigWizardItem,
+        module='res', type_='model')
+    Pool.register(
+        UserConfig,
+        module="res", type_='wizard')
diff -r f6ef988fda71 trytond/res/group.py
--- a/trytond/res/group.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/res/group.py	Sun Sep 23 17:30:03 2012 +0200
@@ -4,22 +4,26 @@
 from itertools import chain
 from ..model import ModelView, ModelSQL, fields
 from ..transaction import Transaction
-from ..pool import Pool
+from ..pool import Pool, PoolMeta
+
+__all__ = [
+    'Group', 'Group2',
+    ]
 
 
 class MenuMany2Many(fields.Many2Many):
 
     def get(self, ids, model, name, values=None):
-        menu_obj = self.get_target()
+        Menu = self.get_target()
         res = super(MenuMany2Many, self).get(ids, model, name,
                 values=values)
         menu_ids = list(set(chain(*res.values())))
         test_ids = []
         for i in range(0, len(menu_ids), Transaction().cursor.IN_MAX):
             sub_ids = menu_ids[i:i + Transaction().cursor.IN_MAX]
-            test_ids.append(menu_obj.search([
-                ('id', 'in', sub_ids),
-                ]))
+            test_ids.append(map(int, Menu.search([
+                            ('id', 'in', sub_ids),
+                            ])))
         menu_ids = set(chain(*test_ids))
         for ids in res.itervalues():
             for id_ in ids[:]:
@@ -30,8 +34,7 @@
 
 class Group(ModelSQL, ModelView):
     "Group"
-    _name = "res.group"
-    _description = __doc__
+    __name__ = "res.group"
     name = fields.Char('Name', required=True, select=True, translate=True)
     model_access = fields.One2Many('ir.model.access', 'group',
        'Access Model')
@@ -43,67 +46,68 @@
     menu_access = MenuMany2Many('ir.ui.menu-res.group',
        'group', 'menu', 'Access Menu')
 
-    def __init__(self):
-        super(Group, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Group, cls).__setup__()
+        cls._sql_constraints += [
             ('name_uniq', 'unique (name)',
                 'The name of the group must be unique!')
         ]
 
-    def copy(self, ids, default=None):
-        int_id = isinstance(ids, (int, long))
-        if int_id:
-            ids = [ids]
-
+    @classmethod
+    def copy(cls, groups, default=None):
         if default is None:
             default = {}
         default = default.copy()
 
-        new_ids = []
-        for group in self.browse(ids):
+        new_groups = []
+        for group in groups:
             i = 1
             while True:
                 name = '%s (%d)' % (group.name, i)
-                if not self.search([('name', '=', name)], order=[]):
+                if not cls.search([('name', '=', name)], order=[]):
                     break
                 i += 1
             default['name'] = name
-            new_ids.append(super(Group, self).copy(group.id, default=default))
-        if int_id:
-            return new_ids[0]
-        return new_ids
+            new_groups.extend(super(Group, cls).copy([group], default=default))
+        return new_groups
 
-    def create(self, vals):
-        res = super(Group, self).create(vals)
+    @classmethod
+    def create(cls, vals):
+        res = super(Group, cls).create(vals)
         pool = Pool()
         # Restart the cache on the domain_get method
-        pool.get('ir.rule').domain_get.reset()
+        pool.get('ir.rule')._domain_get_cache.clear()
         # Restart the cache for get_groups
-        pool.get('res.user').get_groups.reset()
+        pool.get('res.user')._get_groups_cache.clear()
         # Restart the cache for get_preferences
-        pool.get('res.user').get_preferences.reset()
+        pool.get('res.user')._get_preferences_cache.clear()
         return res
 
-    def write(self, ids, vals):
-        res = super(Group, self).write(ids, vals)
+    @classmethod
+    def write(cls, groups, vals):
+        super(Group, cls).write(groups, vals)
         pool = Pool()
         # Restart the cache on the domain_get method
-        pool.get('ir.rule').domain_get.reset()
+        pool.get('ir.rule')._domain_get_cache.clear()
         # Restart the cache for get_groups
-        pool.get('res.user').get_groups.reset()
+        pool.get('res.user')._get_groups_cache.clear()
         # Restart the cache for get_preferences
-        pool.get('res.user').get_preferences.reset()
-        return res
+        pool.get('res.user')._get_preferences_cache.clear()
 
-    def delete(self, ids):
-        res = super(Group, self).delete(ids)
+    @classmethod
+    def delete(cls, groups):
+        super(Group, cls).delete(groups)
         pool = Pool()
         # Restart the cache on the domain_get method
-        pool.get('ir.rule').domain_get.reset()
+        pool.get('ir.rule')._domain_get_cache.clear()
         # Restart the cache for get_groups
-        pool.get('res.user').get_groups.reset()
+        pool.get('res.user')._get_groups_cache.clear()
         # Restart the cache for get_preferences
-        pool.get('res.user').get_preferences.reset()
-        return res
+        pool.get('res.user')._get_preferences_cache.clear()
 
-Group()
+
+class Group2:
+    __metaclass__ = PoolMeta
+    __name__ = "res.group"
+    users = fields.Many2Many('res.user-res.group', 'group', 'user', 'Users')
diff -r f6ef988fda71 trytond/res/ir.py
--- a/trytond/res/ir.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/res/ir.py	Sun Sep 23 17:30:03 2012 +0200
@@ -1,338 +1,320 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
-from ..model import ModelView, ModelSQL, Model, fields
+from ..model import ModelSQL, fields
 from ..backend import TableHandler
 from ..transaction import Transaction
-from ..pool import Pool
+from ..pool import Pool, PoolMeta
+
+__all__ = [
+    'UIMenuGroup', 'ActionGroup', 'ModelFieldGroup', 'ModelButtonGroup',
+    'RuleGroupGroup', 'RuleGroupUser', 'Lang', 'SequenceType',
+    'SequenceTypeGroup', 'Sequence', 'SequenceStrict',
+    'ModuleConfigWizardItem',
+    ]
+__metaclass__ = PoolMeta
 
 
 class UIMenuGroup(ModelSQL):
     "UI Menu - Group"
-    _name = 'ir.ui.menu-res.group'
-    _description = __doc__
+    __name__ = 'ir.ui.menu-res.group'
     menu = fields.Many2One('ir.ui.menu', 'Menu', ondelete='CASCADE',
             select=True, required=True)
     group = fields.Many2One('res.group', 'Group', ondelete='CASCADE',
             select=True, required=True)
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.0 table name change
-        TableHandler.table_rename(cursor, 'ir_ui_menu_group_rel', self._table)
+        TableHandler.table_rename(cursor, 'ir_ui_menu_group_rel', cls._table)
         TableHandler.sequence_rename(cursor, 'ir_ui_menu_group_rel_id_seq',
-                self._table + '_id_seq')
+                cls._table + '_id_seq')
         # Migration from 2.0 menu_id and gid renamed into menu group
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.column_rename('menu_id', 'menu')
         table.column_rename('gid', 'group')
-        super(UIMenuGroup, self).init(module_name)
+        super(UIMenuGroup, cls).__register__(module_name)
 
-    def create(self, vals):
-        res = super(UIMenuGroup, self).create(vals)
+    @classmethod
+    def create(cls, vals):
+        res = super(UIMenuGroup, cls).create(vals)
         # Restart the cache on the domain_get method
-        Pool().get('ir.rule').domain_get.reset()
+        Pool().get('ir.rule')._domain_get_cache.clear()
         return res
 
-    def write(self, ids, vals):
-        res = super(UIMenuGroup, self).write(ids, vals)
+    @classmethod
+    def write(cls, records, vals):
+        super(UIMenuGroup, cls).write(records, vals)
         # Restart the cache on the domain_get method
-        Pool().get('ir.rule').domain_get.reset()
-        return res
+        Pool().get('ir.rule')._domain_get_cache.clear()
 
-    def delete(self, ids):
-        res = super(UIMenuGroup, self).delete(ids)
+    @classmethod
+    def delete(cls, records):
+        super(UIMenuGroup, cls).delete(records)
         # Restart the cache on the domain_get method
-        Pool().get('ir.rule').domain_get.reset()
-        return res
-
-UIMenuGroup()
+        Pool().get('ir.rule')._domain_get_cache.clear()
 
 
 class ActionGroup(ModelSQL):
     "Action - Group"
-    _name = 'ir.action-res.group'
-    _description = __doc__
+    __name__ = 'ir.action-res.group'
     action = fields.Many2One('ir.action', 'Action', ondelete='CASCADE',
             select=True, required=True)
     group = fields.Many2One('res.group', 'Group', ondelete='CASCADE',
             select=True, required=True)
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.0 table name change
-        TableHandler.table_rename(cursor, 'ir_action_group_rel', self._table)
+        TableHandler.table_rename(cursor, 'ir_action_group_rel', cls._table)
         TableHandler.sequence_rename(cursor, 'ir_action_group_rel_id_seq',
-                self._table + '_id_seq')
+                cls._table + '_id_seq')
         # Migration from 2.0 action_id and gid renamed into action and group
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.column_rename('action_id', 'action')
         table.column_rename('gid', 'group')
-        super(ActionGroup, self).init(module_name)
+        super(ActionGroup, cls).__register__(module_name)
 
-    def create(self, vals):
-        action_obj = Pool().get('ir.action')
+    @classmethod
+    def create(cls, vals):
+        Action = Pool().get('ir.action')
         if vals.get('action'):
             vals = vals.copy()
-            vals['action'] = action_obj.get_action_id(vals['action'])
-        res = super(ActionGroup, self).create(vals)
+            vals['action'] = Action.get_action_id(vals['action'])
+        res = super(ActionGroup, cls).create(vals)
         # Restart the cache on the domain_get method
-        Pool().get('ir.rule').domain_get.reset()
+        Pool().get('ir.rule')._domain_get_cache.clear()
         return res
 
-    def write(self, ids, vals):
-        action_obj = Pool().get('ir.action')
+    @classmethod
+    def write(cls, records, vals):
+        Action = Pool().get('ir.action')
         if vals.get('action'):
             vals = vals.copy()
-            vals['action'] = action_obj.get_action_id(vals['action'])
-        res = super(ActionGroup, self).write(ids, vals)
+            vals['action'] = Action.get_action_id(vals['action'])
+        super(ActionGroup, cls).write(records, vals)
         # Restart the cache on the domain_get method
-        Pool().get('ir.rule').domain_get.reset()
-        return res
+        Pool().get('ir.rule')._domain_get_cache.clear()
 
-    def delete(self, ids):
-        res = super(ActionGroup, self).delete(ids)
+    @classmethod
+    def delete(cls, records):
+        super(ActionGroup, cls).delete(records)
         # Restart the cache on the domain_get method
-        Pool().get('ir.rule').domain_get.reset()
-        return res
-
-ActionGroup()
+        Pool().get('ir.rule')._domain_get_cache.clear()
 
 
 class ModelFieldGroup(ModelSQL):
     "Model Field Group Rel"
-    _name = 'ir.model.field-res.group'
-    _description = __doc__
+    __name__ = 'ir.model.field-res.group'
     field_id = fields.Many2One('ir.model.field', 'Model Field',
             ondelete='CASCADE', select=True, required=True)
     group_id = fields.Many2One('res.group', 'Group', ondelete='CASCADE',
             select=True, required=True)
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.0 table name change
         TableHandler.table_rename(cursor, 'ir_model_field_group_rel',
-            self._table)
+            cls._table)
         TableHandler.sequence_rename(cursor, 'ir_model_field_group_rel_id_seq',
-            self._table + '_id_seq')
-        super(ModelFieldGroup, self).init(module_name)
-
-ModelFieldGroup()
+            cls._table + '_id_seq')
+        super(ModelFieldGroup, cls).__register__(module_name)
 
 
 class ModelButtonGroup(ModelSQL):
     "Model Button - Group"
-    _name = 'ir.model.button-res.group'
-    _description = __doc__
+    __name__ = 'ir.model.button-res.group'
     button = fields.Many2One('ir.model.button', 'Button',
         ondelete='CASCADE', select=True, required=True)
     group = fields.Many2One('res.group', 'Group', ondelete='CASCADE',
         select=True, required=True)
     active = fields.Boolean('Active', select=True)
 
-    def __init__(self):
-        super(ModelButtonGroup, self).__init__()
-        for method in self._rpc:
-            self._rpc[method] = False
-
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def create(self, values):
+    @classmethod
+    def create(cls, values):
         pool = Pool()
-        result = super(ModelButtonGroup, self).create(values)
+        result = super(ModelButtonGroup, cls).create(values)
         # Restart the cache for get_groups
-        pool.get('ir.model.button').get_groups.reset()
+        pool.get('ir.model.button')._groups_cache.clear()
         return result
 
-    def write(self, ids, values):
+    @classmethod
+    def write(cls, records, values):
         pool = Pool()
-        result = super(ModelButtonGroup, self).write(ids, values)
+        super(ModelButtonGroup, cls).write(records, values)
         # Restart the cache for get_groups
-        pool.get('ir.model.button').get_groups.reset()
-        return result
+        pool.get('ir.model.button')._groups_cache.clear()
 
-    def delete(self, ids):
+    @classmethod
+    def delete(cls, records):
         pool = Pool()
-        result = super(ModelButtonGroup, self).delete(ids)
+        super(ModelButtonGroup, cls).delete(records)
         # Restart the cache for get_groups
-        pool.get('ir.model.button').get_groups.reset()
-        return result
-
-ModelButtonGroup()
+        pool.get('ir.model.button')._groups_cache.clear()
 
 
 class RuleGroupGroup(ModelSQL):
     "Rule Group - Group"
-    _name = 'ir.rule.group-res.group'
-    _description = __doc__
+    __name__ = 'ir.rule.group-res.group'
     rule_group = fields.Many2One('ir.rule.group', 'Rule Group',
             ondelete='CASCADE', select=True, required=True)
     group = fields.Many2One('res.group', 'Group', ondelete='CASCADE',
             select=True, required=True)
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.0 table name change
-        TableHandler.table_rename(cursor, 'group_rule_group_rel', self._table)
+        TableHandler.table_rename(cursor, 'group_rule_group_rel', cls._table)
         TableHandler.sequence_rename(cursor, 'group_rule_group_rel_id_seq',
-                self._table + '_id_seq')
+                cls._table + '_id_seq')
         # Migration from 2.0 rule_group_id and group_id renamed into rule_group
         # and group
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.column_rename('rule_group_id', 'rule_group')
         table.column_rename('group_id', 'group')
-        super(RuleGroupGroup, self).init(module_name)
-
-RuleGroupGroup()
+        super(RuleGroupGroup, cls).__register__(module_name)
 
 
 class RuleGroupUser(ModelSQL):
     "Rule Group - User"
-    _name = 'ir.rule.group-res.user'
-    _description = __doc__
+    __name__ = 'ir.rule.group-res.user'
     rule_group = fields.Many2One('ir.rule.group', 'Rule Group',
             ondelete='CASCADE', select=True, required=True)
     user = fields.Many2One('res.user', 'User', ondelete='CASCADE',
             select=True, required=True)
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.0 table name change
-        TableHandler.table_rename(cursor, 'user_rule_group_rel', self._table)
+        TableHandler.table_rename(cursor, 'user_rule_group_rel', cls._table)
         TableHandler.sequence_rename(cursor, 'user_rule_group_rel_id_seq',
-                self._table + '_id_seq')
+                cls._table + '_id_seq')
         # Migration from 2.0 rule_group_id and user_id renamed into rule_group
         # and user
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.column_rename('rule_group_id', 'rule_group')
         table.column_rename('user_id', 'user')
-        super(RuleGroupUser, self).init(module_name)
+        super(RuleGroupUser, cls).__register__(module_name)
 
-RuleGroupUser()
 
+class Lang:
+    __name__ = 'ir.lang'
 
-class Lang(ModelSQL, ModelView):
-    _name = 'ir.lang'
+    @classmethod
+    def write(cls, langs, vals):
+        super(Lang, cls).write(langs, vals)
+        # Restart the cache for get_preferences
+        Pool().get('res.user')._get_preferences_cache.clear()
 
-    def write(self, ids, vals):
-        res = super(Lang, self).write(ids, vals)
-        # Restart the cache for get_preferences
-        Pool().get('res.user').get_preferences.reset()
-        return res
 
-Lang()
-
-
-class SequenceType(ModelSQL, ModelView):
-    _name = 'ir.sequence.type'
+class SequenceType:
+    __name__ = 'ir.sequence.type'
     groups = fields.Many2Many('ir.sequence.type-res.group', 'sequence_type',
             'group', 'User Groups',
             help='Groups allowed to edit the sequences of this type')
 
-SequenceType()
-
 
 class SequenceTypeGroup(ModelSQL):
     'Sequence Type - Group'
-    _name = 'ir.sequence.type-res.group'
-    _description = __doc__
+    __name__ = 'ir.sequence.type-res.group'
     sequence_type = fields.Many2One('ir.sequence.type', 'Sequence Type',
             ondelete='CASCADE', select=True, required=True)
     group = fields.Many2One('res.group', 'User Groups',
             ondelete='CASCADE', select=True, required=True)
 
-    def delete(self, ids):
-        rule_obj = Pool().get('ir.rule')
-        res = super(SequenceTypeGroup, self).delete(ids)
+    @classmethod
+    def delete(cls, records):
+        Rule = Pool().get('ir.rule')
+        super(SequenceTypeGroup, cls).delete(records)
         # Restart the cache on the domain_get method of ir.rule
-        rule_obj.domain_get.reset()
+        Rule._domain_get_cache.clear()
+
+    @classmethod
+    def create(cls, vals):
+        Rule = Pool().get('ir.rule')
+        res = super(SequenceTypeGroup, cls).create(vals)
+        # Restart the cache on the domain_get method of ir.rule
+        Rule._domain_get_cache.clear()
         return res
 
-    def create(self, vals):
-        rule_obj = Pool().get('ir.rule')
-        res = super(SequenceTypeGroup, self).create(vals)
-        # Restart the cache on the domain_get method of ir.rule
-        rule_obj.domain_get.reset()
-        return res
+    @classmethod
+    def write(cls, records, vals):
+        Rule = Pool().get('ir.rule')
+        super(SequenceTypeGroup, cls).write(records, vals)
+        # Restart the cache on the domain_get method
+        Rule._domain_get_cache.clear()
 
-    def write(self, ids, vals):
-        rule_obj = Pool().get('ir.rule')
-        res = super(SequenceTypeGroup, self).write(ids, vals)
-        # Restart the cache on the domain_get method
-        rule_obj.domain_get.reset()
-        return res
 
-SequenceTypeGroup()
-
-
-class Sequence(ModelSQL, ModelView):
-    _name = 'ir.sequence'
+class Sequence:
+    __name__ = 'ir.sequence'
     groups = fields.Function(fields.Many2Many('res.group', None, None,
         'User Groups'), 'get_groups', searcher='search_groups')
 
-    def get_groups(self, ids, name):
-        sequence_type_obj = Pool().get('ir.sequence.type')
-        sequences = self.browse(ids)
+    @classmethod
+    def get_groups(cls, sequences, name):
+        SequenceType = Pool().get('ir.sequence.type')
         code2seq = {}
         for sequence in sequences:
             code2seq.setdefault(sequence.code, []).append(sequence.id)
 
-        sequence_type_ids = sequence_type_obj.search([
+        sequence_types = SequenceType.search([
                 ('code', 'in', code2seq.keys()),
                 ])
-        sequence_types = sequence_type_obj.browse(sequence_type_ids)
 
-        res = {}
+        groups = {}
         for sequence_type in sequence_types:
             seq_ids = code2seq[sequence_type.code]
             for seq_id in seq_ids:
-                res.setdefault(seq_id, []).append(sequence_type.id)
+                groups.setdefault(seq_id, []).append(sequence_type.id)
 
-        return res
+        return groups
 
-    def search_groups(self, name, clause):
-        sequence_type_obj = Pool().get('ir.sequence.type')
-        ids = sequence_type_obj.search([clause], order=[])
-        seq_types = sequence_type_obj.browse(ids)
+    @staticmethod
+    def search_groups(name, clause):
+        SequenceType = Pool().get('ir.sequence.type')
+        seq_types = SequenceType.search([clause], order=[])
         codes = set(st.code for st in seq_types)
         return [('code', 'in', list(codes))]
 
-Sequence()
-
 
 class SequenceStrict(Sequence):
     # This empty class declaration is needed to inherit the groups
     # field
-    _name = 'ir.sequence.strict'
+    __name__ = 'ir.sequence.strict'
 
-SequenceStrict()
 
+class ModuleConfigWizardItem:
+    __name__ = 'ir.module.module.config_wizard.item'
 
-class ModuleConfigWizardItem(Model):
-    _name = 'ir.module.module.config_wizard.item'
-
-    def create(self, values):
+    @classmethod
+    def create(cls, values):
         pool = Pool()
-        user_obj = pool.get('res.user')
-        result = super(ModuleConfigWizardItem, self).create(values)
+        User = pool.get('res.user')
+        result = super(ModuleConfigWizardItem, cls).create(values)
         # Restart the cache for get_preferences
-        user_obj.get_preferences.reset()
+        User._get_preferences_cache.clear()
         return result
 
-    def write(self, ids, values):
+    @classmethod
+    def write(cls, items, values):
         pool = Pool()
-        user_obj = pool.get('res.user')
-        result = super(ModuleConfigWizardItem, self).write(ids, values)
+        User = pool.get('res.user')
+        super(ModuleConfigWizardItem, cls).write(items, values)
         # Restart the cache for get_preferences
-        user_obj.get_preferences.reset()
-        return result
+        User._get_preferences_cache.clear()
 
-    def delete(self, ids):
+    @classmethod
+    def delete(cls, items):
         pool = Pool()
-        user_obj = pool.get('res.user')
-        result = super(ModuleConfigWizardItem, self).delete(ids)
+        User = pool.get('res.user')
+        super(ModuleConfigWizardItem, cls).delete(items)
         # Restart the cache for get_preferences
-        user_obj.get_preferences.reset()
-        return result
-
-ModuleConfigWizardItem()
+        User._get_preferences_cache.clear()
diff -r f6ef988fda71 trytond/res/request.py
--- a/trytond/res/request.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/res/request.py	Sun Sep 23 17:30:03 2012 +0200
@@ -5,6 +5,11 @@
 from ..pyson import Eval, If
 from ..transaction import Transaction
 from ..pool import Pool
+from ..rpc import RPC
+
+__all__ = [
+    'Request', 'RequestLink', 'RequestHistory', 'RequestReference',
+    ]
 
 _STATES = [
     ('draft', 'Draft'),
@@ -29,8 +34,7 @@
 
 class Request(ModelSQL, ModelView):
     "Request"
-    _name = 'res.request'
-    _description = __doc__
+    __name__ = 'res.request'
     name = fields.Char('Subject', states={
             'readonly': _READONLY,
             }, required=True, depends=_DEPENDS)
@@ -59,20 +63,21 @@
                 Eval('act_from', 0) != Eval('_user', 0)),
             }, depends=['state', 'act_from'])
     number_references = fields.Function(fields.Integer('Number of References',
-        on_change_with=['references']), 'get_number_references')
+        on_change_with=['references']), 'on_change_with_number_references')
     state = fields.Selection(_STATES, 'State', required=True, readonly=True)
     history = fields.One2Many('res.request.history', 'request',
            'History', readonly=True)
 
-    def __init__(self):
-        super(Request, self).__init__()
-        self._rpc.update({
-            'request_get': False,
-        })
-        self._order.insert(0, ('priority', 'DESC'))
-        self._order.insert(1, ('trigger_date', 'DESC'))
-        self._order.insert(2, ('create_date', 'DESC'))
-        self._buttons.update({
+    @classmethod
+    def __setup__(cls):
+        super(Request, cls).__setup__()
+        cls.__rpc__.update({
+                'request_get': RPC(),
+                })
+        cls._order.insert(0, ('priority', 'DESC'))
+        cls._order.insert(1, ('trigger_date', 'DESC'))
+        cls._order.insert(2, ('create_date', 'DESC'))
+        cls._buttons.update({
                 'send': {
                     'invisible': ~Eval('state').in_(['draft', 'chatting']),
                     'readonly': Eval('act_from') != Eval('_user'),
@@ -87,37 +92,31 @@
                     },
                 })
 
-    def default_act_from(self):
+    @staticmethod
+    def default_act_from():
         return int(Transaction().user)
 
-    def default_state(self):
+    @staticmethod
+    def default_state():
         return 'draft'
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_priority(self):
+    @staticmethod
+    def default_priority():
         return '1'
 
-    def on_change_with_number_references(self, vals):
-        if vals.get('references'):
-            return len(vals['references'])
-        return 0
+    def on_change_with_number_references(self, name=None):
+        return len(self.references or '')
 
-    def get_number_references(self, ids, name):
-        res = {}
-        for request in self.browse(ids):
-            if request.references:
-                res[request.id] = len(request.references)
-            else:
-                res[request.id] = 0
-        return res
-
+    @classmethod
     @ModelView.button
-    def send(self, ids):
+    def send(cls, requests):
         pool = Pool()
-        request_history_obj = pool.get('res.request.history')
-        for request in self.browse(ids):
+        RequestHistory = pool.get('res.request.history')
+        for request in requests:
             values = {
                 'request': request.id,
                 'act_from': request.act_from.id,
@@ -132,17 +131,18 @@
                 values['name'] = values['body'][:125] + '...'
             else:
                 values['name'] = values['body'] or '/'
-            request_history_obj.create(values)
-        self.write(ids, {
+            RequestHistory.create(values)
+        cls.write(requests, {
             'state': 'waiting',
             'date_sent': datetime.datetime.now(),
             })
 
+    @classmethod
     @ModelView.button
-    def reply(self, ids):
+    def reply(cls, requests):
         user = Transaction().user
-        for request in self.browse(ids):
-            self.write(request.id, {
+        for request in requests:
+            cls.write([request], {
                 'state': 'chatting',
                 'act_from': user,
                 'act_to': request.act_from.id,
@@ -150,13 +150,15 @@
                 'body': '',
                 })
 
+    @classmethod
     @ModelView.button
-    def close(self, ids):
-        self.write(ids, {'state': 'closed', 'active': False})
+    def close(cls, requests):
+        cls.write(requests, {'state': 'closed', 'active': False})
 
-    def request_get(self):
+    @classmethod
+    def request_get(cls):
         user = Transaction().user
-        ids = self.search([
+        requests = cls.search([
             ('act_to', '=', user),
             ['OR',
                 ('trigger_date', '<=', datetime.datetime.now()),
@@ -164,48 +166,45 @@
             ],
             ('active', '=', True),
             ])
-        ids2 = self.search([
+        requests2 = cls.search([
             ('act_from', '=', user),
             ('act_to', '!=', user),
             ('state', '!=', 'draft'),
             ('active', '=', True),
             ])
-        return (ids, ids2)
-
-Request()
+        return ([r.id for r in requests], [r.id for r in requests2])
 
 
 class RequestLink(ModelSQL, ModelView):
     "Request link"
-    _name = 'res.request.link'
-    _description = __doc__
+    __name__ = 'res.request.link'
     name = fields.Char('Name', required=True, translate=True)
     model = fields.Selection('models_get', 'Model', required=True)
     priority = fields.Integer('Priority', required=True)
 
-    def __init__(self):
-        super(RequestLink, self).__init__()
-        self._order.insert(0, ('priority', 'ASC'))
+    @classmethod
+    def __setup__(cls):
+        super(RequestLink, cls).__setup__()
+        cls._order.insert(0, ('priority', 'ASC'))
 
-    def default_priority(self):
+    @staticmethod
+    def default_priority():
         return 5
 
-    def models_get(self):
+    @staticmethod
+    def models_get():
         pool = Pool()
-        model_obj = pool.get('ir.model')
-        model_ids = model_obj.search([])
+        Model = pool.get('ir.model')
+        models = Model.search([])
         res = []
-        for model in model_obj.browse(model_ids):
+        for model in models:
             res.append((model.model, model.name))
         return res
 
-RequestLink()
-
 
 class RequestHistory(ModelSQL, ModelView):
     "Request history"
-    _name = 'res.request.history'
-    _description = __doc__
+    __name__ = 'res.request.history'
     name = fields.Char('Summary', required=True, readonly=True)
     request = fields.Many2One('res.request', 'Request', required=True,
        ondelete='CASCADE', select=True, readonly=True)
@@ -220,32 +219,35 @@
     priority = fields.Selection(_PRIORITIES, 'Priority', required=True,
             readonly=True)
 
-    def __init__(self):
-        super(RequestHistory, self).__init__()
-        self._order.insert(0, ('date_sent', 'DESC'))
+    @classmethod
+    def __setup__(cls):
+        super(RequestHistory, cls).__setup__()
+        cls._order.insert(0, ('date_sent', 'DESC'))
 
-    def default_name(self):
+    @staticmethod
+    def default_name():
         return 'No Name'
 
-    def default_act_from(self):
+    @staticmethod
+    def default_act_from():
         return int(Transaction().user)
 
-    def default_act_to(self):
+    @staticmethod
+    def default_act_to():
         return int(Transaction().user)
 
-    def default_date_sent(self):
+    @staticmethod
+    def default_date_sent():
         return datetime.datetime.now()
 
-    def write(self, ids, vals):
+    @staticmethod
+    def write(records, vals):
         pass
 
-RequestHistory()
-
 
 class RequestReference(ModelSQL, ModelView):
     "Request Reference"
-    _name = 'res.request.reference'
-    _description = __doc__
+    __name__ = 'res.request.reference'
     _rec_name = 'reference'
 
     request = fields.Many2One('res.request', 'Request', required=True,
@@ -253,11 +255,9 @@
     reference = fields.Reference('Reference', selection='links_get',
             required=True)
 
-    def links_get(self):
+    @staticmethod
+    def links_get():
         pool = Pool()
-        request_link_obj = pool.get('res.request.link')
-        ids = request_link_obj.search([])
-        request_links = request_link_obj.browse(ids)
+        RequestLink = pool.get('res.request.link')
+        request_links = RequestLink.search([])
         return [(x.model, x.name) for x in request_links]
-
-RequestReference()
diff -r f6ef988fda71 trytond/res/user.py
--- a/trytond/res/user.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/res/user.py	Sun Sep 23 17:30:03 2012 +0200
@@ -16,13 +16,14 @@
 
 from ..model import ModelView, ModelSQL, fields
 from ..wizard import Wizard, StateView, Button, StateTransition
-from ..tools import safe_eval, reduce_ids
+from ..tools import safe_eval
 from ..backend import TableHandler
 from ..transaction import Transaction
 from ..cache import Cache
 from ..pool import Pool
 from ..config import CONFIG
 from ..pyson import PYSONEncoder
+from ..rpc import RPC
 
 try:
     import pytz
@@ -30,11 +31,15 @@
 except ImportError:
     TIMEZONES = []
 
+__all__ = [
+    'User', 'UserAction', 'UserGroup', 'Warning_',
+    'UserConfigStart', 'UserConfig',
+    ]
+
 
 class User(ModelSQL, ModelView):
     "User"
-    _name = "res.user"
-    _description = __doc__
+    __name__ = "res.user"
     name = fields.Char('Name', required=True, select=True, translate=True)
     login = fields.Char('Login', required=True)
     login_try = fields.Integer('Login Try', required=True)
@@ -65,19 +70,23 @@
     warnings = fields.One2Many('res.user.warning', 'user', 'Warnings')
     sessions = fields.Function(fields.Integer('Sessions'),
             'get_sessions')
+    _get_preferences_cache = Cache('res_user.get_preferences')
+    _get_groups_cache = Cache('res_user.get_groups')
+    _get_login_cache = Cache('res_user._get_login', context=False)
 
-    def __init__(self):
-        super(User, self).__init__()
-        self._rpc.update({
-            'get_preferences': False,
-            'set_preferences': True,
-            'get_preferences_fields_view': False,
-        })
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(User, cls).__setup__()
+        cls.__rpc__.update({
+                'get_preferences': RPC(),
+                'set_preferences': RPC(readonly=False),
+                'get_preferences_fields_view': RPC(),
+                })
+        cls._sql_constraints += [
             ('login_key', 'UNIQUE (login)',
                 'You can not have two users with the same login!')
         ]
-        self._preferences_fields = [
+        cls._preferences_fields = [
             'name',
             'password',
             'email',
@@ -88,13 +97,13 @@
             'status_bar',
             'warnings',
         ]
-        self._context_fields = [
+        cls._context_fields = [
             'language',
             'language_direction',
             'timezone',
             'groups',
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'rm_root': 'You can not remove the root user\n' \
                             'as it is used internally for resources\n' \
                             'created by the system ' \
@@ -102,9 +111,10 @@
             'wrong_password': 'Wrong password!',
             })
 
-    def init(self, module_name):
-        super(User, self).init(module_name)
-        table = TableHandler(Transaction().cursor, self, module_name)
+    @classmethod
+    def __register__(cls, module_name):
+        super(User, cls).__register__(module_name)
+        table = TableHandler(Transaction().cursor, cls, module_name)
 
         # Migration from 1.6
 
@@ -124,68 +134,61 @@
         # Migration from 2.2
         table.not_null_action('menu', action='remove')
 
-    def default_login_try(self):
+    @staticmethod
+    def default_login_try():
         return 0
 
-    def default_password(self):
+    @staticmethod
+    def default_password():
         return None
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_menu(self):
+    @staticmethod
+    def default_menu():
         pool = Pool()
-        action_obj = pool.get('ir.action')
-        action_ids = action_obj.search([
+        Action = pool.get('ir.action')
+        actions = Action.search([
             ('usage', '=', 'menu'),
             ], limit=1)
-        if action_ids:
-            return action_ids[0]
+        if actions:
+            return actions[0].id
         return None
 
-    def get_pyson_menu(self, ids, name):
+    def get_pyson_menu(self, name):
         pool = Pool()
-        action_obj = pool.get('ir.action')
+        Action = pool.get('ir.action')
+        encoder = PYSONEncoder()
+        return encoder.encode(
+            Action.get_action_values(self.menu.type, [self.menu.id])[0])
 
-        encoder = PYSONEncoder()
-        result = {}
-        for user in self.browse(ids):
-            result[user.id] = encoder.encode(
-                action_obj.get_action_values(user.menu.type, user.menu.id))
-        return result
+    def get_language_direction(self, name):
+        pool = Pool()
+        Lang = pool.get('ir.lang')
+        if self.language:
+            return self.language.direction
+        else:
+            return Lang.default_direction()
 
-    def get_language_direction(self, ids, name):
-        res = {}
-        pool = Pool()
-        lang_obj = pool.get('ir.lang')
-        default_direction = lang_obj.default_direction()
-        for user in self.browse(ids):
-            if user.language:
-                res[user.id] = user.language.direction
-            else:
-                res[user.id] = default_direction
-        return res
+    def get_status_bar(self, name):
+        return self.name
 
-    def get_status_bar(self, ids, name):
-        res = {}
-        for user in self.browse(ids):
-            res[user.id] = user.name
-        return res
-
-    def get_sessions(self, ids, name):
-        session_obj = Pool().get('ir.session')
+    @staticmethod
+    def get_sessions(users, name):
+        Session = Pool().get('ir.session')
         cursor = Transaction().cursor
         now = datetime.datetime.now()
         timeout = datetime.timedelta(seconds=int(CONFIG['session_timeout']))
-        result = dict((i, 0) for i in ids)
-        for i in range(0, len(ids), cursor.IN_MAX):
-            sub_ids = ids[i:i + cursor.IN_MAX]
+        result = dict((u.id, 0) for u in users)
+        for i in range(0, len(users), cursor.IN_MAX):
+            sub_ids = [u.id for u in users[i:i + cursor.IN_MAX]]
 
             with Transaction().set_user(0):
-                session_ids = session_obj.search([
+                sessions = Session.search([
                         ('create_uid', 'in', sub_ids),
                         ], order=[('create_uid', 'ASC')])
-                sessions = session_obj.browse(session_ids)
 
             def filter_(session):
                 timestamp = session.write_date or session.create_date
@@ -195,12 +198,13 @@
                         attrgetter('create_uid.id'))))
         return result
 
-    def _convert_vals(self, vals):
+    @staticmethod
+    def _convert_vals(vals):
         vals = vals.copy()
         pool = Pool()
-        action_obj = pool.get('ir.action')
+        Action = pool.get('ir.action')
         if 'menu' in vals:
-            vals['menu'] = action_obj.get_action_id(vals['menu'])
+            vals['menu'] = Action.get_action_id(vals['menu'])
         if 'password' in vals:
             if vals['password'] == 'x' * 10:
                 del vals['password']
@@ -210,122 +214,111 @@
                 vals['password'] += vals['salt']
         return vals
 
-    def create(self, vals):
-        vals = self._convert_vals(vals)
-        res = super(User, self).create(vals)
+    @classmethod
+    def create(cls, vals):
+        vals = cls._convert_vals(vals)
+        res = super(User, cls).create(vals)
         # Restart the cache for _get_login
-        self._get_login.reset()
+        cls._get_login_cache.clear()
         return res
 
-    def write(self, ids, vals):
-        vals = self._convert_vals(vals)
-        res = super(User, self).write(ids, vals)
+    @classmethod
+    def write(cls, users, vals):
+        vals = cls._convert_vals(vals)
+        super(User, cls).write(users, vals)
         # Restart the cache for domain_get method
         pool = Pool()
-        pool.get('ir.rule').domain_get.reset()
+        pool.get('ir.rule')._domain_get_cache.clear()
         # Restart the cache for get_groups
-        self.get_groups.reset()
+        cls._get_groups_cache.clear()
         # Restart the cache for _get_login
-        self._get_login.reset()
+        cls._get_login_cache.clear()
         # Restart the cache for get_preferences
-        self.get_preferences.reset()
+        cls._get_preferences_cache.clear()
         # Restart the cache of check
-        pool.get('ir.model.access').check.reset()
+        pool.get('ir.model.access')._get_access_cache.clear()
         # Restart the cache
-        for _, model in pool.iterobject():
-            try:
-                model.fields_view_get.reset()
-            except Exception:
-                pass
+        ModelView._fields_view_get_cache.clear()
+
+    @classmethod
+    def delete(cls, users):
+        if [u.id for u in users if u.id == 0]:
+            cls.raise_user_error('rm_root')
+        super(User, cls).delete(users)
+        # Restart the cache for _get_login
+        cls._get_login_cache.clear()
+
+    @classmethod
+    def read(cls, ids, fields_names=None):
+        res = super(User, cls).read(ids, fields_names=fields_names)
+        for val in res:
+            if 'password' in val:
+                val['password'] = 'x' * 10
         return res
 
-    def delete(self, ids):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        if 0 in ids:
-            self.raise_user_error('rm_root')
-        res = super(User, self).delete(ids)
-        # Restart the cache for _get_login
-        self._get_login.reset()
-        return res
-
-    def read(self, ids, fields_names=None):
-        res = super(User, self).read(ids, fields_names=fields_names)
-        if isinstance(ids, (int, long)):
-            if 'password' in res:
-                res['password'] = 'x' * 10
-        else:
-            for val in res:
-                if 'password' in val:
-                    val['password'] = 'x' * 10
-        return res
-
-    def search_rec_name(self, name, clause):
-        ids = self.search([
+    @classmethod
+    def search_rec_name(cls, name, clause):
+        users = cls.search([
             ('login', '=', clause[2]),
             ], order=[])
-        if len(ids) == 1:
-            return [('id', '=', ids[0])]
-        return [(self._rec_name,) + tuple(clause[1:])]
+        if len(users) == 1:
+            return [('id', '=', users[0].id)]
+        return [(cls._rec_name,) + tuple(clause[1:])]
 
-    def copy(self, ids, default=None):
+    @classmethod
+    def copy(cls, users, default=None):
         if default is None:
             default = {}
         default = default.copy()
 
-        int_id = False
-        if isinstance(ids, (int, long)):
-            int_id = True
-            ids = [ids]
-
         default['password'] = ''
 
-        new_ids = []
-        for user in self.browse(ids):
+        new_users = []
+        for user in users:
             default['login'] = user.login + ' (copy)'
-            new_id = super(User, self).copy(user.id, default)
-            new_ids.append(new_id)
+            new_user, = super(User, cls).copy([user], default)
+            new_users.append(new_user)
+        return new_users
 
-        if int_id:
-            return new_ids[0]
-        return new_ids
-
-    def _get_preferences(self, user, context_only=False):
+    @classmethod
+    def _get_preferences(cls, user, context_only=False):
         pool = Pool()
-        model_data_obj = pool.get('ir.model.data')
-        action_obj = pool.get('ir.action')
-        config_item = pool.get('ir.module.module.config_wizard.item')
+        ModelData = pool.get('ir.model.data')
+        Action = pool.get('ir.action')
+        ConfigItem = pool.get('ir.module.module.config_wizard.item')
 
         res = {}
         if context_only:
-            fields = self._context_fields
+            fields = cls._context_fields
         else:
-            fields = self._preferences_fields + self._context_fields
+            fields = cls._preferences_fields + cls._context_fields
         for field in fields:
-            if self._columns[field]._type in ('many2one',):
+            if cls._fields[field]._type in ('many2one',):
                 if field == 'language':
                     if user.language:
                         res['language'] = user.language.code
                     else:
                         res['language'] = CONFIG['language']
                 else:
-                    res[field] = user[field].id
-                    if user[field].id:
-                        res[field + '.rec_name'] = user[field].rec_name
-            elif self._columns[field]._type in ('one2many', 'many2many'):
-                res[field] = [x.id for x in user[field]]
+                    res[field] = None
+                    if getattr(user, field):
+                        res[field] = getattr(user, field).id
+                        res[field + '.rec_name'] = \
+                            getattr(user, field).rec_name
+            elif cls._fields[field]._type in ('one2many', 'many2many'):
+                res[field] = [x.id for x in getattr(user, field)]
                 if field == 'actions' and user.login == 'admin':
-                    config_wizard_id = model_data_obj.get_id('ir',
+                    config_wizard_id = ModelData.get_id('ir',
                         'act_module_config_wizard')
-                    action_id = action_obj.get_action_id(config_wizard_id)
+                    action_id = Action.get_action_id(config_wizard_id)
                     if action_id in res[field]:
                         res[field].remove(action_id)
-                    if config_item.search([
+                    if ConfigItem.search([
                                 ('state', '=', 'open'),
                                 ]):
                         res[field].insert(0, action_id)
             else:
-                res[field] = user[field]
+                res[field] = getattr(user, field)
 
         if user.language:
             date = user.language.date
@@ -340,52 +333,57 @@
             }
         return res
 
-    @Cache('res_user.get_preferences')
-    def get_preferences(self, context_only=False):
+    @classmethod
+    def get_preferences(cls, context_only=False):
+        key = (Transaction().user, context_only)
+        preferences = cls._get_preferences_cache.get(key)
+        if preferences is not None:
+            return preferences
         user = Transaction().user
         with Transaction().set_user(0):
-            user = self.browse(user)
-        return self._get_preferences(user, context_only=context_only)
+            user = cls(user)
+        preferences = cls._get_preferences(user, context_only=context_only)
+        cls._get_preferences_cache.set(key, preferences)
+        return preferences
 
-    def set_preferences(self, values, old_password=False):
+    @classmethod
+    def set_preferences(cls, values, old_password=False):
         '''
         Set user preferences.
-
-        :param values: a dictionary with values
-        :param old_password: the previous password if password is in values
         '''
         pool = Pool()
-        lang_obj = pool.get('ir.lang')
+        Lang = pool.get('ir.lang')
         values_clean = values.copy()
-        fields = self._preferences_fields + self._context_fields
+        fields = cls._preferences_fields + cls._context_fields
         user_id = Transaction().user
+        with Transaction().set_user(0):
+            user = cls(user_id)
         for field in values:
             if field not in fields or field == 'groups':
                 del values_clean[field]
             if field == 'password':
-                with Transaction().set_user(0):
-                    user = self.browse(user_id)
-                    if not self.get_login(user.login, old_password):
-                        self.raise_user_error('wrong_password')
+                if not cls.get_login(user.login, old_password):
+                    cls.raise_user_error('wrong_password')
             if field == 'language':
-                lang_ids = lang_obj.search([
+                langs = Lang.search([
                     ('code', '=', values['language']),
                     ])
-                if lang_ids:
-                    values_clean['language'] = lang_ids[0]
+                if langs:
+                    values_clean['language'] = langs[0].id
                 else:
                     del values_clean['language']
         with Transaction().set_user(0):
-            self.write(user_id, values_clean)
+            cls.write([user], values_clean)
 
-    def get_preferences_fields_view(self):
+    @classmethod
+    def get_preferences_fields_view(cls):
         pool = Pool()
-        model_data_obj = pool.get('ir.model.data')
-        lang_obj = pool.get('ir.lang')
-        action_obj = pool.get('ir.action')
+        ModelData = pool.get('ir.model.data')
+        Lang = pool.get('ir.lang')
+        Action = pool.get('ir.action')
 
-        view_id = model_data_obj.get_id('res', 'user_view_form_preferences')
-        res = self.fields_view_get(view_id=view_id)
+        view_id = ModelData.get_id('res', 'user_view_form_preferences')
+        res = cls.fields_view_get(view_id=view_id)
         res = copy.deepcopy(res)
         for field in res['fields']:
             if field not in ('groups', 'language_direction'):
@@ -402,57 +400,61 @@
 
         if 'language' in res['fields']:
             selection = convert2selection(res['fields'], 'language')
-            lang_ids = lang_obj.search(['OR',
+            langs = Lang.search(['OR',
                     ('translatable', '=', True),
                     ('code', '=', CONFIG['language']),
                     ])
+            lang_ids = [l.id for l in langs]
             with Transaction().set_context(translate_name=True):
-                for lang in lang_obj.browse(lang_ids):
+                for lang in Lang.browse(lang_ids):
                     selection.append((lang.code, lang.name))
         if 'action' in res['fields']:
             selection = convert2selection(res['fields'], 'action')
             selection.append((None, ''))
-            action_ids = action_obj.search([])
-            for action in action_obj.browse(action_ids):
+            actions = Action.search([])
+            for action in actions:
                 selection.append((action.id, action.rec_name))
         if 'menu' in res['fields']:
             selection = convert2selection(res['fields'], 'menu')
-            action_ids = action_obj.search([
+            actions = Action.search([
                     ('usage', '=', 'menu'),
                     ])
-            for action in action_obj.browse(action_ids):
+            for action in actions:
                 selection.append((action.id, action.rec_name))
         return res
 
-    @Cache('res_user.get_groups')
-    def get_groups(self):
+    @classmethod
+    def get_groups(cls):
         '''
         Return a list of group ids for the user
+        '''
+        user = Transaction().user
+        groups = cls._get_groups_cache.get(user)
+        if groups is not None:
+            return groups
+        groups = cls.read([Transaction().user], ['groups'])[0]['groups']
+        cls._get_groups_cache.set(user, groups)
+        return groups
 
-        :return: a list of group ids
-        '''
-        return self.read(Transaction().user, ['groups'])['groups']
-
-    @Cache('res_user._get_login')
-    def _get_login(self, login):
+    @classmethod
+    def _get_login(cls, login):
+        result = cls._get_groups_cache.get(login)
+        if result:
+            return result
         cursor = Transaction().cursor
         cursor.execute('SELECT id, password, salt ' \
-                'FROM "' + self._table + '" '
+                'FROM "' + cls._table + '" '
                 'WHERE login = %s AND active', (login,))
-        res = cursor.fetchone()
-        if not res:
-            return None, None, None
-        return res
+        result = cursor.fetchone() or (None, None, None)
+        cls._get_groups_cache.set(login, result)
+        return result
 
-    def get_login(self, login, password):
+    @classmethod
+    def get_login(cls, login, password):
         '''
         Return user id if password matches
-
-        :param login: the login name
-        :param password: the password
-        :return: integer
         '''
-        user_id, user_password, salt = self._get_login(login)
+        user_id, user_password, salt = cls._get_login(login)
         if not user_id:
             return 0
         password += salt or ''
@@ -464,118 +466,101 @@
             password_sha = sha.new(password).hexdigest()
         cursor = Transaction().cursor
         if password_sha == user_password:
-            cursor.execute('UPDATE "' + self._table + '" '
+            cursor.execute('UPDATE "' + cls._table + '" '
                 'SET login_try = 0 '
                 'WHERE login = %s', (login,))
             return user_id
-        cursor.execute('UPDATE "' + self._table + '" '
+        cursor.execute('UPDATE "' + cls._table + '" '
             'SET login_try = login_try + 1 '
             'WHERE login = %s', (login,))
-        cursor.execute('SELECT login_try FROM "' + self._table + '" '
+        cursor.execute('SELECT login_try FROM "' + cls._table + '" '
             'WHERE login = %s', (login,))
         login_try, = cursor.fetchone()
         time.sleep(2 ** login_try)
         return 0
 
-User()
-
 
 class UserAction(ModelSQL):
     'User - Action'
-    _name = 'res.user-ir.action'
+    __name__ = 'res.user-ir.action'
     user = fields.Many2One('res.user', 'User', ondelete='CASCADE', select=True,
         required=True)
     action = fields.Many2One('ir.action', 'Action', ondelete='CASCADE',
         select=True, required=True)
 
-    def _convert_values(self, values):
+    @staticmethod
+    def _convert_values(values):
         pool = Pool()
-        action_obj = pool.get('ir.action')
+        Action = pool.get('ir.action')
         values = values.copy()
         if values.get('action'):
-            values['action'] = action_obj.get_action_id(values['action'])
+            values['action'] = Action.get_action_id(values['action'])
         return values
 
-    def create(self, values):
-        values = self._convert_values(values)
-        return super(UserAction, self).create(values)
+    @classmethod
+    def create(cls, values):
+        values = cls._convert_values(values)
+        return super(UserAction, cls).create(values)
 
-    def write(self, ids, values):
-        values = self._convert_values(values)
-        return super(UserAction, self).write(ids, values)
-
-UserAction()
+    @classmethod
+    def write(cls, records, values):
+        values = cls._convert_values(values)
+        super(UserAction, cls).write(records, values)
 
 
 class UserGroup(ModelSQL):
     'User - Group'
-    _name = 'res.user-res.group'
-    _description = __doc__
+    __name__ = 'res.user-res.group'
     user = fields.Many2One('res.user', 'User', ondelete='CASCADE', select=True,
             required=True)
     group = fields.Many2One('res.group', 'Group', ondelete='CASCADE',
             select=True, required=True)
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         cursor = Transaction().cursor
         # Migration from 1.0 table name change
-        TableHandler.table_rename(cursor, 'res_group_user_rel', self._table)
+        TableHandler.table_rename(cursor, 'res_group_user_rel', cls._table)
         TableHandler.sequence_rename(cursor, 'res_group_user_rel_id_seq',
-                self._table + '_id_seq')
+                cls._table + '_id_seq')
         # Migration from 2.0 uid and gid rename into user and group
-        table = TableHandler(cursor, self, module_name)
+        table = TableHandler(cursor, cls, module_name)
         table.column_rename('uid', 'user')
         table.column_rename('gid', 'group')
-        super(UserGroup, self).init(module_name)
+        super(UserGroup, cls).__register__(module_name)
 
-UserGroup()
 
-
-class Group(ModelSQL, ModelView):
-    "Group"
-    _name = "res.group"
-    users = fields.Many2Many('res.user-res.group', 'group', 'user', 'Users')
-
-Group()
-
-
-class Warning(ModelSQL, ModelView):
+class Warning_(ModelSQL, ModelView):
     'User Warning'
-    _name = 'res.user.warning'
-    _description = __doc__
+    __name__ = 'res.user.warning'
 
     user = fields.Many2One('res.user', 'User', required=True, select=True)
     name = fields.Char('Name', required=True, select=True)
     always = fields.Boolean('Always')
 
-    def check(self, warning_name):
+    @classmethod
+    def check(cls, warning_name):
         user = Transaction().user
         if not user:
             return False
-        warning_ids = self.search([
+        warnings = cls.search([
             ('user', '=', user),
             ('name', '=', warning_name),
             ])
-        if not warning_ids:
+        if not warnings:
             return True
-        warnings = self.browse(warning_ids)
-        self.delete([x.id for x in warnings if not x.always])
+        cls.delete([x.id for x in warnings if not x.always])
         return False
 
-Warning()
-
 
 class UserConfigStart(ModelView):
     'User Config Init'
-    _name = 'res.user.config.start'
-    _description = __doc__
-
-UserConfigStart()
+    __name__ = 'res.user.config.start'
 
 
 class UserConfig(Wizard):
     'Configure users'
-    _name = 'res.user.config'
+    __name__ = 'res.user.config'
 
     start = StateView('res.user.config.start',
         'res.user_config_start_view_form', [
@@ -589,20 +574,6 @@
             ])
     add = StateTransition()
 
-    def transition_add(self, session):
-        pool = Pool()
-        user_obj = pool.get('res.user')
-        values = session.data['user'].copy()
-        for fname in values.keys():
-            if fname == 'id':
-                del values[fname]
-                continue
-            field = user_obj._columns[fname]
-            if field._type == 'one2many':
-                values[fname] = [('create', v) for v in values[fname]]
-            elif field._type == 'many2many':
-                values[fname] = [('set', [v['id'] for v in values[fname]])]
-        user_obj.create(values)
+    def transition_add(self):
+        self.user.save()
         return 'user'
-
-UserConfig()
diff -r f6ef988fda71 trytond/rpc.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trytond/rpc.py	Sun Sep 23 17:30:03 2012 +0200
@@ -0,0 +1,45 @@
+#This file is part of Tryton.  The COPYRIGHT file at the top level of
+#this repository contains the full copyright notices and license terms.
+
+__all__ = ['RPC']
+
+
+class RPC(object):
+    '''Define RPC behavior
+
+    readonly: The transaction mode
+    instantiate: The position of the argument to be instanciated
+    result: The function to transform the result
+    '''
+
+    __slots__ = ('readonly', 'instantiate', 'result')
+
+    def __init__(self, readonly=True, instantiate=None, result=None):
+        self.readonly = readonly
+        self.instantiate = instantiate
+        if result is None:
+            result = lambda r: r
+        self.result = result
+
+    def convert(self, obj, *args, **kwargs):
+        args = list(args)
+        if 'context' in kwargs:
+            context = kwargs.pop('context')
+        else:
+            context = args.pop()
+        timestamp = None
+        if '_timestamp' in context:
+            timestamp = context['_timestamp']
+            del context['_timestamp']
+        if self.instantiate is not None:
+
+            def instance(data):
+                if isinstance(data, (int, long)):
+                    return obj(data)
+                elif isinstance(data, dict):
+                    return obj(**data)
+                else:
+                    return obj.browse(data)
+            data = args[self.instantiate]
+            args[self.instantiate] = instance(data)
+        return args, kwargs, context, timestamp
diff -r f6ef988fda71 trytond/security.py
--- a/trytond/security.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/security.py	Sun Sep 23 17:30:03 2012 +0200
@@ -17,16 +17,16 @@
 def login(dbname, loginname, password, cache=True):
     with Transaction().start(dbname, 0) as transaction:
         pool = _get_pool(dbname)
-        user_obj = pool.get('res.user')
+        User = pool.get('res.user')
         password = password.decode('utf-8')
-        user_id = user_obj.get_login(loginname, password)
+        user_id = User.get_login(loginname, password)
         transaction.cursor.commit()
     if user_id:
         if not cache:
             return user_id
         with Transaction().start(dbname, user_id) as transaction:
-            session_obj = pool.get('ir.session')
-            session = session_obj.browse(session_obj.create({}))
+            Session = pool.get('ir.session')
+            session = Session.create({})
             transaction.cursor.commit()
             return user_id, session.key
     return False
@@ -35,13 +35,12 @@
 def logout(dbname, user, session):
     with Transaction().start(dbname, 0) as transaction:
         pool = _get_pool(dbname)
-        session_obj = pool.get('ir.session')
-        session_id, = session_obj.search([
+        Session = pool.get('ir.session')
+        session, = Session.search([
                 ('key', '=', session),
                 ])
-        session = session_obj.browse(session_id)
         name = session.create_uid.login
-        session_obj.delete(session_id)
+        Session.delete([session])
         transaction.cursor.commit()
     return name
 
@@ -60,9 +59,9 @@
         raise NotLogged()
     with Transaction().start(dbname, user) as transaction:
         pool = _get_pool(dbname)
-        session_obj = pool.get('ir.session')
+        Session = pool.get('ir.session')
         try:
-            if not session_obj.check(user, session):
+            if not Session.check(user, session):
                 raise NotLogged()
             else:
                 return user
diff -r f6ef988fda71 trytond/server.py
--- a/trytond/server.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/server.py	Sun Sep 23 17:30:03 2012 +0200
@@ -189,11 +189,11 @@
                     try:
                         if 'ir.cron' not in pool.object_name_list():
                             continue
-                        cron_obj = pool.get('ir.cron')
+                        Cron = pool.get('ir.cron')
                     finally:
                         pool.lock.release()
                     thread = threading.Thread(
-                            target=cron_obj.run,
+                            target=Cron.run,
                             args=(dbname,), kwargs={})
                     thread.start()
                     threads[dbname] = thread
diff -r f6ef988fda71 trytond/test/__init__.py
--- a/trytond/test/__init__.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/test/__init__.py	Sun Sep 23 17:30:03 2012 +0200
@@ -1,6 +1,6 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
-
+from ..pool import Pool
 from .test import *
 from .model import *
 from .mptt import *
@@ -10,3 +10,113 @@
 from .access import *
 from .wizard import *
 from .workflow import *
+
+
+def register():
+    Pool.register(
+        Boolean,
+        BooleanDefault,
+        Integer,
+        IntegerDefault,
+        IntegerRequired,
+        Float,
+        FloatDefault,
+        FloatRequired,
+        FloatDigits,
+        Numeric,
+        NumericDefault,
+        NumericRequired,
+        NumericDigits,
+        Char,
+        CharDefault,
+        CharRequired,
+        CharSize,
+        CharTranslate,
+        Text,
+        TextDefault,
+        TextRequired,
+        TextSize,
+        TextTranslate,
+        Sha,
+        ShaDefault,
+        ShaRequired,
+        Date,
+        DateDefault,
+        DateRequired,
+        DateTime,
+        DateTimeDefault,
+        DateTimeRequired,
+        DateTimeFormat,
+        Time,
+        TimeDefault,
+        TimeRequired,
+        TimeFormat,
+        One2One,
+        One2OneTarget,
+        One2OneRelation,
+        One2OneRequired,
+        One2OneRequiredRelation,
+        One2Many,
+        One2ManyTarget,
+        One2ManyRequired,
+        One2ManyRequiredTarget,
+        One2ManyReference,
+        One2ManyReferenceTarget,
+        One2ManySize,
+        One2ManySizeTarget,
+        One2ManySizePYSON,
+        One2ManySizePYSONTarget,
+        Many2Many,
+        Many2ManyTarget,
+        Many2ManyRelation,
+        Many2ManyRequired,
+        Many2ManyRequiredTarget,
+        Many2ManyRequiredRelation,
+        Many2ManyReference,
+        Many2ManyReferenceTarget,
+        Many2ManyReferenceRelation,
+        Many2ManySize,
+        Many2ManySizeTarget,
+        Many2ManySizeRelation,
+        Reference,
+        ReferenceTarget,
+        ReferenceRequired,
+        Property,
+        Singleton,
+        URLObject,
+        ModelInherits,
+        ModelInheritsSubModel,
+        ModelInheritsSubSubModel,
+        ModelInheritsSubSubSubModel,
+        ModelInheritsOverriddenFieldModel,
+        MPTT,
+        ImportDataBoolean,
+        ImportDataInteger,
+        ImportDataFloat,
+        ImportDataNumeric,
+        ImportDataChar,
+        ImportDataText,
+        ImportDataSha,
+        ImportDataDate,
+        ImportDataDateTime,
+        ImportDataSelection,
+        ImportDataMany2OneTarget,
+        ImportDataMany2One,
+        ImportDataMany2ManyTarget,
+        ImportDataMany2Many,
+        ImportDataMany2ManyRelation,
+        ImportDataOne2Many,
+        ImportDataOne2ManyTarget,
+        ImportDataReferenceSelection,
+        ImportDataReference,
+        ExportDataTarget,
+        ExportData,
+        ExportDataRelation,
+        Triggered,
+        TestAccess,
+        TestWizardStart,
+        WorkflowedModel,
+        module='test', type_='model')
+    Pool.register(
+        TestWizard,
+        module='test', type_='wizard')
diff -r f6ef988fda71 trytond/test/access.py
--- a/trytond/test/access.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/test/access.py	Sun Sep 23 17:30:03 2012 +0200
@@ -2,13 +2,13 @@
 #this repository contains the full copyright notices and license terms.
 from trytond.model import ModelSQL, fields
 
+__all__ = [
+    'TestAccess',
+    ]
+
 
 class TestAccess(ModelSQL):
     'Test Access'
-    _name = 'test.access'
-    _description = __doc__
-
+    __name__ = 'test.access'
     field1 = fields.Char('Field 1')
     field2 = fields.Char('Field 2')
-
-TestAccess()
diff -r f6ef988fda71 trytond/test/export_data.py
--- a/trytond/test/export_data.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/test/export_data.py	Sun Sep 23 17:30:03 2012 +0200
@@ -3,20 +3,20 @@
 "Test for export_data"
 from trytond.model import ModelSQL, fields
 
+__all__ = [
+    'ExportDataTarget', 'ExportData', 'ExportDataRelation',
+    ]
+
 
 class ExportDataTarget(ModelSQL):
     "Export Data Target"
-    _name = 'test.export_data.target'
-    _description = __doc__
+    __name__ = 'test.export_data.target'
     name = fields.Char('Name')
 
-ExportDataTarget()
-
 
 class ExportData(ModelSQL):
     "Export Data"
-    _name = 'test.export_data'
-    _description = __doc__
+    __name__ = 'test.export_data'
     boolean = fields.Boolean('Boolean')
     integer = fields.Integer('Integer')
     float = fields.Float('Float')
@@ -40,20 +40,15 @@
         ('test.export_data.target', 'Target'),
         ])
 
-ExportData()
-
 
 class ExportDataTarget(ExportDataTarget):
+    'Export Date Target'
+    __name__ = 'test.export_data.target'
     one2many = fields.Many2One('test.export_data', 'Export Data')
 
-ExportDataTarget()
-
 
 class ExportDataRelation(ModelSQL):
     "Export Data Many2Many"
-    _name = 'test.export_data.relation'
-    _description = __doc__
+    __name__ = 'test.export_data.relation'
     many2many = fields.Many2One('test.export_data', 'Export Data')
     target = fields.Many2One('test.export_data.target', 'Target')
-
-ExportDataRelation()
diff -r f6ef988fda71 trytond/test/import_data.py
--- a/trytond/test/import_data.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/test/import_data.py	Sun Sep 23 17:30:03 2012 +0200
@@ -3,182 +3,138 @@
 "Test for import_data"
 from trytond.model import ModelSQL, fields
 
+__all__ = [
+    'ImportDataBoolean', 'ImportDataInteger', 'ImportDataFloat',
+    'ImportDataNumeric', 'ImportDataChar', 'ImportDataText', 'ImportDataSha',
+    'ImportDataDate', 'ImportDataDateTime', 'ImportDataSelection',
+    'ImportDataMany2OneTarget', 'ImportDataMany2One',
+    'ImportDataMany2ManyTarget', 'ImportDataMany2Many',
+    'ImportDataMany2ManyRelation', 'ImportDataOne2Many',
+    'ImportDataOne2ManyTarget', 'ImportDataReferenceSelection',
+    'ImportDataReference',
+    ]
+
 
 class ImportDataBoolean(ModelSQL):
     "Import Data Boolean"
-    _name = 'test.import_data.boolean'
-    _description = __doc__
+    __name__ = 'test.import_data.boolean'
     boolean = fields.Boolean('Boolean')
 
-ImportDataBoolean()
-
 
 class ImportDataInteger(ModelSQL):
     "Import Data Integer"
-    _name = 'test.import_data.integer'
-    _description = __doc__
+    __name__ = 'test.import_data.integer'
     integer = fields.Integer('Integer')
 
-ImportDataInteger()
-
 
 class ImportDataFloat(ModelSQL):
     "Import Data Float"
-    _name = 'test.import_data.float'
-    _description = __doc__
+    __name__ = 'test.import_data.float'
     float = fields.Float('Float')
 
-ImportDataFloat()
-
 
 class ImportDataNumeric(ModelSQL):
     "Import Data Numeric"
-    _name = 'test.import_data.numeric'
-    _description = __doc__
+    __name__ = 'test.import_data.numeric'
     numeric = fields.Numeric('Numeric')
 
-ImportDataNumeric()
-
 
 class ImportDataChar(ModelSQL):
     "Import Data Char"
-    _name = 'test.import_data.char'
-    _description = __doc__
+    __name__ = 'test.import_data.char'
     char = fields.Char('Char')
 
-ImportDataChar()
-
 
 class ImportDataText(ModelSQL):
     "Import Data Text"
-    _name = 'test.import_data.text'
-    _description = __doc__
+    __name__ = 'test.import_data.text'
     text = fields.Text('Text')
 
-ImportDataText()
-
 
 class ImportDataSha(ModelSQL):
     "Import Data Sha"
-    _name = 'test.import_data.sha'
-    _description = __doc__
+    __name__ = 'test.import_data.sha'
     sha = fields.Sha('Sha')
 
-ImportDataSha()
-
 
 class ImportDataDate(ModelSQL):
     "Import Data Date"
-    _name = 'test.import_data.date'
-    _description = __doc__
+    __name__ = 'test.import_data.date'
     date = fields.Date('Date')
 
-ImportDataDate()
-
 
 class ImportDataDateTime(ModelSQL):
     "Import Data DateTime"
-    _name = 'test.import_data.datetime'
-    _description = __doc__
+    __name__ = 'test.import_data.datetime'
     datetime = fields.DateTime('DateTime')
 
-ImportDataDateTime()
-
 
 class ImportDataSelection(ModelSQL):
     "Import Data Selection"
-    _name = 'test.import_data.selection'
-    _description = __doc__
+    __name__ = 'test.import_data.selection'
     selection = fields.Selection([
         ('select1', 'Select 1'),
         ('select2', 'Select 2'),
         ], 'Selection')
 
-ImportDataSelection()
-
 
 class ImportDataMany2OneTarget(ModelSQL):
     "Import Data Many2One Target"
-    _name = 'test.import_data.many2one.target'
-    _description = __doc__
+    __name__ = 'test.import_data.many2one.target'
     name = fields.Char('Name')
 
-ImportDataMany2OneTarget()
-
 
 class ImportDataMany2One(ModelSQL):
     "Import Data Many2One"
-    _name = 'test.import_data.many2one'
-    _description = __doc__
+    __name__ = 'test.import_data.many2one'
     many2one = fields.Many2One('test.import_data.many2one.target',
             'Many2One')
 
-ImportDataMany2One()
-
 
 class ImportDataMany2ManyTarget(ModelSQL):
     "Import Data Many2Many Target"
-    _name = 'test.import_data.many2many.target'
-    _description = __doc__
+    __name__ = 'test.import_data.many2many.target'
     name = fields.Char('Name')
 
-ImportDataMany2ManyTarget()
-
 
 class ImportDataMany2Many(ModelSQL):
     "Import Data Many2Many"
-    _name = 'test.import_data.many2many'
-    _description = __doc__
+    __name__ = 'test.import_data.many2many'
     many2many = fields.Many2Many('test.import_data.many2many.relation',
             'many2many', 'target', 'Many2Many')
 
-ImportDataMany2Many()
-
 
 class ImportDataMany2ManyRelation(ModelSQL):
     "Import Data Many2Many Relation"
-    _name = 'test.import_data.many2many.relation'
+    __name__ = 'test.import_data.many2many.relation'
     many2many = fields.Many2One('test.import_data.many2many', 'Many2One')
     target = fields.Many2One('test.import_data.many2many.target', 'Target')
 
-ImportDataMany2ManyRelation()
-
 
 class ImportDataOne2Many(ModelSQL):
     "Import Data One2Many"
-    _name = 'test.import_data.one2many'
-    _description = __doc__
+    __name__ = 'test.import_data.one2many'
     name = fields.Char('Name')
     one2many = fields.One2Many('test.import_data.one2many.target', 'one2many',
             'One2Many')
 
-ImportDataOne2Many()
-
 
 class ImportDataOne2ManyTarget(ModelSQL):
     "Import Data One2Many Target"
-    _name = 'test.import_data.one2many.target'
-    _description = __doc__
+    __name__ = 'test.import_data.one2many.target'
     name = fields.Char('Name')
     one2many = fields.Many2One('test.import_data.one2many', 'One2Many')
 
-ImportDataOne2ManyTarget()
-
 
 class ImportDataReferenceSelection(ModelSQL):
     "Import Data Reference Selection"
-    _name = 'test.import_data.reference.selection'
-    _description = __doc__
+    __name__ = 'test.import_data.reference.selection'
     name = fields.Char('Name')
 
-ImportDataReferenceSelection()
-
 
 class ImportDataReference(ModelSQL):
     "Import Data Reference"
-    _name = 'test.import_data.reference'
+    __name__ = 'test.import_data.reference'
     reference = fields.Reference('Reference', [
         ('test.import_data.reference.selection', 'Test'),
         ])
-
-ImportDataReference()
diff -r f6ef988fda71 trytond/test/model.py
--- a/trytond/test/model.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/test/model.py	Sun Sep 23 17:30:03 2012 +0200
@@ -2,76 +2,71 @@
 #this repository contains the full copyright notices and license terms.
 from trytond.model import ModelSingleton, ModelSQL, fields
 
+__all__ = [
+    'Singleton', 'URLObject', 'ModelInherits', 'ModelInheritsSubModel',
+    'ModelInheritsSubSubModel', 'ModelInheritsSubSubSubModel',
+    'ModelInheritsOverriddenFieldModel',
+    ]
+
 
 class Singleton(ModelSingleton, ModelSQL):
     'Singleton'
-    _name = 'test.singleton'
-    _description = __doc__
+    __name__ = 'test.singleton'
+    name = fields.Char('Name')
+
+    @staticmethod
+    def default_name():
+        return 'test'
+
+
+class URLObject(ModelSQL):
+    'URLObject'
+    __name__ = 'test.urlobject'
+    name = fields.Char('Name')
+
+
+class ModelInherits(ModelSQL):
+    'Model Inherits'
+    __name__ = 'test.model_inherits'
 
     name = fields.Char('Name')
 
-    def default_name(self):
-        return 'test'
-
-Singleton()
-
-
-class URLObject(ModelSQL):
-    _name = 'test.urlobject'
-
-    name = fields.Char('Name')
-
-
-URLObject()
-
-
-class ModelInherits(ModelSQL):
-    _name = 'test.model_inherits'
-
-    name = fields.Char('Name')
-
-ModelInherits()
-
 
 class ModelInheritsSubModel(ModelSQL):
-    _name = 'test.submodel'
+    'Model Inherits Sub-Model'
+    __name__ = 'test.submodel'
     _inherits = {'test.model_inherits': 'parent_model'}
 
     subfield = fields.Char('SubField')
     parent_model = fields.Many2One('test.model_inherits', 'Parent model',
         required=True)
 
-ModelInheritsSubModel()
-
 
 class ModelInheritsSubSubModel(ModelSQL):
-    _name = 'test.subsubmodel'
+    'Model Inherits Sub-Sub-Model'
+    __name__ = 'test.subsubmodel'
     _inherits = {'test.submodel': 'parent_model'}
 
     subsubfield = fields.Char('SubField')
     parent_model = fields.Many2One('test.submodel', 'Parent model',
         required=True)
 
-ModelInheritsSubSubModel()
-
 
 class ModelInheritsSubSubSubModel(ModelSQL):
-    _name = 'test.subsubsubmodel'
+    'Model Inherits Sub-Sub-Sub-Model'
+    __name__ = 'test.subsubsubmodel'
     _inherits = {'test.subsubmodel': 'parent_model'}
 
     subsubsubfield = fields.Char('SubField')
     parent_model = fields.Many2One('test.subsubmodel', 'Parent model',
         required=True)
 
-ModelInheritsSubSubSubModel()
-
 
 class ModelInheritsOverriddenFieldModel(ModelSQL):
-    _name = 'test.overriddeninheritedfieldmodel'
+    'Model Inherits Overridden Field Model'
+    __name__ = 'test.overriddeninheritedfieldmodel'
     _inherits = {'test.subsubmodel': 'parent_model'}
 
     subfield = fields.Integer('Overridden field')
     parent_model = fields.Many2One('test.subsubmodel', 'Parent model',
         required=True)
-
-ModelInheritsOverriddenFieldModel()
diff -r f6ef988fda71 trytond/test/mptt.py
--- a/trytond/test/mptt.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/test/mptt.py	Sun Sep 23 17:30:03 2012 +0200
@@ -3,11 +3,14 @@
 "Test for Tree"
 from trytond.model import ModelView, ModelSQL, fields
 
+__all__ = [
+    'MPTT',
+    ]
+
 
 class MPTT(ModelSQL, ModelView):
     'Modified Preorder Tree Traversal'
-    _name = 'test.mptt'
-    _description = __doc__
+    __name__ = 'test.mptt'
     name = fields.Char('Name', required=True)
     sequence = fields.Integer('Sequence',
         order_field='(%(table)s.sequence IS NULL) %(order)s, '
@@ -19,23 +22,25 @@
     childs = fields.One2Many('test.mptt', 'parent', 'Children')
     active = fields.Boolean('Active')
 
-    def __init__(self):
-        super(MPTT, self).__init__()
-        self._order.insert(0, ('sequence', 'ASC'))
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(MPTT, cls).__setup__()
+        cls._order.insert(0, ('sequence', 'ASC'))
+        cls._constraints += [
             ('check_recursion', 'recursive_mptt'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'recursive_mptt': 'You can not create recursive Tree!',
         })
 
-    def default_active(self):
+    @staticmethod
+    def default_active():
         return True
 
-    def default_left(self):
+    @staticmethod
+    def default_left():
         return 0
 
-    def default_right(self):
+    @staticmethod
+    def default_right():
         return 0
-
-MPTT()
diff -r f6ef988fda71 trytond/test/test.py
--- a/trytond/test/test.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/test/test.py	Sun Sep 23 17:30:03 2012 +0200
@@ -6,738 +6,573 @@
 from trytond.model import ModelSQL, fields
 from trytond.pyson import Eval
 
+__all__ = [
+    'Boolean', 'BooleanDefault',
+    'Integer', 'IntegerDefault', 'IntegerRequired',
+    'Float', 'FloatDefault', 'FloatRequired', 'FloatDigits',
+    'Numeric', 'NumericDefault', 'NumericRequired', 'NumericDigits',
+    'Char', 'CharDefault', 'CharRequired', 'CharSize', 'CharTranslate',
+    'Text', 'TextDefault', 'TextRequired', 'TextSize', 'TextTranslate',
+    'Sha', 'ShaDefault', 'ShaRequired',
+    'Date', 'DateDefault', 'DateRequired',
+    'DateTime', 'DateTimeDefault', 'DateTimeRequired', 'DateTimeFormat',
+    'Time', 'TimeDefault', 'TimeRequired', 'TimeFormat',
+    'One2One', 'One2OneTarget', 'One2OneRelation', 'One2OneRequired',
+    'One2OneRequiredRelation',
+    'One2Many', 'One2ManyTarget',
+    'One2ManyRequired', 'One2ManyRequiredTarget',
+    'One2ManyReference', 'One2ManyReferenceTarget',
+    'One2ManySize', 'One2ManySizeTarget',
+    'One2ManySizePYSON', 'One2ManySizePYSONTarget',
+    'Many2Many', 'Many2ManyTarget', 'Many2ManyRelation',
+    'Many2ManyRequired', 'Many2ManyRequiredTarget',
+    'Many2ManyRequiredRelation',
+    'Many2ManyReference', 'Many2ManyReferenceTarget',
+    'Many2ManyReferenceRelation',
+    'Many2ManySize', 'Many2ManySizeTarget', 'Many2ManySizeRelation',
+    'Reference', 'ReferenceTarget', 'ReferenceRequired',
+    'Property',
+    ]
+
 
 class Boolean(ModelSQL):
     'Boolean'
-    _name = 'test.boolean'
-    _description = __doc__
+    __name__ = 'test.boolean'
     boolean = fields.Boolean(string='Boolean', help='Test boolean')
 
-Boolean()
-
 
 class BooleanDefault(ModelSQL):
     'Boolean Default'
-    _name = 'test.boolean_default'
-    _description = __doc__
+    __name__ = 'test.boolean_default'
     boolean = fields.Boolean(string='Boolean', help='Test boolean')
 
-    def default_boolean(self):
+    @staticmethod
+    def default_boolean():
         return True
 
-BooleanDefault()
-
 
 class Integer(ModelSQL):
     'Integer'
-    _name = 'test.integer'
-    _description = __doc__
+    __name__ = 'test.integer'
     integer = fields.Integer(string='Integer', help='Test integer',
             required=False)
 
-Integer()
-
 
 class IntegerDefault(ModelSQL):
     'Integer Default'
-    _name = 'test.integer_default'
-    _description = __doc__
+    __name__ = 'test.integer_default'
     integer = fields.Integer(string='Integer', help='Test integer',
             required=False)
 
-    def default_integer(self):
+    @staticmethod
+    def default_integer():
         return 5
 
-IntegerDefault()
-
 
 class IntegerRequired(ModelSQL):
     'Integer Required'
-    _name = 'test.integer_required'
-    _description = __doc__
+    __name__ = 'test.integer_required'
     integer = fields.Integer(string='Integer', help='Test integer',
             required=True)
 
-IntegerRequired()
-
 
 class Float(ModelSQL):
     'Float'
-    _name = 'test.float'
-    _description = __doc__
+    __name__ = 'test.float'
     float = fields.Float(string='Float', help='Test float',
             required=False)
 
-Float()
-
 
 class FloatDefault(ModelSQL):
     'Float Default'
-    _name = 'test.float_default'
-    _description = __doc__
+    __name__ = 'test.float_default'
     float = fields.Float(string='Float', help='Test float',
             required=False)
 
-    def default_float(self):
+    @staticmethod
+    def default_float():
         return 5.5
 
-FloatDefault()
-
 
 class FloatRequired(ModelSQL):
     'Float Required'
-    _name = 'test.float_required'
-    _description = __doc__
+    __name__ = 'test.float_required'
     float = fields.Float(string='Float', help='Test float',
             required=True)
 
-FloatRequired()
-
 
 class FloatDigits(ModelSQL):
     'Float Digits'
-    _name = 'test.float_digits'
-    _description = __doc__
+    __name__ = 'test.float_digits'
     digits = fields.Integer('Digits')
     float = fields.Float(string='Float', help='Test float',
         required=False, digits=(16, Eval('digits', 2)),
         depends=['digits'])
 
-FloatDigits()
-
 
 class Numeric(ModelSQL):
     'Numeric'
-    _name = 'test.numeric'
-    _description = __doc__
+    __name__ = 'test.numeric'
     numeric = fields.Numeric(string='Numeric', help='Test numeric',
             required=False)
 
-Numeric()
-
 
 class NumericDefault(ModelSQL):
     'Numeric Default'
-    _name = 'test.numeric_default'
-    _description = __doc__
+    __name__ = 'test.numeric_default'
     numeric = fields.Numeric(string='Numeric', help='Test numeric',
             required=False)
 
-    def default_numeric(self):
+    @staticmethod
+    def default_numeric():
         return Decimal('5.5')
 
-NumericDefault()
-
 
 class NumericRequired(ModelSQL):
     'Numeric Required'
-    _name = 'test.numeric_required'
-    _description = __doc__
+    __name__ = 'test.numeric_required'
     numeric = fields.Numeric(string='Numeric', help='Test numeric',
             required=True)
 
-NumericRequired()
-
 
 class NumericDigits(ModelSQL):
     'Numeric Digits'
-    _name = 'test.numeric_digits'
-    _description = __doc__
+    __name__ = 'test.numeric_digits'
     digits = fields.Integer('Digits')
     numeric = fields.Numeric(string='Numeric', help='Test numeric',
         required=False, digits=(16, Eval('digits', 2)),
         depends=['digits'])
 
-NumericDigits()
-
 
 class Char(ModelSQL):
     'Char'
-    _name = 'test.char'
-    _description = __doc__
+    __name__ = 'test.char'
     char = fields.Char(string='Char', size=None, help='Test char',
             required=False)
 
-Char()
-
 
 class CharDefault(ModelSQL):
     'Char Default'
-    _name = 'test.char_default'
-    _description = __doc__
+    __name__ = 'test.char_default'
     char = fields.Char(string='Char', size=None, help='Test char',
             required=False)
 
-    def default_char(self):
+    @staticmethod
+    def default_char():
         return 'Test'
 
-CharDefault()
-
 
 class CharRequired(ModelSQL):
     'Char Required'
-    _name = 'test.char_required'
-    _description = __doc__
+    __name__ = 'test.char_required'
     char = fields.Char(string='Char', size=None, help='Test char',
             required=True)
 
-CharRequired()
-
 
 class CharSize(ModelSQL):
     'Char Size'
-    _name = 'test.char_size'
-    _description = __doc__
+    __name__ = 'test.char_size'
     char = fields.Char(string='Char', size=5, help='Test char',
             required=False)
 
-CharSize()
-
 
 class CharTranslate(ModelSQL):
     'Char Translate'
-    _name = 'test.char_translate'
-    _description = __doc__
+    __name__ = 'test.char_translate'
     char = fields.Char(string='Char', size=None, help='Test char',
             required=False, translate=True)
 
-CharTranslate()
-
 
 class Text(ModelSQL):
     'Text'
-    _name = 'test.text'
-    _description = __doc__
+    __name__ = 'test.text'
     text = fields.Text(string='Text', size=None, help='Test text',
             required=False)
 
-Text()
-
 
 class TextDefault(ModelSQL):
     'Text Default'
-    _name = 'test.text_default'
-    _description = __doc__
+    __name__ = 'test.text_default'
     text = fields.Text(string='Text', size=None, help='Test text',
             required=False)
 
-    def default_text(self):
+    @staticmethod
+    def default_text():
         return 'Test'
 
-TextDefault()
-
 
 class TextRequired(ModelSQL):
     'Text Required'
-    _name = 'test.text_required'
-    _description = __doc__
+    __name__ = 'test.text_required'
     text = fields.Text(string='Text', size=None, help='Test text',
             required=True)
 
-TextRequired()
-
 
 class TextSize(ModelSQL):
     'Text Size'
-    _name = 'test.text_size'
-    _description = __doc__
+    __name__ = 'test.text_size'
     text = fields.Text(string='Text', size=5, help='Test text',
             required=False)
 
-TextSize()
-
 
 class TextTranslate(ModelSQL):
     'Text Translate'
-    _name = 'test.text_translate'
-    _description = __doc__
+    __name__ = 'test.text_translate'
     text = fields.Text(string='Text', size=None, help='Test text',
             required=False, translate=True)
 
-TextTranslate()
-
 
 class Sha(ModelSQL):
     'Sha'
-    _name = 'test.sha'
-    _description = __doc__
+    __name__ = 'test.sha'
     sha = fields.Sha(string='Sha', help='Test sha',
             required=False)
 
-Sha()
-
 
 class ShaDefault(ModelSQL):
     'Sha Default'
-    _name = 'test.sha_default'
-    _description = __doc__
+    __name__ = 'test.sha_default'
     sha = fields.Sha(string='Sha', help='Test sha',
             required=False)
 
-    def default_sha(self):
+    @staticmethod
+    def default_sha():
         return 'Sha'
 
-ShaDefault()
-
 
 class ShaRequired(ModelSQL):
     'Sha Required'
-    _name = 'test.sha_required'
-    _description = __doc__
+    __name__ = 'test.sha_required'
     sha = fields.Sha(string='Sha', help='Test sha',
             required=True)
 
-ShaRequired()
-
 
 class Date(ModelSQL):
     'Date'
-    _name = 'test.date'
-    _description = __doc__
+    __name__ = 'test.date'
     date = fields.Date(string='Date', help='Test date',
             required=False)
 
-Date()
-
 
 class DateDefault(ModelSQL):
     'Date Default'
-    _name = 'test.date_default'
-    _description = __doc__
+    __name__ = 'test.date_default'
     date = fields.Date(string='Date', help='Test date',
             required=False)
 
-    def default_date(self):
+    @staticmethod
+    def default_date():
         return datetime.date(2000, 1, 1)
 
-DateDefault()
-
 
 class DateRequired(ModelSQL):
     'Date Required'
-    _name = 'test.date_required'
-    _description = __doc__
+    __name__ = 'test.date_required'
     date = fields.Date(string='Date', help='Test date',
             required=True)
 
-DateRequired()
-
 
 class DateTime(ModelSQL):
     'DateTime'
-    _name = 'test.datetime'
-    _description = __doc__
+    __name__ = 'test.datetime'
     datetime = fields.DateTime(string='DateTime', help='Test datetime',
             required=False)
 
-DateTime()
-
 
 class DateTimeDefault(ModelSQL):
     'DateTime Default'
-    _name = 'test.datetime_default'
-    _description = __doc__
+    __name__ = 'test.datetime_default'
     datetime = fields.DateTime(string='DateTime', help='Test datetime',
             required=False)
 
-    def default_datetime(self):
+    @staticmethod
+    def default_datetime():
         return datetime.datetime(2000, 1, 1, 12, 0, 0, 0)
 
-DateTimeDefault()
-
 
 class DateTimeRequired(ModelSQL):
     'DateTime Required'
-    _name = 'test.datetime_required'
-    _description = __doc__
+    __name__ = 'test.datetime_required'
     datetime = fields.DateTime(string='DateTime', help='Test datetime',
             required=True)
 
-DateTimeRequired()
-
 
 class DateTimeFormat(ModelSQL):
     'DateTime Format'
-    _name = 'test.datetime_format'
-    _description = __doc__
+    __name__ = 'test.datetime_format'
     datetime = fields.DateTime(string='DateTime', format='%H:%M')
 
-DateTimeFormat()
-
 
 class Time(ModelSQL):
     'Time'
-    _name = 'test.time'
-    _description = __doc__
+    __name__ = 'test.time'
     time = fields.Time(string='Time', help='Test time', required=False)
 
-Time()
-
 
 class TimeDefault(ModelSQL):
     'Time Default'
-    _name = 'test.time_default'
-    _description = __doc__
+    __name__ = 'test.time_default'
     time = fields.Time(string='Time', help='Test time', required=False)
 
-    def default_time(self):
+    @staticmethod
+    def default_time():
         return datetime.time(16, 30)
 
-TimeDefault()
-
 
 class TimeRequired(ModelSQL):
     'Time'
-    _name = 'test.time_required'
-    _description = __doc__
+    __name__ = 'test.time_required'
     time = fields.Time(string='Time', help='Test time', required=True)
 
-TimeRequired()
-
 
 class TimeFormat(ModelSQL):
     'Time Format'
-    _name = 'test.time_format'
-    _description = __doc__
+    __name__ = 'test.time_format'
     time = fields.Time(string='Time', format='%H:%M')
 
-TimeFormat()
-
 
 class One2One(ModelSQL):
     'One2One'
-    _name = 'test.one2one'
-    _description = __doc__
+    __name__ = 'test.one2one'
     name = fields.Char('Name', required=True)
     one2one = fields.One2One('test.one2one.relation', 'origin', 'target',
             string='One2One', help='Test one2one', required=False)
 
-One2One()
-
 
 class One2OneTarget(ModelSQL):
     'One2One Target'
-    _name = 'test.one2one.target'
+    __name__ = 'test.one2one.target'
     name = fields.Char('Name', required=True)
 
-One2OneTarget()
-
 
 class One2OneRelation(ModelSQL):
     'One2One Relation'
-    _name = 'test.one2one.relation'
-    _description = __doc__
+    __name__ = 'test.one2one.relation'
     origin = fields.Many2One('test.one2one', 'Origin')
     target = fields.Many2One('test.one2one.target', 'Target')
 
-    def __init__(self):
-        super(One2OneRelation, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(One2OneRelation, cls).__setup__()
+        cls._sql_constraints += [
             ('origin_unique', 'UNIQUE(origin)',
                 'Origin must be unique'),
             ('target_unique', 'UNIQUE(target)',
                 'Target must be unique'),
-        ]
-
-One2OneRelation()
+            ]
 
 
 class One2OneRequired(ModelSQL):
     'One2One'
-    _name = 'test.one2one_required'
-    _description = __doc__
+    __name__ = 'test.one2one_required'
     name = fields.Char('Name', required=True)
     one2one = fields.One2One('test.one2one_required.relation', 'origin',
         'target', string='One2One', help='Test one2one', required=True)
 
-One2OneRequired()
-
 
 class One2OneRequiredRelation(ModelSQL):
     'One2One Relation'
-    _name = 'test.one2one_required.relation'
-    _description = __doc__
+    __name__ = 'test.one2one_required.relation'
     origin = fields.Many2One('test.one2one_required', 'Origin')
     target = fields.Many2One('test.one2one.target', 'Target')
 
-    def __init__(self):
-        super(One2OneRequiredRelation, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(One2OneRequiredRelation, cls).__setup__()
+        cls._sql_constraints += [
             ('origin_unique', 'UNIQUE(origin)',
                 'Origin must be unique'),
             ('target_unique', 'UNIQUE(target)',
                 'Target must be unique'),
-        ]
-
-One2OneRequiredRelation()
+            ]
 
 
 class One2Many(ModelSQL):
     'One2Many Relation'
-    _name = 'test.one2many'
-    _description = __doc__
+    __name__ = 'test.one2many'
     name = fields.Char('Name', required=True)
     targets = fields.One2Many('test.one2many.target', 'origin', 'Targets')
 
-One2Many()
-
 
 class One2ManyTarget(ModelSQL):
     'One2Many Target'
-    _name = 'test.one2many.target'
-    _description = __doc__
+    __name__ = 'test.one2many.target'
     name = fields.Char('Name', required=True)
     origin = fields.Many2One('test.one2many', 'Origin')
 
-One2ManyTarget()
-
 
 class One2ManyRequired(ModelSQL):
     'One2Many Required'
-    _name = 'test.one2many_required'
-    _description = __doc__
+    __name__ = 'test.one2many_required'
     name = fields.Char('Name', required=True)
     targets = fields.One2Many('test.one2many_required.target', 'origin',
         'Targets', required=True)
 
-One2ManyRequired()
-
 
 class One2ManyRequiredTarget(ModelSQL):
     'One2Many Required Target'
-    _name = 'test.one2many_required.target'
-    _description = __doc__
+    __name__ = 'test.one2many_required.target'
     name = fields.Char('Name', required=True)
     origin = fields.Many2One('test.one2many_required', 'Origin')
 
-One2ManyRequiredTarget()
-
 
 class One2ManyReference(ModelSQL):
     'One2Many Reference Relation'
-    _name = 'test.one2many_reference'
-    _description = __doc__
+    __name__ = 'test.one2many_reference'
     name = fields.Char('Name', required=True)
     targets = fields.One2Many('test.one2many_reference.target', 'origin',
         'Targets')
 
-One2ManyReference()
-
 
 class One2ManyReferenceTarget(ModelSQL):
     'One2Many Reference Target'
-    _name = 'test.one2many_reference.target'
-    _description = __doc__
+    __name__ = 'test.one2many_reference.target'
     name = fields.Char('Name', required=True)
     origin = fields.Reference('Origin', [
             ('test.one2many_reference', 'One2Many Reference'),
             ])
 
-One2ManyReferenceTarget()
-
 
 class One2ManySize(ModelSQL):
     'One2Many Size Relation'
-    _name = 'test.one2many_size'
-    _description = __doc__
+    __name__ = 'test.one2many_size'
     targets = fields.One2Many('test.one2many_size.target', 'origin', 'Targets',
         size=3)
 
-One2ManySize()
-
 
 class One2ManySizeTarget(ModelSQL):
     'One2Many Size Target'
-    _name = 'test.one2many_size.target'
-    _description = __doc__
+    __name__ = 'test.one2many_size.target'
     origin = fields.Many2One('test.one2many_size', 'Origin')
 
-One2ManySizeTarget()
-
 
 class One2ManySizePYSON(ModelSQL):
     'One2Many Size PYSON Relation'
-    _name = 'test.one2many_size_pyson'
-    _description = __doc__
+    __name__ = 'test.one2many_size_pyson'
     limit = fields.Integer('Limit')
     targets = fields.One2Many('test.one2many_size_pyson.target', 'origin',
         'Targets', size=Eval('limit', 0))
 
-One2ManySizePYSON()
-
 
 class One2ManySizePYSONTarget(ModelSQL):
     'One2Many Size PYSON Target'
-    _name = 'test.one2many_size_pyson.target'
-    _description = __doc__
+    __name__ = 'test.one2many_size_pyson.target'
     origin = fields.Many2One('test.one2many_size_pyson', 'Origin')
 
-One2ManySizePYSONTarget()
-
 
 class Many2Many(ModelSQL):
     'Many2Many'
-    _name = 'test.many2many'
-    _description = __doc__
+    __name__ = 'test.many2many'
     name = fields.Char('Name', required=True)
     targets = fields.Many2Many('test.many2many.relation', 'origin', 'target',
         'Targets')
 
-Many2Many()
-
 
 class Many2ManyTarget(ModelSQL):
     'Many2Many Target'
-    _name = 'test.many2many.target'
-    _description = __doc__
+    __name__ = 'test.many2many.target'
     name = fields.Char('Name', required=True)
 
 
-Many2ManyTarget()
-
-
 class Many2ManyRelation(ModelSQL):
     'Many2Many Relation'
-    _name = 'test.many2many.relation'
-    _description = __doc__
+    __name__ = 'test.many2many.relation'
     origin = fields.Many2One('test.many2many', 'Origin')
     target = fields.Many2One('test.many2many.target', 'Target')
 
-Many2ManyRelation()
-
 
 class Many2ManyRequired(ModelSQL):
     'Many2Many Required'
-    _name = 'test.many2many_required'
-    _description = __doc__
+    __name__ = 'test.many2many_required'
     name = fields.Char('Name', required=True)
     targets = fields.Many2Many('test.many2many_required.relation', 'origin',
         'target', 'Targets', required=True)
 
-Many2ManyRequired()
-
 
 class Many2ManyRequiredTarget(ModelSQL):
     'Many2Many Required Target'
-    _name = 'test.many2many_required.target'
-    _description = __doc__
+    __name__ = 'test.many2many_required.target'
     name = fields.Char('Name', required=True)
 
 
-Many2ManyRequiredTarget()
-
-
 class Many2ManyRequiredRelation(ModelSQL):
     'Many2Many Required Relation'
-    _name = 'test.many2many_required.relation'
-    _description = __doc__
+    __name__ = 'test.many2many_required.relation'
     origin = fields.Many2One('test.many2many_required', 'Origin')
     target = fields.Many2One('test.many2many_required.target', 'Target')
 
-Many2ManyRequiredRelation()
-
 
 class Many2ManyReference(ModelSQL):
     'Many2Many Reference'
-    _name = 'test.many2many_reference'
-    _description = __doc__
+    __name__ = 'test.many2many_reference'
     name = fields.Char('Name', required=True)
     targets = fields.Many2Many('test.many2many_reference.relation', 'origin',
         'target', 'Targets')
 
-Many2ManyReference()
-
 
 class Many2ManyReferenceTarget(ModelSQL):
     'Many2Many Reference Target'
-    _name = 'test.many2many_reference.target'
-    _description = __doc__
+    __name__ = 'test.many2many_reference.target'
     name = fields.Char('Name', required=True)
 
 
-Many2ManyReferenceTarget()
-
-
 class Many2ManyReferenceRelation(ModelSQL):
     'Many2Many Relation'
-    _name = 'test.many2many_reference.relation'
-    _description = __doc__
+    __name__ = 'test.many2many_reference.relation'
     origin = fields.Reference('Origin', [
             ('test.many2many_reference', 'Many2Many Reference'),
             ])
     target = fields.Many2One('test.many2many_reference.target',
         'Reference Target')
 
-Many2ManyReferenceRelation()
-
 
 class Many2ManySize(ModelSQL):
     'Many2Many Size Relation'
-    _name = 'test.many2many_size'
-    _description = __doc__
+    __name__ = 'test.many2many_size'
     targets = fields.Many2Many('test.many2many_size.relation', 'origin',
         'target', 'Targets', size=5)
 
-Many2ManySize()
-
 
 class Many2ManySizeTarget(ModelSQL):
     'Many2Many Size Target'
-    _name = 'test.many2many_size.target'
-    _description = __doc__
+    __name__ = 'test.many2many_size.target'
     name = fields.Char('Name')
 
-Many2ManySizeTarget()
-
 
 class Many2ManySizeRelation(ModelSQL):
     'Many2Many Size Relation'
-    _name = 'test.many2many_size.relation'
-    _description = __doc__
+    __name__ = 'test.many2many_size.relation'
     origin = fields.Many2One('test.many2many_size', 'Origin')
     target = fields.Many2One('test.many2many_size.target', 'Target')
 
-Many2ManySizeRelation()
-
 
 class Reference(ModelSQL):
     'Reference'
-    _name = 'test.reference'
-    _description = __doc__
+    __name__ = 'test.reference'
     name = fields.Char('Name', required=True)
     reference = fields.Reference('Reference', selection=[
             ('test.reference.target', 'Target'),
             ])
 
-Reference()
-
 
 class ReferenceTarget(ModelSQL):
     'Reference Target'
-    _name = 'test.reference.target'
-    _description = __doc__
+    __name__ = 'test.reference.target'
     name = fields.Char('Name', required=True)
 
-ReferenceTarget()
-
 
 class ReferenceRequired(ModelSQL):
     'Reference Required'
-    _name = 'test.reference_required'
-    _description = __doc__
+    __name__ = 'test.reference_required'
     name = fields.Char('Name', required=True)
     reference = fields.Reference('Reference', selection=[
             ('test.reference.target', 'Target'),
             ], required=True)
 
-ReferenceRequired()
-
 
 class Property(ModelSQL):
     'Property'
-    _name = 'test.property'
-    _description = __doc__
-
+    __name__ = 'test.property'
     char = fields.Property(fields.Char('Test Char'))
     many2one = fields.Property(fields.Many2One('test.char',
             'Test Many2One'))
@@ -746,5 +581,3 @@
                 ('option_a', 'Option A'),
                 ('option_b', 'Option B')
             ], 'Test Selection'))
-
-Property()
diff -r f6ef988fda71 trytond/test/trigger.py
--- a/trytond/test/trigger.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/test/trigger.py	Sun Sep 23 17:30:03 2012 +0200
@@ -2,20 +2,21 @@
 #this repository contains the full copyright notices and license terms.
 from trytond.model import ModelSQL, fields
 
+__all__ = [
+    'Triggered',
+    ]
+
 TRIGGER_LOGS = []
 
 
 class Triggered(ModelSQL):
     'Triggered'
-    _name = 'test.triggered'
-    _description = __doc__
-
+    __name__ = 'test.triggered'
     name = fields.Char('Name')
 
-    def trigger(self, ids, trigger_id):
+    @staticmethod
+    def trigger(records, trigger):
         '''
         Trigger function for test
         '''
-        TRIGGER_LOGS.append((ids, trigger_id))
-
-Triggered()
+        TRIGGER_LOGS.append((records, trigger))
diff -r f6ef988fda71 trytond/test/wizard.py
--- a/trytond/test/wizard.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/test/wizard.py	Sun Sep 23 17:30:03 2012 +0200
@@ -1,26 +1,30 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
-from trytond.model import ModelView, fields
+from trytond.model import ModelView, ModelSQL, fields
 from trytond.wizard import Wizard, StateView, StateTransition, StateAction, \
     Button
 from trytond.transaction import Transaction
 
+__all__ = [
+    'TestWizardStart', 'TestWizard',
+    ]
 
-class TestWizardStart(ModelView):
-    _name = 'test.test_wizard.start'
+
+class TestWizardStart(ModelSQL, ModelView):
+    'Test Wizard'
+    __name__ = 'test.test_wizard.start'
     name = fields.Char('Test me')
     user = fields.Many2One('res.user', 'User')
     groups = fields.One2Many('res.group', None, 'Groups')
 
-    def default_user(self):
+    @staticmethod
+    def default_user():
         return Transaction().user
 
-TestWizardStart()
-
 
 class TestWizard(Wizard):
-    _name = 'test.test_wizard'
-
+    'Test Wizard'
+    __name__ = 'test.test_wizard'
     start = StateView('test.test_wizard.start',
         'test.test_wizard_start_view_form', [
             Button('Cancel', 'end', 'tryton-cancel'),
@@ -29,18 +33,20 @@
     next_ = StateTransition()
     action = StateAction('ir.act_menu_tree')
 
-    def default_start(self, session, fields):
+    @staticmethod
+    def default_start(fields):
         return {
             'name': 'Test wizard',
             }
 
-    def transition_next_(self, session):
+    @staticmethod
+    def transition_next_():
         return 'action'
 
-    def do_action(self, session, action):
+    @staticmethod
+    def do_action(action):
         return action, {}
 
-    def transition_action(self, session):
+    @staticmethod
+    def transition_action():
         return 'end'
-
-TestWizard()
diff -r f6ef988fda71 trytond/test/workflow.py
--- a/trytond/test/workflow.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/test/workflow.py	Sun Sep 23 17:30:03 2012 +0200
@@ -3,27 +3,32 @@
 
 from trytond.model import ModelSQL, Workflow, fields
 
+__all__ = [
+    'WorkflowedModel',
+    ]
+
 
 class WorkflowedModel(Workflow, ModelSQL):
-    _name = 'test.workflowed'
-
+    'Workflowed Model'
+    __name__ = 'test.workflowed'
     state = fields.Selection([
             ('start', 'Start'),
             ('running', 'Running'),
             ('end', 'End'),
             ], 'State')
 
-    def __init__(self):
-        super(WorkflowedModel, self).__init__()
-        self._transitions |= set((
+    @classmethod
+    def __setup__(cls):
+        super(WorkflowedModel, cls).__setup__()
+        cls._transitions |= set((
                 ('start', 'running'),
                 ))
 
-    def default_state(self):
+    @staticmethod
+    def default_state(cls):
         return 'start'
 
+    @classmethod
     @Workflow.transition('running')
-    def run(self, ids):
+    def run(cls, records):
         pass
-
-WorkflowedModel()
diff -r f6ef988fda71 trytond/tests/test_access.py
--- a/trytond/tests/test_access.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/tests/test_access.py	Sun Sep 23 17:30:03 2012 +0200
@@ -26,82 +26,79 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            model_id, = self.model.search([('model', '=', 'test.access')])
+            model, = self.model.search([('model', '=', 'test.access')])
 
-            test_id = self.test_access.create({})
+            test = self.test_access.create({})
 
             # Without model access
-            self.test_access.read(test_id)
+            self.test_access.read([test.id])
 
             # With model access
 
             # One access allowed for any group
-            model_access_wo_group_id = self.model_access.create({
-                'model': model_id,
-                'group': False,
-                'perm_read': True,
-                })
-            self.test_access.read(test_id)
+            model_access_wo_group = self.model_access.create({
+                    'model': model.id,
+                    'group': False,
+                    'perm_read': True,
+                    })
+            self.test_access.read([test.id])
 
             # One access disallowed for any group
-            self.model_access.write(model_access_wo_group_id, {
-                'perm_read': False,
-                })
-            self.failUnlessRaises(Exception, self.test_access.read,
-                    test_id)
+            self.model_access.write([model_access_wo_group], {
+                    'perm_read': False,
+                    })
+            self.assertRaises(Exception, self.test_access.read, [test.id])
 
             # Two access rules with one group allowed
-            group_id = self.group.search([('users', '=', USER)])[0]
-            model_access_w_group_id = self.model_access.create({
-                'model': model_id,
-                'group': group_id,
-                'perm_read': True,
-                })
+            group, = self.group.search([('users', '=', USER)])
+            model_access_w_group = self.model_access.create({
+                    'model': model.id,
+                    'group': group.id,
+                    'perm_read': True,
+                    })
 
-            self.test_access.read(test_id)
+            self.test_access.read([test.id])
 
             # Two access rules with both allowed
-            self.model_access.write(model_access_wo_group_id, {
-                'perm_read': True,
-                })
-            self.test_access.read(test_id)
+            self.model_access.write([model_access_wo_group], {
+                    'perm_read': True,
+                    })
+            self.test_access.read([test.id])
 
             # Two access rules with any group allowed
-            self.model_access.write(model_access_w_group_id, {
-                'perm_read': False,
-                })
-            self.test_access.read(test_id)
+            self.model_access.write([model_access_w_group], {
+                    'perm_read': False,
+                    })
+            self.test_access.read([test.id])
 
             # Two access rules with both disallowed
-            self.model_access.write(model_access_wo_group_id, {
-                'perm_read': False,
-                })
-            self.failUnlessRaises(Exception, self.test_access.read,
-                    test_id)
+            self.model_access.write([model_access_wo_group], {
+                    'perm_read': False,
+                    })
+            self.assertRaises(Exception, self.test_access.read, [test.id])
 
             # One access disallowed for one group
-            self.model_access.delete(model_access_wo_group_id)
-            self.failUnlessRaises(Exception, self.test_access.read,
-                    test_id)
+            self.model_access.delete([model_access_wo_group])
+            self.assertRaises(Exception, self.test_access.read, [test.id])
 
             # One access allowed for one group
-            self.model_access.write(model_access_w_group_id, {
-                'perm_read': True,
-                })
-            self.test_access.read(test_id)
+            self.model_access.write([model_access_w_group], {
+                    'perm_read': True,
+                    })
+            self.test_access.read([test.id])
 
             # One access allowed for one other group
-            group_id = self.group.create({'name': 'Test'})
-            self.model_access.write(model_access_w_group_id, {
-                'group': group_id,
-                })
-            self.test_access.read(test_id)
+            group = self.group.create({'name': 'Test'})
+            self.model_access.write([model_access_w_group], {
+                    'group': group.id,
+                    })
+            self.test_access.read([test.id])
 
             # One access disallowed for one other group
-            self.model_access.write(model_access_w_group_id, {
-                'perm_read': False,
-                })
-            self.test_access.read(test_id)
+            self.model_access.write([model_access_w_group], {
+                    'perm_read': False,
+                    })
+            self.test_access.read([test.id])
 
             transaction.cursor.rollback()
 
@@ -111,82 +108,78 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            model_id, = self.model.search([('model', '=', 'test.access')])
+            model, = self.model.search([('model', '=', 'test.access')])
 
-            test_id = self.test_access.create({})
+            test = self.test_access.create({})
 
             # Without model access
-            self.test_access.write(test_id, {})
+            self.test_access.write([test], {})
 
             # With model access
 
             # One access allowed for any group
-            model_access_wo_group_id = self.model_access.create({
-                'model': model_id,
-                'group': False,
-                'perm_write': True,
-                })
-            self.test_access.write(test_id, {})
+            model_access_wo_group = self.model_access.create({
+                    'model': model.id,
+                    'group': False,
+                    'perm_write': True,
+                    })
+            self.test_access.write([test], {})
 
             # One access disallowed for any group
-            self.model_access.write(model_access_wo_group_id, {
-                'perm_write': False,
-                })
-            self.failUnlessRaises(Exception, self.test_access.write,
-                    test_id, {})
+            self.model_access.write([model_access_wo_group], {
+                    'perm_write': False,
+                    })
+            self.assertRaises(Exception, self.test_access.write, [test], {})
 
             # Two access rules with one group allowed
-            group_id = self.group.search([('users', '=', USER)])[0]
-            model_access_w_group_id = self.model_access.create({
-                'model': model_id,
-                'group': group_id,
-                'perm_write': True,
-                })
-
-            self.test_access.write(test_id, {})
+            group, = self.group.search([('users', '=', USER)])
+            model_access_w_group = self.model_access.create({
+                    'model': model.id,
+                    'group': group.id,
+                    'perm_write': True,
+                    })
+            self.test_access.write([test], {})
 
             # Two access rules with both allowed
-            self.model_access.write(model_access_wo_group_id, {
-                'perm_write': True,
-                })
-            self.test_access.write(test_id, {})
+            self.model_access.write([model_access_wo_group], {
+                    'perm_write': True,
+                    })
+            self.test_access.write([test], {})
 
             # Two access rules with any group allowed
-            self.model_access.write(model_access_w_group_id, {
-                'perm_write': False,
-                })
-            self.test_access.write(test_id, {})
+            self.model_access.write([model_access_w_group], {
+                    'perm_write': False,
+                    })
+            self.test_access.write([test], {})
 
             # Two access rules with both disallowed
-            self.model_access.write(model_access_wo_group_id, {
-                'perm_write': False,
-                })
-            self.failUnlessRaises(Exception, self.test_access.write,
-                    test_id, {})
+            self.model_access.write([model_access_wo_group], {
+                    'perm_write': False,
+                    })
+            self.assertRaises(Exception, self.test_access.write, [test], {})
 
             # One access disallowed for one group
-            self.model_access.delete(model_access_wo_group_id)
-            self.failUnlessRaises(Exception, self.test_access.write,
-                    test_id, {})
+            self.model_access.delete([model_access_wo_group])
+            self.assertRaises(Exception, self.test_access.write, [test], {})
 
             # One access allowed for one group
-            self.model_access.write(model_access_w_group_id, {
-                'perm_write': True,
-                })
-            self.test_access.write(test_id, {})
+            self.model_access.write([model_access_w_group], {
+                    'perm_write': True,
+                    })
+            self.test_access.write([test], {})
 
             # One access allowed for one other group
-            group_id = self.group.create({'name': 'Test'})
-            self.model_access.write(model_access_w_group_id, {
-                'group': group_id,
-                })
-            self.test_access.write(test_id, {})
+            group = self.group.create({'name': 'Test'})
+            self.model_access.write([model_access_w_group], {
+                    'group': group.id,
+                    })
+            self.test_access.write([test], {})
 
             # One access disallowed for one other group
-            self.model_access.write(model_access_w_group_id, {
-                'perm_write': False,
-                })
-            self.test_access.write(test_id, {})
+            self.model_access.write([model_access_w_group], {
+                    'perm_write': False,
+                    })
+            self.test_access.write([test], {})
 
             transaction.cursor.rollback()
 
@@ -196,7 +189,7 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            model_id, = self.model.search([('model', '=', 'test.access')])
+            model, = self.model.search([('model', '=', 'test.access')])
 
             # Without model access
             self.test_access.create({})
@@ -204,68 +197,68 @@
             # With model access
 
             # One access allowed for any group
-            model_access_wo_group_id = self.model_access.create({
-                'model': model_id,
-                'group': False,
-                'perm_create': True,
-                })
+            model_access_wo_group = self.model_access.create({
+                    'model': model.id,
+                    'group': False,
+                    'perm_create': True,
+                    })
             self.test_access.create({})
 
             # One access disallowed for any group
-            self.model_access.write(model_access_wo_group_id, {
-                'perm_create': False,
-                })
-            self.failUnlessRaises(Exception, self.test_access.create, {})
+            self.model_access.write([model_access_wo_group], {
+                    'perm_create': False,
+                    })
+            self.assertRaises(Exception, self.test_access.create, {})
 
             # Two access rules with one group allowed
-            group_id = self.group.search([('users', '=', USER)])[0]
-            model_access_w_group_id = self.model_access.create({
-                'model': model_id,
-                'group': group_id,
-                'perm_create': True,
-                })
+            group, = self.group.search([('users', '=', USER)])
+            model_access_w_group = self.model_access.create({
+                    'model': model.id,
+                    'group': group.id,
+                    'perm_create': True,
+                    })
 
             self.test_access.create({})
 
             # Two access rules with both allowed
-            self.model_access.write(model_access_wo_group_id, {
-                'perm_create': True,
-                })
+            self.model_access.write([model_access_wo_group], {
+                    'perm_create': True,
+                    })
             self.test_access.create({})
 
             # Two access rules with any group allowed
-            self.model_access.write(model_access_w_group_id, {
-                'perm_create': False,
-                })
+            self.model_access.write([model_access_w_group], {
+                    'perm_create': False,
+                    })
             self.test_access.create({})
 
             # Two access rules with both disallowed
-            self.model_access.write(model_access_wo_group_id, {
-                'perm_create': False,
-                })
-            self.failUnlessRaises(Exception, self.test_access.create, {})
+            self.model_access.write([model_access_wo_group], {
+                    'perm_create': False,
+                    })
+            self.assertRaises(Exception, self.test_access.create, {})
 
             # One access disallowed for one group
-            self.model_access.delete(model_access_wo_group_id)
-            self.failUnlessRaises(Exception, self.test_access.create, {})
+            self.model_access.delete([model_access_wo_group])
+            self.assertRaises(Exception, self.test_access.create, {})
 
             # One access allowed for one group
-            self.model_access.write(model_access_w_group_id, {
-                'perm_create': True,
-                })
+            self.model_access.write([model_access_w_group], {
+                    'perm_create': True,
+                    })
             self.test_access.create({})
 
             # One access allowed for one other group
-            group_id = self.group.create({'name': 'Test'})
-            self.model_access.write(model_access_w_group_id, {
-                'group': group_id,
-                })
+            group = self.group.create({'name': 'Test'})
+            self.model_access.write([model_access_w_group], {
+                    'group': group.id,
+                    })
             self.test_access.create({})
 
             # One access disallowed for one other group
-            self.model_access.write(model_access_w_group_id, {
-                'perm_create': False,
-                })
+            self.model_access.write([model_access_w_group], {
+                    'perm_create': False,
+                    })
             self.test_access.create({})
 
             transaction.cursor.rollback()
@@ -276,82 +269,82 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            model_id, = self.model.search([('model', '=', 'test.access')])
+            model, = self.model.search([('model', '=', 'test.access')])
 
-            test_ids = [self.test_access.create({}) for x in range(11)]
+            tests = [self.test_access.create({}) for x in range(11)]
 
             # Without model access
-            self.test_access.delete(test_ids.pop())
+            self.test_access.delete([tests.pop()])
 
             # With model access
 
             # One access allowed for any group
-            model_access_wo_group_id = self.model_access.create({
-                'model': model_id,
-                'group': False,
-                'perm_delete': True,
-                })
-            self.test_access.delete(test_ids.pop())
+            model_access_wo_group = self.model_access.create({
+                    'model': model.id,
+                    'group': False,
+                    'perm_delete': True,
+                    })
+            self.test_access.delete([tests.pop()])
 
             # One access disallowed for any group
-            self.model_access.write(model_access_wo_group_id, {
-                'perm_delete': False,
-                })
-            self.failUnlessRaises(Exception, self.test_access.delete,
-                    test_ids.pop())
+            self.model_access.write([model_access_wo_group], {
+                    'perm_delete': False,
+                    })
+            self.assertRaises(Exception, self.test_access.delete,
+                [tests.pop()])
 
             # Two access rules with one group allowed
-            group_id = self.group.search([('users', '=', USER)])[0]
-            model_access_w_group_id = self.model_access.create({
-                'model': model_id,
-                'group': group_id,
-                'perm_delete': True,
-                })
+            group = self.group.search([('users', '=', USER)])[0]
+            model_access_w_group = self.model_access.create({
+                    'model': model.id,
+                    'group': group.id,
+                    'perm_delete': True,
+                    })
 
-            self.test_access.delete(test_ids.pop())
+            self.test_access.delete([tests.pop()])
 
             # Two access rules with both allowed
-            self.model_access.write(model_access_wo_group_id, {
-                'perm_delete': True,
-                })
-            self.test_access.delete(test_ids.pop())
+            self.model_access.write([model_access_wo_group], {
+                    'perm_delete': True,
+                    })
+            self.test_access.delete([tests.pop()])
 
             # Two access rules with any group allowed
-            self.model_access.write(model_access_w_group_id, {
-                'perm_delete': False,
-                })
-            self.test_access.delete(test_ids.pop())
+            self.model_access.write([model_access_w_group], {
+                    'perm_delete': False,
+                    })
+            self.test_access.delete([tests.pop()])
 
             # Two access rules with both disallowed
-            self.model_access.write(model_access_wo_group_id, {
-                'perm_delete': False,
-                })
-            self.failUnlessRaises(Exception, self.test_access.delete,
-                    test_ids.pop())
+            self.model_access.write([model_access_wo_group], {
+                    'perm_delete': False,
+                    })
+            self.assertRaises(Exception, self.test_access.delete,
+                [tests.pop()])
 
             # One access disallowed for one group
-            self.model_access.delete(model_access_wo_group_id)
-            self.failUnlessRaises(Exception, self.test_access.delete,
-                    test_ids.pop())
+            self.model_access.delete([model_access_wo_group])
+            self.assertRaises(Exception, self.test_access.delete,
+                [tests.pop()])
 
             # One access allowed for one group
-            self.model_access.write(model_access_w_group_id, {
-                'perm_delete': True,
-                })
-            self.test_access.delete(test_ids.pop())
+            self.model_access.write([model_access_w_group], {
+                    'perm_delete': True,
+                    })
+            self.test_access.delete([tests.pop()])
 
             # One access allowed for one other group
-            group_id = self.group.create({'name': 'Test'})
-            self.model_access.write(model_access_w_group_id, {
-                'group': group_id,
-                })
-            self.test_access.delete(test_ids.pop())
+            group = self.group.create({'name': 'Test'})
+            self.model_access.write([model_access_w_group], {
+                    'group': group.id,
+                    })
+            self.test_access.delete([tests.pop()])
 
             # One access disallowed for one other group
-            self.model_access.write(model_access_w_group_id, {
-                'perm_delete': False,
-                })
-            self.test_access.delete(test_ids.pop())
+            self.model_access.write([model_access_w_group], {
+                    'perm_delete': False,
+                    })
+            self.test_access.delete([tests.pop()])
 
             transaction.cursor.rollback()
 
@@ -374,208 +367,208 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            field1_id, = self.field.search([
-                ('model.model', '=', 'test.access'),
-                ('name', '=', 'field1'),
-                ])
-            field2_id, = self.field.search([
-                ('model.model', '=', 'test.access'),
-                ('name', '=', 'field2'),
-                ])
+            field1, = self.field.search([
+                    ('model.model', '=', 'test.access'),
+                    ('name', '=', 'field1'),
+                    ])
+            field2, = self.field.search([
+                    ('model.model', '=', 'test.access'),
+                    ('name', '=', 'field2'),
+                    ])
 
-            test_id = self.test_access.create({
-                'field1': 'ham',
-                'field2': 'spam',
-                })
+            test = self.test_access.create({
+                    'field1': 'ham',
+                    'field2': 'spam',
+                    })
 
             # Without field access
-            self.test_access.read(test_id, ['field1'])
-            self.test_access.read(test_id, ['field2'])
-            self.test_access.read(test_id)
-            test_access = self.test_access.browse(test_id)
-            test_access.field1
-            test_access.field2
+            self.test_access.read([test.id], ['field1'])
+            self.test_access.read([test.id], ['field2'])
+            self.test_access.read([test.id])
+            test.field1
+            test.field2
             transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
 
             # With field access
 
             # One access allowed for any group
-            field_access_wo_group_id = self.field_access.create({
-                'field': field1_id,
-                'group': False,
-                'perm_read': True,
-                })
-            self.test_access.read(test_id, ['field1'])
-            self.test_access.read(test_id, ['field2'])
-            self.test_access.read(test_id)
-            test_access = self.test_access.browse(test_id)
-            test_access.field1
-            test_access.field2
+            field_access_wo_group = self.field_access.create({
+                    'field': field1.id,
+                    'group': False,
+                    'perm_read': True,
+                    })
+            self.test_access.read([test.id], ['field1'])
+            self.test_access.read([test.id], ['field2'])
+            self.test_access.read([test.id])
+            test.field1
+            test.field2
             transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
 
             # One access disallowed for any group
-            self.field_access.write(field_access_wo_group_id, {
+            self.field_access.write([field_access_wo_group], {
+                    'perm_read': False,
+                    })
+
+            self.assertRaises(Exception, self.test_access.read, [test.id],
+                ['field1'])
+            self.test_access.read([test.id], ['field2'])
+            self.assertRaises(Exception, self.test_access.read, [test.id])
+            self.assertRaises(Exception, getattr, test, 'field1')
+            test.field2
+            transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
+
+            # Two access rules with one group allowed
+            group = self.group.search([('users', '=', USER)])[0]
+            field_access_w_group = self.field_access.create({
+                    'field': field1.id,
+                    'group': group.id,
+                    'perm_read': True,
+                    })
+
+            self.test_access.read([test.id], ['field1'])
+            self.test_access.read([test.id], ['field2'])
+            self.test_access.read([test.id])
+            test.field1
+            test.field2
+            transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
+
+            # Two access rules with both allowed
+            self.field_access.write([field_access_wo_group], {
+                    'perm_read': True,
+                    })
+            self.test_access.read([test.id], ['field1'])
+            self.test_access.read([test.id], ['field2'])
+            self.test_access.read([test.id])
+            test.field1
+            test.field2
+            transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
+
+            # Two access rules with any group allowed
+            self.field_access.write([field_access_w_group], {
+                    'perm_read': False,
+                    })
+            self.test_access.read([test.id], ['field1'])
+            self.test_access.read([test.id], ['field2'])
+            self.test_access.read([test.id])
+            test.field1
+            test.field2
+            transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
+
+            # Two access rules with both disallowed
+            self.field_access.write([field_access_wo_group], {
                 'perm_read': False,
                 })
+            self.assertRaises(Exception, self.test_access.read, [test.id],
+                ['field1'])
+            self.test_access.read([test.id], ['field2'])
+            self.assertRaises(Exception, self.test_access.read, [test.id])
+            self.assertRaises(Exception, getattr, test, 'field1')
+            test.field2
+            transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
 
-            self.failUnlessRaises(Exception, self.test_access.read, test_id,
-                    ['field1'])
-            self.test_access.read(test_id, ['field2'])
-            self.failUnlessRaises(Exception, self.test_access.read, test_id)
-            test_access = self.test_access.browse(test_id)
-            self.failUnlessRaises(Exception, getattr, test_access, 'field1')
-            test_access.field2
+            # One access disallowed for one group
+            self.field_access.delete([field_access_wo_group])
+            self.assertRaises(Exception, self.test_access.read, [test.id],
+                ['field1'])
+            self.test_access.read([test.id], ['field2'])
+            self.assertRaises(Exception, self.test_access.read, [test.id])
+            self.assertRaises(Exception, getattr, test, 'field1')
+            test.field2
             transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
 
-            # Two access rules with one group allowed
-            group_id = self.group.search([('users', '=', USER)])[0]
-            field_access_w_group_id = self.field_access.create({
-                'field': field1_id,
-                'group': group_id,
-                'perm_read': True,
-                })
+            # One access allowed for one group
+            self.field_access.write([field_access_w_group], {
+                    'perm_read': True,
+                    })
+            self.test_access.read([test.id], ['field1'])
+            self.test_access.read([test.id], ['field2'])
+            self.test_access.read([test.id])
+            test.field1
+            test.field2
+            transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
 
-            self.test_access.read(test_id, ['field1'])
-            self.test_access.read(test_id, ['field2'])
-            self.test_access.read(test_id)
-            test_access = self.test_access.browse(test_id)
-            test_access.field1
-            test_access.field2
+            # One access allowed for one other group
+            group = self.group.create({'name': 'Test'})
+            self.field_access.write([field_access_w_group], {
+                    'group': group.id,
+                    })
+            self.test_access.read([test.id], ['field1'])
+            self.test_access.read([test.id], ['field2'])
+            self.test_access.read([test.id])
+            test.field1
+            test.field2
             transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
 
-            # Two access rules with both allowed
-            self.field_access.write(field_access_wo_group_id, {
-                'perm_read': True,
-                })
-            self.test_access.read(test_id, ['field1'])
-            self.test_access.read(test_id, ['field2'])
-            self.test_access.read(test_id)
-            test_access = self.test_access.browse(test_id)
-            test_access.field1
-            test_access.field2
+            # One access disallowed for one other group
+            self.field_access.write([field_access_w_group], {
+                    'perm_read': False,
+                    })
+            self.test_access.read([test.id], ['field1'])
+            self.test_access.read([test.id], ['field2'])
+            self.test_access.read([test.id])
+            test.field1
+            test.field2
             transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
 
-            # Two access rules with any group allowed
-            self.field_access.write(field_access_w_group_id, {
+            # Two access rules on both fields allowed
+            self.field_access.delete([field_access_w_group])
+
+            field_access1 = self.field_access.create({
+                    'field': field1.id,
+                    'group': False,
+                    'perm_read': True,
+                    })
+            field_access2 = self.field_access.create({
+                    'field': field2.id,
+                    'group': False,
+                    'perm_read': True,
+                    })
+
+            self.test_access.read([test.id], ['field1'])
+            self.test_access.read([test.id], ['field2'])
+            self.test_access.read([test.id])
+            test.field1
+            test.field2
+            transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
+
+            # Two access rules on both fields one allowed and one disallowed
+            self.field_access.write([field_access2], {
                 'perm_read': False,
                 })
-            self.test_access.read(test_id, ['field1'])
-            self.test_access.read(test_id, ['field2'])
-            self.test_access.read(test_id)
-            test_access = self.test_access.browse(test_id)
-            test_access.field1
-            test_access.field2
+            self.test_access.read([test.id], ['field1'])
+            self.assertRaises(Exception, self.test_access.read, [test.id],
+                ['field2'])
+            self.assertRaises(Exception, self.test_access.read, [test.id])
+            test.field1
+            self.assertRaises(Exception, getattr, test, 'field2')
             transaction.cursor.cache.clear()
-
-            # Two access rules with both disallowed
-            self.field_access.write(field_access_wo_group_id, {
-                'perm_read': False,
-                })
-            self.failUnlessRaises(Exception, self.test_access.read, test_id,
-                    ['field1'])
-            self.test_access.read(test_id, ['field2'])
-            self.failUnlessRaises(Exception, self.test_access.read, test_id)
-            test_access = self.test_access.browse(test_id)
-            self.failUnlessRaises(Exception, getattr, test_access, 'field1')
-            test_access.field2
-            transaction.cursor.cache.clear()
-
-            # One access disallowed for one group
-            self.field_access.delete(field_access_wo_group_id)
-            self.failUnlessRaises(Exception, self.test_access.read, test_id,
-                    ['field1'])
-            self.test_access.read(test_id, ['field2'])
-            self.failUnlessRaises(Exception, self.test_access.read, test_id)
-            test_access = self.test_access.browse(test_id)
-            self.failUnlessRaises(Exception, getattr, test_access, 'field1')
-            test_access.field2
-            transaction.cursor.cache.clear()
-
-            # One access allowed for one group
-            self.field_access.write(field_access_w_group_id, {
-                'perm_read': True,
-                })
-            self.test_access.read(test_id, ['field1'])
-            self.test_access.read(test_id, ['field2'])
-            self.test_access.read(test_id)
-            test_access = self.test_access.browse(test_id)
-            test_access.field1
-            test_access.field2
-            transaction.cursor.cache.clear()
-
-            # One access allowed for one other group
-            group_id = self.group.create({'name': 'Test'})
-            self.field_access.write(field_access_w_group_id, {
-                'group': group_id,
-                })
-            self.test_access.read(test_id, ['field1'])
-            self.test_access.read(test_id, ['field2'])
-            self.test_access.read(test_id)
-            test_access = self.test_access.browse(test_id)
-            test_access.field1
-            test_access.field2
-            transaction.cursor.cache.clear()
-
-            # One access disallowed for one other group
-            self.field_access.write(field_access_w_group_id, {
-                'perm_read': False,
-                })
-            self.test_access.read(test_id, ['field1'])
-            self.test_access.read(test_id, ['field2'])
-            self.test_access.read(test_id)
-            test_access = self.test_access.browse(test_id)
-            test_access.field1
-            test_access.field2
-            transaction.cursor.cache.clear()
-
-            # Two access rules on both fields allowed
-            self.field_access.delete(field_access_w_group_id)
-
-            field_access1 = self.field_access.create({
-                'field': field1_id,
-                'group': False,
-                'perm_read': True,
-                })
-            field_access2 = self.field_access.create({
-                'field': field2_id,
-                'group': False,
-                'perm_read': True,
-                })
-
-            self.test_access.read(test_id, ['field1'])
-            self.test_access.read(test_id, ['field2'])
-            self.test_access.read(test_id)
-            test_access = self.test_access.browse(test_id)
-            test_access.field1
-            test_access.field2
-            transaction.cursor.cache.clear()
-
-            # Two access rules on both fields one allowed and one disallowed
-            self.field_access.write(field_access2, {
-                'perm_read': False,
-                })
-            self.test_access.read(test_id, ['field1'])
-            self.failUnlessRaises(Exception, self.test_access.read, test_id,
-                    ['field2'])
-            self.failUnlessRaises(Exception, self.test_access.read, test_id)
-            test_access = self.test_access.browse(test_id)
-            test_access.field1
-            self.failUnlessRaises(Exception, getattr, test_access, 'field2')
-            transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
 
             # Two access rules on both fields disallowed
-            self.field_access.write(field_access1, {
-                'perm_read': False,
-                })
-            self.failUnlessRaises(Exception, self.test_access.read, test_id,
-                    ['field1'])
-            self.failUnlessRaises(Exception, self.test_access.read, test_id,
-                    ['field2'])
-            self.failUnlessRaises(Exception, self.test_access.read, test_id)
-            test_access = self.test_access.browse(test_id)
-            self.failUnlessRaises(Exception, getattr, test_access, 'field1')
-            self.failUnlessRaises(Exception, getattr, test_access, 'field2')
+            self.field_access.write([field_access1], {
+                    'perm_read': False,
+                    })
+            self.assertRaises(Exception, self.test_access.read, [test.id],
+                ['field1'])
+            self.assertRaises(Exception, self.test_access.read, [test.id],
+                ['field2'])
+            self.assertRaises(Exception, self.test_access.read, [test.id])
+            self.assertRaises(Exception, getattr, test, 'field1')
+            self.assertRaises(Exception, getattr, test, 'field2')
             transaction.cursor.cache.clear()
+            test = self.test_access(test.id)
 
             transaction.cursor.rollback()
 
@@ -585,201 +578,201 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            field1_id, = self.field.search([
-                ('model.model', '=', 'test.access'),
-                ('name', '=', 'field1'),
-                ])
-            field2_id, = self.field.search([
-                ('model.model', '=', 'test.access'),
-                ('name', '=', 'field2'),
-                ])
+            field1, = self.field.search([
+                    ('model.model', '=', 'test.access'),
+                    ('name', '=', 'field1'),
+                    ])
+            field2, = self.field.search([
+                    ('model.model', '=', 'test.access'),
+                    ('name', '=', 'field2'),
+                    ])
 
-            test_id = self.test_access.create({
-                'field1': 'ham',
-                'field2': 'spam',
-                })
+            test = self.test_access.create({
+                    'field1': 'ham',
+                    'field2': 'spam',
+                    })
 
             # Without field access
-            self.test_access.write(test_id, {})
-            self.test_access.write(test_id, {'field1': 'ham'})
-            self.test_access.write(test_id, {'field2': 'spam'})
+            self.test_access.write([test], {})
+            self.test_access.write([test], {'field1': 'ham'})
+            self.test_access.write([test], {'field2': 'spam'})
 
             # With field access
 
             # One access allowed for any group
-            field_access_wo_group_id = self.field_access.create({
-                'field': field1_id,
-                'group': False,
-                'perm_write': True,
-                })
-            self.test_access.write(test_id, {})
-            self.test_access.write(test_id, {'field1': 'ham'})
-            self.test_access.write(test_id, {'field2': 'spam'})
-            self.test_access.write(test_id, {
-                'field1': 'ham',
-                'field2': 'spam',
-                })
+            field_access_wo_group = self.field_access.create({
+                    'field': field1.id,
+                    'group': False,
+                    'perm_write': True,
+                    })
+            self.test_access.write([test], {})
+            self.test_access.write([test], {'field1': 'ham'})
+            self.test_access.write([test], {'field2': 'spam'})
+            self.test_access.write([test], {
+                    'field1': 'ham',
+                    'field2': 'spam',
+                    })
 
             # One access disallowed for any group
-            self.field_access.write(field_access_wo_group_id, {
-                'perm_write': False,
-                })
+            self.field_access.write([field_access_wo_group], {
+                    'perm_write': False,
+                    })
 
-            self.test_access.write(test_id, {})
-            self.failUnlessRaises(Exception, self.test_access.write, test_id,
-                    {'field1': 'ham'})
-            self.test_access.write(test_id, {'field2': 'spam'})
-            self.failUnlessRaises(Exception, self.test_access.write, test_id, {
+            self.test_access.write([test], {})
+            self.assertRaises(Exception, self.test_access.write, [test],
+                {'field1': 'ham'})
+            self.test_access.write([test], {'field2': 'spam'})
+            self.assertRaises(Exception, self.test_access.write, [test], {
                     'field1': 'ham',
                     'field2': 'spam',
                     })
 
             # Two access rules with one group allowed
-            group_id = self.group.search([('users', '=', USER)])[0]
-            field_access_w_group_id = self.field_access.create({
-                'field': field1_id,
-                'group': group_id,
-                'perm_write': True,
-                })
+            group = self.group.search([('users', '=', USER)])[0]
+            field_access_w_group = self.field_access.create({
+                    'field': field1.id,
+                    'group': group.id,
+                    'perm_write': True,
+                    })
 
-            self.test_access.write(test_id, {})
-            self.test_access.write(test_id, {'field1': 'ham'})
-            self.test_access.write(test_id, {'field2': 'spam'})
-            self.test_access.write(test_id, {
-                'field1': 'ham',
-                'field2': 'spam',
-                })
+            self.test_access.write([test], {})
+            self.test_access.write([test], {'field1': 'ham'})
+            self.test_access.write([test], {'field2': 'spam'})
+            self.test_access.write([test], {
+                    'field1': 'ham',
+                    'field2': 'spam',
+                    })
 
             # Two access rules with both allowed
-            self.field_access.write(field_access_wo_group_id, {
-                'perm_write': True,
-                })
-            self.test_access.write(test_id, {})
-            self.test_access.write(test_id, {'field1': 'ham'})
-            self.test_access.write(test_id, {'field2': 'spam'})
-            self.test_access.write(test_id, {
-                'field1': 'ham',
-                'field2': 'spam',
-                })
+            self.field_access.write([field_access_wo_group], {
+                    'perm_write': True,
+                    })
+            self.test_access.write([test], {})
+            self.test_access.write([test], {'field1': 'ham'})
+            self.test_access.write([test], {'field2': 'spam'})
+            self.test_access.write([test], {
+                    'field1': 'ham',
+                    'field2': 'spam',
+                    })
 
             # Two access rules with any group allowed
-            self.field_access.write(field_access_w_group_id, {
-                'perm_write': False,
-                })
-            self.test_access.write(test_id, {})
-            self.test_access.write(test_id, {'field1': 'ham'})
-            self.test_access.write(test_id, {'field2': 'spam'})
-            self.test_access.write(test_id, {
-                'field1': 'ham',
-                'field2': 'spam',
-                })
+            self.field_access.write([field_access_w_group], {
+                    'perm_write': False,
+                    })
+            self.test_access.write([test], {})
+            self.test_access.write([test], {'field1': 'ham'})
+            self.test_access.write([test], {'field2': 'spam'})
+            self.test_access.write([test], {
+                    'field1': 'ham',
+                    'field2': 'spam',
+                    })
 
             # Two access rules with both disallowed
-            self.field_access.write(field_access_wo_group_id, {
-                'perm_write': False,
-                })
-            self.test_access.write(test_id, {})
-            self.failUnlessRaises(Exception, self.test_access.write, test_id,
-                    {'field1': 'ham'})
-            self.test_access.write(test_id, {'field2': 'spam'})
-            self.failUnlessRaises(Exception, self.test_access.write, test_id, {
+            self.field_access.write([field_access_wo_group], {
+                    'perm_write': False,
+                    })
+            self.test_access.write([test], {})
+            self.assertRaises(Exception, self.test_access.write, [test],
+                {'field1': 'ham'})
+            self.test_access.write([test], {'field2': 'spam'})
+            self.assertRaises(Exception, self.test_access.write, [test], {
                     'field1': 'ham',
                     'field2': 'spam',
                     })
 
             # One access disallowed for one group
-            self.field_access.delete(field_access_wo_group_id)
-            self.test_access.write(test_id, {})
-            self.failUnlessRaises(Exception, self.test_access.write, test_id,
-                    {'field1': 'ham'})
-            self.test_access.write(test_id, {'field2': 'ham'})
-            self.failUnlessRaises(Exception, self.test_access.write, test_id, {
+            self.field_access.delete([field_access_wo_group])
+            self.test_access.write([test], {})
+            self.assertRaises(Exception, self.test_access.write, [test],
+                {'field1': 'ham'})
+            self.test_access.write([test], {'field2': 'ham'})
+            self.assertRaises(Exception, self.test_access.write, [test], {
                     'field1': 'ham',
                     'field2': 'spam',
                     })
 
             # One access allowed for one group
-            self.field_access.write(field_access_w_group_id, {
-                'perm_write': True,
-                })
-            self.test_access.write(test_id, {})
-            self.test_access.write(test_id, {'field1': 'ham'})
-            self.test_access.write(test_id, {'field2': 'spam'})
-            self.test_access.write(test_id, {
-                'field1': 'ham',
-                'field2': 'spam',
-                })
+            self.field_access.write([field_access_w_group], {
+                    'perm_write': True,
+                    })
+            self.test_access.write([test], {})
+            self.test_access.write([test], {'field1': 'ham'})
+            self.test_access.write([test], {'field2': 'spam'})
+            self.test_access.write([test], {
+                    'field1': 'ham',
+                    'field2': 'spam',
+                    })
 
             # One access allowed for one other group
-            group_id = self.group.create({'name': 'Test'})
-            self.field_access.write(field_access_w_group_id, {
-                'group': group_id,
-                })
-            self.test_access.write(test_id, {})
-            self.test_access.write(test_id, {'field1': 'ham'})
-            self.test_access.write(test_id, {'field2': 'spam'})
-            self.test_access.write(test_id, {
-                'field1': 'ham',
-                'field2': 'spam',
-                })
+            group = self.group.create({'name': 'Test'})
+            self.field_access.write([field_access_w_group], {
+                    'group': group.id,
+                    })
+            self.test_access.write([test], {})
+            self.test_access.write([test], {'field1': 'ham'})
+            self.test_access.write([test], {'field2': 'spam'})
+            self.test_access.write([test], {
+                    'field1': 'ham',
+                    'field2': 'spam',
+                    })
 
             # One access disallowed for one other group
-            self.field_access.write(field_access_w_group_id, {
-                'perm_write': False,
-                })
-            self.test_access.write(test_id, {})
-            self.test_access.write(test_id, {'field1': 'ham'})
-            self.test_access.write(test_id, {'field2': 'spam'})
-            self.test_access.write(test_id, {
-                'field1': 'ham',
-                'field2': 'spam',
-                })
+            self.field_access.write([field_access_w_group], {
+                    'perm_write': False,
+                    })
+            self.test_access.write([test], {})
+            self.test_access.write([test], {'field1': 'ham'})
+            self.test_access.write([test], {'field2': 'spam'})
+            self.test_access.write([test], {
+                    'field1': 'ham',
+                    'field2': 'spam',
+                    })
 
             # Two access rules on both fields allowed
-            self.field_access.delete(field_access_w_group_id)
+            self.field_access.delete([field_access_w_group])
 
             field_access1 = self.field_access.create({
-                'field': field1_id,
-                'group': False,
-                'perm_write': True,
-                })
+                    'field': field1.id,
+                    'group': False,
+                    'perm_write': True,
+                    })
             field_access2 = self.field_access.create({
-                'field': field2_id,
-                'group': False,
-                'perm_write': True,
-                })
+                    'field': field2.id,
+                    'group': False,
+                    'perm_write': True,
+                    })
 
-            self.test_access.write(test_id, {})
-            self.test_access.write(test_id, {'field1': 'ham'})
-            self.test_access.write(test_id, {'field2': 'spam'})
-            self.test_access.write(test_id, {
-                'field1': 'ham',
-                'field2': 'spam',
-                })
+            self.test_access.write([test], {})
+            self.test_access.write([test], {'field1': 'ham'})
+            self.test_access.write([test], {'field2': 'spam'})
+            self.test_access.write([test], {
+                    'field1': 'ham',
+                    'field2': 'spam',
+                    })
 
             # Two access rules on both fields one allowed and one disallowed
-            self.field_access.write(field_access2, {
-                'perm_write': False,
-                })
-            self.test_access.write(test_id, {})
-            self.test_access.write(test_id, {'field1': 'ham'})
-            self.failUnlessRaises(Exception, self.test_access.write, test_id,
-                    {'field2': 'spam'})
-            self.failUnlessRaises(Exception, self.test_access.write, test_id, {
+            self.field_access.write([field_access2], {
+                    'perm_write': False,
+                    })
+            self.test_access.write([test], {})
+            self.test_access.write([test], {'field1': 'ham'})
+            self.assertRaises(Exception, self.test_access.write, [test], {
+                    'field2': 'spam'})
+            self.assertRaises(Exception, self.test_access.write, [test], {
                     'field1': 'ham',
                     'field2': 'spam',
                     })
 
             # Two access rules on both fields disallowed
-            self.field_access.write(field_access1, {
-                'perm_write': False,
-                })
-            self.test_access.write(test_id, {})
-            self.failUnlessRaises(Exception, self.test_access.write, test_id,
-                    {'field1': 'ham'})
-            self.failUnlessRaises(Exception, self.test_access.write, test_id,
-                    {'field2': 'spam'})
-            self.failUnlessRaises(Exception, self.test_access.write, test_id, {
+            self.field_access.write([field_access1], {
+                    'perm_write': False,
+                    })
+            self.test_access.write([test], {})
+            self.assertRaises(Exception, self.test_access.write, [test], {
+                    'field1': 'ham'})
+            self.assertRaises(Exception, self.test_access.write, [test], {
+                    'field2': 'spam'})
+            self.assertRaises(Exception, self.test_access.write, [test], {
                     'field1': 'ham',
                     'field2': 'spam',
                     })
diff -r f6ef988fda71 trytond/tests/test_exportdata.py
--- a/trytond/tests/test_exportdata.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/tests/test_exportdata.py	Sun Sep 23 17:30:03 2012 +0200
@@ -35,20 +35,23 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            export1_id = self.export_data.create({
-                'boolean': True,
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['boolean']) == [[True]])
+            export1 = self.export_data.create({
+                    'boolean': True,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['boolean']), [[True]])
 
-            export2_id = self.export_data.create({
-                'boolean': False,
-                })
-            self.assert_(self.export_data.export_data([export2_id],
-                ['boolean']) == [[False]])
+            export2 = self.export_data.create({
+                    'boolean': False,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export2], ['boolean']),
+                [[False]])
 
-            self.assert_(self.export_data.export_data([export1_id,
-                export2_id], ['boolean']) == [[True], [False]])
+            self.assertEqual(
+                self.export_data.export_data([export1, export2],
+                    ['boolean']),
+                [[True], [False]])
 
             transaction.cursor.rollback()
 
@@ -58,20 +61,21 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            export1_id = self.export_data.create({
-                'integer': 2,
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['integer']) == [[2]])
+            export1 = self.export_data.create({
+                    'integer': 2,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['integer']), [[2]])
 
-            export2_id = self.export_data.create({
-                'integer': 0,
-                })
-            self.assert_(self.export_data.export_data([export2_id],
-                ['integer']) == [[0]])
+            export2 = self.export_data.create({
+                    'integer': 0,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export2], ['integer']), [[0]])
 
-            self.assert_(self.export_data.export_data([export1_id, export2_id],
-                ['integer']) == [[2], [0]])
+            self.assertEqual(
+                self.export_data.export_data([export1, export2], ['integer']),
+                [[2], [0]])
 
             transaction.cursor.rollback()
 
@@ -81,20 +85,21 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            export1_id = self.export_data.create({
-                'float': 1.1,
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['float']) == [[1.1]])
+            export1 = self.export_data.create({
+                    'float': 1.1,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['float']), [[1.1]])
 
-            export2_id = self.export_data.create({
-                'float': 0,
-                })
-            self.assert_(self.export_data.export_data([export2_id],
-                ['float']) == [[0]])
+            export2 = self.export_data.create({
+                    'float': 0,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export2], ['float']), [[0]])
 
-            self.assert_(self.export_data.export_data([export1_id, export2_id],
-                ['float']) == [[1.1], [0]])
+            self.assertEqual(
+                self.export_data.export_data([export1, export2], ['float']),
+                [[1.1], [0]])
 
             transaction.cursor.rollback()
 
@@ -104,20 +109,23 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            export1_id = self.export_data.create({
-                'numeric': Decimal('1.1'),
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['numeric']) == [[Decimal('1.1')]])
+            export1 = self.export_data.create({
+                    'numeric': Decimal('1.1'),
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['numeric']),
+                [[Decimal('1.1')]])
 
-            export2_id = self.export_data.create({
-                'numeric': Decimal('0'),
-                })
-            self.assert_(self.export_data.export_data([export2_id],
-                ['numeric']) == [[Decimal('0')]])
+            export2 = self.export_data.create({
+                    'numeric': Decimal('0'),
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export2], ['numeric']),
+                [[Decimal('0')]])
 
-            self.assert_(self.export_data.export_data([export1_id, export2_id],
-                ['numeric']) == [[Decimal('1.1')], [Decimal('0')]])
+            self.assertEqual(
+                self.export_data.export_data([export1, export2], ['numeric']),
+                [[Decimal('1.1')], [Decimal('0')]])
 
             transaction.cursor.rollback()
 
@@ -127,20 +135,21 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            export1_id = self.export_data.create({
-                'char': 'test',
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['char']) == [['test']])
+            export1 = self.export_data.create({
+                    'char': 'test',
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['char']), [['test']])
 
-            export2_id = self.export_data.create({
-                'char': None,
-                })
-            self.assert_(self.export_data.export_data([export2_id],
-                ['char']) == [['']])
+            export2 = self.export_data.create({
+                    'char': None,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export2], ['char']), [['']])
 
-            self.assert_(self.export_data.export_data([export1_id, export2_id],
-                ['char']) == [['test'], ['']])
+            self.assertEqual(
+                self.export_data.export_data([export1, export2], ['char']),
+                [['test'], ['']])
 
             transaction.cursor.rollback()
 
@@ -150,20 +159,21 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            export1_id = self.export_data.create({
-                'text': 'test',
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['text']) == [['test']])
+            export1 = self.export_data.create({
+                    'text': 'test',
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['text']), [['test']])
 
-            export2_id = self.export_data.create({
-                'text': None,
-                })
-            self.assert_(self.export_data.export_data([export2_id],
-                ['text']) == [['']])
+            export2 = self.export_data.create({
+                    'text': None,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export2], ['text']), [['']])
 
-            self.assert_(self.export_data.export_data([export1_id, export2_id],
-                ['text']) == [['test'], ['']])
+            self.assertEqual(
+                self.export_data.export_data([export1, export2], ['text']),
+                [['test'], ['']])
 
             transaction.cursor.rollback()
 
@@ -173,11 +183,12 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            export1_id = self.export_data.create({
-                'sha': 'Test',
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['sha']) == [['640ab2bae07bedc4c163f679a746f7ab7fb5d1fa']])
+            export1 = self.export_data.create({
+                    'sha': 'Test',
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['sha']),
+                [['640ab2bae07bedc4c163f679a746f7ab7fb5d1fa']])
 
             transaction.cursor.rollback()
 
@@ -187,20 +198,21 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            export1_id = self.export_data.create({
+            export1 = self.export_data.create({
                 'date': datetime.date(2010, 1, 1),
                 })
-            self.assert_(self.export_data.export_data([export1_id],
+            self.assert_(self.export_data.export_data([export1],
                 ['date']) == [[datetime.date(2010, 1, 1)]])
 
-            export2_id = self.export_data.create({
-                'date': None,
-                })
-            self.assert_(self.export_data.export_data([export2_id],
-                ['date']) == [['']])
+            export2 = self.export_data.create({
+                    'date': None,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export2], ['date']), [['']])
 
-            self.assert_(self.export_data.export_data([export1_id, export2_id],
-                ['date']) == [[datetime.date(2010, 1, 1)], ['']])
+            self.assertEqual(
+                self.export_data.export_data([export1, export2], ['date']),
+                [[datetime.date(2010, 1, 1)], ['']])
 
             transaction.cursor.rollback()
 
@@ -210,21 +222,23 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            export1_id = self.export_data.create({
-                'datetime': datetime.datetime(2010, 1, 1, 12, 0, 0),
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['datetime']) == [[datetime.datetime(2010, 1, 1, 12, 0, 0)]])
+            export1 = self.export_data.create({
+                    'datetime': datetime.datetime(2010, 1, 1, 12, 0, 0),
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['datetime']),
+                [[datetime.datetime(2010, 1, 1, 12, 0, 0)]])
 
-            export2_id = self.export_data.create({
-                'datetime': None,
-                })
-            self.assert_(self.export_data.export_data([export2_id],
-                ['datetime']) == [['']])
+            export2 = self.export_data.create({
+                    'datetime': None,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export2], ['datetime']),
+                [['']])
 
-            self.assert_(self.export_data.export_data([export1_id, export2_id],
-                ['datetime']) == [[datetime.datetime(2010, 1, 1, 12, 0, 0)],
-                    ['']])
+            self.assertEqual(
+                self.export_data.export_data([export1, export2], ['datetime']),
+                [[datetime.datetime(2010, 1, 1, 12, 0, 0)], ['']])
 
             transaction.cursor.rollback()
 
@@ -234,20 +248,23 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            export1_id = self.export_data.create({
-                'selection': 'select1',
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['selection']) == [['select1']])
+            export1 = self.export_data.create({
+                    'selection': 'select1',
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['selection']),
+                [['select1']])
 
-            export2_id = self.export_data.create({
-                'selection': None,
-                })
-            self.assert_(self.export_data.export_data([export2_id],
-                ['selection']) == [['']])
+            export2 = self.export_data.create({
+                    'selection': None,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export2], ['selection']), [['']])
 
-            self.assert_(self.export_data.export_data([export1_id, export2_id],
-                ['selection']) == [['select1'], ['']])
+            self.assertEqual(
+                self.export_data.export_data([export1, export2],
+                    ['selection']),
+                [['select1'], ['']])
 
             transaction.cursor.rollback()
 
@@ -257,23 +274,27 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            target_id = self.export_data_target.create({
-                'name': 'Target Test',
-                })
-            export1_id = self.export_data.create({
-                'many2one': target_id,
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['many2one/name']) == [['Target Test']])
+            target = self.export_data_target.create({
+                    'name': 'Target Test',
+                    })
+            export1 = self.export_data.create({
+                    'many2one': target.id,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['many2one/name']),
+                [['Target Test']])
 
-            export2_id = self.export_data.create({
-                'many2one': None,
-                })
-            self.assert_(self.export_data.export_data([export2_id],
-                ['many2one/name']) == [['']])
+            export2 = self.export_data.create({
+                    'many2one': None,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export2], ['many2one/name']),
+                [['']])
 
-            self.assert_(self.export_data.export_data([export1_id, export2_id],
-                ['many2one/name']) == [['Target Test'], ['']])
+            self.assertEqual(
+                self.export_data.export_data([export1, export2],
+                    ['many2one/name']),
+                [['Target Test'], ['']])
 
             transaction.cursor.rollback()
 
@@ -283,34 +304,38 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            target1_id = self.export_data_target.create({
-                'name': 'Target 1',
-                })
-            export1_id = self.export_data.create({
-                'many2many': [('set', [target1_id])],
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['many2many/name']) == [['Target 1']])
+            target1 = self.export_data_target.create({
+                    'name': 'Target 1',
+                    })
+            export1 = self.export_data.create({
+                    'many2many': [('set', [target1])],
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['many2many/name']),
+                [['Target 1']])
 
-            target2_id = self.export_data_target.create({
-                'name': 'Target 2',
-                })
-            self.export_data.write(export1_id, {
-                'many2many': [('set', [target1_id, target2_id])],
-                })
-            self.assert_(self.export_data.export_data([export1_id], ['id',
-                'many2many/name']) == [[export1_id, 'Target 1'],
-                ['', 'Target 2']])
+            target2 = self.export_data_target.create({
+                    'name': 'Target 2',
+                    })
+            self.export_data.write([export1], {
+                    'many2many': [('set', [target1.id, target2.id])],
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['id',
+                        'many2many/name']),
+                [[export1.id, 'Target 1'], ['', 'Target 2']])
 
-            export2_id = self.export_data.create({
-                'many2many': None,
-                })
-            self.assert_(self.export_data.export_data([export2_id],
-                ['many2many/name']) == [['']])
+            export2 = self.export_data.create({
+                    'many2many': None,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export2], ['many2many/name']),
+                [['']])
 
-            self.assert_(self.export_data.export_data([export1_id, export2_id],
-                ['id', 'many2many/name']) == [[export1_id, 'Target 1'],
-                    ['', 'Target 2'], [export2_id, '']])
+            self.assertEqual(
+                self.export_data.export_data([export1, export2],
+                    ['id', 'many2many/name']),
+                [[export1.id, 'Target 1'], ['', 'Target 2'], [export2.id, '']])
 
             transaction.cursor.rollback()
 
@@ -320,29 +345,33 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            export1_id = self.export_data.create({})
+            export1 = self.export_data.create({})
             self.export_data_target.create({
-                'name': 'Target 1',
-                'one2many': export1_id,
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['one2many/name']) == [['Target 1']])
+                    'name': 'Target 1',
+                    'one2many': export1.id,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['one2many/name']),
+                [['Target 1']])
 
             self.export_data_target.create({
-                'name': 'Target 2',
-                'one2many': export1_id,
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['id', 'one2many/name']) ==
-                [[export1_id, 'Target 1'], ['', 'Target 2']])
+                    'name': 'Target 2',
+                    'one2many': export1.id,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1],
+                    ['id', 'one2many/name']),
+                [[export1.id, 'Target 1'], ['', 'Target 2']])
 
-            export2_id = self.export_data.create({})
-            self.assert_(self.export_data.export_data([export2_id],
-                ['one2many/name']) == [['']])
+            export2 = self.export_data.create({})
+            self.assertEqual(
+                self.export_data.export_data([export2], ['one2many/name']),
+                [['']])
 
-            self.assert_(self.export_data.export_data([export1_id, export2_id],
-                ['id', 'one2many/name']) == [[export1_id, 'Target 1'],
-                    ['', 'Target 2'], [export2_id, '']])
+            self.assertEqual(
+                self.export_data.export_data([export1, export2], ['id',
+                        'one2many/name']),
+                [[export1.id, 'Target 1'], ['', 'Target 2'], [export2.id, '']])
 
             transaction.cursor.rollback()
 
@@ -352,23 +381,24 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            target1_id = self.export_data_target.create({})
-            export1_id = self.export_data.create({
-                'reference': 'test.export_data.target,%s' % target1_id,
-                })
-            self.assert_(self.export_data.export_data([export1_id],
-                ['reference']) ==
-                [['test.export_data.target,%s' % target1_id]])
+            target1 = self.export_data_target.create({})
+            export1 = self.export_data.create({
+                    'reference': str(target1),
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export1], ['reference']),
+                [[str(target1)]])
 
-            export2_id = self.export_data.create({
-                'reference': None,
-                })
-            self.assert_(self.export_data.export_data([export2_id],
-                ['reference']) == [['']])
+            export2 = self.export_data.create({
+                    'reference': None,
+                    })
+            self.assertEqual(
+                self.export_data.export_data([export2], ['reference']), [['']])
 
-            self.assert_(self.export_data.export_data([export1_id, export2_id],
-                ['reference']) == [['test.export_data.target,%s' % target1_id],
-                    ['']])
+            self.assertEqual(
+                self.export_data.export_data([export1, export2],
+                    ['reference']),
+                [[str(target1)], ['']])
 
             transaction.cursor.rollback()
 
diff -r f6ef988fda71 trytond/tests/test_fields.py
--- a/trytond/tests/test_fields.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/tests/test_fields.py	Sun Sep 23 17:30:03 2012 +0200
@@ -99,6 +99,8 @@
         self.reference_required = POOL.get('test.reference_required')
 
         self.property_ = POOL.get('test.property')
+        self.ir_property = POOL.get('ir.property')
+        self.model_field = POOL.get('ir.model.field')
 
     def test0010boolean(self):
         '''
@@ -106,107 +108,97 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            boolean1_id = self.boolean.create({
-                'boolean': True,
-                })
-            self.assert_(boolean1_id)
-
-            boolean1 = self.boolean.read(boolean1_id, ['boolean'])
-            self.assert_(boolean1['boolean'] == True)
-
-            boolean_ids = self.boolean.search([
-                ('boolean', '=', True),
-                ])
-            self.assert_(boolean_ids == [boolean1_id])
-
-            boolean_ids = self.boolean.search([
-                ('boolean', '!=', True),
-                ])
-            self.assert_(boolean_ids == [])
-
-            boolean_ids = self.boolean.search([
-                ('boolean', 'in', [True]),
-                ])
-            self.assert_(boolean_ids == [boolean1_id])
-
-            boolean_ids = self.boolean.search([
-                ('boolean', 'in', [False]),
-                ])
-            self.assert_(boolean_ids == [])
-
-            boolean_ids = self.boolean.search([
-                ('boolean', 'not in', [True]),
-                ])
-            self.assert_(boolean_ids == [])
-
-            boolean_ids = self.boolean.search([
-                ('boolean', 'not in', [False]),
-                ])
-            self.assert_(boolean_ids == [boolean1_id])
-
-            boolean2_id = self.boolean.create({
-                'boolean': False,
-                })
-            self.assert_(boolean2_id)
-
-            boolean2 = self.boolean.read(boolean2_id, ['boolean'])
-            self.assert_(boolean2['boolean'] == False)
-
-            boolean_ids = self.boolean.search([
-                ('boolean', '=', False),
-                ])
-            self.assert_(boolean_ids == [boolean2_id])
-
-            boolean_ids = self.boolean.search([
-                ('boolean', 'in', [True, False]),
-                ])
-            self.assert_(boolean_ids == [boolean1_id, boolean2_id])
-
-            boolean_ids = self.boolean.search([
-                ('boolean', 'not in', [True, False]),
-                ])
-            self.assert_(boolean_ids == [])
-
-            boolean3_id = self.boolean.create({})
-            self.assert_(boolean3_id)
+            boolean1 = self.boolean.create({
+                    'boolean': True,
+                    })
+            self.assert_(boolean1)
+            self.assertEqual(boolean1.boolean, True)
+
+            booleans = self.boolean.search([
+                    ('boolean', '=', True),
+                    ])
+            self.assertEqual(booleans, [boolean1])
+
+            booleans = self.boolean.search([
+                    ('boolean', '!=', True),
+                    ])
+            self.assertEqual(booleans, [])
+
+            booleans = self.boolean.search([
+                    ('boolean', 'in', [True]),
+                    ])
+            self.assertEqual(booleans, [boolean1])
+
+            booleans = self.boolean.search([
+                    ('boolean', 'in', [False]),
+                    ])
+            self.assertEqual(booleans, [])
+
+            booleans = self.boolean.search([
+                    ('boolean', 'not in', [True]),
+                    ])
+            self.assertEqual(booleans, [])
+
+            booleans = self.boolean.search([
+                    ('boolean', 'not in', [False]),
+                    ])
+            self.assertEqual(booleans, [boolean1])
+
+            boolean2 = self.boolean.create({
+                    'boolean': False,
+                    })
+            self.assert_(boolean2)
+            self.assertEqual(boolean2.boolean, False)
+
+            booleans = self.boolean.search([
+                    ('boolean', '=', False),
+                    ])
+            self.assertEqual(booleans, [boolean2])
+
+            booleans = self.boolean.search([
+                    ('boolean', 'in', [True, False]),
+                    ])
+            self.assertEqual(booleans, [boolean1, boolean2])
+
+            booleans = self.boolean.search([
+                    ('boolean', 'not in', [True, False]),
+                    ])
+            self.assertEqual(booleans, [])
+
+            boolean3 = self.boolean.create({})
+            self.assert_(boolean3)
+            self.assertEqual(boolean3.boolean, False)
 
             # Test search with NULL value
-            boolean4_id = self.boolean.create({
+            boolean4 = self.boolean.create({
                     'boolean': None,
                     })
-            self.assert_(boolean4_id)
-
-            boolean_ids = self.boolean.search([
+            self.assert_(boolean4)
+
+            booleans = self.boolean.search([
                     ('boolean', '=', False),
                     ])
-            self.assertEqual(boolean_ids,
-                [boolean2_id, boolean3_id, boolean4_id])
-
-            boolean_ids = self.boolean.search([
+            self.assertEqual(booleans,
+                [boolean2, boolean3, boolean4])
+
+            booleans = self.boolean.search([
                     ('boolean', '!=', False),
                     ])
-            self.assertEqual(boolean_ids, [boolean1_id])
-
-            boolean3 = self.boolean.read(boolean3_id, ['boolean'])
-            self.assert_(boolean3['boolean'] == False)
-
-            boolean4_id = self.boolean_default.create({})
-            self.assert_(boolean4_id)
-
-            boolean4 = self.boolean_default.read(boolean4_id, ['boolean'])
-            self.assert_(boolean4['boolean'] == True)
-
-            self.boolean.write(boolean1_id, {
-                'boolean': False,
-                })
-            boolean1 = self.boolean.read(boolean1_id, ['boolean'])
-            self.assert_(boolean1['boolean'] == False)
-
-            self.boolean.write(boolean2_id, {
-                'boolean': True,
-                })
-            boolean2 = self.boolean.read(boolean2_id, ['boolean'])
-            self.assert_(boolean2['boolean'] == True)
+            self.assertEqual(booleans, [boolean1])
+
+            boolean4 = self.boolean_default.create({})
+            self.assert_(boolean4)
+            self.assert_(boolean4.boolean == True)
+
+            self.boolean.write([boolean1], {
+                    'boolean': False,
+                    })
+            self.assertEqual(boolean1.boolean, False)
+
+            self.boolean.write([boolean2], {
+                    'boolean': True,
+                    })
+            self.assertEqual(boolean2.boolean, True)
 
             transaction.cursor.rollback()
 
@@ -216,191 +208,179 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            integer1_id = self.integer.create({
-                'integer': 1,
-                })
-            self.assert_(integer1_id)
-
-            integer1 = self.integer.read(integer1_id, ['integer'])
-            self.assert_(integer1['integer'] == 1)
-
-            integer_ids = self.integer.search([
-                ('integer', '=', 1),
-                ])
-            self.assert_(integer_ids == [integer1_id])
-
-            integer_ids = self.integer.search([
-                ('integer', '=', 0),
-                ])
-            self.assert_(integer_ids == [])
-
-            integer_ids = self.integer.search([
-                ('integer', '!=', 1),
-                ])
-            self.assert_(integer_ids == [])
-
-            integer_ids = self.integer.search([
-                ('integer', '!=', 0),
-                ])
-            self.assert_(integer_ids == [integer1_id])
-
-            integer_ids = self.integer.search([
-                ('integer', 'in', [1]),
-                ])
-            self.assert_(integer_ids == [integer1_id])
-
-            integer_ids = self.integer.search([
-                ('integer', 'in', [0]),
-                ])
-            self.assert_(integer_ids == [])
-
-            integer_ids = self.integer.search([
-                ('integer', 'in', []),
-                ])
-            self.assert_(integer_ids == [])
-
-            integer_ids = self.integer.search([
-                ('integer', 'not in', [1]),
-                ])
-            self.assert_(integer_ids == [])
-
-            integer_ids = self.integer.search([
-                ('integer', 'not in', [0]),
-                ])
-            self.assert_(integer_ids == [integer1_id])
-
-            integer_ids = self.integer.search([
-                ('integer', 'not in', []),
-                ])
-            self.assert_(integer_ids == [integer1_id])
-
-            integer_ids = self.integer.search([
-                ('integer', '<', 5),
-                ])
-            self.assert_(integer_ids == [integer1_id])
-
-            integer_ids = self.integer.search([
-                ('integer', '<', -5),
-                ])
-            self.assert_(integer_ids == [])
-
-            integer_ids = self.integer.search([
-                ('integer', '<', 1),
-                ])
-            self.assert_(integer_ids == [])
-
-            integer_ids = self.integer.search([
-                ('integer', '<=', 5),
-                ])
-            self.assert_(integer_ids == [integer1_id])
-
-            integer_ids = self.integer.search([
-                ('integer', '<=', -5),
-                ])
-            self.assert_(integer_ids == [])
-
-            integer_ids = self.integer.search([
-                ('integer', '<=', 1),
-                ])
-            self.assert_(integer_ids == [integer1_id])
-
-            integer_ids = self.integer.search([
-                ('integer', '>', 5),
-                ])
-            self.assert_(integer_ids == [])
-
-            integer_ids = self.integer.search([
-                ('integer', '>', -5),
-                ])
-            self.assert_(integer_ids == [integer1_id])
-
-            integer_ids = self.integer.search([
-                ('integer', '>', 1),
-                ])
-            self.assert_(integer_ids == [])
-
-            integer_ids = self.integer.search([
-                ('integer', '>=', 5),
-                ])
-            self.assert_(integer_ids == [])
-
-            integer_ids = self.integer.search([
-                ('integer', '>=', -5),
-                ])
-            self.assert_(integer_ids == [integer1_id])
-
-            integer_ids = self.integer.search([
-                ('integer', '>=', 1),
-                ])
-            self.assert_(integer_ids == [integer1_id])
-
-            integer2_id = self.integer.create({
-                'integer': 0,
-                })
-            self.assert_(integer2_id)
-
-            integer2 = self.integer.read(integer2_id, ['integer'])
-            self.assert_(integer2['integer'] == 0)
-
-            integer_ids = self.integer.search([
-                ('integer', '=', 0),
-                ])
-            self.assert_(integer_ids == [integer2_id])
-
-            integer_ids = self.integer.search([
-                ('integer', 'in', [0, 1]),
-                ])
-            self.assert_(integer_ids == [integer1_id, integer2_id])
-
-            integer_ids = self.integer.search([
-                ('integer', 'not in', [0, 1]),
-                ])
-            self.assert_(integer_ids == [])
-
-            integer3_id = self.integer.create({})
-            self.assert_(integer3_id)
-
-            integer3 = self.integer.read(integer3_id, ['integer'])
-            self.assert_(integer3['integer'] is None)
-
-            integer4_id = self.integer_default.create({})
-            self.assert_(integer4_id)
-
-            integer4 = self.integer_default.read(integer4_id, ['integer'])
-            self.assert_(integer4['integer'] == 5)
-
-            self.integer.write(integer1_id, {
-                'integer': 0,
-                })
-            integer1 = self.integer.read(integer1_id, ['integer'])
-            self.assert_(integer1['integer'] == 0)
-
-            self.integer.write(integer2_id, {
-                'integer': 1,
-                })
-            integer2 = self.integer.read(integer2_id, ['integer'])
-            self.assert_(integer2['integer'] == 1)
-
-            self.failUnlessRaises(Exception, self.integer.create, {
-                'integer': 'test',
-                })
-
-            self.failUnlessRaises(Exception, self.integer.write, integer1_id, {
-                'integer': 'test',
-                })
+            integer1 = self.integer.create({
+                    'integer': 1,
+                    })
+            self.assert_(integer1)
+            self.assertEqual(integer1.integer, 1)
+
+            integers = self.integer.search([
+                    ('integer', '=', 1),
+                    ])
+            self.assertEqual(integers, [integer1])
+
+            integers = self.integer.search([
+                    ('integer', '=', 0),
+                    ])
+            self.assertEqual(integers, [])
+
+            integers = self.integer.search([
+                    ('integer', '!=', 1),
+                    ])
+            self.assertEqual(integers, [])
+
+            integers = self.integer.search([
+                    ('integer', '!=', 0),
+                    ])
+            self.assertEqual(integers, [integer1])
+
+            integers = self.integer.search([
+                    ('integer', 'in', [1]),
+                    ])
+            self.assertEqual(integers, [integer1])
+
+            integers = self.integer.search([
+                    ('integer', 'in', [0]),
+                    ])
+            self.assertEqual(integers, [])
+
+            integers = self.integer.search([
+                    ('integer', 'in', []),
+                    ])
+            self.assertEqual(integers, [])
+
+            integers = self.integer.search([
+                    ('integer', 'not in', [1]),
+                    ])
+            self.assertEqual(integers, [])
+
+            integers = self.integer.search([
+                    ('integer', 'not in', [0]),
+                    ])
+            self.assertEqual(integers, [integer1])
+
+            integers = self.integer.search([
+                    ('integer', 'not in', []),
+                    ])
+            self.assertEqual(integers, [integer1])
+
+            integers = self.integer.search([
+                    ('integer', '<', 5),
+                    ])
+            self.assertEqual(integers, [integer1])
+
+            integers = self.integer.search([
+                    ('integer', '<', -5),
+                    ])
+            self.assertEqual(integers, [])
+
+            integers = self.integer.search([
+                    ('integer', '<', 1),
+                    ])
+            self.assertEqual(integers, [])
+
+            integers = self.integer.search([
+                    ('integer', '<=', 5),
+                    ])
+            self.assertEqual(integers, [integer1])
+
+            integers = self.integer.search([
+                    ('integer', '<=', -5),
+                    ])
+            self.assertEqual(integers, [])
+
+            integers = self.integer.search([
+                    ('integer', '<=', 1),
+                    ])
+            self.assertEqual(integers, [integer1])
+
+            integers = self.integer.search([
+                    ('integer', '>', 5),
+                    ])
+            self.assertEqual(integers, [])
+
+            integers = self.integer.search([
+                    ('integer', '>', -5),
+                    ])
+            self.assertEqual(integers, [integer1])
+
+            integers = self.integer.search([
+                    ('integer', '>', 1),
+                    ])
+            self.assertEqual(integers, [])
+
+            integers = self.integer.search([
+                    ('integer', '>=', 5),
+                    ])
+            self.assertEqual(integers, [])
+
+            integers = self.integer.search([
+                    ('integer', '>=', -5),
+                    ])
+            self.assertEqual(integers, [integer1])
+
+            integers = self.integer.search([
+                    ('integer', '>=', 1),
+                    ])
+            self.assertEqual(integers, [integer1])
+
+            integer2 = self.integer.create({
+                    'integer': 0,
+                    })
+            self.assert_(integer2)
+            self.assertEqual(integer2.integer, 0)
+
+            integers = self.integer.search([
+                    ('integer', '=', 0),
+                    ])
+            self.assertEqual(integers, [integer2])
+
+            integers = self.integer.search([
+                    ('integer', 'in', [0, 1]),
+                    ])
+            self.assertEqual(integers, [integer1, integer2])
+
+            integers = self.integer.search([
+                    ('integer', 'not in', [0, 1]),
+                    ])
+            self.assertEqual(integers, [])
+
+            integer3 = self.integer.create({})
+            self.assert_(integer3)
+            self.assertEqual(integer3.integer, None)
+
+            integer4 = self.integer_default.create({})
+            self.assert_(integer4)
+            self.assertEqual(integer4.integer, 5)
+
+            self.integer.write([integer1], {
+                    'integer': 0,
+                    })
+            self.assertEqual(integer1.integer, 0)
+
+            self.integer.write([integer2], {
+                    'integer': 1,
+                    })
+            self.assertEqual(integer2.integer, 1)
+
+            self.assertRaises(Exception, self.integer.create, {
+                    'integer': 'test',
+                    })
+
+            self.assertRaises(Exception, self.integer.write, [integer1], {
+                    'integer': 'test',
+                    })
 
             # We should catch UserError but mysql does not raise an
             # IntegrityError but an OperationalError
             self.assertRaises(Exception, self.integer_required.create, {})
             transaction.cursor.rollback()
 
-            integer5_id = self.integer_required.create({
+            integer5 = self.integer_required.create({
                     'integer': 0,
                     })
-            self.assert_(integer5_id)
-
-            integer5 = self.integer_required.read(integer5_id, ['integer'])
-            self.assert_(integer5['integer'] == 0)
+            self.assert_(integer5)
+            self.assertEqual(integer5.integer, 0)
 
             transaction.cursor.rollback()
 
@@ -410,215 +390,202 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            float1_id = self.float.create({
-                'float': 1.1,
-                })
-            self.assert_(float1_id)
-
-            float1 = self.float.read(float1_id, ['float'])
-            self.assert_(float1['float'] == 1.1)
-
-            float_ids = self.float.search([
-                ('float', '=', 1.1),
-                ])
-            self.assert_(float_ids == [float1_id])
-
-            float_ids = self.float.search([
-                ('float', '=', 0),
-                ])
-            self.assert_(float_ids == [])
-
-            float_ids = self.float.search([
-                ('float', '!=', 1.1),
-                ])
-            self.assert_(float_ids == [])
-
-            float_ids = self.float.search([
-                ('float', '!=', 0),
-                ])
-            self.assert_(float_ids == [float1_id])
-
-            float_ids = self.float.search([
-                ('float', 'in', [1.1]),
-                ])
-            self.assert_(float_ids == [float1_id])
-
-            float_ids = self.float.search([
-                ('float', 'in', [0]),
-                ])
-            self.assert_(float_ids == [])
-
-            float_ids = self.float.search([
-                ('float', 'in', []),
-                ])
-            self.assert_(float_ids == [])
-
-            float_ids = self.float.search([
-                ('float', 'not in', [1.1]),
-                ])
-            self.assert_(float_ids == [])
-
-            float_ids = self.float.search([
-                ('float', 'not in', [0]),
-                ])
-            self.assert_(float_ids == [float1_id])
-
-            float_ids = self.float.search([
-                ('float', 'not in', []),
-                ])
-            self.assert_(float_ids == [float1_id])
-
-            float_ids = self.float.search([
-                ('float', '<', 5),
-                ])
-            self.assert_(float_ids == [float1_id])
-
-            float_ids = self.float.search([
-                ('float', '<', -5),
-                ])
-            self.assert_(float_ids == [])
-
-            float_ids = self.float.search([
-                ('float', '<', 1.1),
-                ])
-            self.assert_(float_ids == [])
-
-            float_ids = self.float.search([
-                ('float', '<=', 5),
-                ])
-            self.assert_(float_ids == [float1_id])
-
-            float_ids = self.float.search([
-                ('float', '<=', -5),
-                ])
-            self.assert_(float_ids == [])
-
-            float_ids = self.float.search([
-                ('float', '<=', 1.1),
-                ])
-            self.assert_(float_ids == [float1_id])
-
-            float_ids = self.float.search([
-                ('float', '>', 5),
-                ])
-            self.assert_(float_ids == [])
-
-            float_ids = self.float.search([
-                ('float', '>', -5),
-                ])
-            self.assert_(float_ids == [float1_id])
-
-            float_ids = self.float.search([
-                ('float', '>', 1.1),
-                ])
-            self.assert_(float_ids == [])
-
-            float_ids = self.float.search([
-                ('float', '>=', 5),
-                ])
-            self.assert_(float_ids == [])
-
-            float_ids = self.float.search([
-                ('float', '>=', -5),
-                ])
-            self.assert_(float_ids == [float1_id])
-
-            float_ids = self.float.search([
-                ('float', '>=', 1.1),
-                ])
-            self.assert_(float_ids == [float1_id])
-
-            float2_id = self.float.create({
-                'float': 0,
-                })
-            self.assert_(float2_id)
-
-            float2 = self.float.read(float2_id, ['float'])
-            self.assert_(float2['float'] == 0)
-
-            float_ids = self.float.search([
-                ('float', '=', 0),
-                ])
-            self.assert_(float_ids == [float2_id])
-
-            float_ids = self.float.search([
-                ('float', 'in', [0, 1.1]),
-                ])
-            self.assert_(float_ids == [float1_id, float2_id])
-
-            float_ids = self.float.search([
-                ('float', 'not in', [0, 1.1]),
-                ])
-            self.assert_(float_ids == [])
-
-            float3_id = self.float.create({})
-            self.assert_(float3_id)
-
-            float3 = self.float.read(float3_id, ['float'])
-            self.assert_(float3['float'] is None)
-
-            float4_id = self.float_default.create({})
-            self.assert_(float4_id)
-
-            float4 = self.float_default.read(float4_id, ['float'])
-            self.assert_(float4['float'] == 5.5)
-
-            self.float.write(float1_id, {
-                'float': 0,
-                })
-            float1 = self.float.read(float1_id, ['float'])
-            self.assert_(float1['float'] == 0)
-
-            self.float.write(float2_id, {
-                'float': 1.1,
-                })
-            float2 = self.float.read(float2_id, ['float'])
-            self.assert_(float2['float'] == 1.1)
-
-            self.failUnlessRaises(Exception, self.float.create, {
-                'float': 'test',
-                })
-
-            self.failUnlessRaises(Exception, self.float.write, float1_id, {
-                'float': 'test',
-                })
+            float1 = self.float.create({
+                    'float': 1.1,
+                    })
+            self.assert_(float1)
+            self.assertEqual(float1.float, 1.1)
+
+            floats = self.float.search([
+                    ('float', '=', 1.1),
+                    ])
+            self.assertEqual(floats, [float1])
+
+            floats = self.float.search([
+                    ('float', '=', 0),
+                    ])
+            self.assertEqual(floats, [])
+
+            floats = self.float.search([
+                    ('float', '!=', 1.1),
+                    ])
+            self.assertEqual(floats, [])
+
+            floats = self.float.search([
+                    ('float', '!=', 0),
+                    ])
+            self.assertEqual(floats, [float1])
+
+            floats = self.float.search([
+                    ('float', 'in', [1.1]),
+                    ])
+            self.assertEqual(floats, [float1])
+
+            floats = self.float.search([
+                    ('float', 'in', [0]),
+                    ])
+            self.assertEqual(floats, [])
+
+            floats = self.float.search([
+                    ('float', 'in', []),
+                    ])
+            self.assertEqual(floats, [])
+
+            floats = self.float.search([
+                    ('float', 'not in', [1.1]),
+                    ])
+            self.assertEqual(floats, [])
+
+            floats = self.float.search([
+                    ('float', 'not in', [0]),
+                    ])
+            self.assertEqual(floats, [float1])
+
+            floats = self.float.search([
+                    ('float', 'not in', []),
+                    ])
+            self.assertEqual(floats, [float1])
+
+            floats = self.float.search([
+                    ('float', '<', 5),
+                    ])
+            self.assertEqual(floats, [float1])
+
+            floats = self.float.search([
+                    ('float', '<', -5),
+                    ])
+            self.assertEqual(floats, [])
+
+            floats = self.float.search([
+                    ('float', '<', 1.1),
+                    ])
+            self.assertEqual(floats, [])
+
+            floats = self.float.search([
+                    ('float', '<=', 5),
+                    ])
+            self.assertEqual(floats, [float1])
+
+            floats = self.float.search([
+                    ('float', '<=', -5),
+                    ])
+            self.assertEqual(floats, [])
+
+            floats = self.float.search([
+                    ('float', '<=', 1.1),
+                    ])
+            self.assertEqual(floats, [float1])
+
+            floats = self.float.search([
+                    ('float', '>', 5),
+                    ])
+            self.assertEqual(floats, [])
+
+            floats = self.float.search([
+                    ('float', '>', -5),
+                    ])
+            self.assertEqual(floats, [float1])
+
+            floats = self.float.search([
+                    ('float', '>', 1.1),
+                    ])
+            self.assertEqual(floats, [])
+
+            floats = self.float.search([
+                    ('float', '>=', 5),
+                    ])
+            self.assertEqual(floats, [])
+
+            floats = self.float.search([
+                    ('float', '>=', -5),
+                    ])
+            self.assertEqual(floats, [float1])
+
+            floats = self.float.search([
+                    ('float', '>=', 1.1),
+                    ])
+            self.assertEqual(floats, [float1])
+
+            float2 = self.float.create({
+                    'float': 0,
+                    })
+            self.assert_(float2)
+            self.assertEqual(float2.float, 0)
+
+            floats = self.float.search([
+                    ('float', '=', 0),
+                    ])
+            self.assertEqual(floats, [float2])
+
+            floats = self.float.search([
+                    ('float', 'in', [0, 1.1]),
+                    ])
+            self.assertEqual(floats, [float1, float2])
+
+            floats = self.float.search([
+                    ('float', 'not in', [0, 1.1]),
+                    ])
+            self.assertEqual(floats, [])
+
+            float3 = self.float.create({})
+            self.assert_(float3)
+            self.assertEqual(float3.float, None)
+
+            float4 = self.float_default.create({})
+            self.assert_(float4)
+            self.assertEqual(float4.float, 5.5)
+
+            self.float.write([float1], {
+                    'float': 0,
+                    })
+            self.assertEqual(float1.float, 0)
+
+            self.float.write([float2], {
+                    'float': 1.1,
+                    })
+            self.assertEqual(float2.float, 1.1)
+
+            self.assertRaises(Exception, self.float.create, {
+                    'float': 'test',
+                    })
+
+            self.assertRaises(Exception, self.float.write, [float1], {
+                    'float': 'test',
+                    })
 
             self.assertRaises(Exception, self.float_required.create, {})
             transaction.cursor.rollback()
 
-            float5_id = self.float_required.create({
+            float5 = self.float_required.create({
                     'float': 0.0,
                     })
-            float5 = self.float_required.read(float5_id)
-            self.assert_(float5['float'] == 0.0)
-
-            float6_id = self.float_digits.create({
-                'digits': 1,
-                'float': 1.1,
-                })
-            self.assert_(float6_id)
-
-            self.failUnlessRaises(Exception, self.float_digits.create, {
-                'digits': 1,
-                'float': 1.11,
-                })
-
-            self.failUnlessRaises(Exception, self.float_digits.write,
-                float6_id, {
+            self.assertEqual(float5.float, 0.0)
+
+            float6 = self.float_digits.create({
+                    'digits': 1,
+                    'float': 1.1,
+                    })
+            self.assert_(float6)
+
+            self.assertRaises(Exception, self.float_digits.create, {
+                    'digits': 1,
                     'float': 1.11,
                     })
 
-            self.failUnlessRaises(Exception, self.float_digits.write,
-                float6_id, {
+            self.assertRaises(Exception, self.float_digits.write,
+                [float6], {
+                    'float': 1.11,
+                    })
+
+            self.assertRaises(Exception, self.float_digits.write,
+                [float6], {
                     'digits': 0,
                     })
 
-            float7_id = self.float.create({
-                'float': 0.123456789012345,
-                })
-
-            float7 = self.float.read(float7_id, ['float'])
-            self.assert_(float7['float'] == 0.123456789012345)
+            float7 = self.float.create({
+                    'float': 0.123456789012345,
+                    })
+            self.assertEqual(float7.float, 0.123456789012345)
 
             transaction.cursor.rollback()
 
@@ -628,221 +595,208 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            numeric1_id = self.numeric.create({
-                'numeric': Decimal('1.1'),
-                })
-            self.assert_(numeric1_id)
-
-            numeric1 = self.numeric.read(numeric1_id, ['numeric'])
-            self.assert_(numeric1['numeric'] == Decimal('1.1'))
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '=', Decimal('1.1')),
-                ])
-            self.assert_(numeric_ids == [numeric1_id])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '=', Decimal('0')),
-                ])
-            self.assert_(numeric_ids == [])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '!=', Decimal('1.1')),
-                ])
-            self.assert_(numeric_ids == [])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '!=', Decimal('0')),
-                ])
-            self.assert_(numeric_ids == [numeric1_id])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', 'in', [Decimal('1.1')]),
-                ])
-            self.assert_(numeric_ids == [numeric1_id])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', 'in', [Decimal('0')]),
-                ])
-            self.assert_(numeric_ids == [])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', 'in', []),
-                ])
-            self.assert_(numeric_ids == [])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', 'not in', [Decimal('1.1')]),
-                ])
-            self.assert_(numeric_ids == [])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', 'not in', [Decimal('0')]),
-                ])
-            self.assert_(numeric_ids == [numeric1_id])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', 'not in', []),
-                ])
-            self.assert_(numeric_ids == [numeric1_id])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '<', Decimal('5')),
-                ])
-            self.assert_(numeric_ids == [numeric1_id])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '<', Decimal('-5')),
-                ])
-            self.assert_(numeric_ids == [])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '<', Decimal('1.1')),
-                ])
-            self.assert_(numeric_ids == [])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '<=', Decimal('5')),
-                ])
-            self.assert_(numeric_ids == [numeric1_id])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '<=', Decimal('-5')),
-                ])
-            self.assert_(numeric_ids == [])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '<=', Decimal('1.1')),
-                ])
-            self.assert_(numeric_ids == [numeric1_id])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '>', Decimal('5')),
-                ])
-            self.assert_(numeric_ids == [])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '>', Decimal('-5')),
-                ])
-            self.assert_(numeric_ids == [numeric1_id])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '>', Decimal('1.1')),
-                ])
-            self.assert_(numeric_ids == [])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '>=', Decimal('5')),
-                ])
-            self.assert_(numeric_ids == [])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '>=', Decimal('-5')),
-                ])
-            self.assert_(numeric_ids == [numeric1_id])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '>=', Decimal('1.1')),
-                ])
-            self.assert_(numeric_ids == [numeric1_id])
-
-            numeric2_id = self.numeric.create({
-                'numeric': Decimal('0'),
-                })
-            self.assert_(numeric2_id)
-
-            numeric2 = self.numeric.read(numeric2_id, ['numeric'])
-            self.assert_(numeric2['numeric'] == Decimal('0'))
-
-            numeric_ids = self.numeric.search([
-                ('numeric', '=', Decimal('0')),
-                ])
-            self.assert_(numeric_ids == [numeric2_id])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', 'in', [Decimal('0'), Decimal('1.1')]),
-                ])
-            self.assert_(numeric_ids == [numeric1_id, numeric2_id])
-
-            numeric_ids = self.numeric.search([
-                ('numeric', 'not in', [Decimal('0'), Decimal('1.1')]),
-                ])
-            self.assert_(numeric_ids == [])
-
-            numeric3_id = self.numeric.create({})
-            self.assert_(numeric3_id)
-
-            numeric3 = self.numeric.read(numeric3_id, ['numeric'])
-            self.assert_(numeric3['numeric'] is None)
-
-            numeric4_id = self.numeric_default.create({})
-            self.assert_(numeric4_id)
-
-            numeric4 = self.numeric_default.read(numeric4_id, ['numeric'])
-            self.assert_(numeric4['numeric'] == Decimal('5.5'))
-
-            self.numeric.write(numeric1_id, {
-                'numeric': Decimal('0'),
-                })
-            numeric1 = self.numeric.read(numeric1_id, ['numeric'])
-            self.assert_(numeric1['numeric'] == Decimal('0'))
-
-            self.numeric.write(numeric2_id, {
-                'numeric': Decimal('1.1'),
-                })
-            numeric2 = self.numeric.read(numeric2_id, ['numeric'])
-            self.assert_(numeric2['numeric'] == Decimal('1.1'))
-
-            self.failUnlessRaises(Exception, self.numeric.create, {
-                'numeric': 'test',
-                })
-
-            self.failUnlessRaises(Exception, self.numeric.write, numeric1_id, {
-                'numeric': 'test',
-                })
+            numeric1 = self.numeric.create({
+                    'numeric': Decimal('1.1'),
+                    })
+            self.assert_(numeric1)
+            self.assertEqual(numeric1.numeric, Decimal('1.1'))
+
+            numerics = self.numeric.search([
+                    ('numeric', '=', Decimal('1.1')),
+                    ])
+            self.assertEqual(numerics, [numeric1])
+
+            numerics = self.numeric.search([
+                    ('numeric', '=', Decimal('0')),
+                    ])
+            self.assertEqual(numerics, [])
+
+            numerics = self.numeric.search([
+                    ('numeric', '!=', Decimal('1.1')),
+                    ])
+            self.assertEqual(numerics, [])
+
+            numerics = self.numeric.search([
+                    ('numeric', '!=', Decimal('0')),
+                    ])
+            self.assertEqual(numerics, [numeric1])
+
+            numerics = self.numeric.search([
+                    ('numeric', 'in', [Decimal('1.1')]),
+                    ])
+            self.assertEqual(numerics, [numeric1])
+
+            numerics = self.numeric.search([
+                    ('numeric', 'in', [Decimal('0')]),
+                    ])
+            self.assertEqual(numerics, [])
+
+            numerics = self.numeric.search([
+                    ('numeric', 'in', []),
+                    ])
+            self.assertEqual(numerics, [])
+
+            numerics = self.numeric.search([
+                    ('numeric', 'not in', [Decimal('1.1')]),
+                    ])
+            self.assertEqual(numerics, [])
+
+            numerics = self.numeric.search([
+                    ('numeric', 'not in', [Decimal('0')]),
+                    ])
+            self.assertEqual(numerics, [numeric1])
+
+            numerics = self.numeric.search([
+                    ('numeric', 'not in', []),
+                    ])
+            self.assertEqual(numerics, [numeric1])
+
+            numerics = self.numeric.search([
+                    ('numeric', '<', Decimal('5')),
+                    ])
+            self.assertEqual(numerics, [numeric1])
+
+            numerics = self.numeric.search([
+                    ('numeric', '<', Decimal('-5')),
+                    ])
+            self.assertEqual(numerics, [])
+
+            numerics = self.numeric.search([
+                    ('numeric', '<', Decimal('1.1')),
+                    ])
+            self.assertEqual(numerics, [])
+
+            numerics = self.numeric.search([
+                    ('numeric', '<=', Decimal('5')),
+                    ])
+            self.assertEqual(numerics, [numeric1])
+
+            numerics = self.numeric.search([
+                    ('numeric', '<=', Decimal('-5')),
+                    ])
+            self.assertEqual(numerics, [])
+
+            numerics = self.numeric.search([
+                    ('numeric', '<=', Decimal('1.1')),
+                    ])
+            self.assertEqual(numerics, [numeric1])
+
+            numerics = self.numeric.search([
+                    ('numeric', '>', Decimal('5')),
+                    ])
+            self.assertEqual(numerics, [])
+
+            numerics = self.numeric.search([
+                    ('numeric', '>', Decimal('-5')),
+                    ])
+            self.assertEqual(numerics, [numeric1])
+
+            numerics = self.numeric.search([
+                    ('numeric', '>', Decimal('1.1')),
+                    ])
+            self.assertEqual(numerics, [])
+
+            numerics = self.numeric.search([
+                    ('numeric', '>=', Decimal('5')),
+                    ])
+            self.assertEqual(numerics, [])
+
+            numerics = self.numeric.search([
+                    ('numeric', '>=', Decimal('-5')),
+                    ])
+            self.assertEqual(numerics, [numeric1])
+
+            numerics = self.numeric.search([
+                    ('numeric', '>=', Decimal('1.1')),
+                    ])
+            self.assertEqual(numerics, [numeric1])
+
+            numeric2 = self.numeric.create({
+                    'numeric': Decimal('0'),
+                    })
+            self.assert_(numeric2)
+            self.assertEqual(numeric2.numeric, Decimal('0'))
+
+            numerics = self.numeric.search([
+                    ('numeric', '=', Decimal('0')),
+                    ])
+            self.assertEqual(numerics, [numeric2])
+
+            numerics = self.numeric.search([
+                    ('numeric', 'in', [Decimal('0'), Decimal('1.1')]),
+                    ])
+            self.assertEqual(numerics, [numeric1, numeric2])
+
+            numerics = self.numeric.search([
+                    ('numeric', 'not in', [Decimal('0'), Decimal('1.1')]),
+                    ])
+            self.assertEqual(numerics, [])
+
+            numeric3 = self.numeric.create({})
+            self.assert_(numeric3)
+            self.assertEqual(numeric3.numeric, None)
+
+            numeric4 = self.numeric_default.create({})
+            self.assert_(numeric4)
+            self.assertEqual(numeric4.numeric, Decimal('5.5'))
+
+            self.numeric.write([numeric1], {
+                    'numeric': Decimal('0'),
+                    })
+            self.assertEqual(numeric1.numeric, Decimal('0'))
+
+            self.numeric.write([numeric2], {
+                    'numeric': Decimal('1.1'),
+                    })
+            self.assertEqual(numeric2.numeric, Decimal('1.1'))
+
+            self.assertRaises(Exception, self.numeric.create, {
+                    'numeric': 'test',
+                    })
+
+            self.assertRaises(Exception, self.numeric.write, [numeric1], {
+                    'numeric': 'test',
+                    })
 
             self.assertRaises(Exception, self.numeric_required.create, {})
             transaction.cursor.rollback()
 
-            numeric5_id = self.numeric_required.create({
+            numeric5 = self.numeric_required.create({
                     'numeric': Decimal(0),
                     })
-            numeric5 = self.numeric_required.read(numeric5_id)
-            self.assert_(numeric5['numeric'] == 0)
-
-            numeric6_id = self.numeric_digits.create({
-                'digits': 1,
-                'numeric': Decimal('1.1'),
-                })
-            self.assert_(numeric6_id)
-
-            self.failUnlessRaises(Exception, self.numeric_digits.create, {
-                'digits': 1,
-                'numeric': Decimal('1.11'),
-                })
-
-            self.failUnlessRaises(Exception, self.numeric_digits.write,
-                    numeric6_id, {
-                        'numeric': Decimal('1.11'),
-                        })
-
-            self.failUnlessRaises(Exception, self.numeric_digits.write,
-                    numeric6_id, {
-                        'numeric': Decimal('0.10000000000000001'),
-                        })
-
-            self.failUnlessRaises(Exception, self.numeric_digits.write,
-                    numeric6_id, {
-                        'digits': 0,
-                        })
-
-            numeric7_id = self.numeric.create({
-                'numeric': Decimal('0.1234567890123456789'),
-                })
-
-            numeric7 = self.numeric.read(numeric7_id, ['numeric'])
-            self.assert_(numeric7['numeric'] ==
-                    Decimal('0.1234567890123456789'))
+            self.assertEqual(numeric5.numeric, 0)
+
+            numeric6 = self.numeric_digits.create({
+                    'digits': 1,
+                    'numeric': Decimal('1.1'),
+                    })
+            self.assert_(numeric6)
+
+            self.assertRaises(Exception, self.numeric_digits.create, {
+                    'digits': 1,
+                    'numeric': Decimal('1.11'),
+                    })
+
+            self.assertRaises(Exception, self.numeric_digits.write,
+                [numeric6], {
+                    'numeric': Decimal('1.11'),
+                    })
+
+            self.assertRaises(Exception, self.numeric_digits.write,
+                [numeric6], {
+                    'numeric': Decimal('0.10000000000000001'),
+                    })
+
+            self.assertRaises(Exception, self.numeric_digits.write,
+                [numeric6], {
+                    'digits': 0,
+                    })
+
+            numeric7 = self.numeric.create({
+                    'numeric': Decimal('0.1234567890123456789'),
+                    })
+            self.assertEqual(numeric7.numeric,
+                Decimal('0.1234567890123456789'))
 
             transaction.cursor.rollback()
 
@@ -852,262 +806,249 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            for char in (self.char, self.char_translate):
-                char1_id = char.create({
+            for char in (self.char_translate, self.char):
+                char1 = char.create({
+                        'char': 'Test',
+                        })
+                self.assert_(char1)
+                self.assertEqual(char1.char, 'Test')
+
+                chars = char.search([
+                        ('char', '=', 'Test'),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', '=', 'Foo'),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', '=', None),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', '!=', 'Test'),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', '!=', 'Foo'),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', '!=', None),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', 'in', ['Test']),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', 'in', ['Foo']),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', 'in', [None]),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', 'in', []),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', 'not in', ['Test']),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', 'not in', ['Foo']),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', 'not in', [None]),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', 'not in', []),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', 'like', 'Test'),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', 'like', 'T%'),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', 'like', 'Foo'),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', 'like', 'F%'),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', 'ilike', 'test'),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', 'ilike', 't%'),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', 'ilike', 'foo'),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', 'ilike', 'f%'),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', 'not like', 'Test'),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', 'not like', 'T%'),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', 'not like', 'Foo'),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', 'not like', 'F%'),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', 'not ilike', 'test'),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', 'not ilike', 't%'),
+                        ])
+                self.assertEqual(chars, [])
+
+                chars = char.search([
+                        ('char', 'not ilike', 'foo'),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                chars = char.search([
+                        ('char', 'not ilike', 'f%'),
+                        ])
+                self.assertEqual(chars, [char1])
+
+                char2 = char.create({
+                        'char': None,
+                        })
+                self.assert_(char2)
+                self.assertEqual(char2.char, None)
+
+                chars = char.search([
+                        ('char', '=', None),
+                        ])
+                self.assertEqual(chars, [char2])
+
+                chars = char.search([
+                        ('char', 'in', [None, 'Test']),
+                        ])
+                self.assertEqual(chars, [char1, char2])
+
+                chars = char.search([
+                        ('char', 'not in', [None, 'Test']),
+                        ])
+                self.assertEqual(chars, [])
+
+            char3 = self.char.create({})
+            self.assert_(char3)
+            self.assertEqual(char3.char, None)
+
+            char4 = self.char_default.create({})
+            self.assert_(char4)
+            self.assertEqual(char4.char, 'Test')
+
+            self.char.write([char1], {
+                    'char': None,
+                    })
+            self.assertEqual(char1.char, None)
+
+            self.char.write([char2], {
                     'char': 'Test',
                     })
-                self.assert_(char1_id)
-
-                char1 = char.read(char1_id, ['char'])
-                self.assert_(char1['char'] == 'Test')
-
-                char_ids = char.search([
-                    ('char', '=', 'Test'),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', '=', 'Foo'),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', '=', False),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', '!=', 'Test'),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', '!=', 'Foo'),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', '!=', False),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', 'in', ['Test']),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', 'in', ['Foo']),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', 'in', [False]),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', 'in', []),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', 'not in', ['Test']),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', 'not in', ['Foo']),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', 'not in', [False]),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', 'not in', []),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', 'like', 'Test'),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', 'like', 'T%'),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', 'like', 'Foo'),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', 'like', 'F%'),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', 'ilike', 'test'),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', 'ilike', 't%'),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', 'ilike', 'foo'),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', 'ilike', 'f%'),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', 'not like', 'Test'),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', 'not like', 'T%'),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', 'not like', 'Foo'),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', 'not like', 'F%'),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', 'not ilike', 'test'),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', 'not ilike', 't%'),
-                    ])
-                self.assert_(char_ids == [])
-
-                char_ids = char.search([
-                    ('char', 'not ilike', 'foo'),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char_ids = char.search([
-                    ('char', 'not ilike', 'f%'),
-                    ])
-                self.assert_(char_ids == [char1_id])
-
-                char2_id = char.create({
-                    'char': None,
-                    })
-                self.assert_(char2_id)
-
-                char2 = char.read(char2_id, ['char'])
-                self.assert_(char2['char'] == None)
-
-                char_ids = char.search([
-                    ('char', '=', False),
-                    ])
-                self.assert_(char_ids == [char2_id])
-
-                char_ids = char.search([
-                    ('char', 'in', [False, 'Test']),
-                    ])
-                self.assert_(char_ids == [char1_id, char2_id])
-
-                char_ids = char.search([
-                    ('char', 'not in', [False, 'Test']),
-                    ])
-                self.assert_(char_ids == [])
-
-            char3_id = self.char.create({})
-            self.assert_(char3_id)
-
-            char3 = self.char.read(char3_id, ['char'])
-            self.assert_(char3['char'] == None)
-
-            char4_id = self.char_default.create({})
-            self.assert_(char4_id)
-
-            char4 = self.char_default.read(char4_id, ['char'])
-            self.assert_(char4['char'] == 'Test')
-
-            self.char.write(char1_id, {
-                'char': None,
-                })
-            char1 = self.char.read(char1_id, ['char'])
-            self.assert_(char1['char'] == None)
-
-            self.char.write(char2_id, {
-                'char': 'Test',
-                })
-            char2 = self.char.read(char2_id, ['char'])
-            self.assert_(char2['char'] == 'Test')
-
-            self.failUnlessRaises(Exception, self.char_required.create, {})
+            self.assertEqual(char2.char, 'Test')
+
+            self.assertRaises(Exception, self.char_required.create, {})
             transaction.cursor.rollback()
 
-            self.failUnlessRaises(Exception, self.char_required.create, {
+            self.assertRaises(Exception, self.char_required.create, {
                     'char': '',
                     })
             transaction.cursor.rollback()
 
-            char5_id = self.char_required.create({
-                'char': 'Test',
-                })
-            self.assert_(char5_id)
-
-            char6_id = self.char_size.create({
-                'char': 'Test',
-                })
-            self.assert_(char6_id)
-
-            self.failUnlessRaises(Exception, self.char_size.create, {
-                'char': 'foobar',
-                })
-
-            self.failUnlessRaises(Exception, self.char_size.write, char6_id, {
-                'char': 'foobar',
-                })
+            char5 = self.char_required.create({
+                    'char': 'Test',
+                    })
+            self.assert_(char5)
+
+            char6 = self.char_size.create({
+                    'char': 'Test',
+                    })
+            self.assert_(char6)
+
+            self.assertRaises(Exception, self.char_size.create, {
+                    'char': 'foobar',
+                    })
+
+            self.assertRaises(Exception, self.char_size.write, [char6], {
+                    'char': 'foobar',
+                    })
             transaction.cursor.rollback()
 
-            char7_id = self.char.create({
-                'char': u'é',
-                })
-            self.assert_(char7_id)
-
-            char7 = self.char.read(char7_id, ['char'])
-            self.assert_(char7['char'] == u'é')
-
-            char_ids = self.char.search([
-                ('char', '=', u'é'),
-                ])
-            self.assert_(char_ids == [char7_id])
-
-            self.char.write(char7_id, {
-                'char': 'é',
-                })
-            char7 = self.char.read(char7_id, ['char'])
-            self.assert_(char7['char'] == u'é')
-
-            char_ids = self.char.search([
-                ('char', '=', 'é'),
-                ])
-            self.assert_(char_ids == [char7_id])
+            char7 = self.char.create({
+                    'char': u'é',
+                    })
+            self.assert_(char7)
+            self.assertEqual(char7.char, u'é')
+
+            chars = self.char.search([
+                    ('char', '=', u'é'),
+                    ])
+            self.assertEqual(chars, [char7])
+
+            self.char.write([char7], {
+                    'char': 'é',
+                    })
+            self.assertEqual(char7.char, u'é')
+
+            chars = self.char.search([
+                    ('char', '=', 'é'),
+                    ])
+            self.assertEqual(chars, [char7])
 
             transaction.cursor.rollback()
 
@@ -1117,261 +1058,248 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            for text in (self.text, self.text_translate):
-                text1_id = text.create({
+            for text in (self.text_translate, self.text):
+                text1 = text.create({
+                        'text': 'Test',
+                        })
+                self.assert_(text1)
+                self.assertEqual(text1.text, 'Test')
+
+                texts = text.search([
+                        ('text', '=', 'Test'),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', '=', 'Foo'),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', '=', None),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', '!=', 'Test'),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', '!=', 'Foo'),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', '!=', None),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', 'in', ['Test']),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', 'in', ['Foo']),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', 'in', [None]),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', 'in', []),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', 'not in', ['Test']),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', 'not in', ['Foo']),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', 'not in', [None]),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', 'not in', []),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', 'like', 'Test'),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', 'like', 'T%'),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', 'like', 'Foo'),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', 'like', 'F%'),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', 'ilike', 'test'),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', 'ilike', 't%'),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', 'ilike', 'foo'),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', 'ilike', 'f%'),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', 'not like', 'Test'),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', 'not like', 'T%'),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', 'not like', 'Foo'),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', 'not like', 'F%'),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', 'not ilike', 'test'),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', 'not ilike', 't%'),
+                        ])
+                self.assertEqual(texts, [])
+
+                texts = text.search([
+                        ('text', 'not ilike', 'foo'),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                texts = text.search([
+                        ('text', 'not ilike', 'f%'),
+                        ])
+                self.assertEqual(texts, [text1])
+
+                text2 = text.create({
+                        'text': None,
+                        })
+                self.assert_(text2)
+                self.assertEqual(text2.text, None)
+
+                texts = text.search([
+                        ('text', '=', None),
+                        ])
+                self.assertEqual(texts, [text2])
+
+                texts = text.search([
+                        ('text', 'in', [None, 'Test']),
+                        ])
+                self.assertEqual(texts, [text1, text2])
+
+                texts = text.search([
+                        ('text', 'not in', [None, 'Test']),
+                        ])
+                self.assertEqual(texts, [])
+
+            text3 = self.text.create({})
+            self.assert_(text3)
+            self.assertEqual(text3.text, None)
+
+            text4 = self.text_default.create({})
+            self.assert_(text4)
+            self.assertEqual(text4.text, 'Test')
+
+            self.text.write([text1], {
+                    'text': None,
+                    })
+            self.assertEqual(text1.text, None)
+
+            self.text.write([text2], {
                     'text': 'Test',
                     })
-                self.assert_(text1_id)
-
-                text1 = text.read(text1_id, ['text'])
-                self.assert_(text1['text'] == 'Test')
-
-                text_ids = text.search([
-                    ('text', '=', 'Test'),
+            self.assertEqual(text2.text, 'Test')
+
+            self.assertRaises(Exception, self.text_required.create, {})
+            transaction.cursor.rollback()
+
+            text5 = self.text_required.create({
+                    'text': 'Test',
+                    })
+            self.assert_(text5)
+
+            text6 = self.text_size.create({
+                    'text': 'Test',
+                    })
+            self.assert_(text6)
+
+            self.assertRaises(Exception, self.text_size.create, {
+                    'text': 'foobar',
+                    })
+
+            self.assertRaises(Exception, self.text_size.write, [text6], {
+                    'text': 'foobar',
+                    })
+
+            text7 = self.text.create({
+                    'text': 'Foo\nBar',
+                    })
+            self.assert_(text7)
+
+            text8 = self.text.create({
+                    'text': u'é',
+                    })
+            self.assert_(text8)
+            self.assertEqual(text8.text, u'é')
+
+            texts = self.text.search([
+                    ('text', '=', u'é'),
                     ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', '=', 'Foo'),
+            self.assertEqual(texts, [text8])
+
+            self.text.write([text8], {
+                    'text': 'é',
+                    })
+            self.assertEqual(text8.text, u'é')
+
+            texts = self.text.search([
+                    ('text', '=', 'é'),
                     ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', '=', False),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', '!=', 'Test'),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', '!=', 'Foo'),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', '!=', False),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', 'in', ['Test']),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', 'in', ['Foo']),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', 'in', [False]),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', 'in', []),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', 'not in', ['Test']),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', 'not in', ['Foo']),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', 'not in', [False]),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', 'not in', []),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', 'like', 'Test'),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', 'like', 'T%'),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', 'like', 'Foo'),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', 'like', 'F%'),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', 'ilike', 'test'),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', 'ilike', 't%'),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', 'ilike', 'foo'),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', 'ilike', 'f%'),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', 'not like', 'Test'),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', 'not like', 'T%'),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', 'not like', 'Foo'),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', 'not like', 'F%'),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', 'not ilike', 'test'),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', 'not ilike', 't%'),
-                    ])
-                self.assert_(text_ids == [])
-
-                text_ids = text.search([
-                    ('text', 'not ilike', 'foo'),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text_ids = text.search([
-                    ('text', 'not ilike', 'f%'),
-                    ])
-                self.assert_(text_ids == [text1_id])
-
-                text2_id = text.create({
-                    'text': None,
-                    })
-                self.assert_(text2_id)
-
-                text2 = text.read(text2_id, ['text'])
-                self.assert_(text2['text'] == None)
-
-                text_ids = text.search([
-                    ('text', '=', False),
-                    ])
-                self.assert_(text_ids == [text2_id])
-
-                text_ids = text.search([
-                    ('text', 'in', [False, 'Test']),
-                    ])
-                self.assert_(text_ids == [text1_id, text2_id])
-
-                text_ids = text.search([
-                    ('text', 'not in', [False, 'Test']),
-                    ])
-                self.assert_(text_ids == [])
-
-            text3_id = self.text.create({})
-            self.assert_(text3_id)
-
-            text3 = self.text.read(text3_id, ['text'])
-            self.assert_(text3['text'] == None)
-
-            text4_id = self.text_default.create({})
-            self.assert_(text4_id)
-
-            text4 = self.text_default.read(text4_id, ['text'])
-            self.assert_(text4['text'] == 'Test')
-
-            self.text.write(text1_id, {
-                'text': None,
-                })
-            text1 = self.text.read(text1_id, ['text'])
-            self.assert_(text1['text'] == None)
-
-            self.text.write(text2_id, {
-                'text': 'Test',
-                })
-            text2 = self.text.read(text2_id, ['text'])
-            self.assert_(text2['text'] == 'Test')
-
-            self.failUnlessRaises(Exception, self.text_required.create, {})
-            transaction.cursor.rollback()
-
-            text5_id = self.text_required.create({
-                'text': 'Test',
-                })
-            self.assert_(text5_id)
-
-            text6_id = self.text_size.create({
-                'text': 'Test',
-                })
-            self.assert_(text6_id)
-
-            self.failUnlessRaises(Exception, self.text_size.create, {
-                'text': 'foobar',
-                })
-
-            self.failUnlessRaises(Exception, self.text_size.write, text6_id, {
-                'text': 'foobar',
-                })
-
-            text7_id = self.text.create({
-                'text': 'Foo\nBar',
-                })
-            self.assert_(text7_id)
-
-            text8_id = self.text.create({
-                'text': u'é',
-                })
-            self.assert_(text8_id)
-
-            text8 = self.text.read(text8_id, ['text'])
-            self.assert_(text8['text'] == u'é')
-
-            text_ids = self.text.search([
-                ('text', '=', u'é'),
-                ])
-            self.assert_(text_ids == [text8_id])
-
-            self.text.write(text8_id, {
-                'text': 'é',
-                })
-            text8 = self.text.read(text8_id, ['text'])
-            self.assert_(text8['text'] == u'é')
-
-            text_ids = self.text.search([
-                ('text', '=', 'é'),
-                ])
-            self.assert_(text_ids == [text8_id])
+            self.assertEqual(texts, [text8])
 
             transaction.cursor.rollback()
 
@@ -1381,202 +1309,189 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            sha1_id = self.sha.create({
-                'sha': 'Test',
-                })
-            self.assert_(sha1_id)
-
-            sha1 = self.sha.read(sha1_id, ['sha'])
-            self.assertEqual(sha1['sha'],
+            sha1 = self.sha.create({
+                    'sha': 'Test',
+                    })
+            self.assert_(sha1)
+            self.assertEqual(sha1.sha,
                 '640ab2bae07bedc4c163f679a746f7ab7fb5d1fa')
 
-            sha_ids = self.sha.search([
-                ('sha', '=', 'Test'),
-                ])
-            self.assert_(sha_ids == [sha1_id])
-
-            sha_ids = self.sha.search([
-                ('sha', '=', 'Foo'),
-                ])
-            self.assert_(sha_ids == [])
-
-            sha_ids = self.sha.search([
-                ('sha', '=', False),
-                ])
-            self.assert_(sha_ids == [])
-
-            sha_ids = self.sha.search([
-                ('sha', '!=', 'Test'),
-                ])
-            self.assert_(sha_ids == [])
-
-            sha_ids = self.sha.search([
-                ('sha', '!=', 'Foo'),
-                ])
-            self.assert_(sha_ids == [sha1_id])
-
-            sha_ids = self.sha.search([
-                ('sha', '!=', False),
-                ])
-            self.assert_(sha_ids == [sha1_id])
-
-            sha_ids = self.sha.search([
-                ('sha', 'in', ['Test']),
-                ])
-            self.assert_(sha_ids == [sha1_id])
-
-            sha_ids = self.sha.search([
-                ('sha', 'in', ['Foo']),
-                ])
-            self.assert_(sha_ids == [])
-
-            sha_ids = self.sha.search([
-                ('sha', 'in', [False]),
-                ])
-            self.assert_(sha_ids == [])
-
-            sha_ids = self.sha.search([
-                ('sha', 'in', []),
-                ])
-            self.assert_(sha_ids == [])
-
-            sha_ids = self.sha.search([
-                ('sha', 'not in', ['Test']),
-                ])
-            self.assert_(sha_ids == [])
-
-            sha_ids = self.sha.search([
-                ('sha', 'not in', ['Foo']),
-                ])
-            self.assert_(sha_ids == [sha1_id])
-
-            sha_ids = self.sha.search([
-                ('sha', 'not in', [False]),
-                ])
-            self.assert_(sha_ids == [sha1_id])
-
-            sha_ids = self.sha.search([
-                ('sha', 'not in', []),
-                ])
-            self.assert_(sha_ids == [sha1_id])
-
-            sha_ids = self.sha.search([
-                ('sha', 'like', 'Test'),
-                ])
-            self.assert_(sha_ids == [sha1_id])
-
-            sha_ids = self.sha.search([
-                ('sha', 'like', 'Foo'),
-                ])
-            self.assert_(sha_ids == [])
-
-            sha_ids = self.sha.search([
-                ('sha', 'ilike', 'Test'),
-                ])
-            self.assert_(sha_ids == [sha1_id])
-
-            sha_ids = self.sha.search([
-                ('sha', 'ilike', 'foo'),
-                ])
-            self.assert_(sha_ids == [])
-
-            sha_ids = self.sha.search([
-                ('sha', 'not like', 'Test'),
-                ])
-            self.assert_(sha_ids == [])
-
-            sha_ids = self.sha.search([
-                ('sha', 'not like', 'Foo'),
-                ])
-            self.assert_(sha_ids == [sha1_id])
-
-            sha_ids = self.sha.search([
-                ('sha', 'not ilike', 'foo'),
-                ])
-            self.assert_(sha_ids == [sha1_id])
-
-            sha2_id = self.sha.create({
-                'sha': None,
-                })
-            self.assert_(sha2_id)
-
-            sha2 = self.sha.read(sha2_id, ['sha'])
-            self.assert_(sha2['sha'] == None)
-
-            sha_ids = self.sha.search([
-                ('sha', '=', False),
-                ])
-            self.assert_(sha_ids == [sha2_id])
-
-            sha_ids = self.sha.search([
-                ('sha', 'in', [False, 'Test']),
-                ])
-            self.assert_(sha_ids == [sha1_id, sha2_id])
-
-            sha_ids = self.sha.search([
-                ('sha', 'not in', [False, 'Test']),
-                ])
-            self.assert_(sha_ids == [])
-
-            sha3_id = self.sha.create({})
-            self.assert_(sha3_id)
-
-            sha3 = self.sha.read(sha3_id, ['sha'])
-            self.assert_(sha3['sha'] == None)
-
-            sha4_id = self.sha_default.create({})
-            self.assert_(sha4_id)
-
-            sha4 = self.sha_default.read(sha4_id, ['sha'])
-            self.assertEqual(sha4['sha'],
+            sha = self.sha.search([
+                    ('sha', '=', 'Test'),
+                    ])
+            self.assertEqual(sha, [sha1])
+
+            sha = self.sha.search([
+                    ('sha', '=', 'Foo'),
+                    ])
+            self.assertEqual(sha, [])
+
+            sha = self.sha.search([
+                    ('sha', '=', None),
+                    ])
+            self.assertEqual(sha, [])
+
+            sha = self.sha.search([
+                    ('sha', '!=', 'Test'),
+                    ])
+            self.assertEqual(sha, [])
+
+            sha = self.sha.search([
+                    ('sha', '!=', 'Foo'),
+                    ])
+            self.assert_(sha, [sha1])
+
+            sha = self.sha.search([
+                    ('sha', '!=', None),
+                    ])
+            self.assertEqual(sha, [sha1])
+
+            sha = self.sha.search([
+                    ('sha', 'in', ['Test']),
+                    ])
+            self.assertEqual(sha, [sha1])
+
+            sha = self.sha.search([
+                    ('sha', 'in', ['Foo']),
+                    ])
+            self.assertEqual(sha, [])
+
+            sha = self.sha.search([
+                    ('sha', 'in', [None]),
+                    ])
+            self.assertEqual(sha, [])
+
+            sha = self.sha.search([
+                    ('sha', 'in', []),
+                    ])
+            self.assertEqual(sha, [])
+
+            sha = self.sha.search([
+                    ('sha', 'not in', ['Test']),
+                    ])
+            self.assertEqual(sha, [])
+
+            sha = self.sha.search([
+                    ('sha', 'not in', ['Foo']),
+                    ])
+            self.assertEqual(sha, [sha1])
+
+            sha = self.sha.search([
+                    ('sha', 'not in', [None]),
+                    ])
+            self.assertEqual(sha, [sha1])
+
+            sha = self.sha.search([
+                    ('sha', 'not in', []),
+                    ])
+            self.assertEqual(sha, [sha1])
+
+            sha = self.sha.search([
+                    ('sha', 'like', 'Test'),
+                    ])
+            self.assertEqual(sha, [sha1])
+
+            sha = self.sha.search([
+                    ('sha', 'like', 'Foo'),
+                    ])
+            self.assertEqual(sha, [])
+
+            sha = self.sha.search([
+                    ('sha', 'ilike', 'Test'),
+                    ])
+            self.assertEqual(sha, [sha1])
+
+            sha = self.sha.search([
+                    ('sha', 'ilike', 'foo'),
+                    ])
+            self.assertEqual(sha, [])
+
+            sha = self.sha.search([
+                    ('sha', 'not like', 'Test'),
+                    ])
+            self.assertEqual(sha, [])
+
+            sha = self.sha.search([
+                    ('sha', 'not like', 'Foo'),
+                    ])
+            self.assertEqual(sha, [sha1])
+
+            sha = self.sha.search([
+                    ('sha', 'not ilike', 'foo'),
+                    ])
+            self.assertEqual(sha, [sha1])
+
+            sha2 = self.sha.create({
+                    'sha': None,
+                    })
+            self.assert_(sha2)
+            self.assertEqual(sha2.sha, None)
+
+            sha = self.sha.search([
+                    ('sha', '=', None),
+                    ])
+            self.assertEqual(sha, [sha2])
+
+            sha = self.sha.search([
+                    ('sha', 'in', [None, 'Test']),
+                    ])
+            self.assertEqual(sha, [sha1, sha2])
+
+            sha = self.sha.search([
+                    ('sha', 'not in', [None, 'Test']),
+                    ])
+            self.assertEqual(sha, [])
+
+            sha3 = self.sha.create({})
+            self.assert_(sha3)
+            self.assertEqual(sha3.sha, None)
+
+            sha4 = self.sha_default.create({})
+            self.assert_(sha4)
+            self.assertEqual(sha4.sha,
                 'ba79baeb9f10896a46ae74715271b7f586e74640')
 
-            self.sha.write(sha1_id, {
-                'sha': None,
-                })
-            sha1 = self.sha.read(sha1_id, ['sha'])
-            self.assert_(sha1['sha'] == None)
-
-            self.sha.write(sha2_id, {
-                'sha': 'Test',
-                })
-            sha2 = self.sha.read(sha2_id, ['sha'])
-            self.assertEqual(sha2['sha'],
+            self.sha.write([sha1], {
+                    'sha': None,
+                    })
+            self.assertEqual(sha1.sha, None)
+
+            self.sha.write([sha2], {
+                    'sha': 'Test',
+                    })
+            self.assertEqual(sha2.sha,
                 '640ab2bae07bedc4c163f679a746f7ab7fb5d1fa')
 
-            self.failUnlessRaises(Exception, self.sha_required.create, {})
+            self.assertRaises(Exception, self.sha_required.create, {})
             transaction.cursor.rollback()
 
-            sha5_id = self.sha_required.create({
-                'sha': 'Test',
-                })
-            self.assert_(sha5_id)
-
-            sha6_id = self.sha.create({
-                'sha': u'é',
-                })
-            self.assert_(sha6_id)
-
-            sha6 = self.sha.read(sha6_id, ['sha'])
-            self.assert_(sha6['sha'] ==
-                    u'bf15be717ac1b080b4f1c456692825891ff5073d')
-
-            sha_ids = self.sha.search([
-                ('sha', '=', u'é'),
-                ])
-            self.assert_(sha_ids == [sha6_id])
-
-            self.sha.write(sha6_id, {
-                'sha': 'é',
-                })
-            sha6 = self.sha.read(sha6_id, ['sha'])
-            self.assert_(sha6['sha'] ==
-                    u'bf15be717ac1b080b4f1c456692825891ff5073d')
-
-            sha_ids = self.sha.search([
-                ('sha', '=', 'é'),
-                ])
-            self.assert_(sha_ids == [sha6_id])
+            sha5 = self.sha_required.create({
+                    'sha': 'Test',
+                    })
+            self.assert_(sha5)
+
+            sha6 = self.sha.create({
+                    'sha': u'é',
+                    })
+            self.assert_(sha6)
+            self.assertEqual(sha6.sha,
+                u'bf15be717ac1b080b4f1c456692825891ff5073d')
+
+            sha = self.sha.search([
+                    ('sha', '=', u'é'),
+                    ])
+            self.assertEqual(sha, [sha6])
+
+            self.sha.write([sha6], {
+                    'sha': 'é',
+                    })
+            self.assertEqual(sha6.sha,
+                u'bf15be717ac1b080b4f1c456692825891ff5073d')
+
+            sha = self.sha.search([
+                    ('sha', '=', 'é'),
+                    ])
+            self.assertEqual(sha, [sha6])
 
             transaction.cursor.rollback()
 
@@ -1591,247 +1506,236 @@
             yesterday = today - datetime.timedelta(1)
             default_date = datetime.date(2000, 1, 1)
 
-            date1_id = self.date.create({
-                'date': today,
-                })
-            self.assert_(date1_id)
-
-            date1 = self.date.read(date1_id, ['date'])
-            self.assert_(date1['date'] == today)
-
-            date_ids = self.date.search([
-                ('date', '=', today),
-                ])
-            self.assert_(date_ids == [date1_id])
-
-            date_ids = self.date.search([
-                ('date', '=', tomorrow),
-                ])
-            self.assert_(date_ids == [])
-
-            date_ids = self.date.search([
-                ('date', '=', False),
-                ])
-            self.assert_(date_ids == [])
-
-            date_ids = self.date.search([
-                ('date', '!=', today),
-                ])
-            self.assert_(date_ids == [])
-
-            date_ids = self.date.search([
-                ('date', '!=', tomorrow),
-                ])
-            self.assert_(date_ids == [date1_id])
-
-            date_ids = self.date.search([
-                ('date', '!=', False),
-                ])
-            self.assert_(date_ids == [date1_id])
-
-            date_ids = self.date.search([
-                ('date', 'in', [today]),
-                ])
-            self.assert_(date_ids == [date1_id])
-
-            date_ids = self.date.search([
-                ('date', 'in', [tomorrow]),
-                ])
-            self.assert_(date_ids == [])
-
-            date_ids = self.date.search([
-                ('date', 'in', [False]),
-                ])
-            self.assert_(date_ids == [])
-
-            date_ids = self.date.search([
-                ('date', 'in', []),
-                ])
-            self.assert_(date_ids == [])
-
-            date_ids = self.date.search([
-                ('date', 'not in', [today]),
-                ])
-            self.assert_(date_ids == [])
-
-            date_ids = self.date.search([
-                ('date', 'not in', [tomorrow]),
-                ])
-            self.assert_(date_ids == [date1_id])
-
-            date_ids = self.date.search([
-                ('date', 'not in', [False]),
-                ])
-            self.assert_(date_ids == [date1_id])
-
-            date_ids = self.date.search([
-                ('date', 'not in', []),
-                ])
-            self.assert_(date_ids == [date1_id])
-
-            date_ids = self.date.search([
-                ('date', '<', tomorrow),
-                ])
-            self.assert_(date_ids == [date1_id])
-
-            date_ids = self.date.search([
-                ('date', '<', yesterday),
-                ])
-            self.assert_(date_ids == [])
-
-            date_ids = self.date.search([
-                ('date', '<', today),
-                ])
-            self.assert_(date_ids == [])
-
-            date_ids = self.date.search([
-                ('date', '<=', today),
-                ])
-            self.assert_(date_ids == [date1_id])
-
-            date_ids = self.date.search([
-                ('date', '<=', yesterday),
-                ])
-            self.assert_(date_ids == [])
-
-            date_ids = self.date.search([
-                ('date', '<=', tomorrow),
-                ])
-            self.assert_(date_ids == [date1_id])
-
-            date_ids = self.date.search([
-                ('date', '>', tomorrow),
-                ])
-            self.assert_(date_ids == [])
-
-            date_ids = self.date.search([
-                ('date', '>', yesterday),
-                ])
-            self.assert_(date_ids == [date1_id])
-
-            date_ids = self.date.search([
-                ('date', '>', today),
-                ])
-            self.assert_(date_ids == [])
-
-            date_ids = self.date.search([
-                ('date', '>=', tomorrow),
-                ])
-            self.assert_(date_ids == [])
-
-            date_ids = self.date.search([
-                ('date', '>=', yesterday),
-                ])
-            self.assert_(date_ids == [date1_id])
-
-            date_ids = self.date.search([
-                ('date', '>=', today),
-                ])
-            self.assert_(date_ids == [date1_id])
-
-            date2_id = self.date.create({
-                'date': yesterday,
-                })
-            self.assert_(date2_id)
-
-            date2 = self.date.read(date2_id, ['date'])
-            self.assert_(date2['date'] == yesterday)
-
-            date_ids = self.date.search([
-                ('date', '=', yesterday),
-                ])
-            self.assert_(date_ids == [date2_id])
-
-            date_ids = self.date.search([
-                ('date', 'in', [yesterday, today]),
-                ])
-            self.assert_(date_ids == [date1_id, date2_id])
-
-            date_ids = self.date.search([
-                ('date', 'not in', [yesterday, today]),
-                ])
-            self.assert_(date_ids == [])
-
-            date3_id = self.date.create({})
-            self.assert_(date3_id)
-
-            date3 = self.date.read(date3_id, ['date'])
-            self.assert_(date3['date'] == None)
-
-            date4_id = self.date_default.create({})
-            self.assert_(date4_id)
-
-            date4 = self.date_default.read(date4_id, ['date'])
-            self.assert_(date4['date'] == default_date)
-
-            self.date.write(date1_id, {
-                'date': yesterday,
-                })
-            date1 = self.date.read(date1_id, ['date'])
-            self.assert_(date1['date'] == yesterday)
-
-            self.date.write(date2_id, {
-                'date': today,
-                })
-            date2 = self.date.read(date2_id, ['date'])
-            self.assert_(date2['date'] == today)
-
-            self.failUnlessRaises(Exception, self.date.create, {
-                'date': 'test',
-                })
-
-            self.failUnlessRaises(Exception, self.date.write, date1_id, {
-                'date': 'test',
-                })
-
-            self.failUnlessRaises(Exception, self.date.create, {
-                'date': 1,
-                })
-
-            self.failUnlessRaises(Exception, self.date.write, date1_id, {
-                'date': 1,
-                })
-
-            self.failUnlessRaises(Exception, self.date.create, {
-                'date': datetime.datetime.now(),
-                })
-
-            self.failUnlessRaises(Exception, self.date.write, date1_id, {
-                'date': datetime.datetime.now(),
-                })
-
-            self.failUnlessRaises(Exception, self.date.create, {
-                'date': '2009-13-01',
-                })
-
-            self.failUnlessRaises(Exception, self.date.write, date1_id, {
-                'date': '2009-02-29',
-                })
-
-            date5_id = self.date.create({
-                'date': '2009-01-01',
-                })
-            self.assert_(date5_id)
-            date5 = self.date.read(date5_id, ['date'])
-            self.assert_(date5['date'] == datetime.date(2009, 1, 1))
-
-            self.failUnlessRaises(Exception, self.date_required.create, {})
+            date1 = self.date.create({
+                    'date': today,
+                    })
+            self.assert_(date1)
+            self.assertEqual(date1.date, today)
+
+            dates = self.date.search([
+                    ('date', '=', today),
+                    ])
+            self.assertEqual(dates, [date1])
+
+            dates = self.date.search([
+                    ('date', '=', tomorrow),
+                    ])
+            self.assertEqual(dates, [])
+
+            dates = self.date.search([
+                    ('date', '=', None),
+                    ])
+            self.assertEqual(dates, [])
+
+            dates = self.date.search([
+                    ('date', '!=', today),
+                    ])
+            self.assertEqual(dates, [])
+
+            dates = self.date.search([
+                    ('date', '!=', tomorrow),
+                    ])
+            self.assertEqual(dates, [date1])
+
+            dates = self.date.search([
+                    ('date', '!=', None),
+                    ])
+            self.assertEqual(dates, [date1])
+
+            dates = self.date.search([
+                    ('date', 'in', [today]),
+                    ])
+            self.assertEqual(dates, [date1])
+
+            dates = self.date.search([
+                    ('date', 'in', [tomorrow]),
+                    ])
+            self.assertEqual(dates, [])
+
+            dates = self.date.search([
+                    ('date', 'in', [None]),
+                    ])
+            self.assertEqual(dates, [])
+
+            dates = self.date.search([
+                    ('date', 'in', []),
+                    ])
+            self.assertEqual(dates, [])
+
+            dates = self.date.search([
+                    ('date', 'not in', [today]),
+                    ])
+            self.assertEqual(dates, [])
+
+            dates = self.date.search([
+                    ('date', 'not in', [tomorrow]),
+                    ])
+            self.assertEqual(dates, [date1])
+
+            dates = self.date.search([
+                    ('date', 'not in', [None]),
+                    ])
+            self.assertEqual(dates, [date1])
+
+            dates = self.date.search([
+                    ('date', 'not in', []),
+                    ])
+            self.assertEqual(dates, [date1])
+
+            dates = self.date.search([
+                    ('date', '<', tomorrow),
+                    ])
+            self.assertEqual(dates, [date1])
+
+            dates = self.date.search([
+                    ('date', '<', yesterday),
+                    ])
+            self.assertEqual(dates, [])
+
+            dates = self.date.search([
+                    ('date', '<', today),
+                    ])
+            self.assertEqual(dates, [])
+
+            dates = self.date.search([
+                    ('date', '<=', today),
+                    ])
+            self.assertEqual(dates, [date1])
+
+            dates = self.date.search([
+                    ('date', '<=', yesterday),
+                    ])
+            self.assertEqual(dates, [])
+
+            dates = self.date.search([
+                    ('date', '<=', tomorrow),
+                    ])
+            self.assertEqual(dates, [date1])
+
+            dates = self.date.search([
+                    ('date', '>', tomorrow),
+                    ])
+            self.assertEqual(dates, [])
+
+            dates = self.date.search([
+                    ('date', '>', yesterday),
+                    ])
+            self.assertEqual(dates, [date1])
+
+            dates = self.date.search([
+                    ('date', '>', today),
+                    ])
+            self.assertEqual(dates, [])
+
+            dates = self.date.search([
+                    ('date', '>=', tomorrow),
+                    ])
+            self.assertEqual(dates, [])
+
+            dates = self.date.search([
+                    ('date', '>=', yesterday),
+                    ])
+            self.assertEqual(dates, [date1])
+
+            dates = self.date.search([
+                    ('date', '>=', today),
+                    ])
+            self.assertEqual(dates, [date1])
+
+            date2 = self.date.create({
+                    'date': yesterday,
+                    })
+            self.assert_(date2)
+            self.assertEqual(date2.date, yesterday)
+
+            dates = self.date.search([
+                    ('date', '=', yesterday),
+                    ])
+            self.assertEqual(dates, [date2])
+
+            dates = self.date.search([
+                    ('date', 'in', [yesterday, today]),
+                    ])
+            self.assertEqual(dates, [date1, date2])
+
+            dates = self.date.search([
+                    ('date', 'not in', [yesterday, today]),
+                    ])
+            self.assertEqual(dates, [])
+
+            date3 = self.date.create({})
+            self.assert_(date3)
+            self.assertEqual(date3.date, None)
+
+            date4 = self.date_default.create({})
+            self.assert_(date4)
+            self.assertEqual(date4.date, default_date)
+
+            self.date.write([date1], {
+                    'date': yesterday,
+                    })
+            self.assertEqual(date1.date, yesterday)
+
+            self.date.write([date2], {
+                    'date': today,
+                    })
+            self.assertEqual(date2.date, today)
+
+            self.assertRaises(Exception, self.date.create, {
+                    'date': 'test',
+                    })
+
+            self.assertRaises(Exception, self.date.write, [date1], {
+                    'date': 'test',
+                    })
+
+            self.assertRaises(Exception, self.date.create, {
+                    'date': 1,
+                    })
+
+            self.assertRaises(Exception, self.date.write, [date1], {
+                    'date': 1,
+                    })
+
+            self.assertRaises(Exception, self.date.create, {
+                    'date': datetime.datetime.now(),
+                    })
+
+            self.assertRaises(Exception, self.date.write, [date1], {
+                    'date': datetime.datetime.now(),
+                    })
+
+            self.assertRaises(Exception, self.date.create, {
+                    'date': '2009-13-01',
+                    })
+
+            self.assertRaises(Exception, self.date.write, [date1], {
+                    'date': '2009-02-29',
+                    })
+
+            date5 = self.date.create({
+                    'date': '2009-01-01',
+                    })
+            self.assert_(date5)
+            self.assertEqual(date5.date, datetime.date(2009, 1, 1))
+
+            self.assertRaises(Exception, self.date_required.create, {})
             transaction.cursor.rollback()
 
-            date6_id = self.date_required.create({
-                'date': today,
-                })
-            self.assert_(date6_id)
-
-            date7_id = self.date.create({
-                'date': None,
-                })
-            self.assert_(date7_id)
-
-            date8_id = self.date.create({
-                'date': None,
-                })
-            self.assert_(date8_id)
+            date6 = self.date_required.create({
+                    'date': today,
+                    })
+            self.assert_(date6)
+
+            date7 = self.date.create({
+                    'date': None,
+                    })
+            self.assert_(date7)
+
+            date8 = self.date.create({
+                    'date': None,
+                    })
+            self.assert_(date8)
 
             transaction.cursor.rollback()
 
@@ -1846,270 +1750,258 @@
             yesterday = today - datetime.timedelta(1)
             default_datetime = datetime.datetime(2000, 1, 1, 12, 0, 0)
 
-            datetime1_id = self.datetime.create({
-                'datetime': today,
-                })
-            self.assert_(datetime1_id)
-
-            datetime1 = self.datetime.read(datetime1_id, ['datetime'])
-            self.assert_(datetime1['datetime'] == today)
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '=', today),
-                ])
-            self.assert_(datetime_ids == [datetime1_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '=', tomorrow),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '=', False),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '!=', today),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '!=', tomorrow),
-                ])
-            self.assert_(datetime_ids == [datetime1_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '!=', False),
-                ])
-            self.assert_(datetime_ids == [datetime1_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', 'in', [today]),
-                ])
-            self.assert_(datetime_ids == [datetime1_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', 'in', [tomorrow]),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', 'in', [False]),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', 'in', []),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', 'not in', [today]),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', 'not in', [tomorrow]),
-                ])
-            self.assert_(datetime_ids == [datetime1_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', 'not in', [False]),
-                ])
-            self.assert_(datetime_ids == [datetime1_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', 'not in', []),
-                ])
-            self.assert_(datetime_ids == [datetime1_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '<', tomorrow),
-                ])
-            self.assert_(datetime_ids == [datetime1_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '<', yesterday),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '<', today),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '<=', today),
-                ])
-            self.assert_(datetime_ids == [datetime1_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '<=', yesterday),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '<=', tomorrow),
-                ])
-            self.assert_(datetime_ids == [datetime1_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '>', tomorrow),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '>', yesterday),
-                ])
-            self.assert_(datetime_ids == [datetime1_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '>', today),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '>=', tomorrow),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '>=', yesterday),
-                ])
-            self.assert_(datetime_ids == [datetime1_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '>=', today),
-                ])
-            self.assert_(datetime_ids == [datetime1_id])
-
-            datetime2_id = self.datetime.create({
-                'datetime': yesterday,
-                })
-            self.assert_(datetime2_id)
-
-            datetime2 = self.datetime.read(datetime2_id, ['datetime'])
-            self.assert_(datetime2['datetime'] == yesterday)
-
-            datetime_ids = self.datetime.search([
-                ('datetime', '=', yesterday),
-                ])
-            self.assert_(datetime_ids == [datetime2_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', 'in', [yesterday, today]),
-                ])
-            self.assert_(datetime_ids == [datetime1_id, datetime2_id])
-
-            datetime_ids = self.datetime.search([
-                ('datetime', 'not in', [yesterday, today]),
-                ])
-            self.assert_(datetime_ids == [])
-
-            datetime3_id = self.datetime.create({})
-            self.assert_(datetime3_id)
-
-            datetime3 = self.datetime.read(datetime3_id, ['datetime'])
-            self.assert_(datetime3['datetime'] == None)
-
-            datetime4_id = self.datetime_default.create({})
-            self.assert_(datetime4_id)
-
-            datetime4 = self.datetime_default.read(datetime4_id, ['datetime'])
-            self.assert_(datetime4['datetime'] == default_datetime)
-
-            self.datetime.write(datetime1_id, {
-                'datetime': yesterday,
-                })
-            datetime1 = self.datetime.read(datetime1_id, ['datetime'])
-            self.assert_(datetime1['datetime'] == yesterday)
-
-            self.datetime.write(datetime2_id, {
-                'datetime': today,
-                })
-            datetime2 = self.datetime.read(datetime2_id, ['datetime'])
-            self.assert_(datetime2['datetime'] == today)
-
-            self.failUnlessRaises(Exception, self.datetime.create, {
-                'datetime': 'test',
-                })
-
-            self.failUnlessRaises(Exception, self.datetime.write, datetime1_id,
-                    {
-                        'datetime': 'test',
+            datetime1 = self.datetime.create({
+                    'datetime': today,
                     })
-
-            self.failUnlessRaises(Exception, self.datetime.create, {
-                'datetime': 1,
-                })
-
-            self.failUnlessRaises(Exception, self.datetime.write, datetime1_id,
-                    {
-                        'datetime': 1,
+            self.assert_(datetime1)
+            self.assertEqual(datetime1.datetime, today)
+
+            datetimes = self.datetime.search([
+                    ('datetime', '=', today),
+                    ])
+            self.assertEqual(datetimes, [datetime1])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '=', tomorrow),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '=', None),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '!=', today),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '!=', tomorrow),
+                    ])
+            self.assertEqual(datetimes, [datetime1])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '!=', None),
+                    ])
+            self.assertEqual(datetimes, [datetime1])
+
+            datetimes = self.datetime.search([
+                    ('datetime', 'in', [today]),
+                    ])
+            self.assertEqual(datetimes, [datetime1])
+
+            datetimes = self.datetime.search([
+                    ('datetime', 'in', [tomorrow]),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetimes = self.datetime.search([
+                    ('datetime', 'in', [None]),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetimes = self.datetime.search([
+                    ('datetime', 'in', []),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetimes = self.datetime.search([
+                    ('datetime', 'not in', [today]),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetimes = self.datetime.search([
+                    ('datetime', 'not in', [tomorrow]),
+                    ])
+            self.assertEqual(datetimes, [datetime1])
+
+            datetimes = self.datetime.search([
+                    ('datetime', 'not in', [None]),
+                    ])
+            self.assertEqual(datetimes, [datetime1])
+
+            datetimes = self.datetime.search([
+                    ('datetime', 'not in', []),
+                    ])
+            self.assertEqual(datetimes, [datetime1])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '<', tomorrow),
+                    ])
+            self.assertEqual(datetimes, [datetime1])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '<', yesterday),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '<', today),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '<=', today),
+                    ])
+            self.assertEqual(datetimes, [datetime1])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '<=', yesterday),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '<=', tomorrow),
+                    ])
+            self.assertEqual(datetimes, [datetime1])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '>', tomorrow),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '>', yesterday),
+                    ])
+            self.assertEqual(datetimes, [datetime1])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '>', today),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '>=', tomorrow),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '>=', yesterday),
+                    ])
+            self.assertEqual(datetimes, [datetime1])
+
+            datetimes = self.datetime.search([
+                    ('datetime', '>=', today),
+                    ])
+            self.assertEqual(datetimes, [datetime1])
+
+            datetime2 = self.datetime.create({
+                    'datetime': yesterday,
                     })
-
-            self.failUnlessRaises(Exception, self.datetime.create, {
-                'datetime': datetime.date.today(),
-                })
-
-            self.failUnlessRaises(Exception, self.datetime.write, datetime1_id,
-                    {
-                        'datetime': datetime.date.today(),
+            self.assert_(datetime2)
+            self.assertEqual(datetime2.datetime, yesterday)
+
+            datetimes = self.datetime.search([
+                    ('datetime', '=', yesterday),
+                    ])
+            self.assertEqual(datetimes, [datetime2])
+
+            datetimes = self.datetime.search([
+                    ('datetime', 'in', [yesterday, today]),
+                    ])
+            self.assertEqual(datetimes, [datetime1, datetime2])
+
+            datetimes = self.datetime.search([
+                    ('datetime', 'not in', [yesterday, today]),
+                    ])
+            self.assertEqual(datetimes, [])
+
+            datetime3 = self.datetime.create({})
+            self.assert_(datetime3)
+            self.assertEqual(datetime3.datetime, None)
+
+            datetime4 = self.datetime_default.create({})
+            self.assert_(datetime4)
+            self.assertEqual(datetime4.datetime, default_datetime)
+
+            self.datetime.write([datetime1], {
+                    'datetime': yesterday,
                     })
-
-            self.failUnlessRaises(Exception, self.datetime.create, {
-                'datetime': '2009-13-01 12:30:00',
-                })
-
-            self.failUnlessRaises(Exception, self.datetime.write, datetime1_id,
-                    {
-                        'datetime': '2009-02-29 12:30:00',
+            self.assertEqual(datetime1.datetime, yesterday)
+
+            self.datetime.write([datetime2], {
+                    'datetime': today,
                     })
-
-            self.failUnlessRaises(Exception, self.datetime.write, datetime1_id,
-                    {
-                        'datetime': '2009-01-01 25:00:00',
+            self.assertEqual(datetime2.datetime, today)
+
+            self.assertRaises(Exception, self.datetime.create, {
+                    'datetime': 'test',
                     })
 
-            datetime5_id = self.datetime.create({
-                'datetime': '2009-01-01 12:00:00',
-                })
-            self.assert_(datetime5_id)
-            datetime5 = self.datetime.read(datetime5_id, ['datetime'])
-            self.assertEqual(datetime5['datetime'],
+            self.assertRaises(Exception, self.datetime.write, [datetime1],
+                {
+                    'datetime': 'test',
+                    })
+
+            self.assertRaises(Exception, self.datetime.create, {
+                    'datetime': 1,
+                    })
+
+            self.assertRaises(Exception, self.datetime.write, [datetime1],
+                {
+                    'datetime': 1,
+                    })
+
+            self.assertRaises(Exception, self.datetime.create, {
+                    'datetime': datetime.date.today(),
+                    })
+
+            self.assertRaises(Exception, self.datetime.write, [datetime1],
+                {
+                    'datetime': datetime.date.today(),
+                    })
+
+            self.assertRaises(Exception, self.datetime.create, {
+                    'datetime': '2009-13-01 12:30:00',
+                    })
+
+            self.assertRaises(Exception, self.datetime.write, [datetime1],
+                {
+                    'datetime': '2009-02-29 12:30:00',
+                    })
+
+            self.assertRaises(Exception, self.datetime.write, [datetime1],
+                {
+                    'datetime': '2009-01-01 25:00:00',
+                    })
+
+            datetime5 = self.datetime.create({
+                    'datetime': '2009-01-01 12:00:00',
+                    })
+            self.assert_(datetime5)
+            self.assertEqual(datetime5.datetime,
                 datetime.datetime(2009, 1, 1, 12, 0, 0))
 
-            self.failUnlessRaises(Exception, self.datetime_required.create, {})
+            self.assertRaises(Exception, self.datetime_required.create, {})
             transaction.cursor.rollback()
 
-            datetime6_id = self.datetime_required.create({
-                'datetime': today,
-                })
-            self.assert_(datetime6_id)
-
-            datetime7_id = self.datetime.create({
-                'datetime': None,
-                })
-            self.assert_(datetime7_id)
-
-            datetime8_id = self.datetime.create({
-                'datetime': None,
-                })
-            self.assert_(datetime8_id)
-
-            datetime9_id = self.datetime.create({
-                'datetime': today.replace(microsecond=1),
-                })
-            self.assert_(datetime9_id)
-            datetime9 = self.datetime.read(datetime9_id, ['datetime'])
-            self.assert_(datetime9['datetime'] == today)
+            datetime6 = self.datetime_required.create({
+                    'datetime': today,
+                    })
+            self.assert_(datetime6)
+
+            datetime7 = self.datetime.create({
+                    'datetime': None,
+                    })
+            self.assert_(datetime7)
+
+            datetime8 = self.datetime.create({
+                    'datetime': None,
+                    })
+            self.assert_(datetime8)
+
+            datetime9 = self.datetime.create({
+                    'datetime': today.replace(microsecond=1),
+                    })
+            self.assert_(datetime9)
+            self.assertEqual(datetime9.datetime, today)
 
             # Test format
             self.assert_(self.datetime_format.create({
                         'datetime': datetime.datetime(2009, 1, 1, 12, 30),
                         }))
-            self.failUnlessRaises(Exception, self.datetime_format.create, {
+            self.assertRaises(Exception, self.datetime_format.create, {
                     'datetime': datetime.datetime(2009, 1, 1, 12, 30, 25),
                     })
 
@@ -2126,251 +2018,239 @@
             night = datetime.time(20, 00)
             default_time = datetime.time(16, 30)
 
-            time1_id = self.time.create({
+            time1 = self.time.create({
                     'time': evening,
                     })
-            self.assert_(time1_id)
-
-            time1 = self.time.read(time1_id, ['time'])
-            self.assertEqual(time1['time'], evening)
-
-            time_ids = self.time.search([
-                ('time', '=', evening),
-                ])
-            self.assert_(time_ids == [time1_id])
-
-            time_ids = self.time.search([
-                ('time', '=', night),
-                ])
-            self.assert_(time_ids == [])
-
-            time_ids = self.time.search([
-                ('time', '=', False),
-                ])
-            self.assert_(time_ids == [])
-
-            time_ids = self.time.search([
-                ('time', '!=', evening),
-                ])
-            self.assert_(time_ids == [])
-
-            time_ids = self.time.search([
-                ('time', '!=', night),
-                ])
-            self.assert_(time_ids == [time1_id])
-
-            time_ids = self.time.search([
-                ('time', '!=', False),
-                ])
-            self.assert_(time_ids == [time1_id])
-
-            time_ids = self.time.search([
-                ('time', 'in', [evening]),
-                ])
-            self.assert_(time_ids == [time1_id])
-
-            time_ids = self.time.search([
-                ('time', 'in', [night]),
-                ])
-            self.assert_(time_ids == [])
-
-            time_ids = self.time.search([
-                ('time', 'in', [False]),
-                ])
-            self.assert_(time_ids == [])
-
-            time_ids = self.time.search([
-                ('time', 'in', []),
-                ])
-            self.assert_(time_ids == [])
-
-            time_ids = self.time.search([
-                ('time', 'not in', [evening]),
-                ])
-            self.assert_(time_ids == [])
-
-            time_ids = self.time.search([
-                ('time', 'not in', [night]),
-                ])
-            self.assert_(time_ids == [time1_id])
-
-            time_ids = self.time.search([
-                ('time', 'not in', [False]),
-                ])
-            self.assert_(time_ids == [time1_id])
-
-            time_ids = self.time.search([
-                ('time', 'not in', []),
-                ])
-            self.assert_(time_ids == [time1_id])
-
-            time_ids = self.time.search([
-                ('time', '<', night),
-                ])
-            self.assert_(time_ids == [time1_id])
-
-            time_ids = self.time.search([
-                ('time', '<', pre_evening),
-                ])
-            self.assert_(time_ids == [])
-
-            time_ids = self.time.search([
-                ('time', '<', evening),
-                ])
-            self.assert_(time_ids == [])
-
-            time_ids = self.time.search([
-                ('time', '<=', evening),
-                ])
-            self.assert_(time_ids == [time1_id])
-
-            time_ids = self.time.search([
-                ('time', '<=', pre_evening),
-                ])
-            self.assert_(time_ids == [])
-
-            time_ids = self.time.search([
-                ('time', '<=', night),
-                ])
-            self.assert_(time_ids == [time1_id])
-
-            time_ids = self.time.search([
-                ('time', '>', night),
-                ])
-            self.assert_(time_ids == [])
-
-            time_ids = self.time.search([
-                ('time', '>', pre_evening),
-                ])
-            self.assert_(time_ids == [time1_id])
-
-            time_ids = self.time.search([
-                ('time', '>', evening),
-                ])
-            self.assert_(time_ids == [])
-
-            time_ids = self.time.search([
-                ('time', '>=', night),
-                ])
-            self.assert_(time_ids == [])
-
-            time_ids = self.time.search([
-                ('time', '>=', pre_evening),
-                ])
-            self.assert_(time_ids == [time1_id])
-
-            time_ids = self.time.search([
-                ('time', '>=', evening),
-                ])
-            self.assert_(time_ids == [time1_id])
-
-            time2_id = self.time.create({
-                'time': pre_evening,
-                })
-            self.assert_(time2_id)
-
-            time2 = self.time.read(time2_id, ['time'])
-            self.assert_(time2['time'] == pre_evening)
-
-            time_ids = self.time.search([
-                ('time', '=', pre_evening),
-                ])
-            self.assert_(time_ids == [time2_id])
-
-            time_ids = self.time.search([
-                ('time', 'in', [pre_evening, evening]),
-                ])
-            self.assert_(time_ids == [time1_id, time2_id])
-
-            time_ids = self.time.search([
-                ('time', 'not in', [pre_evening, evening]),
-                ])
-            self.assert_(time_ids == [])
-
-            time3_id = self.time.create({})
-            self.assert_(time3_id)
-
-            time3 = self.time.read(time3_id, ['time'])
-            self.assert_(time3['time'] == None)
-
-            time4_id = self.time_default.create({})
-            self.assert_(time4_id)
-
-            time4 = self.time_default.read(time4_id, ['time'])
-            self.assert_(time4['time'] == default_time)
-
-            self.time.write(time1_id, {
-                'time': pre_evening,
-                })
-            time1 = self.time.read(time1_id, ['time'])
-            self.assert_(time1['time'] == pre_evening)
-
-            self.time.write(time2_id, {
-                'time': evening,
-                })
-            time2 = self.time.read(time2_id, ['time'])
-            self.assert_(time2['time'] == evening)
-
-            self.failUnlessRaises(Exception, self.time.create, {
+            self.assert_(time1)
+            self.assertEqual(time1.time, evening)
+
+            times = self.time.search([
+                    ('time', '=', evening),
+                    ])
+            self.assertEqual(times, [time1])
+
+            times = self.time.search([
+                    ('time', '=', night),
+                    ])
+            self.assertEqual(times, [])
+
+            times = self.time.search([
+                    ('time', '=', None),
+                    ])
+            self.assertEqual(times, [])
+
+            times = self.time.search([
+                    ('time', '!=', evening),
+                    ])
+            self.assertEqual(times, [])
+
+            times = self.time.search([
+                    ('time', '!=', night),
+                    ])
+            self.assertEqual(times, [time1])
+
+            times = self.time.search([
+                    ('time', '!=', None),
+                    ])
+            self.assertEqual(times, [time1])
+
+            times = self.time.search([
+                    ('time', 'in', [evening]),
+                    ])
+            self.assertEqual(times, [time1])
+
+            times = self.time.search([
+                    ('time', 'in', [night]),
+                    ])
+            self.assertEqual(times, [])
+
+            times = self.time.search([
+                    ('time', 'in', [None]),
+                    ])
+            self.assertEqual(times, [])
+
+            times = self.time.search([
+                    ('time', 'in', []),
+                    ])
+            self.assertEqual(times, [])
+
+            times = self.time.search([
+                    ('time', 'not in', [evening]),
+                    ])
+            self.assertEqual(times, [])
+
+            times = self.time.search([
+                    ('time', 'not in', [night]),
+                    ])
+            self.assertEqual(times, [time1])
+
+            times = self.time.search([
+                    ('time', 'not in', [None]),
+                    ])
+            self.assertEqual(times, [time1])
+
+            times = self.time.search([
+                    ('time', 'not in', []),
+                    ])
+            self.assertEqual(times, [time1])
+
+            times = self.time.search([
+                    ('time', '<', night),
+                    ])
+            self.assertEqual(times, [time1])
+
+            times = self.time.search([
+                    ('time', '<', pre_evening),
+                    ])
+            self.assertEqual(times, [])
+
+            times = self.time.search([
+                    ('time', '<', evening),
+                    ])
+            self.assertEqual(times, [])
+
+            times = self.time.search([
+                    ('time', '<=', evening),
+                    ])
+            self.assertEqual(times, [time1])
+
+            times = self.time.search([
+                    ('time', '<=', pre_evening),
+                    ])
+            self.assertEqual(times, [])
+
+            times = self.time.search([
+                    ('time', '<=', night),
+                    ])
+            self.assertEqual(times, [time1])
+
+            times = self.time.search([
+                    ('time', '>', night),
+                    ])
+            self.assertEqual(times, [])
+
+            times = self.time.search([
+                    ('time', '>', pre_evening),
+                    ])
+            self.assertEqual(times, [time1])
+
+            times = self.time.search([
+                    ('time', '>', evening),
+                    ])
+            self.assertEqual(times, [])
+
+            times = self.time.search([
+                    ('time', '>=', night),
+                    ])
+            self.assertEqual(times, [])
+
+            times = self.time.search([
+                    ('time', '>=', pre_evening),
+                    ])
+            self.assertEqual(times, [time1])
+
+            times = self.time.search([
+                    ('time', '>=', evening),
+                    ])
+            self.assertEqual(times, [time1])
+
+            time2 = self.time.create({
+                    'time': pre_evening,
+                    })
+            self.assert_(time2)
+            self.assertEqual(time2.time, pre_evening)
+
+            times = self.time.search([
+                    ('time', '=', pre_evening),
+                    ])
+            self.assertEqual(times, [time2])
+
+            times = self.time.search([
+                    ('time', 'in', [pre_evening, evening]),
+                    ])
+            self.assertEqual(times, [time1, time2])
+
+            times = self.time.search([
+                    ('time', 'not in', [pre_evening, evening]),
+                    ])
+            self.assertEqual(times, [])
+
+            time3 = self.time.create({})
+            self.assert_(time3)
+            self.assertEqual(time3.time, None)
+
+            time4 = self.time_default.create({})
+            self.assert_(time4)
+            self.assertEqual(time4.time, default_time)
+
+            self.time.write([time1], {
+                    'time': pre_evening,
+                    })
+            self.assertEqual(time1.time, pre_evening)
+
+            self.time.write([time2], {
+                    'time': evening,
+                    })
+            self.assertEqual(time2.time, evening)
+
+            self.assertRaises(Exception, self.time.create, {
                     'time': 'test',
                     })
 
-            self.failUnlessRaises(Exception, self.time.write, time1_id,
+            self.assertRaises(Exception, self.time.write, [time1],
                 {
                     'time': 'test',
                     })
 
-            self.failUnlessRaises(Exception, self.time.create, {
+            self.assertRaises(Exception, self.time.create, {
                     'time': 1,
                     })
 
-            self.failUnlessRaises(Exception, self.time.write, time1_id,
+            self.assertRaises(Exception, self.time.write, [time1],
                 {
                     'time': 1,
                     })
 
-            self.failUnlessRaises(Exception, self.time.write, time1_id,
+            self.assertRaises(Exception, self.time.write, [time1],
                 {
                     'time': '25:00:00',
                     })
 
-            time5_id = self.time.create({
-                'time': '12:00:00',
-                })
-            self.assert_(time5_id)
-            time5 = self.time.read(time5_id, ['time'])
-            self.assert_(time5['time'] == datetime.time(12, 0))
-
-            self.failUnlessRaises(Exception, self.time_required.create, {})
+            time5 = self.time.create({
+                    'time': '12:00:00',
+                    })
+            self.assert_(time5)
+            self.assertEqual(time5.time, datetime.time(12, 0))
+
+            self.assertRaises(Exception, self.time_required.create, {})
             transaction.cursor.rollback()
 
-            time6_id = self.time_required.create({
-                'time': evening,
-                })
-            self.assert_(time6_id)
-
-            time7_id = self.time.create({
-                'time': None,
-                })
-            self.assert_(time7_id)
-
-            time8_id = self.time.create({
-                'time': False,
-                })
-            self.assert_(time8_id)
-
-            time9_id = self.time.create({
-                'time': evening.replace(microsecond=1),
-                })
-            self.assert_(time9_id)
-            time9 = self.time.read(time9_id, ['time'])
-            self.assert_(time9['time'] == evening)
+            time6 = self.time_required.create({
+                    'time': evening,
+                    })
+            self.assert_(time6)
+
+            time7 = self.time.create({
+                    'time': None,
+                    })
+            self.assert_(time7)
+
+            time8 = self.time.create({
+                    'time': False,
+                    })
+            self.assert_(time8)
+
+            time9 = self.time.create({
+                    'time': evening.replace(microsecond=1),
+                    })
+            self.assert_(time9)
+            self.assertEqual(time9.time, evening)
 
             # Test format
             self.assert_(self.time_format.create({
                         'time': datetime.time(12, 30),
                         }))
-            self.failUnlessRaises(Exception, self.time_format.create, {
+            self.assertRaises(Exception, self.time_format.create, {
                     'time': datetime.time(12, 30, 25),
                     })
 
@@ -2382,122 +2262,108 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            target1_id = self.one2one_target.create({
-                'name': 'target1',
-                })
-            one2one1_id = self.one2one.create({
-                'name': 'origin1',
-                'one2one': target1_id,
-                })
-            self.assert_(one2one1_id)
-
-            one2one1 = self.one2one.read(one2one1_id, ['one2one',
-                'one2one.name'])
-            self.assert_(one2one1['one2one'] == target1_id)
-            self.assert_(one2one1['one2one.name'] == 'target1')
-
-            one2one_ids = self.one2one.search([
-                ('one2one', '=', 'target1'),
-                ])
-            self.assert_(one2one_ids == [one2one1_id])
-
-            one2one_ids = self.one2one.search([
-                ('one2one', '!=', 'target1'),
-                ])
-            self.assert_(one2one_ids == [])
-
-            one2one_ids = self.one2one.search([
-                ('one2one', 'in', [target1_id]),
-                ])
-            self.assert_(one2one_ids == [one2one1_id])
-
-            one2one_ids = self.one2one.search([
-                ('one2one', 'in', [0]),
-                ])
-            self.assert_(one2one_ids == [])
-
-            one2one_ids = self.one2one.search([
-                ('one2one', 'not in', [target1_id]),
-                ])
-            self.assert_(one2one_ids == [])
-
-            one2one_ids = self.one2one.search([
-                ('one2one', 'not in', [0]),
-                ])
-            self.assert_(one2one_ids == [one2one1_id])
-
-            one2one_ids = self.one2one.search([
-                ('one2one.name', '=', 'target1'),
-                ])
-            self.assert_(one2one_ids == [one2one1_id])
-
-            one2one_ids = self.one2one.search([
-                ('one2one.name', '!=', 'target1'),
-                ])
-            self.assert_(one2one_ids == [])
-
-            one2one = self.one2one.browse(one2one1_id)
-            self.assert_(one2one.one2one.name == 'target1')
-
-            one2one2_id = self.one2one.create({
-                'name': 'origin2',
-                })
-            self.assert_(one2one2_id)
-
-            one2one2 = self.one2one.read(one2one2_id, ['one2one'])
-            self.assert_(one2one2['one2one'] == False)
-
-            one2one_ids = self.one2one.search([
-                ('one2one', '=', False),
-                ])
-            self.assert_(one2one_ids == [one2one2_id])
-
-            target2_id = self.one2one_target.create({
-                'name': 'target2',
-                })
-            self.one2one.write(one2one2_id, {
-                'one2one': target2_id,
-                })
-            target2_id = self.one2one_target.search([
-                ('name', '=', 'target2'),
-                ])[0]
-            one2one2 = self.one2one.read(one2one2_id, ['one2one'])
-            self.assert_(one2one2['one2one'] == target2_id)
-
-            self.one2one.write(one2one2_id, {
-                'one2one': False,
-                })
-            one2one2 = self.one2one.read(one2one2_id, ['one2one'])
-            self.assert_(one2one2['one2one'] == False)
-
-            one2one2 = self.one2one.browse(one2one2_id)
-            self.assert_(not one2one2.one2one)
-
-            self.failUnlessRaises(Exception, self.one2one.create, {
-                'name': 'one2one3',
-                'one2one': target1_id,
-                })
+            target1 = self.one2one_target.create({
+                    'name': 'target1',
+                    })
+            one2one1 = self.one2one.create({
+                    'name': 'origin1',
+                    'one2one': target1.id,
+                    })
+            self.assert_(one2one1)
+            self.assertEqual(one2one1.one2one, target1)
+
+            self.assertEqual(self.one2one.read([one2one1.id],
+                    ['one2one.name'])[0]['one2one.name'], 'target1')
+
+            one2ones = self.one2one.search([
+                    ('one2one', '=', 'target1'),
+                    ])
+            self.assertEqual(one2ones, [one2one1])
+
+            one2ones = self.one2one.search([
+                    ('one2one', '!=', 'target1'),
+                    ])
+            self.assertEqual(one2ones, [])
+
+            one2ones = self.one2one.search([
+                    ('one2one', 'in', [target1.id]),
+                    ])
+            self.assertEqual(one2ones, [one2one1])
+
+            one2ones = self.one2one.search([
+                    ('one2one', 'in', [0]),
+                    ])
+            self.assertEqual(one2ones, [])
+
+            one2ones = self.one2one.search([
+                    ('one2one', 'not in', [target1.id]),
+                    ])
+            self.assertEqual(one2ones, [])
+
+            one2ones = self.one2one.search([
+                    ('one2one', 'not in', [0]),
+                    ])
+            self.assertEqual(one2ones, [one2one1])
+
+            one2ones = self.one2one.search([
+                    ('one2one.name', '=', 'target1'),
+                    ])
+            self.assertEqual(one2ones, [one2one1])
+
+            one2ones = self.one2one.search([
+                    ('one2one.name', '!=', 'target1'),
+                    ])
+            self.assertEqual(one2ones, [])
+
+            one2one2 = self.one2one.create({
+                    'name': 'origin2',
+                    })
+            self.assert_(one2one2)
+            self.assertEqual(one2one2.one2one, None)
+
+            one2ones = self.one2one.search([
+                    ('one2one', '=', None),
+                    ])
+            self.assertEqual(one2ones, [one2one2])
+
+            target2 = self.one2one_target.create({
+                    'name': 'target2',
+                    })
+            self.one2one.write([one2one2], {
+                    'one2one': target2.id,
+                    })
+            self.assertEqual(one2one2.one2one, target2)
+
+            self.one2one.write([one2one2], {
+                    'one2one': None,
+                    })
+            self.assertEqual(one2one2.one2one, None)
+
+            self.assertRaises(Exception, self.one2one.create, {
+                    'name': 'one2one3',
+                    'one2one': target1.id,
+                    })
             transaction.cursor.rollback()
 
-            self.failUnlessRaises(Exception, self.one2one.write, one2one2_id, {
-                'one2one': target1_id,
-                })
+            self.assertRaises(Exception, self.one2one.write, [one2one2], {
+                    'one2one': target1.id,
+                    })
             transaction.cursor.rollback()
 
-            self.failUnlessRaises(Exception, self.one2one_required.create, {
-                'name': 'one2one3',
-                })
+            self.assertRaises(Exception, self.one2one_required.create, {
+                    'name': 'one2one3',
+                    })
             transaction.cursor.rollback()
 
-            target3_id = self.one2one_target.create({
-                'name': 'target3_id',
-                })
-
-            one2one3_id = self.one2one_required.create({
-                'name': 'one2one3',
-                'one2one': target3_id,
-                })
-            self.assert_(one2one3_id)
+            target3 = self.one2one_target.create({
+                    'name': 'target3',
+                    })
+
+            one2one3 = self.one2one_required.create({
+                    'name': 'one2one3',
+                    'one2one': target3.id,
+                    })
+            self.assert_(one2one3)
 
             transaction.cursor.rollback()
 
@@ -2511,7 +2377,7 @@
                     (self.one2many, self.one2many_target),
                     (self.one2many_reference, self.one2many_reference_target),
                     ):
-                one2many1_id = one2many.create({
+                one2many1 = one2many.create({
                         'name': 'origin1',
                         'targets': [
                             ('create', {
@@ -2519,149 +2385,140 @@
                                     }),
                             ],
                         })
-                self.assert_(one2many1_id)
-
-                one2many1 = one2many.read(one2many1_id, ['targets'])
-                self.assertEqual(len(one2many1['targets']), 1)
-                target1_id, = one2many1['targets']
+                self.assert_(one2many1)
+
+                self.assertEqual(len(one2many1.targets), 1)
+                target1, = one2many1.targets
 
                 # Try with target1 stored in cache
-                target1 = one2many_target.browse(target1_id)
+                target1 = one2many_target(target1.id)
                 target1.origin
-                one2many1 = one2many.read(one2many1_id, ['targets'])
-                self.assertEqual(one2many1['targets'], [target1_id])
-
-                one2many_ids = one2many.search([
+                one2many1 = one2many(one2many1)
+                self.assertEqual(one2many1.targets, (target1,))
+
+                one2manys = one2many.search([
                         ('targets', '=', 'target1'),
                         ])
-                self.assertEqual(one2many_ids, [one2many1_id])
-
-                one2many_ids = one2many.search([
+                self.assertEqual(one2manys, [one2many1])
+
+                one2manys = one2many.search([
                         ('targets', '!=', 'target1'),
                         ])
-                self.assertEqual(one2many_ids, [])
-
-                one2many_ids = one2many.search([
-                        ('targets', 'in', [target1_id]),
+                self.assertEqual(one2manys, [])
+
+                one2manys = one2many.search([
+                        ('targets', 'in', [target1.id]),
                         ])
-                self.assertEqual(one2many_ids, [one2many1_id])
-
-                one2many_ids = one2many.search([
+                self.assertEqual(one2manys, [one2many1])
+
+                one2manys = one2many.search([
                         ('targets', 'in', [0]),
                         ])
-                self.assertEqual(one2many_ids, [])
-
-                one2many_ids = one2many.search([
-                        ('targets', 'not in', [target1_id]),
+                self.assertEqual(one2manys, [])
+
+                one2manys = one2many.search([
+                        ('targets', 'not in', (target1.id,)),
                         ])
-                self.assertEqual(one2many_ids, [])
-
-                one2many_ids = one2many.search([
+                self.assertEqual(one2manys, [])
+
+                one2manys = one2many.search([
                         ('targets', 'not in', [0]),
                         ])
-                self.assertEqual(one2many_ids, [one2many1_id])
-
-                one2many_ids = one2many.search([
+                self.assertEqual(one2manys, [one2many1])
+
+                one2manys = one2many.search([
                         ('targets.name', '=', 'target1'),
                         ])
-                self.assertEqual(one2many_ids, [one2many1_id])
-
-                one2many_ids = one2many.search([
+                self.assertEqual(one2manys, [one2many1])
+
+                one2manys = one2many.search([
                         ('targets.name', '!=', 'target1'),
                         ])
-                self.assertEqual(one2many_ids, [])
-
-                one2many2_id = one2many.create({
+                self.assertEqual(one2manys, [])
+
+                one2many2 = one2many.create({
                         'name': 'origin2',
                         })
-                self.assert_(one2many2_id)
-
-                one2many2 = one2many.read(one2many2_id, ['targets'])
-                self.assertEqual(one2many2['targets'], [])
-
-                one2many_ids = one2many.search([
+                self.assert_(one2many2)
+
+                self.assertEqual(one2many2.targets, ())
+
+                one2manys = one2many.search([
                         ('targets', '=', None),
                         ])
-                self.assertEqual(one2many_ids, [one2many2_id])
-
-                one2many.write(one2many1_id, {
+                self.assertEqual(one2manys, [one2many2])
+
+                one2many.write([one2many1], {
                         'targets': [
-                            ('write', [target1_id], {
+                            ('write', [target1.id], {
                                     'name': 'target1bis',
                                     }),
                             ],
                         })
-                target1 = one2many_target.read(target1_id, ['name'])
-                self.assertEqual(target1['name'], 'target1bis')
-
-                target2_id = one2many_target.create({
+                self.assertEqual(target1.name, 'target1bis')
+
+                target2 = one2many_target.create({
                         'name': 'target2',
                         })
-                one2many.write(one2many1_id, {
+                one2many.write([one2many1], {
                         'targets': [
-                            ('add', [target2_id]),
+                            ('add', [target2.id]),
                             ],
                         })
-                one2many1 = one2many.read(one2many1_id, ['targets'])
-                self.assertEqual(one2many1['targets'],
-                    [target1_id, target2_id])
-
-                one2many.write(one2many1_id, {
+                self.assertEqual(one2many1.targets,
+                    (target1, target2))
+
+                one2many.write([one2many1], {
                         'targets': [
-                            ('unlink', [target2_id]),
+                            ('unlink', [target2.id]),
                             ],
                         })
-                one2many1 = one2many.read(one2many1_id, ['targets'])
-                self.assertEqual(one2many1['targets'], [target1_id])
-                target2_id, = one2many_target.search([
-                        ('id', '=', target2_id),
+                self.assertEqual(one2many1.targets, (target1,))
+                target2, = one2many_target.search([
+                        ('id', '=', target2.id),
                         ])
-                self.assert_(target2_id)
-
-                one2many.write(one2many1_id, {
+                self.assert_(target2)
+
+                one2many.write([one2many1], {
                         'targets': [
                             ('unlink_all',),
                             ],
                         })
-                one2many1 = one2many.read(one2many1_id, ['targets'])
-                self.assertEqual(one2many1['targets'], [])
-                target_ids = one2many_target.search([
-                        ('id', 'in', [target1_id, target2_id]),
+                self.assertEqual(one2many1.targets, ())
+                targets = one2many_target.search([
+                        ('id', 'in', [target1.id, target2.id]),
                         ])
-                self.assertEqual(target_ids, [target1_id, target2_id])
-
-                one2many.write(one2many1_id, {
+                self.assertEqual(targets, [target1, target2])
+
+                one2many.write([one2many1], {
                         'targets': [
-                            ('set', [target1_id, target2_id]),
+                            ('set', [target1.id, target2.id]),
                             ],
                         })
-                one2many1 = one2many.read(one2many1_id, ['targets'])
-                self.assertEqual(one2many1['targets'],
-                    [target1_id, target2_id])
-
-                one2many.write(one2many1_id, {
+                self.assertEqual(one2many1.targets,
+                    (target1, target2))
+
+                one2many.write([one2many1], {
                         'targets': [
-                            ('delete', [target2_id]),
+                            ('delete', [target2.id]),
                             ],
                         })
-                one2many1 = one2many.read(one2many1_id, ['targets'])
-                self.assertEqual(one2many1['targets'], [target1_id])
-                target_ids = one2many_target.search([
-                        ('id', '=', target2_id),
+                self.assertEqual(one2many1.targets, (target1,))
+                targets = one2many_target.search([
+                        ('id', '=', target2.id),
                         ])
-                self.assertEqual(target_ids, [])
-
-                one2many.write(one2many1_id, {
+                self.assertEqual(targets, [])
+
+                one2many.write([one2many1], {
                         'targets': [
                             ('delete_all',),
                             ],
                         })
-                one2many1 = one2many.read(one2many1_id, ['targets'])
-                self.assertEqual(one2many1['targets'], [])
-                target_ids = one2many_target.search([
-                        ('id', '=', target1_id),
+                self.assertEqual(one2many1.targets, ())
+                targets = one2many_target.search([
+                        ('id', '=', target1.id),
                         ])
-                self.assertEqual(target_ids, [])
+                self.assertEqual(targets, [])
 
                 transaction.cursor.rollback()
 
@@ -2708,7 +2565,7 @@
                     (self.many2many_reference,
                         self.many2many_reference_target),
                     ):
-                many2many1_id = many2many.create({
+                many2many1 = many2many.create({
                         'name': 'origin1',
                         'targets': [
                             ('create', {
@@ -2716,143 +2573,134 @@
                                     }),
                             ],
                         })
-                self.assert_(many2many1_id)
-
-                many2many1 = many2many.read(many2many1_id, ['targets'])
-                self.assertEqual(len(many2many1['targets']), 1)
-                target1_id, = many2many1['targets']
-
-                many2many_ids = many2many.search([
+                self.assert_(many2many1)
+
+                self.assertEqual(len(many2many1.targets), 1)
+                target1, = many2many1.targets
+
+                many2manys = many2many.search([
                         ('targets', '=', 'target1'),
                         ])
-                self.assertEqual(many2many_ids, [many2many1_id])
-
-                many2many_ids = many2many.search([
+                self.assertEqual(many2manys, [many2many1])
+
+                many2manys = many2many.search([
                         ('targets', '!=', 'target1'),
                         ])
-                self.assertEqual(many2many_ids, [])
-
-                many2many_ids = many2many.search([
-                        ('targets', 'in', [target1_id]),
+                self.assertEqual(many2manys, [])
+
+                many2manys = many2many.search([
+                        ('targets', 'in', [target1.id]),
                         ])
-                self.assertEqual(many2many_ids, [many2many1_id])
-
-                many2many_ids = many2many.search([
+                self.assertEqual(many2manys, [many2many1])
+
+                many2manys = many2many.search([
                         ('targets', 'in', [0]),
                         ])
-                self.assertEqual(many2many_ids, [])
-
-                many2many_ids = many2many.search([
-                        ('targets', 'not in', [target1_id]),
+                self.assertEqual(many2manys, [])
+
+                many2manys = many2many.search([
+                        ('targets', 'not in', [target1.id]),
                         ])
-                self.assertEqual(many2many_ids, [])
-
-                many2many_ids = many2many.search([
+                self.assertEqual(many2manys, [])
+
+                many2manys = many2many.search([
                         ('targets', 'not in', [0]),
                         ])
-                self.assertEqual(many2many_ids, [many2many1_id])
-
-                many2many_ids = many2many.search([
+                self.assertEqual(many2manys, [many2many1])
+
+                many2manys = many2many.search([
                         ('targets.name', '=', 'target1'),
                         ])
-                self.assertEqual(many2many_ids, [many2many1_id])
-
-                many2many_ids = many2many.search([
+                self.assertEqual(many2manys, [many2many1])
+
+                many2manys = many2many.search([
                         ('targets.name', '!=', 'target1'),
                         ])
-                self.assertEqual(many2many_ids, [])
-
-                many2many2_id = many2many.create({
+                self.assertEqual(many2manys, [])
+
+                many2many2 = many2many.create({
                         'name': 'origin2',
                         })
-                self.assert_(many2many2_id)
-
-                many2many2 = many2many.read(many2many2_id, ['targets'])
-                self.assertEqual(many2many2['targets'], [])
-
-                many2many_ids = many2many.search([
+                self.assert_(many2many2)
+
+                self.assertEqual(many2many2.targets, ())
+
+                many2manys = many2many.search([
                         ('targets', '=', None),
                         ])
-                self.assertEqual(many2many_ids, [many2many2_id])
-
-                many2many.write(many2many1_id, {
+                self.assertEqual(many2manys, [many2many2])
+
+                many2many.write([many2many1], {
                         'targets': [
-                            ('write', [target1_id], {
+                            ('write', [target1.id], {
                                     'name': 'target1bis',
                                     }),
                             ],
                         })
-                target1 = many2many_target.read(target1_id, ['name'])
-                self.assertEqual(target1['name'], 'target1bis')
-
-                target2_id = many2many_target.create({
+                self.assertEqual(target1.name, 'target1bis')
+
+                target2 = many2many_target.create({
                         'name': 'target2',
                         })
-                many2many.write(many2many1_id, {
+                many2many.write([many2many1], {
                         'targets': [
-                            ('add', [target2_id]),
+                            ('add', [target2.id]),
                             ],
                         })
-                many2many1 = many2many.read(many2many1_id, ['targets'])
-                self.assertEqual(many2many1['targets'],
-                    [target1_id, target2_id])
-
-                many2many.write(many2many1_id, {
+                self.assertEqual(many2many1.targets,
+                    (target1, target2))
+
+                many2many.write([many2many1], {
                         'targets': [
-                            ('unlink', [target2_id]),
+                            ('unlink', [target2.id]),
                             ],
                         })
-                many2many1 = many2many.read(many2many1_id, ['targets'])
-                self.assertEqual(many2many1['targets'], [target1_id])
-                target2_id, = many2many_target.search([
-                        ('id', '=', target2_id),
+                self.assertEqual(many2many1.targets, (target1,))
+                target2, = many2many_target.search([
+                        ('id', '=', target2.id),
                         ])
-                self.assert_(target2_id)
-
-                many2many.write(many2many1_id, {
+                self.assert_(target2)
+
+                many2many.write([many2many1], {
                         'targets': [
                             ('unlink_all',),
                             ],
                         })
-                many2many1 = many2many.read(many2many1_id, ['targets'])
-                self.assertEqual(many2many1['targets'], [])
-                target_ids = many2many_target.search([
-                        ('id', 'in', [target1_id, target2_id]),
+                self.assertEqual(many2many1.targets, ())
+                targets = many2many_target.search([
+                        ('id', 'in', [target1.id, target2.id]),
                         ])
-                self.assertEqual(target_ids, [target1_id, target2_id])
-
-                many2many.write(many2many1_id, {
+                self.assertEqual(targets, [target1, target2])
+
+                many2many.write([many2many1], {
                         'targets': [
-                            ('set', [target1_id, target2_id]),
+                            ('set', [target1.id, target2.id]),
                             ],
                         })
-                many2many1 = many2many.read(many2many1_id, ['targets'])
-                self.assertEqual(many2many1['targets'],
-                    [target1_id, target2_id])
-
-                many2many.write(many2many1_id, {
+                self.assertEqual(many2many1.targets,
+                    (target1, target2))
+
+                many2many.write([many2many1], {
                         'targets': [
-                            ('delete', [target2_id]),
+                            ('delete', [target2.id]),
                             ],
                         })
-                many2many1 = many2many.read(many2many1_id, ['targets'])
-                self.assertEqual(many2many1['targets'], [target1_id])
-                target_ids = many2many_target.search([
-                        ('id', '=', target2_id),
+                self.assertEqual(many2many1.targets, (target1,))
+                targets = many2many_target.search([
+                        ('id', '=', target2.id),
                         ])
-                self.assertEqual(target_ids, [])
-
-                many2many.write(many2many1_id, {
+                self.assertEqual(targets, [])
+
+                many2many.write([many2many1], {
                         'targets': [
                             ('delete_all',),
                             ],
                         })
-                many2many1 = many2many.read(many2many1_id, ['targets'])
-                self.assertEqual(many2many1['targets'], [])
-                target_ids = many2many_target.search([
-                        ('id', '=', target1_id),
+                self.assertEqual(many2many1.targets, ())
+                targets = many2many_target.search([
+                        ('id', '=', target1.id),
                         ])
-                self.assertEqual(target_ids, [])
+                self.assertEqual(targets, [])
 
                 transaction.cursor.rollback()
 
@@ -2891,140 +2739,125 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            target1_id = self.reference_target.create({
+            target1 = self.reference_target.create({
                     'name': 'target1',
                     })
-            reference1_id = self.reference.create({
+            reference1 = self.reference.create({
                     'name': 'reference1',
-                    'reference': 'test.reference.target,%s' % target1_id,
+                    'reference': str(target1),
                     })
-            self.assert_(reference1_id)
-
-            reference1 = self.reference.read(reference1_id, ['reference'])
-            self.assertEqual(reference1['reference'],
-                'test.reference.target,%s' % target1_id)
-
-            reference_ids = self.reference.search([
-                    ('reference', '=',
-                        'test.reference.target,%s' % target1_id),
+            self.assert_(reference1)
+
+            self.assertEqual(reference1.reference, target1)
+
+            references = self.reference.search([
+                    ('reference', '=', str(target1)),
                     ])
-            self.assertEqual(reference_ids, [reference1_id])
-
-            reference_ids = self.reference.search([
-                    ('reference', '=',
-                        ('test.reference.target', target1_id)),
+            self.assertEqual(references, [reference1])
+
+            references = self.reference.search([
+                    ('reference', '=', str(target1)),
                     ])
-            self.assertEqual(reference_ids, [reference1_id])
-
-            reference_ids = self.reference.search([
-                    ('reference', '=',
-                        ['test.reference.target', target1_id]),
+            self.assertEqual(references, [reference1])
+
+            references = self.reference.search([
+                    ('reference', '=', str(target1)),
                     ])
-            self.assertEqual(reference_ids, [reference1_id])
-
-            reference_ids = self.reference.search([
-                    ('reference', '!=',
-                        'test.reference.target,%s' % target1_id),
+            self.assertEqual(references, [reference1])
+
+            references = self.reference.search([
+                    ('reference', '!=', str(target1)),
                     ])
-            self.assertEqual(reference_ids, [])
-
-            reference_ids = self.reference.search([
-                    ('reference', '!=',
-                        ('test.reference.target', target1_id)),
+            self.assertEqual(references, [])
+
+            references = self.reference.search([
+                    ('reference', '!=', str(target1)),
                     ])
-            self.assertEqual(reference_ids, [])
-
-            reference_ids = self.reference.search([
+            self.assertEqual(references, [])
+
+            references = self.reference.search([
+                    ('reference', 'in', [str(target1)]),
+                    ])
+            self.assertEqual(references, [reference1])
+
+            references = self.reference.search([
                     ('reference', 'in',
-                        ['test.reference.target,%s' % target1_id]),
+                        [('test.reference.target', target1.id)]),
                     ])
-            self.assertEqual(reference_ids, [reference1_id])
-
-            reference_ids = self.reference.search([
-                    ('reference', 'in',
-                        [('test.reference.target', target1_id)]),
-                    ])
-            self.assertEqual(reference_ids, [reference1_id])
-
-            reference_ids = self.reference.search([
+            self.assertEqual(references, [reference1])
+
+            references = self.reference.search([
                     ('reference', 'in', [None]),
                     ])
-            self.assertEqual(reference_ids, [])
-
-            reference_ids = self.reference.search([
+            self.assertEqual(references, [])
+
+            references = self.reference.search([
+                    ('reference', 'not in', [str(target1)]),
+                    ])
+            self.assertEqual(references, [])
+
+            references = self.reference.search([
                     ('reference', 'not in',
-                        ['test.reference.target,%s' % target1_id]),
+                        [('test.reference.target', target1.id)]),
                     ])
-            self.assertEqual(reference_ids, [])
-
-            reference_ids = self.reference.search([
-                    ('reference', 'not in',
-                        [('test.reference.target', target1_id)]),
-                    ])
-            self.assertEqual(reference_ids, [])
-
-            reference_ids = self.reference.search([
+            self.assertEqual(references, [])
+
+            references = self.reference.search([
                     ('reference', 'not in', [None]),
                     ])
-            self.assertEqual(reference_ids, [reference1_id])
-
-            reference2_id = self.reference.create({
+            self.assertEqual(references, [reference1])
+
+            reference2 = self.reference.create({
                     'name': 'reference2',
                     })
-            self.assert_(reference2_id)
-
-            reference2 = self.reference.read(reference2_id, ['reference'])
-            self.assertEqual(reference2['reference'], None)
-
-            reference_ids = self.reference.search([
+            self.assert_(reference2)
+
+            self.assertEqual(reference2.reference, None)
+
+            references = self.reference.search([
                     ('reference', '=', None),
                     ])
-            self.assertEqual(reference_ids, [reference2_id])
-
-            target2_id = self.reference_target.create({
+            self.assertEqual(references, [reference2])
+
+            target2 = self.reference_target.create({
                     'name': 'target2',
                     })
 
-            self.reference.write(reference2_id, {
-                    'reference': 'test.reference.target,%s' % target2_id,
+            self.reference.write([reference2], {
+                    'reference': str(target2),
                     })
-            reference2 = self.reference.read(reference2_id, ['reference'])
-            self.assertEqual(reference2['reference'],
-                'test.reference.target,%s' % target2_id)
-
-            self.reference.write(reference2_id, {
+            self.assertEqual(reference2.reference, target2)
+
+            self.reference.write([reference2], {
                     'reference': None,
                     })
-            reference2 = self.reference.read(reference2_id, ['reference'])
-            self.assertEqual(reference2['reference'], None)
-
-            self.reference.write(reference2_id, {
-                    'reference': ('test.reference.target', target2_id),
+            self.assertEqual(reference2.reference, None)
+
+            self.reference.write([reference2], {
+                    'reference': ('test.reference.target', target2.id),
                     })
-            reference2 = self.reference.read(reference2_id, ['reference'])
-            self.assertEqual(reference2['reference'],
-                'test.reference.target,%s' % target2_id)
-
-            reference3_id = self.reference.create({
+            self.assertEqual(reference2.reference, target2)
+
+            reference3 = self.reference.create({
                     'name': 'reference3',
-                    'reference': ('test.reference.target', target1_id),
+                    'reference': ('test.reference.target', target1.id),
                     })
-            self.assert_(reference3_id)
+            self.assert_(reference3)
 
             self.assertRaises(Exception, self.reference_required.create, {
                     'name': 'reference4',
                     })
             transaction.cursor.rollback()
 
-            target4_id = self.reference_target.create({
+            target4 = self.reference_target.create({
                     'name': 'target4_id',
                     })
 
-            reference4_id = self.reference_required.create({
+            reference4 = self.reference_required.create({
                     'name': 'reference4',
-                    'reference': 'test.reference.target,%s' % target4_id,
+                    'reference': str(target4),
                     })
-            self.assert_(reference4_id)
+            self.assert_(reference4)
 
             transaction.cursor.rollback()
 
@@ -3036,272 +2869,264 @@
                 context=CONTEXT) as transaction:
 
             # Test Char
-            prop_id_a = self.property_.create({'char': 'Test'})
-            self.assert_(prop_id_a)
-
-            prop_id_b = self.property_.create({})
-            self.assert_(prop_id_b)
-
-            prop_id_c = self.property_.create({'char': 'FooBar'})
-            self.assert_(prop_id_c)
-
-            prop_a = self.property_.read(prop_id_a, ['char'])
-            self.assert_(prop_a['char'] == 'Test')
-
-            prop_ids = self.property_.search([('char', '=', 'Test')])
-            self.assert_(prop_ids == [prop_id_a])
-
-            prop_ids = self.property_.search([('char', '=', False)])
-            self.assert_(prop_ids == [prop_id_b])
-
-            prop_ids = self.property_.search([('char', '!=', False)])
-            self.assert_(prop_ids == [prop_id_a, prop_id_c])
-
-            prop_ids = self.property_.search([('char', 'like', 'Tes%')])
-            self.assert_(prop_ids == [prop_id_a])
-
-            prop_ids = self.property_.search([('char', 'like', '%Bar')])
-            self.assert_(prop_ids == [prop_id_c])
-
-            prop_ids = self.property_.search([('char', 'not like', 'Tes%')])
-            self.assert_(prop_ids == [prop_id_b, prop_id_c])
-
-            prop_ids = self.property_.search([('char', 'ilike', 'tes%')])
-            self.assert_(prop_ids == [prop_id_a])
-
-            prop_ids = self.property_.search([('char', 'ilike', '%bar')])
-            self.assert_(prop_ids == [prop_id_c])
-
-            prop_ids = self.property_.search([('char', 'not ilike', 'tes%')])
-            self.assert_(prop_ids == [prop_id_b, prop_id_c])
-
-            prop_ids = self.property_.search([('char', 'in', ['Test'])])
-            self.assert_(prop_ids == [prop_id_a])
-
-            prop_ids = self.property_.search([
+            prop_a = self.property_.create({'char': 'Test'})
+            self.assert_(prop_a)
+            self.assertEqual(prop_a.char, 'Test')
+
+            prop_b = self.property_.create({})
+            self.assert_(prop_b)
+            self.assertEqual(prop_b.char, None)
+
+            prop_c = self.property_.create({'char': 'FooBar'})
+            self.assert_(prop_c)
+            self.assertEqual(prop_c.char, 'FooBar')
+
+            props = self.property_.search([('char', '=', 'Test')])
+            self.assertEqual(props, [prop_a])
+
+            props = self.property_.search([('char', '=', None)])
+            self.assertEqual(props, [prop_b])
+
+            props = self.property_.search([('char', '!=', None)])
+            self.assertEqual(props, [prop_a, prop_c])
+
+            props = self.property_.search([('char', 'like', 'Tes%')])
+            self.assertEqual(props, [prop_a])
+
+            props = self.property_.search([('char', 'like', '%Bar')])
+            self.assertEqual(props, [prop_c])
+
+            props = self.property_.search([('char', 'not like', 'Tes%')])
+            self.assertEqual(props, [prop_b, prop_c])
+
+            props = self.property_.search([('char', 'ilike', 'tes%')])
+            self.assert_(props, [prop_a])
+
+            props = self.property_.search([('char', 'ilike', '%bar')])
+            self.assertEqual(props, [prop_c])
+
+            props = self.property_.search([('char', 'not ilike', 'tes%')])
+            self.assertEqual(props, [prop_b, prop_c])
+
+            props = self.property_.search([('char', 'in', ['Test'])])
+            self.assertEqual(props, [prop_a])
+
+            props = self.property_.search([
                     ('char', 'in', ['Test', 'FooBar'])])
-            self.assert_(prop_ids == [prop_id_a, prop_id_c])
-
-            prop_ids = self.property_.search([
+            self.assertEqual(props, [prop_a, prop_c])
+
+            props = self.property_.search([
                     ('char', 'not in', ['Test', 'FooBar'])])
-            self.assert_(prop_ids == [prop_id_b])
-
-            model_field_obj = POOL.get('ir.model.field')
-            property_obj = POOL.get('ir.property')
+            self.assertEqual(props, [prop_b])
 
             # Test default value
-            property_field_id, = model_field_obj.search([
-                        ('model.model', '=', 'test.property'),
-                        ('name', '=', 'char'),
+            property_field, = self.model_field.search([
+                    ('model.model', '=', 'test.property'),
+                    ('name', '=', 'char'),
                     ], limit=1)
-            property_obj.create({
-                        'field': property_field_id,
-                        'value': ',DEFAULT_VALUE',
+            self.ir_property.create({
+                    'field': property_field.id,
+                    'value': ',DEFAULT_VALUE',
                     })
 
-            prop_id_d = self.property_.create({})
-            self.assert_(prop_id_d)
-
-            prop = self.property_.read(prop_id_d, ['char'])
-            self.assert_(prop['char'] == 'DEFAULT_VALUE')
-
-            prop_ids = self.property_.search([('char', '!=', False)])
-            self.assert_(prop_ids == [prop_id_a, prop_id_c, prop_id_d])
-
-            self.property_.write(prop_id_a, {'char': None})
-            prop_a = self.property_.read(prop_id_a, ['char'])
-            self.assert_(prop_a['char'] == None)
-
-            self.property_.write(prop_id_b, {'char': 'Test'})
-            prop_b = self.property_.read(prop_id_b, ['char'])
-            self.assert_(prop_b['char'] == 'Test')
+            prop_d = self.property_.create({})
+            self.assert_(prop_d)
+            self.assertEqual(prop_d.char, 'DEFAULT_VALUE')
+
+            props = self.property_.search([('char', '!=', None)])
+            self.assertEqual(props, [prop_a, prop_c, prop_d])
+
+            self.property_.write([prop_a], {'char': None})
+            self.assertEqual(prop_a.char, None)
+
+            self.property_.write([prop_b], {'char': 'Test'})
+            self.assertEqual(prop_b.char, 'Test')
 
             transaction.cursor.rollback()
 
             # Test Many2One
-            char_id_a = self.char.create({'char': 'Test'})
-            self.assert_(char_id_a)
-
-            char_id_b = self.char.create({'char': 'FooBar'})
-            self.assert_(char_id_b)
-
-            prop_id_a = self.property_.create({'many2one': char_id_a})
-            self.assert_(prop_id_a)
-
-            prop_id_b = self.property_.create({'many2one': char_id_b})
-            self.assert_(prop_id_b)
-
-            prop_id_c = self.property_.create({})
-            self.assert_(prop_id_c)
-
-            prop_ids = self.property_.search([('many2one', '=', char_id_a)])
-            self.assert_(prop_ids == [prop_id_a])
-
-            prop_ids = self.property_.search([('many2one', '!=', False)])
-            self.assert_(prop_ids == [prop_id_a, prop_id_b])
-
-            prop_ids = self.property_.search([('many2one', '=', False)])
-            self.assert_(prop_ids == [prop_id_c])
-
-            prop_a = self.property_.read(prop_id_a, ['many2one'])
-            self.assert_(prop_a['many2one'] == char_id_a)
-
-            prop_ids = self.property_.search([
-                    ('many2one', 'in', [char_id_a, char_id_b])])
-            self.assert_(prop_ids == [prop_id_a, prop_id_b])
-
-            prop_ids = self.property_.search([
-                    ('many2one', 'not in', [char_id_a, char_id_b])])
-            self.assert_(prop_ids == [prop_id_c])
-
-            self.property_.write(prop_id_b, {'many2one': char_id_a})
-            prop_b = self.property_.read(prop_id_b, ['many2one'])
-            self.assert_(prop_b['many2one'] == char_id_a)
+            char_a = self.char.create({'char': 'Test'})
+            self.assert_(char_a)
+
+            char_b = self.char.create({'char': 'FooBar'})
+            self.assert_(char_b)
+
+            prop_a = self.property_.create({'many2one': char_a.id})
+            self.assert_(prop_a)
+            self.assertEqual(prop_a.many2one, char_a)
+
+            prop_b = self.property_.create({'many2one': char_b.id})
+            self.assert_(prop_b)
+            self.assertEqual(prop_b.many2one, char_b)
+
+            prop_c = self.property_.create({})
+            self.assert_(prop_c)
+            self.assertEqual(prop_c.many2one, None)
+
+            props = self.property_.search([('many2one', '=', char_a.id)])
+            self.assertEqual(props, [prop_a])
+
+            props = self.property_.search([('many2one', '!=', None)])
+            self.assertEqual(props, [prop_a, prop_b])
+
+            props = self.property_.search([('many2one', '=', None)])
+            self.assertEqual(props, [prop_c])
+
+            self.assertEqual(prop_a.many2one, char_a)
+
+            props = self.property_.search([
+                    ('many2one', 'in', [char_a.id, char_b.id])])
+            self.assertEqual(props, [prop_a, prop_b])
+
+            props = self.property_.search([
+                    ('many2one', 'not in', [char_a.id, char_b.id])])
+            self.assertEqual(props, [prop_c])
+
+            self.property_.write([prop_b], {'many2one': char_a.id})
+            self.assertEqual(prop_b.many2one, char_a)
 
             transaction.cursor.rollback()
 
             # Test Numeric
-            prop_id_a = self.property_.create({'numeric': Decimal('1.1')})
-            self.assert_(prop_id_a)
-
-            prop_id_b = self.property_.create({'numeric': Decimal('2.6')})
-            self.assert_(prop_id_b)
-
-            prop_id_c = self.property_.create({})
-            self.assert_(prop_id_c)
-
-            prop_ids = self.property_.search([('numeric', '!=', False)])
-            self.assert_(prop_ids == [prop_id_a, prop_id_b])
-
-            prop_ids = self.property_.search([('numeric', '=', False)])
-            self.assert_(prop_ids == [prop_id_c])
-
-            prop_ids = self.property_.search([
+            prop_a = self.property_.create({'numeric': Decimal('1.1')})
+            self.assert_(prop_a)
+            self.assertEqual(prop_a.numeric, Decimal('1.1'))
+
+            prop_b = self.property_.create({'numeric': Decimal('2.6')})
+            self.assert_(prop_b)
+            self.assertEqual(prop_b.numeric, Decimal('2.6'))
+
+            prop_c = self.property_.create({})
+            self.assert_(prop_c)
+            self.assertEqual(prop_c.numeric, None)
+
+            props = self.property_.search([('numeric', '!=', None)])
+            self.assertEqual(props, [prop_a, prop_b])
+
+            props = self.property_.search([('numeric', '=', None)])
+            self.assertEqual(props, [prop_c])
+
+            props = self.property_.search([
                     ('numeric', '=', Decimal('1.1')),
                     ])
-            self.assert_(prop_ids == [prop_id_a])
-
-            prop_ids = self.property_.search([
+            self.assertEqual(props, [prop_a])
+
+            props = self.property_.search([
                     ('numeric', '!=', Decimal('1.1'))])
-            self.assert_(prop_ids == [prop_id_b, prop_id_c])
-
-            prop_ids = self.property_.search([
+            self.assertEqual(props, [prop_b, prop_c])
+
+            props = self.property_.search([
                     ('numeric', '<', Decimal('2.6')),
                     ])
-            self.assert_(prop_ids == [prop_id_a])
-
-            prop_ids = self.property_.search([
+            self.assertEqual(props, [prop_a])
+
+            props = self.property_.search([
                     ('numeric', '<=', Decimal('2.6'))])
-            self.assert_(prop_ids == [prop_id_a, prop_id_b])
-
-            prop_ids = self.property_.search([
+            self.assertEqual(props, [prop_a, prop_b])
+
+            props = self.property_.search([
                     ('numeric', '>', Decimal('1.1')),
                     ])
-            self.assert_(prop_ids == [prop_id_b])
-
-            prop_ids = self.property_.search([
+            self.assertEqual(props, [prop_b])
+
+            props = self.property_.search([
                     ('numeric', '>=', Decimal('1.1'))])
-            self.assert_(prop_ids == [prop_id_a, prop_id_b])
-
-            prop_ids = self.property_.search([
+            self.assertEqual(props, [prop_a, prop_b])
+
+            props = self.property_.search([
                     ('numeric', 'in', [Decimal('1.1')])])
-            self.assert_(prop_ids == [prop_id_a])
-
-            prop_ids = self.property_.search([
+            self.assertEqual(props, [prop_a])
+
+            props = self.property_.search([
                     ('numeric', 'in', [Decimal('1.1'), Decimal('2.6')])])
-            self.assert_(prop_ids == [prop_id_a, prop_id_b])
-
-            prop_ids = self.property_.search([
+            self.assertEqual(props, [prop_a, prop_b])
+
+            props = self.property_.search([
                     ('numeric', 'not in', [Decimal('1.1')])])
-            self.assert_(prop_ids == [prop_id_b, prop_id_c])
-
-            prop_ids = self.property_.search([
+            self.assertEqual(props, [prop_b, prop_c])
+
+            props = self.property_.search([
                     ('numeric', 'not in', [Decimal('1.1'), Decimal('2.6')])])
-            self.assert_(prop_ids == [prop_id_c])
+            self.assertEqual(props, [prop_c])
 
             # Test default value
-            property_field_id, = model_field_obj.search([
-                        ('model.model', '=', 'test.property'),
-                        ('name', '=', 'numeric'),
+            property_field, = self.model_field.search([
+                    ('model.model', '=', 'test.property'),
+                    ('name', '=', 'numeric'),
                     ], limit=1)
-            property_obj.create({
-                        'field': property_field_id,
-                        'value': ',3.7',
+            self.ir_property.create({
+                    'field': property_field.id,
+                    'value': ',3.7',
                     })
 
-            prop_id_d = self.property_.create({})
-            self.assert_(prop_id_d)
-
-            prop_d = self.property_.read(prop_id_d, ['numeric'])
-            self.assert_(prop_d['numeric'] == Decimal('3.7'))
-
-            self.property_.write(prop_id_a, {'numeric': None})
-            prop_a = self.property_.read(prop_id_a, ['numeric'])
-            self.assert_(prop_a['numeric'] == None)
-
-            self.property_.write(prop_id_b, {'numeric': Decimal('3.11')})
-            prop_b = self.property_.read(prop_id_b, ['numeric'])
-            self.assert_(prop_b['numeric'] == Decimal('3.11'))
+            prop_d = self.property_.create({})
+            self.assert_(prop_d)
+            self.assertEqual(prop_d.numeric, Decimal('3.7'))
+
+            self.property_.write([prop_a], {'numeric': None})
+            self.assertEqual(prop_a.numeric, None)
+
+            self.property_.write([prop_b], {'numeric': Decimal('3.11')})
+            self.assertEqual(prop_b.numeric, Decimal('3.11'))
 
             transaction.cursor.rollback()
 
             # Test Selection
-            prop_id_a = self.property_.create({'selection': 'option_a'})
-            self.assert_(prop_id_a)
-
-            prop_id_b = self.property_.create({'selection': 'option_b'})
-            self.assert_(prop_id_b)
-
-            prop_id_c = self.property_.create({})
-            self.assert_(prop_id_c)
-
-            prop_ids = self.property_.search([('selection', '=', 'option_a')])
-            self.assert_(prop_ids == [prop_id_a])
-
-            prop_ids = self.property_.search([('selection', '!=', False)])
-            self.assert_(prop_ids == [prop_id_a, prop_id_b])
-
-            prop_ids = self.property_.search([('selection', '=', False)])
-            self.assert_(prop_ids == [prop_id_c])
-
-            prop_ids = self.property_.search([('selection', '!=', 'option_a')])
-            self.assert_(prop_ids == [prop_id_b, prop_id_c])
-
-            prop_ids = self.property_.search([
+            prop_a = self.property_.create({'selection': 'option_a'})
+            self.assert_(prop_a)
+            self.assertEqual(prop_a.selection, 'option_a')
+
+            prop_b = self.property_.create({'selection': 'option_b'})
+            self.assert_(prop_b)
+            self.assertEqual(prop_b.selection, 'option_b')
+
+            prop_c = self.property_.create({})
+            self.assert_(prop_c)
+            self.assertEqual(prop_c.selection, None)
+
+            props = self.property_.search([('selection', '=', 'option_a')])
+            self.assertEqual(props, [prop_a])
+
+            props = self.property_.search([('selection', '!=', None)])
+            self.assertEqual(props, [prop_a, prop_b])
+
+            props = self.property_.search([('selection', '=', None)])
+            self.assertEqual(props, [prop_c])
+
+            props = self.property_.search([('selection', '!=', 'option_a')])
+            self.assertEqual(props, [prop_b, prop_c])
+
+            props = self.property_.search([
                     ('selection', 'in', ['option_a'])])
-            self.assert_(prop_ids == [prop_id_a])
-
-            prop_ids = self.property_.search([
+            self.assertEqual(props, [prop_a])
+
+            props = self.property_.search([
                     ('selection', 'in', ['option_a', 'option_b'])])
-            self.assert_(prop_ids == [prop_id_a, prop_id_b])
-
-            prop_ids = self.property_.search([
+            self.assertEqual(props, [prop_a, prop_b])
+
+            props = self.property_.search([
                     ('selection', 'not in', ['option_a'])])
-            self.assert_(prop_ids == [prop_id_b, prop_id_c])
+            self.assertEqual(props, [prop_b, prop_c])
 
             # Test default value
-            property_field_id, = model_field_obj.search([
-                        ('model.model', '=', 'test.property'),
-                        ('name', '=', 'selection'),
+            property_field, = self.model_field.search([
+                    ('model.model', '=', 'test.property'),
+                    ('name', '=', 'selection'),
                     ], limit=1)
-            property_obj.create({
-                        'field': property_field_id,
-                        'value': ',option_a',
+            self.ir_property.create({
+                    'field': property_field.id,
+                    'value': ',option_a',
                     })
 
-            prop_id_d = self.property_.create({})
-            self.assert_(prop_id_d)
-
-            prop_d = self.property_.read(prop_id_d, ['selection'])
-            self.assert_(prop_d['selection'] == 'option_a')
-
-            self.property_.write(prop_id_a, {'selection': None})
-            prop_a = self.property_.read(prop_id_a, ['selection'])
-            self.assert_(prop_a['selection'] == None)
-
-            self.property_.write(prop_id_c, {'selection': 'option_b'})
-            prop_c = self.property_.read(prop_id_c, ['selection'])
-            self.assert_(prop_c['selection'] == 'option_b')
+            prop_d = self.property_.create({})
+            self.assert_(prop_d)
+            self.assertEqual(prop_d.selection, 'option_a')
+
+            self.property_.write([prop_a], {'selection': None})
+            self.assertEqual(prop_a.selection, None)
+
+            self.property_.write([prop_c], {'selection': 'option_b'})
+            self.assertEqual(prop_c.selection, 'option_b')
 
             transaction.cursor.rollback()
 
diff -r f6ef988fda71 trytond/tests/test_mixins.py
--- a/trytond/tests/test_mixins.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/tests/test_mixins.py	Sun Sep 23 17:30:03 2012 +0200
@@ -25,27 +25,31 @@
     def testModelURL(self):
         "Test model URLs"
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
-            self.assertEqual(self.urlmodel.get_url(),
+            self.assertEqual(self.urlmodel.__url__,
                 'tryton://%s/%s/model/test.urlobject' % (self.hostname,
                     urllib.quote(DB_NAME)))
 
             server_name = 'michaelscott.paper.test'
             CONFIG['hostname_jsonrpc'] = server_name
-            self.assertEqual(self.urlmodel.get_url(),
+            self.assertEqual(self.urlmodel.__url__,
                 'tryton://%s/%s/model/test.urlobject' % (server_name,
                     urllib.quote(DB_NAME)))
 
+            self.assertEqual(self.urlmodel(1).__url__,
+                'tryton://%s/%s/model/test.urlobject/1' % (server_name,
+                    urllib.quote(DB_NAME)))
+
     def testWizardURL(self):
         "Test wizard URLs"
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
             CONFIG['hostname_jsonrpc'] = None
-            self.assertEqual(self.urlwizard.get_url(),
+            self.assertEqual(self.urlwizard.__url__,
                 'tryton://%s/%s/wizard/test.test_wizard' % (self.hostname,
                     urllib.quote(DB_NAME)))
 
             server_name = 'michaelscott.paper.test'
             CONFIG['hostname_jsonrpc'] = server_name
-            self.assertEqual(self.urlwizard.get_url(),
+            self.assertEqual(self.urlwizard.__url__,
                 'tryton://%s/%s/wizard/test.test_wizard' % (server_name,
                     urllib.quote(DB_NAME)))
 
diff -r f6ef988fda71 trytond/tests/test_modelsingleton.py
--- a/trytond/tests/test_modelsingleton.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/tests/test_modelsingleton.py	Sun Sep 23 17:30:03 2012 +0200
@@ -24,7 +24,7 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            singleton = self.singleton.read(1, ['name'])
+            singleton, = self.singleton.read([1], ['name'])
             self.assert_(singleton['name'] == 'test')
             self.assert_(singleton['id'] == 1)
 
@@ -32,7 +32,7 @@
             self.assert_(singleton['name'] == 'test')
             self.assert_(singleton['id'] == 1)
 
-            singleton = self.singleton.read(1, [
+            singleton, = self.singleton.read([1], [
                 'create_uid',
                 'create_date',
                 'write_uid',
@@ -51,20 +51,17 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            singleton_id = self.singleton.create({'name': 'bar'})
-            self.assert_(singleton_id)
+            singleton = self.singleton.create({'name': 'bar'})
+            self.assert_(singleton)
+            self.assertEqual(singleton.name, 'bar')
 
-            singleton = self.singleton.read(singleton_id, ['name'])
-            self.assert_(singleton['name'] == 'bar')
+            singleton2 = self.singleton.create({'name': 'foo'})
+            self.assertEqual(singleton2, singleton)
 
-            singleton2_id = self.singleton.create({'name': 'foo'})
-            self.assert_(singleton2_id == singleton_id)
+            self.assertEqual(singleton.name, 'foo')
 
-            singleton = self.singleton.read(singleton_id, ['name'])
-            self.assert_(singleton['name'] == 'foo')
-
-            singleton_ids = self.singleton.search([])
-            self.assert_(len(singleton_ids) == 1)
+            singletons = self.singleton.search([])
+            self.assertEqual(singletons, [singleton])
 
             transaction.cursor.rollback()
 
@@ -74,19 +71,19 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            singleton_id = self.singleton.search([])[0]
+            singleton, = self.singleton.search([])
 
-            singleton2_id = self.singleton.copy(singleton_id)
-            self.assert_(singleton2_id == singleton_id)
+            singleton2, = self.singleton.copy([singleton])
+            self.assertEqual(singleton2, singleton)
 
-            singleton_ids = self.singleton.search([])
-            self.assert_(len(singleton_ids) == 1)
+            singletons = self.singleton.search([])
+            self.assertEqual(singletons, [singleton])
 
-            singleton3_id = self.singleton.copy(singleton_id, {'name': 'bar'})
-            self.assert_(singleton3_id == singleton_id)
+            singleton3, = self.singleton.copy([singleton], {'name': 'bar'})
+            self.assertEqual(singleton3, singleton)
 
-            singleton_ids = self.singleton.search([])
-            self.assert_(len(singleton_ids) == 1)
+            singletons = self.singleton.search([])
+            self.assertEqual(singletons, [singleton])
 
             transaction.cursor.rollback()
 
@@ -97,26 +94,26 @@
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
             default = self.singleton.default_get(['name'])
-            self.assert_(default == {'name': 'test'})
+            self.assertEqual(default, {'name': 'test'})
 
             default = self.singleton.default_get(['create_uid'])
-            self.assert_(len(default) == 2)
+            self.assertEqual(len(default), 2)
 
             default = self.singleton.default_get(['create_uid'],
                     with_rec_name=False)
-            self.assert_(len(default) == 1)
+            self.assertEqual(len(default), 1)
 
             self.singleton.create({'name': 'bar'})
 
             default = self.singleton.default_get(['name'])
-            self.assert_(default == {'name': 'bar'})
+            self.assertEqual(default, {'name': 'bar'})
 
             default = self.singleton.default_get(['create_uid'])
-            self.assert_(len(default) == 2)
+            self.assertEqual(len(default), 2)
 
             default = self.singleton.default_get(['create_uid'],
                     with_rec_name=False)
-            self.assert_(len(default) == 1)
+            self.assertEqual(len(default), 1)
 
             transaction.cursor.rollback()
 
@@ -126,14 +123,14 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            singleton_ids = self.singleton.search([])
-            self.assert_(singleton_ids == [1])
+            singletons = self.singleton.search([])
+            self.assertEqual(map(int, singletons), [1])
 
-            singleton_ids = self.singleton.search([])
-            self.assert_(singleton_ids == [1])
+            singletons = self.singleton.search([])
+            self.assertEqual(map(int, singletons), [1])
 
-            singleton_ids = self.singleton.search([], count=True)
-            self.assert_(singleton_ids == 1)
+            count = self.singleton.search([], count=True)
+            self.assertEqual(count, 1)
 
             transaction.cursor.rollback()
 
diff -r f6ef988fda71 trytond/tests/test_mptt.py
--- a/trytond/tests/test_mptt.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/tests/test_mptt.py	Sun Sep 23 17:30:03 2012 +0200
@@ -18,80 +18,76 @@
         self.mptt = POOL.get('test.mptt')
 
     def CheckTree(self, parent_id=False, left=0, right=0):
-        child_ids = self.mptt.search([
-            ('parent', '=', parent_id),
-            ])
-        childs = self.mptt.read(child_ids, ['left', 'right'])
-        childs.sort(key=lambda x: child_ids.index(x['id']))
+        childs = self.mptt.search([
+                ('parent', '=', parent_id),
+                ])
+        child_index = dict((x.id, i) for i, x in enumerate(childs))
+        childs.sort(key=lambda x: child_index[x.id])
         for child in childs:
-            assert child['left'] > left, \
-                    'Record (%d): left %d <= parent left %d' % \
-                    (child['id'], child['left'], left)
-            assert child['left'] < child['right'], \
-                    'Record (%d): left %d >= right %d' % \
-                    (child['id'], child['left'], child['right'])
-            assert right == 0 or child['right'] < right, \
-                    'Record (%d): right %d >= parent right %d' % \
-                    (child['id'], child['right'], right)
-            self.CheckTree(child['id'], left=child['left'],
-                    right=child['right'])
+            assert child.left > left, \
+                '%s: left %d <= parent left %d' % \
+                (child, child.left, left)
+            assert child.left < child.right, \
+                '%s: left %d >= right %d' % \
+                (child, child.left, child.right)
+            assert right == 0 or child.right < right, \
+                '%s: right %d >= parent right %d' % \
+                (child, child.right, right)
+            self.CheckTree(child.id, left=child.left,
+                right=child.right)
         next_left = 0
         for child in childs:
-            assert child['left'] > next_left, \
-                    'Record (%d): left %d <= next left %d' % \
-                    (child['id'], child['left'], next_left)
-            next_left = child['right']
+            assert child.left > next_left, \
+                '%s: left %d <= next left %d' % \
+                (child, child.left, next_left)
+            next_left = child.right
         childs.reverse()
         previous_right = 0
         for child in childs:
-            assert previous_right == 0 or child['right'] < previous_right, \
-                'Record (%d): right %d >= previous right %d' \
-                % (child['id'], child['right'], previous_right)
-            previous_right = child['left']
+            assert previous_right == 0 or child.right < previous_right, \
+                '%s: right %d >= previous right %d' \
+                % (child, child.right, previous_right)
+            previous_right = child.left
 
-    def ReParent(self, parent_id=False):
-        record_ids = self.mptt.search([
-            ('parent', '=', parent_id),
-            ])
-        if not record_ids:
+    def ReParent(self, parent=None):
+        records = self.mptt.search([
+                ('parent', '=', parent),
+                ])
+        if not records:
             return
-        for record_id in record_ids:
-            for record2_id in record_ids:
-                if record_id != record2_id:
-                    self.mptt.write(record_id, {
-                        'parent': record2_id,
-                        })
-                    self.mptt.write(record_id, {
-                        'parent': parent_id,
-                        })
-        for record_id in record_ids:
-            self.ReParent(record_id)
+        for record in records:
+            for record2 in records:
+                if record != record2:
+                    record.parent = record2
+                    record.save()
+                    record.parent = parent
+                    record.save()
+        for record in records:
+            self.ReParent(record)
 
-    def ReOrder(self, parent_id=False):
-        record_ids = self.mptt.search([
-            ('parent', '=', parent_id),
-            ])
-        if not record_ids:
+    def ReOrder(self, parent=None):
+        records = self.mptt.search([
+                ('parent', '=', parent),
+                ])
+        if not records:
             return
-        i = len(record_ids)
-        for record_id in record_ids:
-            self.mptt.write(record_id, {
-                'sequence': i,
-                })
+        i = len(records)
+        for record in records:
+            record.sequence = i
+            record.save()
             i -= 1
         i = 0
-        for record_id in record_ids:
-            self.mptt.write(record_id, {
-                'sequence': i,
+        for record in records:
+            record.sequence = i
+            record.save()
+            i += 1
+        for record in records:
+            self.ReOrder(record)
+
+        records = self.mptt.search([])
+        self.mptt.write(records, {
+                'sequence': 0,
                 })
-            i += 1
-        for record_id in record_ids:
-            self.ReOrder(record_id)
-
-        record_ids = self.mptt.search([])
-        self.mptt.write(record_ids, {
-            'sequence': 0,
-            })
 
     def test0010create(self):
         '''
@@ -106,11 +102,12 @@
                 k = 0
                 for parent_record in parent_records:
                     for i in range(3):
-                        record_id = self.mptt.create({
+                        record = self.mptt.create({
                             'name': 'Test %d %d %d' % (j, k, i),
-                            'parent': parent_record,
+                            'parent': (parent_record.id
+                                    if parent_record else None),
                             })
-                        new_records.append(record_id)
+                        new_records.append(record)
                     k += 1
             self.CheckTree()
 
@@ -137,12 +134,11 @@
     def test0040active(self):
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            record_ids = self.mptt.search([])
-            for record_id in record_ids:
-                if record_id % 2:
-                    self.mptt.write(record_id, {
-                            'active': False
-                            })
+            records = self.mptt.search([])
+            for record in records:
+                if record.id % 2:
+                    record.active = False
+                    record.save()
             self.CheckTree()
             self.ReParent()
             self.CheckTree()
@@ -153,8 +149,8 @@
 
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            record_ids = self.mptt.search([])
-            self.mptt.write(record_ids[:len(record_ids) // 2], {
+            records = self.mptt.search([])
+            self.mptt.write(records[:len(records) // 2], {
                     'active': False
                     })
             self.CheckTree()
@@ -163,8 +159,8 @@
 
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            record_ids = self.mptt.search([])
-            self.mptt.write(record_ids, {
+            records = self.mptt.search([])
+            self.mptt.write(records, {
                     'active': False
                     })
             self.ReParent()
@@ -180,26 +176,26 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            record_ids = self.mptt.search([])
-            for record_id in record_ids:
-                if record_id % 2:
-                    self.mptt.delete(record_id)
+            records = self.mptt.search([])
+            for record in records:
+                if record.id % 2:
+                    self.mptt.delete([record])
             self.CheckTree()
 
             transaction.cursor.rollback()
 
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            record_ids = self.mptt.search([])
-            self.mptt.delete(record_ids[:len(record_ids) // 2])
+            records = self.mptt.search([])
+            self.mptt.delete(records[:len(records) // 2])
             self.CheckTree()
 
             transaction.cursor.rollback()
 
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            record_ids = self.mptt.search([])
-            self.mptt.delete(record_ids)
+            records = self.mptt.search([])
+            self.mptt.delete(records)
             self.CheckTree()
 
             transaction.cursor.rollback()
diff -r f6ef988fda71 trytond/tests/test_sequence.py
--- a/trytond/tests/test_sequence.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/tests/test_sequence.py	Sun Sep 23 17:30:03 2012 +0200
@@ -24,25 +24,25 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            sequence_id = self.sequence.create({
-                'name': 'Test incremental',
-                'code': 'test',
-                'prefix': '',
-                'suffix': '',
-                'type': 'incremental',
-                })
-            self.assertEqual(self.sequence.get_id(sequence_id), '1')
+            sequence = self.sequence.create({
+                    'name': 'Test incremental',
+                    'code': 'test',
+                    'prefix': '',
+                    'suffix': '',
+                    'type': 'incremental',
+                    })
+            self.assertEqual(self.sequence.get_id(sequence), '1')
 
-            self.sequence.write(sequence_id, {
-                'number_increment': 10,
-                })
-            self.assertEqual(self.sequence.get_id(sequence_id), '2')
-            self.assertEqual(self.sequence.get_id(sequence_id), '12')
+            self.sequence.write([sequence], {
+                    'number_increment': 10,
+                    })
+            self.assertEqual(self.sequence.get_id([sequence]), '2')
+            self.assertEqual(self.sequence.get_id([sequence]), '12')
 
-            self.sequence.write(sequence_id, {
-                'padding': 3,
-                })
-            self.assertEqual(self.sequence.get_id(sequence_id), '022')
+            self.sequence.write([sequence], {
+                    'padding': 3,
+                    })
+            self.assertEqual(self.sequence.get_id([sequence]), '022')
 
             transaction.cursor.rollback()
 
@@ -52,24 +52,22 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            sequence_id = self.sequence.create({
-                'name': 'Test decimal timestamp',
-                'code': 'test',
-                'prefix': '',
-                'suffix': '',
-                'type': 'decimal timestamp',
-                })
-            timestamp = self.sequence.get_id(sequence_id)
-            sequence = self.sequence.read(sequence_id, ['last_timestamp'])
-            self.assertEqual(timestamp, str(sequence['last_timestamp']))
+            sequence = self.sequence.create({
+                    'name': 'Test decimal timestamp',
+                    'code': 'test',
+                    'prefix': '',
+                    'suffix': '',
+                    'type': 'decimal timestamp',
+                    })
+            timestamp = self.sequence.get_id(sequence)
+            self.assertEqual(timestamp, str(sequence.last_timestamp))
 
-            self.assertNotEqual(self.sequence.get_id(sequence_id), timestamp)
+            self.assertNotEqual(self.sequence.get_id(sequence), timestamp)
 
-            sequence = self.sequence.browse(sequence_id)
             next_timestamp = self.sequence._timestamp(sequence)
-            self.assertRaises(Exception, self.sequence.write, sequence_id, {
-                'last_timestamp': next_timestamp + 100,
-                })
+            self.assertRaises(Exception, self.sequence.write, [sequence], {
+                    'last_timestamp': next_timestamp + 100,
+                    })
 
             transaction.cursor.rollback()
 
@@ -79,25 +77,23 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            sequence_id = self.sequence.create({
-                'name': 'Test hexadecimal timestamp',
-                'code': 'test',
-                'prefix': '',
-                'suffix': '',
-                'type': 'hexadecimal timestamp',
-                })
-            timestamp = self.sequence.get_id(sequence_id)
-            sequence = self.sequence.read(sequence_id, ['last_timestamp'])
+            sequence = self.sequence.create({
+                    'name': 'Test hexadecimal timestamp',
+                    'code': 'test',
+                    'prefix': '',
+                    'suffix': '',
+                    'type': 'hexadecimal timestamp',
+                    })
+            timestamp = self.sequence.get_id(sequence)
             self.assertEqual(timestamp,
-                    hex(int(sequence['last_timestamp']))[2:].upper())
+                hex(int(sequence.last_timestamp))[2:].upper())
 
-            self.assertNotEqual(self.sequence.get_id(sequence_id), timestamp)
+            self.assertNotEqual(self.sequence.get_id(sequence), timestamp)
 
-            sequence = self.sequence.browse(sequence_id)
             next_timestamp = self.sequence._timestamp(sequence)
-            self.assertRaises(Exception, self.sequence.write, sequence_id, {
-                'last_timestamp': next_timestamp + 100,
-                })
+            self.assertRaises(Exception, self.sequence.write, [sequence], {
+                    'last_timestamp': next_timestamp + 100,
+                    })
 
             transaction.cursor.rollback()
 
@@ -106,23 +102,23 @@
         Test prefix/suffix
         '''
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
-            sequence_id = self.sequence.create({
-                'name': 'Test incremental',
-                'code': 'test',
-                'prefix': 'prefix/',
-                'suffix': '/suffix',
-                'type': 'incremental',
-                })
-            self.assertEqual(self.sequence.get_id(sequence_id),
-                    'prefix/1/suffix')
+            sequence = self.sequence.create({
+                    'name': 'Test incremental',
+                    'code': 'test',
+                    'prefix': 'prefix/',
+                    'suffix': '/suffix',
+                    'type': 'incremental',
+                    })
+            self.assertEqual(self.sequence.get_id(sequence),
+                'prefix/1/suffix')
 
-            self.sequence.write(sequence_id, {
-                'prefix': '${year}-${month}-${day}/',
-                'suffix': '/${day}.${month}.${year}',
-                })
+            self.sequence.write([sequence], {
+                    'prefix': '${year}-${month}-${day}/',
+                    'suffix': '/${day}.${month}.${year}',
+                    })
             with Transaction().set_context(date=datetime.date(2010, 8, 15)):
-                self.assertEqual(self.sequence.get_id(sequence_id),
-                        '2010-08-15/2/15.08.2010')
+                self.assertEqual(self.sequence.get_id(sequence),
+                    '2010-08-15/2/15.08.2010')
 
 
 def suite():
diff -r f6ef988fda71 trytond/tests/test_trigger.py
--- a/trytond/tests/test_trigger.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/tests/test_trigger.py	Sun Sep 23 17:30:03 2012 +0200
@@ -49,18 +49,18 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            model_id = self.model.search([
-                ('model', '=', 'test.triggered'),
-                ])[0]
+            model, = self.model.search([
+                    ('model', '=', 'test.triggered'),
+                    ])
 
             values = {
                 'name': 'Test',
-                'model': model_id,
+                'model': model.id,
                 'on_time': True,
                 'condition': 'True',
-                'action_model': model_id,
+                'action_model': model.id,
                 'action_function': 'test',
-            }
+                }
             self.assert_(self.trigger.create(values))
 
             # on_exclusive
@@ -70,17 +70,17 @@
                     combination_values = values.copy()
                     for mode in combination:
                         combination_values['on_%s' % mode] = True
-                    self.failUnlessRaises(Exception, self.trigger.create,
-                            combination_values)
+                    self.assertRaises(Exception, self.trigger.create,
+                        combination_values)
 
             # check_condition
             condition_values = values.copy()
             condition_values['condition'] = '='
-            self.failUnlessRaises(Exception, self.trigger.create,
-                    condition_values)
+            self.assertRaises(Exception, self.trigger.create,
+                condition_values)
 
             # Restart the cache on the get_triggers method of ir.trigger
-            self.trigger.get_triggers.reset()
+            self.trigger._get_triggers_cache.clear()
             transaction.cursor.rollback()
 
     def test0020on_create(self):
@@ -89,68 +89,68 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            model_id = self.model.search([
-                ('model', '=', 'test.triggered'),
-                ])[0]
+            model, = self.model.search([
+                    ('model', '=', 'test.triggered'),
+                    ])
 
-            trigger_id = self.trigger.create({
-                'name': 'Test',
-                'model': model_id,
-                'on_create': True,
-                'condition': 'True',
-                'action_model': model_id,
-                'action_function': 'trigger',
-                })
+            trigger = self.trigger.create({
+                    'name': 'Test',
+                    'model': model.id,
+                    'on_create': True,
+                    'condition': 'True',
+                    'action_model': model.id,
+                    'action_function': 'trigger',
+                    })
 
-            triggered_id = self.triggered.create({
-                'name': 'Test',
-                })
+            triggered = self.triggered.create({
+                    'name': 'Test',
+                    })
 
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
             TRIGGER_LOGS.pop()
 
             # Trigger with condition
-            self.trigger.write(trigger_id, {
-                'condition': 'self.name == "Bar"',
-                })
+            self.trigger.write([trigger], {
+                    'condition': 'self.name == "Bar"',
+                    })
 
             # Matching condition
-            triggered_id = self.triggered.create({
-                'name': 'Bar',
-                })
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            triggered = self.triggered.create({
+                    'name': 'Bar',
+                    })
+            self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
             TRIGGER_LOGS.pop()
 
             # Non matching condition
-            triggered_id = self.triggered.create({
-                'name': 'Foo',
-                })
-            self.assert_(TRIGGER_LOGS == [])
+            triggered = self.triggered.create({
+                    'name': 'Foo',
+                    })
+            self.assertEqual(TRIGGER_LOGS, [])
 
             # With limit number
-            self.trigger.write(trigger_id, {
-                'condition': 'True',
-                'limit_number': 1,
-                })
-            triggered_id = self.triggered.create({
-                'name': 'Test',
-                })
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            self.trigger.write([trigger], {
+                    'condition': 'True',
+                    'limit_number': 1,
+                    })
+            triggered = self.triggered.create({
+                    'name': 'Test',
+                    })
+            self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
             TRIGGER_LOGS.pop()
 
             # With minimum delay
-            self.trigger.write(trigger_id, {
-                'limit_number': 0,
-                'minimum_delay': 1,
-                })
-            triggered_id = self.triggered.create({
-                'name': 'Test',
-                })
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            self.trigger.write([trigger], {
+                    'limit_number': 0,
+                    'minimum_delay': 1,
+                    })
+            triggered = self.triggered.create({
+                    'name': 'Test',
+                    })
+            self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
             TRIGGER_LOGS.pop()
 
             # Restart the cache on the get_triggers method of ir.trigger
-            self.trigger.get_triggers.reset()
+            self.trigger._get_triggers_cache.clear()
             transaction.cursor.rollback()
 
     def test0030on_write(self):
@@ -159,108 +159,108 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            model_id = self.model.search([
-                ('model', '=', 'test.triggered'),
-                ])[0]
+            model, = self.model.search([
+                    ('model', '=', 'test.triggered'),
+                    ])
 
-            trigger_id = self.trigger.create({
-                'name': 'Test',
-                'model': model_id,
-                'on_write': True,
-                'condition': 'True',
-                'action_model': model_id,
-                'action_function': 'trigger',
-                })
+            trigger = self.trigger.create({
+                    'name': 'Test',
+                    'model': model.id,
+                    'on_write': True,
+                    'condition': 'True',
+                    'action_model': model.id,
+                    'action_function': 'trigger',
+                    })
 
-            triggered_id = self.triggered.create({
-                'name': 'Test',
-                })
+            triggered = self.triggered.create({
+                    'name': 'Test',
+                    })
 
-            self.triggered.write(triggered_id, {
-                'name': 'Foo',
-                })
-            self.assert_(TRIGGER_LOGS == [])
+            self.triggered.write([triggered], {
+                    'name': 'Foo',
+                    })
+            self.assertEqual(TRIGGER_LOGS, [])
 
             # Trigger with condition
-            self.trigger.write(trigger_id, {
-                'condition': 'self.name == "Bar"',
-                })
+            self.trigger.write([trigger], {
+                    'condition': 'self.name == "Bar"',
+                    })
 
             # Matching condition
-            self.triggered.write(triggered_id, {
-                'name': 'Bar',
-                })
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            self.triggered.write([triggered], {
+                    'name': 'Bar',
+                    })
+            self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
             TRIGGER_LOGS.pop()
 
             # No change in condition
-            self.triggered.write(triggered_id, {
-                'name': 'Bar',
-                })
-            self.assert_(TRIGGER_LOGS == [])
+            self.triggered.write([triggered], {
+                    'name': 'Bar',
+                    })
+            self.assertEqual(TRIGGER_LOGS, [])
 
             # Different change in condition
-            self.triggered.write(triggered_id, {
-                'name': 'Foo',
-                })
-            self.assert_(TRIGGER_LOGS == [])
+            self.triggered.write([triggered], {
+                    'name': 'Foo',
+                    })
+            self.assertEqual(TRIGGER_LOGS, [])
 
             # With limit number
-            self.trigger.write(trigger_id, {
-                'condition': 'self.name == "Bar"',
-                'limit_number': 1,
-                })
-            triggered_id = self.triggered.create({
-                'name': 'Foo',
-                })
-            self.triggered.write(triggered_id, {
-                'name': 'Bar',
-                })
-            self.triggered.write(triggered_id, {
-                'name': 'Foo',
-                })
-            self.triggered.write(triggered_id, {
-                'name': 'Bar',
-                })
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            self.trigger.write([trigger], {
+                    'condition': 'self.name == "Bar"',
+                    'limit_number': 1,
+                    })
+            triggered = self.triggered.create({
+                    'name': 'Foo',
+                    })
+            self.triggered.write([triggered], {
+                    'name': 'Bar',
+                    })
+            self.triggered.write([triggered], {
+                    'name': 'Foo',
+                    })
+            self.triggered.write([triggered], {
+                    'name': 'Bar',
+                    })
+            self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
             TRIGGER_LOGS.pop()
 
             # With minimum delay
-            self.trigger.write(trigger_id, {
-                'limit_number': 0,
-                'minimum_delay': MAXINT,
-                })
-            triggered_id = self.triggered.create({
-                'name': 'Foo',
-                })
+            self.trigger.write([trigger], {
+                    'limit_number': 0,
+                    'minimum_delay': MAXINT,
+                    })
+            triggered = self.triggered.create({
+                    'name': 'Foo',
+                    })
             for name in ('Bar', 'Foo', 'Bar'):
-                self.triggered.write(triggered_id, {
-                    'name': name,
-                    })
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+                self.triggered.write([triggered], {
+                        'name': name,
+                        })
+            self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
             TRIGGER_LOGS.pop()
 
-            self.trigger.write(trigger_id, {
-                'minimum_delay': 0.02,
-                })
-            triggered_id = self.triggered.create({
-                'name': 'Foo',
-                })
+            self.trigger.write([trigger], {
+                    'minimum_delay': 0.02,
+                    })
+            triggered = self.triggered.create({
+                    'name': 'Foo',
+                    })
             for name in ('Bar', 'Foo'):
-                self.triggered.write(triggered_id, {
-                    'name': name,
+                self.triggered.write([triggered], {
+                        'name': name,
+                        })
+            time.sleep(1.2)
+            self.triggered.write([triggered], {
+                    'name': 'Bar',
                     })
-            time.sleep(1.2)
-            self.triggered.write(triggered_id, {
-                'name': 'Bar',
-                })
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id),
-                ([triggered_id], trigger_id)])
+            self.assertEqual(TRIGGER_LOGS,
+                [([triggered], trigger), ([triggered], trigger)])
             TRIGGER_LOGS.pop()
             TRIGGER_LOGS.pop()
 
             # Restart the cache on the get_triggers method of ir.trigger
-            self.trigger.get_triggers.reset()
+            self.trigger._get_triggers_cache.clear()
             transaction.cursor.rollback()
 
     def test0040on_delete(self):
@@ -269,86 +269,86 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            model_id = self.model.search([
-                ('model', '=', 'test.triggered'),
-                ])[0]
+            model, = self.model.search([
+                    ('model', '=', 'test.triggered'),
+                    ])
 
-            triggered_id = self.triggered.create({
-                'name': 'Test',
-                })
+            triggered = self.triggered.create({
+                    'name': 'Test',
+                    })
 
-            trigger_id = self.trigger.create({
-                'name': 'Test',
-                'model': model_id,
-                'on_delete': True,
-                'condition': 'True',
-                'action_model': model_id,
-                'action_function': 'trigger',
-                })
+            trigger = self.trigger.create({
+                    'name': 'Test',
+                    'model': model.id,
+                    'on_delete': True,
+                    'condition': 'True',
+                    'action_model': model.id,
+                    'action_function': 'trigger',
+                    })
 
-            self.triggered.delete(triggered_id)
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            self.triggered.delete([triggered])
+            self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
             TRIGGER_LOGS.pop()
             Transaction().delete = {}
 
             # Trigger with condition
-            self.trigger.write(trigger_id, {
-                'condition': 'self.name == "Bar"',
-                })
+            self.trigger.write([trigger], {
+                    'condition': 'self.name == "Bar"',
+                    })
 
-            triggered_id = self.triggered.create({
-                'name': 'Bar',
-                })
+            triggered = self.triggered.create({
+                    'name': 'Bar',
+                    })
 
             # Matching condition
-            self.triggered.delete(triggered_id)
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            self.triggered.delete([triggered])
+            self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
             TRIGGER_LOGS.pop()
             Transaction().delete = {}
 
-            triggered_id = self.triggered.create({
-                'name': 'Foo',
-                })
+            triggered = self.triggered.create({
+                    'name': 'Foo',
+                    })
 
             # Non matching condition
-            self.triggered.delete(triggered_id)
-            self.assert_(TRIGGER_LOGS == [])
+            self.triggered.delete([triggered])
+            self.assertEqual(TRIGGER_LOGS, [])
             Transaction().delete = {}
 
-            triggered_id = self.triggered.create({
-                'name': 'Test',
-                })
+            triggered = self.triggered.create({
+                    'name': 'Test',
+                    })
 
             # With limit number
-            self.trigger.write(trigger_id, {
-                'condition': 'True',
-                'limit_number': 1,
-                })
-            self.triggered.delete(triggered_id)
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            self.trigger.write([trigger], {
+                    'condition': 'True',
+                    'limit_number': 1,
+                    })
+            self.triggered.delete([triggered])
+            self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
             TRIGGER_LOGS.pop()
             Transaction().delete = {}
             # Delete trigger logs because SQLite reuse the same triggered_id
             self.trigger_log.delete(self.trigger_log.search([
-                        ('trigger', '=', trigger_id),
+                        ('trigger', '=', trigger.id),
                         ]))
 
-            triggered_id = self.triggered.create({
-                'name': 'Test',
-                })
+            triggered = self.triggered.create({
+                    'name': 'Test',
+                    })
 
             # With minimum delay
-            self.trigger.write(trigger_id, {
-                'limit_number': 0,
-                'minimum_delay': 1,
-                })
-            self.triggered.delete(triggered_id)
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            self.trigger.write([trigger], {
+                    'limit_number': 0,
+                    'minimum_delay': 1,
+                    })
+            self.triggered.delete([triggered])
+            self.assertEqual(TRIGGER_LOGS, [([triggered], trigger)])
             TRIGGER_LOGS.pop()
             Transaction().delete = {}
 
             # Restart the cache on the get_triggers method of ir.trigger
-            self.trigger.get_triggers.reset()
+            self.trigger._get_triggers_cache.clear()
             transaction.cursor.rollback()
 
     def test0050on_time(self):
@@ -357,91 +357,91 @@
         '''
         with Transaction().start(DB_NAME, USER,
                 context=CONTEXT) as transaction:
-            model_id = self.model.search([
-                ('model', '=', 'test.triggered'),
-                ])[0]
+            model, = self.model.search([
+                    ('model', '=', 'test.triggered'),
+                    ])
 
-            trigger_id = self.trigger.create({
-                'name': 'Test',
-                'model': model_id,
-                'on_time': True,
-                'condition': 'True',
-                'action_model': model_id,
-                'action_function': 'trigger',
-                })
+            trigger = self.trigger.create({
+                    'name': 'Test',
+                    'model': model.id,
+                    'on_time': True,
+                    'condition': 'True',
+                    'action_model': model.id,
+                    'action_function': 'trigger',
+                    })
 
-            triggered_id = self.triggered.create({
-                'name': 'Test',
-                })
+            triggered = self.triggered.create({
+                    'name': 'Test',
+                    })
             self.trigger.trigger_time()
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            self.assert_(TRIGGER_LOGS == [([triggered], trigger)])
             TRIGGER_LOGS.pop()
 
             # Trigger with condition
-            self.trigger.write(trigger_id, {
-                'condition': 'self.name == "Bar"',
-                })
+            self.trigger.write([trigger], {
+                    'condition': 'self.name == "Bar"',
+                    })
 
             # Matching condition
-            self.triggered.write(triggered_id, {
-                'name': 'Bar',
-                })
+            self.triggered.write([triggered], {
+                    'name': 'Bar',
+                    })
             self.trigger.trigger_time()
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            self.assert_(TRIGGER_LOGS == [([triggered], trigger)])
             TRIGGER_LOGS.pop()
 
             # Non matching condition
-            self.triggered.write(triggered_id, {
-                'name': 'Foo',
-                })
+            self.triggered.write([triggered], {
+                    'name': 'Foo',
+                    })
             self.trigger.trigger_time()
             self.assert_(TRIGGER_LOGS == [])
 
             # With limit number
-            self.trigger.write(trigger_id, {
-                'condition': 'True',
-                'limit_number': 1,
-                })
+            self.trigger.write([trigger], {
+                    'condition': 'True',
+                    'limit_number': 1,
+                    })
             self.trigger.trigger_time()
             self.trigger.trigger_time()
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            self.assert_(TRIGGER_LOGS == [([triggered], trigger)])
             TRIGGER_LOGS.pop()
 
             # Delete trigger logs of limit number test
             self.trigger_log.delete(self.trigger_log.search([
-                        ('trigger', '=', trigger_id),
+                        ('trigger', '=', trigger.id),
                         ]))
 
             # With minimum delay
-            self.trigger.write(trigger_id, {
-                'limit_number': 0,
-                'minimum_delay': MAXINT,
-                })
+            self.trigger.write([trigger], {
+                    'limit_number': 0,
+                    'minimum_delay': MAXINT,
+                    })
             self.trigger.trigger_time()
             self.trigger.trigger_time()
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id)])
+            self.assert_(TRIGGER_LOGS == [([triggered], trigger)])
             TRIGGER_LOGS.pop()
             Transaction().delete = {}
 
             # Delete trigger logs of previous minimum delay test
             self.trigger_log.delete(self.trigger_log.search([
-                ('trigger', '=', trigger_id),
-                ]))
+                        ('trigger', '=', trigger.id),
+                        ]))
 
-            self.trigger.write(trigger_id, {
-                'minimum_delay': 0.02,
-                })
+            self.trigger.write([trigger], {
+                    'minimum_delay': 0.02,
+                    })
             self.trigger.trigger_time()
             time.sleep(1.2)
             self.trigger.trigger_time()
-            self.assert_(TRIGGER_LOGS == [([triggered_id], trigger_id),
-                ([triggered_id], trigger_id)])
+            self.assert_(TRIGGER_LOGS == [([triggered], trigger),
+                    ([triggered], trigger)])
             TRIGGER_LOGS.pop()
             TRIGGER_LOGS.pop()
             Transaction().delete = {}
 
             # Restart the cache on the get_triggers method of ir.trigger
-            self.trigger.get_triggers.reset()
+            self.trigger._get_triggers_cache.clear()
             transaction.cursor.rollback()
 
 
diff -r f6ef988fda71 trytond/tests/test_tryton.py
--- a/trytond/tests/test_tryton.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/tests/test_tryton.py	Sun Sep 23 17:30:03 2012 +0200
@@ -41,14 +41,13 @@
 if not CONFIG['admin_passwd']:
     CONFIG['admin_passwd'] = 'admin'
 
-from trytond.modules import register_classes
 from trytond.pool import Pool
 from trytond.backend import Database
 from trytond.protocols.dispatcher import create
 from trytond.transaction import Transaction
 from trytond.pyson import PYSONEncoder, Eval
 
-register_classes()
+Pool.start()
 
 if CONFIG['db_type'] == 'sqlite':
     DB_NAME = ':memory:'
@@ -121,27 +120,29 @@
         create(DB_NAME, CONFIG['admin_passwd'], 'en_US', USER_PASSWORD)
     with Transaction().start(DB_NAME, USER,
             context=CONTEXT) as transaction:
-        module_obj = POOL.get('ir.module.module')
+        Module = POOL.get('ir.module.module')
 
-        module_ids = module_obj.search([
-            ('name', '=', name),
-            ])
-        assert module_ids
+        modules = Module.search([
+                ('name', '=', name),
+                ])
+        assert modules
 
-        module_ids = module_obj.search([
-            ('name', '=', name),
-            ('state', '!=', 'installed'),
-            ])
+        modules = Module.search([
+                ('name', '=', name),
+                ('state', '!=', 'installed'),
+                ])
 
-        if not module_ids:
+        if not modules:
             return
 
-        module_obj.install(module_ids)
+        Module.install(modules)
         transaction.cursor.commit()
 
-        install_upgrade_obj = POOL.get('ir.module.module.install_upgrade',
-                type='wizard')
-        install_upgrade_obj.transition_upgrade(None)
+        InstallUpgrade = POOL.get('ir.module.module.install_upgrade',
+            type='wizard')
+        instance_id, _, _ = InstallUpgrade.create()
+        InstallUpgrade(instance_id).transition_upgrade()
+        InstallUpgrade.delete(instance_id)
         transaction.cursor.commit()
 
 
@@ -151,17 +152,17 @@
     '''
     with Transaction().start(DB_NAME, USER,
             context=CONTEXT) as transaction:
-        view_obj = POOL.get('ir.ui.view')
-        view_ids = view_obj.search([
-            ('module', '=', module_name),
-            ('model', '!=', ''),
-            ])
-        assert view_ids, "No views for %s" % module_name
-        for view in view_obj.browse(view_ids):
+        View = POOL.get('ir.ui.view')
+        views = View.search([
+                ('module', '=', module_name),
+                ('model', '!=', ''),
+                ])
+        assert views, "No views for %s" % module_name
+        for view in views:
             view_id = view.inherit and view.inherit.id or view.id
             model = view.model
-            model_obj = POOL.get(model)
-            res = model_obj.fields_view_get(view_id)
+            Model = POOL.get(model)
+            res = Model.fields_view_get(view_id)
             assert res['model'] == model
             tree = etree.fromstring(res['arch'])
             tree_root = tree.getroottree().getroot()
@@ -195,7 +196,7 @@
 
     with Transaction().start(DB_NAME, USER, context=CONTEXT):
         for mname, model in Pool().iterobject():
-            for fname, field in model._columns.iteritems():
+            for fname, field in model._fields.iteritems():
                 encoder = Encoder()
                 encoder.encode(field.domain)
                 if hasattr(field, 'digits'):
diff -r f6ef988fda71 trytond/tests/test_wizard.py
--- a/trytond/tests/test_wizard.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/tests/test_wizard.py	Sun Sep 23 17:30:03 2012 +0200
@@ -4,7 +4,6 @@
 from trytond.tests.test_tryton import (POOL, DB_NAME, USER, CONTEXT,
     install_module)
 from trytond.transaction import Transaction
-from trytond.wizard import Session
 
 
 class WizardTestCase(unittest.TestCase):
@@ -13,6 +12,7 @@
         install_module('test')
         self.wizard = POOL.get('test.test_wizard', type='wizard')
         self.session = POOL.get('ir.session.wizard')
+        self.group = POOL.get('res.group')
 
     def test0010create(self):
         'Create Session Wizard'
@@ -32,17 +32,27 @@
         'Session Wizard'
         with Transaction().start(DB_NAME, USER, CONTEXT):
             session_id = self.session.create({})
-            session = Session(self.wizard, session_id)
-            self.assertEqual(session.start.name, None)
+            session = self.wizard(session_id)
+            self.assertEqual(session.start.id, None)
+            self.assertRaises(AttributeError, getattr, session.start, 'name')
+            self.assertEqual(hasattr(session.start, 'name'), False)
             session.start.name = 'Test'
-            self.assertEqual(session.start.user.id, None)
+            self.assertRaises(AttributeError, getattr, session.start, 'user')
+            self.assertEqual(hasattr(session.start, 'user'), False)
             session.start.user = USER
+            group_a = self.group.create({
+                    'name': 'Group A',
+                    })
+            group_b = self.group.create({
+                    'name': 'Group B',
+                    })
             session.start.groups = [
-                {'name': 'Group A'},
-                {'name': 'Group B'},
+                group_a,
+                group_b,
                 ]
-            session.save()
-            session = Session(self.wizard, session_id)
+            session._save()
+            session = self.wizard(session_id)
+            self.assertEqual(session.start.id, None)
             self.assertEqual(session.start.name, 'Test')
             self.assertEqual(session.start.user.id, USER)
             self.assertEqual(session.start.user.login, 'admin')
diff -r f6ef988fda71 trytond/tests/test_workflow.py
--- a/trytond/tests/test_workflow.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/tests/test_workflow.py	Sun Sep 23 17:30:03 2012 +0200
@@ -12,23 +12,21 @@
 
     def setUp(self):
         install_module('test')
-        self.workflow_obj = POOL.get('test.workflowed')
+        self.workflow = POOL.get('test.workflowed')
 
     # TODO add test for Workflow.transition
     def test0010transition(self):
         'Test transition'
         with Transaction().start(DB_NAME, USER, context=CONTEXT):
-            wkf_id = self.workflow_obj.create({})
+            wkf = self.workflow.create({})
 
-            self.workflow_obj.run([wkf_id])
-            self.assertEqual(self.workflow_obj.read(wkf_id)['state'],
-                'running')
+            self.workflow.run([wkf])
+            self.assertEqual(wkf.state, 'running')
 
-            self.workflow_obj.write(wkf_id, {
-                    'state': 'end',
-                    })
-            self.workflow_obj.run([wkf_id])
-            self.assertEqual(self.workflow_obj.read(wkf_id)['state'], 'end')
+            wkf.state = 'end'
+            wkf.save()
+            self.workflow.run([wkf])
+            self.assertEqual(wkf.state, 'end')
 
 
 def suite():
diff -r f6ef988fda71 trytond/tools/__init__.py
--- a/trytond/tools/__init__.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/tools/__init__.py	Sun Sep 23 17:30:03 2012 +0200
@@ -6,3 +6,8 @@
     from collections import OrderedDict
 except ImportError:
     from .ordereddict import OrderedDict
+
+
+class ClassProperty(property):
+    def __get__(self, cls, owner):
+        return self.fget.__get__(None, owner)()
diff -r f6ef988fda71 trytond/transaction.py
--- a/trytond/transaction.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/transaction.py	Sun Sep 23 17:30:03 2012 +0200
@@ -79,6 +79,7 @@
         self.delete_records = {}
         self.delete = {}
         self.timestamp = {}
+        self.counter = 0
         return _TransactionManager()
 
     def stop(self):
diff -r f6ef988fda71 trytond/url.py
--- a/trytond/url.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/url.py	Sun Sep 23 17:30:03 2012 +0200
@@ -8,10 +8,12 @@
 from trytond.config import CONFIG
 from trytond.transaction import Transaction
 
+__all__ = ['URLMixin']
 
-class URLMixin(object):
 
-    def get_url(self):
+class URLAccessor(object):
+
+    def __get__(self, inst, cls):
         from trytond.model import Model
         from trytond.wizard import Wizard
         from trytond.report import Report
@@ -22,17 +24,24 @@
             hostname.split('.'))
 
         url_part = {}
-        if isinstance(self, Model):
+        if issubclass(cls, Model):
             url_part['type'] = 'model'
-        elif isinstance(self, Wizard):
+        elif issubclass(cls, Wizard):
             url_part['type'] = 'wizard'
-        elif isinstance(self, Report):
+        elif issubclass(cls, Report):
             url_part['type'] = 'report'
         else:
             raise NotImplementedError
 
-        url_part['name'] = self._name
+        url_part['name'] = cls.__name__
         url_part['database'] = Transaction().cursor.database_name
 
         local_part = urllib.quote('%(database)s/%(type)s/%(name)s' % url_part)
+        if isinstance(inst, Model) and inst.id:
+            local_part += '/%d' % inst.id
         return 'tryton://%s/%s' % (hostname, local_part)
+
+
+class URLMixin(object):
+
+        __url__ = URLAccessor()
diff -r f6ef988fda71 trytond/webdav/__init__.py
--- a/trytond/webdav/__init__.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/webdav/__init__.py	Sun Sep 23 17:30:03 2012 +0200
@@ -1,3 +1,12 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
+from ..pool import Pool
 from .webdav import *
+
+
+def register():
+    Pool.register(
+        Collection,
+        Share,
+        Attachment,
+        module='webdav', type_='model')
diff -r f6ef988fda71 trytond/webdav/webdav.py
--- a/trytond/webdav/webdav.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/webdav/webdav.py	Sun Sep 23 17:30:03 2012 +0200
@@ -16,6 +16,10 @@
 from trytond.config import CONFIG
 from trytond.pyson import Eval
 
+__all__ = [
+    'Collection', 'Share', 'Attachment',
+    ]
+
 
 def get_webdav_url():
     if CONFIG['ssl_webdav']:
@@ -32,8 +36,7 @@
 
 class Collection(ModelSQL, ModelView):
     "Collection"
-    _name = "webdav.collection"
-    _description = __doc__
+    __name__ = "webdav.collection"
     name = fields.Char('Name', required=True, select=True)
     parent = fields.Many2One('webdav.collection', 'Parent',
        ondelete='RESTRICT', domain=[('model', '=', None)])
@@ -43,66 +46,61 @@
     complete_name = fields.Function(fields.Char('Complete Name',
             order_field='name'), 'get_rec_name')
 
-    def __init__(self):
-        super(Collection, self).__init__()
-        self._sql_constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Collection, cls).__setup__()
+        cls._sql_constraints += [
             ('name_parent_uniq', 'UNIQUE (name, parent)',
                 'The collection name must be unique inside a collection!'),
         ]
-        self._constraints += [
+        cls._constraints += [
             ('check_recursion', 'recursive_collections'),
             ('check_attachment', 'collection_file_name'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'recursive_collections': 'You can not create recursive ' \
                     'collections!',
             'collection_file_name': 'You can not create a collection\n' \
                     'in a collection with the name of an ' \
                     'existing file!',
         })
-        self.ext2mime = {
+        cls.ext2mime = {
             '.png': 'image/png',
             '.odt': 'application/vnd.oasis.opendocument.text',
             '.pdf': 'application/pdf',
         }
 
-    def default_domain(self):
+    @staticmethod
+    def default_domain():
         return '[]'
 
-    def get_rec_name(self, ids, name):
-        if not ids:
-            return {}
-        names = {}
+    def get_rec_name(self, name):
+        if self.parent:
+            return self.parent.rec_name + '/' + self.name
+        else:
+            return self.name
 
-        def _name(collection):
-            if collection.id in names:
-                return names[collection.id]
-            elif collection.parent:
-                return _name(collection.parent) + '/' + collection.name
-            else:
-                return collection.name
-        for collection in self.browse(ids):
-            names[collection.id] = _name(collection)
-        return names
-
-    def check_attachment(self, ids):
+    @classmethod
+    def check_attachment(cls, collections):
         pool = Pool()
-        attachment_obj = pool.get('ir.attachment')
-        for collection in self.browse(ids):
+        Attachment = pool.get('ir.attachment')
+        for collection in collections:
             if collection.parent:
-                attachment_ids = attachment_obj.search([
+                attachments = Attachment.search([
                     ('resource', '=', '%s,%s' %
-                        (self._name, collection.parent.id)),
+                        (cls.__name__, collection.parent.id)),
                     ])
-                for attachment in attachment_obj.browse(attachment_ids):
+                for attachment in attachments:
                     if attachment.name == collection.name:
                         return False
         return True
 
-    def _uri2object(self, uri, object_name=_name, object_id=None, cache=None):
+    @classmethod
+    def _uri2object(cls, uri, object_name=__name__, object_id=None,
+            cache=None):
         pool = Pool()
-        attachment_obj = pool.get('ir.attachment')
-        report_obj = pool.get('ir.action.report')
+        Attachment = pool.get('ir.attachment')
+        Report = pool.get('ir.action.report')
         cache_uri = uri
 
         if cache is not None:
@@ -112,10 +110,10 @@
 
         if not uri:
             if cache is not None:
-                cache['_uri2object'][cache_uri] = (self._name, None)
-            return self._name, None
+                cache['_uri2object'][cache_uri] = (cls.__name__, None)
+            return cls.__name__, None
         name, uri = (uri.split('/', 1) + [None])[0:2]
-        if object_name == self._name:
+        if object_name == cls.__name__:
             collection_ids = None
             if cache is not None:
                 cache.setdefault('_parent2collection_ids', {})
@@ -123,10 +121,9 @@
                     collection_ids = cache['_parent2collection_ids'][
                         object_id].get(name, [])
             if collection_ids is None:
-                collection_ids = self.search([
+                collections = cls.search([
                     ('parent', '=', object_id),
                     ])
-                collections = self.browse(collection_ids)
                 collection_ids = []
                 if cache is not None:
                     cache['_parent2collection_ids'].setdefault(object_id, {})
@@ -142,7 +139,7 @@
                                 collection.model.model
                         else:
                             cache['_collection_name'][collection.id] = \
-                                self._name
+                                cls.__name__
                     if collection.name == name:
                         collection_ids.append(collection.id)
             if collection_ids:
@@ -153,7 +150,7 @@
                     if object_id in cache['_collection_name']:
                         object_name2 = cache['_collection_name'][object_id]
                 if object_name2 is None:
-                    collection = self.browse(object_id)
+                    collection = cls(object_id)
                     if collection.model and uri:
                         object_name = collection.model.model
                         if cache is not None:
@@ -175,10 +172,9 @@
                             (object_name, object_id)].get(name, [])
                 attachment_id = None
                 if attachment_ids is None:
-                    attachment_ids = attachment_obj.search([
+                    attachments = Attachment.search([
                         ('resource', '=', '%s,%s' % (object_name, object_id)),
                         ])
-                    attachments = attachment_obj.browse(attachment_ids)
                     key = (object_name, object_id)
                     attachment_ids = []
                     if cache is not None:
@@ -219,10 +215,9 @@
                     if cache is not None:
                         cache['_uri2object'][cache_uri] = (None, 0)
                     return None, 0
-                report_ids = report_obj.search([
+                reports = Report.search([
                     ('model', '=', object_name),
                     ])
-                reports = report_obj.browse(report_ids)
                 for report in reports:
                     report_name = report.name + '-' + str(report.id) \
                             + '.' + report.extension
@@ -240,10 +235,9 @@
                         attachment_ids = cache['_model&id2attachment_ids'][
                             (object_name, object_id)].get(name, [])
                 if attachment_ids is None:
-                    attachment_ids = attachment_obj.search([
+                    attachments = Attachment.search([
                         ('resource', '=', '%s,%s' % (object_name, object_id)),
                         ])
-                    attachments = attachment_obj.browse(attachment_ids)
                     key = (object_name, object_id)
                     attachment_ids = []
                     if cache is not None:
@@ -266,7 +260,7 @@
                     cache['_uri2object'][cache_uri] = (object_name, object_id)
                 return object_name, object_id
         if uri:
-            res = self._uri2object(uri, object_name, object_id, cache=cache)
+            res = cls._uri2object(uri, object_name, object_id, cache=cache)
             if cache is not None:
                 cache['_uri2object'][cache_uri] = res
             return res
@@ -274,40 +268,41 @@
             cache['_uri2object'][cache_uri] = (object_name, object_id)
         return object_name, object_id
 
-    def get_childs(self, uri, filter=None, cache=None):
+    @classmethod
+    def get_childs(cls, uri, filter=None, cache=None):
         pool = Pool()
-        report_obj = pool.get('ir.action.report')
+        Report = pool.get('ir.action.report')
         res = []
         if filter:
             return []
         if not uri:
-            collection_ids = self.search([
+            collections = cls.search([
                 ('parent', '=', None),
                 ])
-            for collection in self.browse(collection_ids):
+            for collection in collections:
                 if '/' in collection.name:
                     continue
                 res.append(collection.name)
                 if cache is not None:
-                    cache.setdefault(self._name, {})
-                    cache[self._name][collection.id] = {}
+                    cache.setdefault(cls.__name__, {})
+                    cache[cls.__name__][collection.id] = {}
             return res
-        object_name, object_id = self._uri2object(uri, cache=cache)
-        if object_name == self._name and object_id:
-            collection = self.browse(object_id)
+        object_name, object_id = cls._uri2object(uri, cache=cache)
+        if object_name == cls.__name__ and object_id:
+            collection = cls(object_id)
             if collection.model:
-                model_obj = pool.get(collection.model.model)
-                if not model_obj:
+                Model = pool.get(collection.model.model)
+                if not Model:
                     return res
-                model_ids = model_obj.search(
+                models = Model.search(
                         safe_eval(collection.domain or "[]"))
-                for child in model_obj.browse(model_ids):
+                for child in models:
                     if '/' in child.rec_name:
                         continue
                     res.append(child.rec_name + '-' + str(child.id))
                     if cache is not None:
-                        cache.setdefault(model_obj._name, {})
-                        cache[model_obj._name][child.id] = {}
+                        cache.setdefault(Model.__name__, {})
+                        cache[Model.__name__][child.id] = {}
                 return res
             else:
                 for child in collection.childs:
@@ -315,13 +310,12 @@
                         continue
                     res.append(child.name)
                     if cache is not None:
-                        cache.setdefault(self._name, {})
-                        cache[self._name][child.id] = {}
+                        cache.setdefault(cls.__name__, {})
+                        cache[cls.__name__][child.id] = {}
         if object_name not in ('ir.attachment', 'ir.action.report'):
-            report_ids = report_obj.search([
+            reports = Report.search([
                 ('model', '=', object_name),
                 ])
-            reports = report_obj.browse(report_ids)
             for report in reports:
                 report_name = report.name + '-' + str(report.id) \
                         + '.' + report.extension
@@ -329,40 +323,43 @@
                     continue
                 res.append(report_name)
                 if cache is not None:
-                    cache.setdefault(report_obj._name, {})
-                    cache[report_obj._name][report.id] = {}
+                    cache.setdefault(Report.__name__, {})
+                    cache[Report.__name__][report.id] = {}
 
-            attachment_obj = pool.get('ir.attachment')
-            attachment_ids = attachment_obj.search([
-                ('resource', '=', '%s,%s' % (object_name, object_id)),
-                ])
-            for attachment in attachment_obj.browse(attachment_ids):
+            Attachment = pool.get('ir.attachment')
+            attachments = Attachment.search([
+                    ('resource', '=', '%s,%s' % (object_name, object_id)),
+                    ])
+            for attachment in attachments:
                 if attachment.name and not attachment.link:
                     if '/' in attachment.name:
                         continue
                     res.append(attachment.name)
                     if cache is not None:
-                        cache.setdefault(attachment_obj._name, {})
-                        cache[attachment_obj._name][attachment.id] = {}
+                        cache.setdefault(Attachment.__name__, {})
+                        cache[Attachment.__name__][attachment.id] = {}
         return res
 
-    def get_resourcetype(self, uri, cache=None):
+    @classmethod
+    def get_resourcetype(cls, uri, cache=None):
         from pywebdav.lib.constants import COLLECTION, OBJECT
-        object_name, object_id = self._uri2object(uri, cache=cache)
+        object_name, object_id = cls._uri2object(uri, cache=cache)
         if object_name in ('ir.attachment', 'ir.action.report'):
             return OBJECT
         return COLLECTION
 
-    def get_displayname(self, uri, cache=None):
-        object_name, object_id = self._uri2object(uri, cache=cache)
-        model_obj = Pool().get(object_name)
-        return model_obj.browse(object_id).rec_name
+    @classmethod
+    def get_displayname(cls, uri, cache=None):
+        object_name, object_id = cls._uri2object(uri, cache=cache)
+        Model = Pool().get(object_name)
+        return Model(object_id).rec_name
 
-    def get_contentlength(self, uri, cache=None):
+    @classmethod
+    def get_contentlength(cls, uri, cache=None):
         pool = Pool()
-        attachment_obj = pool.get('ir.attachment')
+        Attachment = pool.get('ir.attachment')
 
-        object_name, object_id = self._uri2object(uri, cache=cache)
+        object_name, object_id = cls._uri2object(uri, cache=cache)
         if object_name == 'ir.attachment':
 
             if cache is not None:
@@ -375,7 +372,7 @@
             else:
                 ids = [object_id]
 
-            attachments = attachment_obj.browse(ids)
+            attachments = Attachment.browse(ids)
 
             res = '0'
             for attachment in attachments:
@@ -394,28 +391,30 @@
             return res
         return '0'
 
-    def get_contenttype(self, uri, cache=None):
-        object_name, object_id = self._uri2object(uri, cache=cache)
+    @classmethod
+    def get_contenttype(cls, uri, cache=None):
+        object_name, object_id = cls._uri2object(uri, cache=cache)
         if object_name in ('ir.attachment', 'ir.action.report'):
             ext = os.path.splitext(uri)[1]
             if not ext:
                 return "application/octet-stream"
-            return self.ext2mime.get(ext, 'application/octet-stream')
+            return cls.ext2mime.get(ext, 'application/octet-stream')
         return "application/octet-stream"
 
-    def get_creationdate(self, uri, cache=None):
+    @classmethod
+    def get_creationdate(cls, uri, cache=None):
         pool = Pool()
-        object_name, object_id = self._uri2object(uri, cache=cache)
+        object_name, object_id = cls._uri2object(uri, cache=cache)
         if object_name == 'ir.attachment':
-            model_obj = pool.get(object_name)
+            Model = pool.get(object_name)
             if object_id:
                 if cache is not None:
-                    cache.setdefault(model_obj._name, {})
-                    ids = cache[model_obj._name].keys()
+                    cache.setdefault(Model.__name__, {})
+                    ids = cache[Model.__name__].keys()
                     if object_id not in ids:
                         ids.append(object_id)
-                    elif 'creationdate' in cache[model_obj._name][object_id]:
-                        return cache[model_obj._name][object_id][
+                    elif 'creationdate' in cache[Model.__name__][object_id]:
+                        return cache[Model.__name__][object_id][
                             'creationdate']
                 else:
                     ids = [object_id]
@@ -426,32 +425,33 @@
                     red_sql, red_ids = reduce_ids('id', sub_ids)
                     cursor.execute('SELECT id, '
                         'EXTRACT(epoch FROM create_date) '
-                        'FROM "' + model_obj._table + '" '
+                        'FROM "' + Model._table + '" '
                         'WHERE ' + red_sql, red_ids)
                     for object_id2, date in cursor.fetchall():
                         if object_id2 == object_id:
                             res = date
                         if cache is not None:
-                            cache[model_obj._name].setdefault(object_id2, {})
-                            cache[model_obj._name][object_id2][
+                            cache[Model.__name__].setdefault(object_id2, {})
+                            cache[Model.__name__][object_id2][
                                 'creationdate'] = date
                 if res is not None:
                     return res
         return time.time()
 
-    def get_lastmodified(self, uri, cache=None):
+    @classmethod
+    def get_lastmodified(cls, uri, cache=None):
         pool = Pool()
-        object_name, object_id = self._uri2object(uri, cache=cache)
+        object_name, object_id = cls._uri2object(uri, cache=cache)
         if object_name == 'ir.attachment':
-            model_obj = pool.get(object_name)
+            Model = pool.get(object_name)
             if object_id:
                 if cache is not None:
-                    cache.setdefault(model_obj._name, {})
-                    ids = cache[model_obj._name].keys()
+                    cache.setdefault(Model.__name__, {})
+                    ids = cache[Model.__name__].keys()
                     if object_id not in ids:
                         ids.append(object_id)
-                    elif 'lastmodified' in cache[model_obj._name][object_id]:
-                        return cache[model_obj._name][object_id][
+                    elif 'lastmodified' in cache[Model.__name__][object_id]:
+                        return cache[Model.__name__][object_id][
                             'lastmodified']
                 else:
                     ids = [object_id]
@@ -463,27 +463,28 @@
                     cursor.execute('SELECT id, '
                         'EXTRACT(epoch FROM '
                             'COALESCE(write_date, create_date)) '
-                        'FROM "' + model_obj._table + '" '
+                        'FROM "' + Model._table + '" '
                         'WHERE ' + red_sql, red_ids)
                     for object_id2, date in cursor.fetchall():
                         if object_id2 == object_id:
                             res = date
                         if cache is not None:
-                            cache[model_obj._name].setdefault(object_id2, {})
-                            cache[model_obj._name][object_id2][
+                            cache[Model.__name__].setdefault(object_id2, {})
+                            cache[Model.__name__][object_id2][
                                 'lastmodified'] = date
                 if res is not None:
                     return res
         return time.time()
 
-    def get_data(self, uri, cache=None):
+    @classmethod
+    def get_data(cls, uri, cache=None):
         from pywebdav.lib.errors import DAV_NotFound
         pool = Pool()
-        attachment_obj = pool.get('ir.attachment')
-        report_obj = pool.get('ir.action.report')
+        Attachment = pool.get('ir.attachment')
+        Report = pool.get('ir.action.report')
 
         if uri:
-            object_name, object_id = self._uri2object(uri, cache=cache)
+            object_name, object_id = cls._uri2object(uri, cache=cache)
 
             if object_name == 'ir.attachment' and object_id:
                 if cache is not None:
@@ -498,7 +499,7 @@
                         return res
                 else:
                     ids = [object_id]
-                attachments = attachment_obj.browse(ids)
+                attachments = Attachment.browse(ids)
 
                 res = DAV_NotFound
                 for attachment in attachments:
@@ -520,31 +521,32 @@
             if object_name == 'ir.action.report' and object_id:
                 report_id = int(uri.rsplit('/', 1)[-1].rsplit('-',
                     1)[-1].rsplit('.', 1)[0])
-                report = report_obj.browse(report_id)
+                report = Report(report_id)
                 if report.report_name:
-                    report_obj = pool.get(report.report_name,
+                    Report = pool.get(report.report_name,
                             type='report')
-                    val = report_obj.execute([object_id],
-                            {'id': object_id, 'ids': [object_id]})
+                    val = Report.execute([object_id],
+                        {'id': object_id, 'ids': [object_id]})
                     return val[1]
         raise DAV_NotFound
 
-    def put(self, uri, data, content_type, cache=None):
+    @classmethod
+    def put(cls, uri, data, content_type, cache=None):
         from pywebdav.lib.errors import DAV_Forbidden
         from pywebdav.lib.utils import get_uriparentpath, get_urifilename
-        object_name, object_id = self._uri2object(get_uriparentpath(uri),
+        object_name, object_id = cls._uri2object(get_uriparentpath(uri),
                 cache=cache)
         if not object_name \
                 or object_name in ('ir.attachment') \
                 or not object_id:
             raise DAV_Forbidden
         pool = Pool()
-        attachment_obj = pool.get('ir.attachment')
-        object_name2, object_id2 = self._uri2object(uri, cache=cache)
+        Attachment = pool.get('ir.attachment')
+        object_name2, object_id2 = cls._uri2object(uri, cache=cache)
         if not object_id2:
             name = get_urifilename(uri)
             try:
-                attachment_obj.create({
+                Attachment.create({
                     'name': name,
                     'data': data,
                     'resource': '%s,%s' % (object_name, object_id),
@@ -553,25 +555,26 @@
                 raise DAV_Forbidden
         else:
             try:
-                attachment_obj.write(object_id2, {
+                Attachment.write(object_id2, {
                     'data': data,
                     })
             except Exception:
                 raise DAV_Forbidden
         return
 
-    def mkcol(self, uri, cache=None):
+    @classmethod
+    def mkcol(cls, uri, cache=None):
         from pywebdav.lib.errors import DAV_Forbidden
         from pywebdav.lib.utils import get_uriparentpath, get_urifilename
         if uri[-1:] == '/':
             uri = uri[:-1]
-        object_name, object_id = self._uri2object(get_uriparentpath(uri),
+        object_name, object_id = cls._uri2object(get_uriparentpath(uri),
                 cache=cache)
         if object_name != 'webdav.collection':
             raise DAV_Forbidden
         name = get_urifilename(uri)
         try:
-            self.create({
+            cls.create({
                 'name': name,
                 'parent': object_id,
                 })
@@ -579,50 +582,51 @@
             raise DAV_Forbidden
         return 201
 
-    def rmcol(self, uri, cache=None):
+    @classmethod
+    def rmcol(cls, uri, cache=None):
         from pywebdav.errors import DAV_Forbidden
-        object_name, object_id = self._uri2object(uri, cache=cache)
+        object_name, object_id = cls._uri2object(uri, cache=cache)
         if object_name != 'webdav.collection' \
                 or not object_id:
             raise DAV_Forbidden
         try:
-            self.delete(object_id)
+            cls.delete(object_id)
         except Exception:
             raise DAV_Forbidden
         return 200
 
-    def rm(self, uri, cache=None):
+    @classmethod
+    def rm(cls, uri, cache=None):
         from pywebdav.errors import DAV_Forbidden
-        object_name, object_id = self._uri2object(uri, cache=cache)
+        object_name, object_id = cls._uri2object(uri, cache=cache)
         if not object_name:
             raise DAV_Forbidden
         if object_name != 'ir.attachment' \
                 or not object_id:
             raise DAV_Forbidden
         pool = Pool()
-        model_obj = pool.get(object_name)
+        Model = pool.get(object_name)
         try:
-            model_obj.delete(object_id)
+            Model.delete(object_id)
         except Exception:
             raise DAV_Forbidden
         return 200
 
-    def exists(self, uri, cache=None):
-        object_name, object_id = self._uri2object(uri, cache=cache)
+    @classmethod
+    def exists(cls, uri, cache=None):
+        object_name, object_id = cls._uri2object(uri, cache=cache)
         if object_name and object_id:
             return 1
         return None
 
-    def current_user_privilege_set(self, uri, cache=None):
+    @staticmethod
+    def current_user_privilege_set(uri, cache=None):
         return ['create', 'read', 'write', 'delete']
 
-Collection()
-
 
 class Share(ModelSQL, ModelView):
     "Share"
-    _name = 'webdav.share'
-    _description = __doc__
+    __name__ = 'webdav.share'
     _rec_name = 'key'
 
     path = fields.Char('Path', required=True, select=True)
@@ -635,50 +639,49 @@
     note = fields.Text('Note')
     url = fields.Function(fields.Char('URL'), 'get_url')
 
-    def default_key(self):
+    @staticmethod
+    def default_key():
         return uuid.uuid4().hex
 
-    def default_user(self):
+    @staticmethod
+    def default_user():
         return Transaction().user
 
-    def default_expiration_date(self):
+    @staticmethod
+    def default_expiration_date():
         return datetime.date.today() + relativedelta(months=1)
 
-    def get_url(self, ids, name):
-        urls = {}
-        webdav_url = get_webdav_url()
-        for share in self.browse(ids):
-            urls[share.id] = urlparse.urljoin(webdav_url,
-                urlparse.urlunsplit((None, None, urllib.quote(share.path),
-                        urllib.urlencode([('key', share.key)]), None)))
-        return urls
+    def get_url(self, name):
+        return urlparse.urljoin(get_webdav_url(),
+            urlparse.urlunsplit((None, None, urllib.quote(self.path),
+                    urllib.urlencode([('key', self.key)]), None)))
 
-    def match(self, share, command, path):
+    @staticmethod
+    def match(share, command, path):
         "Test if share match with command and path"
         today = datetime.date.today()
         return (path.startswith(share.path)
             and share.expiration_date > today
             and command == 'GET')
 
-    def get_login(self, key, command, path):
+    @classmethod
+    def get_login(cls, key, command, path):
         """Validate the key for the command and path
         Return the user id if succeed or None
         """
-        share_ids = self.search([
+        shares = cls.search([
                 ('key', '=', key),
                 ])
-        if not share_ids:
+        if not shares:
             return None
-        for share in self.browse(share_ids):
-            if self.match(share, command, path):
+        for share in shares:
+            if cls.match(share, command, path):
                 return share.user.id
         return None
 
-Share()
-
 
 class Attachment(ModelSQL, ModelView):
-    _name = 'ir.attachment'
+    __name__ = 'ir.attachment'
 
     path = fields.Function(fields.Char('Path'), 'get_path')
     url = fields.Function(fields.Char('URL'), 'get_url')
@@ -688,36 +691,38 @@
                 ],
             depends=['path']), 'get_shares', 'set_shares')
 
-    def __init__(self):
-        super(Attachment, self).__init__()
-        self._constraints += [
+    @classmethod
+    def __setup__(cls):
+        super(Attachment, cls).__setup__()
+        cls._constraints += [
             ('check_collection', 'collection_attachment_name'),
         ]
-        self._error_messages.update({
+        cls._error_messages.update({
             'collection_attachment_name': ('You can not create an attachment\n'
                     'in a collection with the name\n'
                     'of an existing child collection!'),
         })
 
-    def check_collection(self, ids):
+    @classmethod
+    def check_collection(cls, attachments):
         pool = Pool()
-        collection_obj = pool.get('webdav.collection')
-        for attachment in self.browse(ids):
+        Collection = pool.get('webdav.collection')
+        for attachment in attachments:
             if attachment.resource:
                 model_name, record_id = attachment.resource.split(',')
                 if model_name == 'webdav.collection':
-                    collection = collection_obj.browse(int(record_id))
+                    collection = Collection(int(record_id))
                     for child in collection.childs:
                         if child.name == attachment.name:
                             return False
         return True
 
-    def get_path(self, ids, name):
+    @classmethod
+    def get_path(cls, attachments, name):
         pool = Pool()
-        collection_obj = pool.get('webdav.collection')
-        paths = dict((x, None) for x in ids)
+        Collection = pool.get('webdav.collection')
+        paths = dict((a.id, None) for a in attachments)
 
-        attachments = self.browse(ids)
         resources = {}
         resource2attachments = {}
         for attachment in attachments:
@@ -728,17 +733,17 @@
             resources.setdefault(model_name, set()).add(record_id)
             resource2attachments.setdefault((model_name, record_id),
                 []).append(attachment)
-        collection_ids = collection_obj.search([
+        collections = Collection.search([
                 ('model.model', 'in', resources.keys()),
                 ])
-        for collection in collection_obj.browse(collection_ids):
+        for collection in collections:
             model_name = collection.model.model
-            model_obj = pool.get(model_name)
+            Model = pool.get(model_name)
             ids = list(resources[model_name])
             domain = safe_eval(collection.domain or '[]')
             domain = [domain, ('id', 'in', ids)]
-            record_ids = model_obj.search(domain)
-            for record in model_obj.browse(record_ids):
+            records = Model.search(domain)
+            for record in records:
                 for attachment in resource2attachments[
                         (model_name, record.id)]:
                     paths[attachment.id] = '/'.join((collection.rec_name,
@@ -746,59 +751,52 @@
                             attachment.name))
         if 'webdav.collection' in resources:
             collection_ids = list(resources['webdav.collection'])
-            for collection in collection_obj.browse(collection_ids):
+            for collection in Collection.browse(collection_ids):
                 for attachment in resource2attachments[
                         ('webdav.collection', collection.id)]:
                     paths[attachment.id] = '/'.join((collection.rec_name,
                             attachment.name))
         return paths
 
-    def get_url(self, ids, name):
-        webdav_url = get_webdav_url()
-        urls = {}
-        for attachment in self.browse(ids):
-            if attachment.path:
-                urls[attachment.id] = urlparse.urljoin(webdav_url,
-                    urllib.quote(attachment.path))
-            else:
-                urls[attachment.id] = None
-        return urls
+    def get_url(self, name):
+        if self.path:
+            return urlparse.urljoin(get_webdav_url(), urllib.quote(self.path))
 
-    def get_shares(self, ids, name):
-        share_obj = Pool().get('webdav.share')
-        shares = dict((x, []) for x in ids)
-        attachements = self.browse(ids)
-        path2attachement = dict((a.path, a) for a in attachements)
-        share_ids = share_obj.search([
+    @classmethod
+    def get_shares(cls, attachments, name):
+        Share = Pool().get('webdav.share')
+        shares = dict((a.id, []) for a in attachments)
+        path2attachement = dict((a.path, a) for a in attachments)
+        shares = Share.search([
                 ('path', 'in', path2attachement.keys()),
                 ])
-        for share in share_obj.browse(share_ids):
+        for share in shares:
             attachment = path2attachement[share.path]
             shares[attachment.id].append(share.id)
         return shares
 
-    def set_shares(self, ids, name, value):
-        share_obj = Pool().get('webdav.share')
+    @classmethod
+    def set_shares(cls, attachments, name, value):
+        Share = Pool().get('webdav.share')
 
         if not value:
             return
 
-        attachments = self.browse(ids)
         for action in value:
             if action[0] == 'create':
                 for attachment in attachments:
                     action[1]['path'] = attachment.path
-                    share_obj.create(action[1])
+                    Share.create(action[1])
             elif action[0] == 'write':
-                share_obj.write(action[1], action[2])
+                Share.write(action[1], action[2])
             elif action[0] == 'delete':
-                share_obj.delete(action[1])
+                Share.delete(Share.browse(action[1]))
             elif action[0] == 'delete_all':
                 paths = [a.path for a in attachments]
-                share_ids = share_obj.search([
+                shares = Share.search([
                         ('path', 'in', paths),
                         ])
-                share_obj.delete(share_ids)
+                Share.delete(shares)
             elif action[0] == 'unlink':
                 pass
             elif action[0] == 'add':
@@ -809,5 +807,3 @@
                 pass
             else:
                 raise Exception('Bad arguments')
-
-Attachment()
diff -r f6ef988fda71 trytond/wizard/__init__.py
--- a/trytond/wizard/__init__.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/wizard/__init__.py	Sun Sep 23 17:30:03 2012 +0200
@@ -1,3 +1,5 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
-from .wizard import *
+from .wizard import Wizard, StateView, StateTransition, StateAction, Button
+
+__all__ = ['Wizard', 'StateView', 'StateTransition', 'StateAction', 'Button']
diff -r f6ef988fda71 trytond/wizard/wizard.py
--- a/trytond/wizard/wizard.py	Fri Sep 21 16:04:02 2012 +0200
+++ b/trytond/wizard/wizard.py	Sun Sep 23 17:30:03 2012 +0200
@@ -1,22 +1,22 @@
 #This file is part of Tryton.  The COPYRIGHT file at the top level of
 #this repository contains the full copyright notices and license terms.
 
-__all__ = ['Wizard', 'StateView', 'StateTransition', 'StateAction', 'Button',
-    'Session']
+__all__ = ['Wizard', 'StateView', 'StateTransition', 'StateAction', 'Button']
 
 try:
     import simplejson as json
 except ImportError:
     import json
+import copy
 
-from trytond.pool import Pool
+from trytond.pool import Pool, PoolMeta
 from trytond.transaction import Transaction
 from trytond.error import WarningErrorMixin
 from trytond.url import URLMixin
 from trytond.protocols.jsonrpc import object_hook, JSONEncoder
 from trytond.model.fields import states_validate
 from trytond.pyson import PYSONEncoder
-from trytond.model.browse import BrowseRecordNull
+from trytond.rpc import RPC
 
 
 class Button(object):
@@ -46,6 +46,7 @@
     '''
     A State of a wizard.
     '''
+    name = None
 
 
 class StateView(State):
@@ -69,34 +70,34 @@
         '''
         Returns the view definition
         '''
-        model_obj = Pool().get(self.model_name)
-        model_data_obj = Pool().get('ir.model.data')
+        Model_ = Pool().get(self.model_name)
+        ModelData = Pool().get('ir.model.data')
         module, fs_id = self.view.split('.')
-        view_id = model_data_obj.get_id(module, fs_id)
-        return model_obj.fields_view_get(view_id=view_id, view_type='form')
+        view_id = ModelData.get_id(module, fs_id)
+        return Model_.fields_view_get(view_id=view_id, view_type='form')
 
-    def get_defaults(self, wizard, session, state_name, fields):
+    def get_defaults(self, wizard, state_name, fields):
         '''
         Returns defaults values for the fields
         '''
-        model_obj = Pool().get(self.model_name)
-        defaults = model_obj.default_get(fields)
+        Model_ = Pool().get(self.model_name)
+        defaults = Model_.default_get(fields)
         default = getattr(wizard, 'default_%s' % state_name, None)
         if default:
-            defaults.update(default(session, fields))
+            defaults.update(default(fields))
         return defaults
 
     def get_buttons(self, wizard, state_name):
         '''
         Returns button definitions translated
         '''
-        translation_obj = Pool().get('ir.translation')
+        Translation = Pool().get('ir.translation')
 
         def translation_key(button):
-            return (','.join((wizard._name, state_name, button.state)),
+            return (','.join((wizard.__name__, state_name, button.state)),
                 'wizard_button', Transaction().language, button.string)
         translation_keys = [translation_key(button) for button in self.buttons]
-        translations = translation_obj.get_sources(translation_keys)
+        translations = Translation.get_sources(translation_keys)
         encoder = PYSONEncoder()
         result = []
         for button in self.buttons:
@@ -132,131 +133,48 @@
     def get_action(self):
         "Returns action definition"
         pool = Pool()
-        model_data_obj = pool.get('ir.model.data')
-        action_obj = pool.get('ir.action')
+        ModelData = pool.get('ir.model.data')
+        Action = pool.get('ir.action')
         module, fs_id = self.action_id.split('.')
-        action_id = action_obj.get_action_id(
-            model_data_obj.get_id(module, fs_id))
-        action = action_obj.browse(action_id)
-        return action_obj.get_action_values(action.type, action.id)
-
-
-class _SessionRecord(object):
-    '''
-    A record of a wizard form.
-    '''
-    # Declared in class to prevent:
-    # 'maximum recursion depth exceeded in __subclasscheck__'
-    _model = None
-    _data = None
-    __cache = None
-
-    def __init__(self, model, data):
-        self._model = model
-        self._data = data
-        self.__cache = {}
-        self.dirty = False
-
-    def __getattr__(self, name):
-        if name in self.__cache:
-            return self.__cache[name]
-        field = self._model._columns[name]
-        data = self._data.get(name, None)
-        target_obj = None
-        if hasattr(field, 'model_name'):
-            target_obj = Pool().get(field.model_name)
-        elif hasattr(field, 'get_target'):
-            target_obj = field.get_target()
-        if data:
-            if target_obj:
-                def instance(data):
-                    if isinstance(data, dict):
-                        return _SessionRecord(target_obj, data)
-                    return target_obj.browse(data)
-                if isinstance(data, list):
-                    data = [instance(x) for x in data]
-                else:
-                    data = instance(data)
-        elif target_obj and not isinstance(data, list):
-            return BrowseRecordNull()
-        self.__cache[name] = data
-        return data
-
-    def __setattr__(self, name, value):
-        if (self._model is not None
-                and (name in self._model._columns
-                    or name in self._model._inherit_fields)):
-            self.__cache.pop(name, None)
-            self._data[name] = value
-            self.dirty = True
-        else:
-            super(_SessionRecord, self).__setattr__(name, value)
-
-
-class Session(object):
-    '''
-    A wizard session.
-    '''
-
-    def __init__(self, wizard, session_id):
-        pool = Pool()
-        session_obj = pool.get('ir.session.wizard')
-        self._session = session_obj.browse(session_id)
-        self.data = json.loads(self._session.data.encode('utf-8'),
-            object_hook=object_hook)
-        for state_name, state in wizard.states.iteritems():
-            if isinstance(state, StateView):
-                model = pool.get(state.model_name)
-                self.data.setdefault(state_name, {})
-                setattr(self, state_name,
-                    _SessionRecord(model, self.data[state_name]))
-
-    @property
-    def dirty(self):
-        return any(getattr(self, state_name).dirty
-            for state_name in self.data)
-
-    def save(self):
-        "Save the session in database"
-        session_obj = Pool().get('ir.session.wizard')
-        if self.dirty:
-            session_obj.write(self._session.id, {
-                    'data': json.dumps(self.data, cls=JSONEncoder),
-                    })
+        action_id = Action.get_action_id(
+            ModelData.get_id(module, fs_id))
+        action = Action(action_id)
+        return Action.get_action_values(action.type, [action.id])[0]
 
 
 class Wizard(WarningErrorMixin, URLMixin):
-    _name = ""
+    __metaclass__ = PoolMeta
     start_state = 'start'
     end_state = 'end'
 
-    def __new__(cls):
-        Pool.register(cls, type='wizard')
+    @classmethod
+    def __setup__(cls):
+        cls.__rpc__ = {
+            'create': RPC(readonly=False),
+            'delete': RPC(readonly=False),
+            'execute': RPC(readonly=False),
+            }
+        cls._error_messages = {}
 
-    def __init__(self):
-        super(Wizard, self).__init__()
-        self._rpc = {
-            'create': True,
-            'delete': True,
-            'execute': True,
-        }
-        self._error_messages = {}
+        # Copy states
+        for attr in dir(cls):
+            if isinstance(getattr(cls, attr), State):
+                setattr(cls, attr, copy.deepcopy(getattr(cls, attr)))
 
-    @property
-    def states(self):
-        result = {}
-        for attr in dir(self):
-            if attr == 'states':
-                continue
-            if isinstance(getattr(self, attr), State):
-                result[attr] = getattr(self, attr)
-        return result
+    @classmethod
+    def __post_setup__(cls):
+        # Set states
+        cls.states = {}
+        for attr in dir(cls):
+            if isinstance(getattr(cls, attr), State):
+                cls.states[attr] = getattr(cls, attr)
 
-    def init(self, module_name):
+    @classmethod
+    def __register__(cls, module_name):
         pool = Pool()
-        translation_obj = pool.get('ir.translation')
+        Translation = pool.get('ir.translation')
         cursor = Transaction().cursor
-        for state_name, state in self.states.iteritems():
+        for state_name, state in cls.states.iteritems():
             if isinstance(state, StateView):
                 for button in state.buttons:
                     cursor.execute('SELECT id, name, src '
@@ -266,16 +184,16 @@
                             'AND type = %s '
                             'AND name = %s',
                         (module_name, 'en_US', 'wizard_button',
-                            self._name + ',' + state_name + ',' +
+                            cls.__name__ + ',' + state_name + ',' +
                             button.state))
                     res = cursor.dictfetchall()
-                    src_md5 = translation_obj.get_src_md5(button.string)
+                    src_md5 = Translation.get_src_md5(button.string)
                     if not res:
                         cursor.execute('INSERT INTO ir_translation '
                             '(name, lang, type, src, src_md5, value, module, '
                                 'fuzzy) '
                             'VALUES (%s, %s, %s, %s, %s, %s, %s, %s)',
-                            (self._name + ',' + state_name + ',' +
+                            (cls.__name__ + ',' + state_name + ',' +
                                 button.state,
                                 'en_US', 'wizard_button', button.string,
                                 src_md5, '', module_name, False))
@@ -289,31 +207,34 @@
             'WHERE lang = %s '
                 'AND type = %s '
                 'AND name = %s',
-            ('en_US', 'error', self._name))
+            ('en_US', 'error', cls.__name__))
         trans_error = {}
         for trans in cursor.dictfetchall():
             trans_error[trans['src']] = trans
 
-        for error in self._error_messages.values():
+        for error in cls._error_messages.values():
             if error not in trans_error:
-                error_md5 = translation_obj.get_src_md5(error)
+                error_md5 = Translation.get_src_md5(error)
                 cursor.execute('INSERT INTO ir_translation '
                     '(name, lang, type, src, src_md5,  value, module, fuzzy) '
                     'VALUES (%s, %s, %s, %s, %s, %s, %s, %s)',
-                    (self._name, 'en_US', 'error', error, error_md5, '',
+                    (cls.__name__, 'en_US', 'error', error, error_md5, '',
                         module_name, False))
 
-    def create(self):
+    @classmethod
+    def create(cls):
         "Create a session"
-        session_obj = Pool().get('ir.session.wizard')
-        return (session_obj.create({}), self.start_state, self.end_state)
+        Session = Pool().get('ir.session.wizard')
+        return (Session.create({}).id, cls.start_state, cls.end_state)
 
-    def delete(self, session_id):
+    @classmethod
+    def delete(cls, session_id):
         "Delete the session"
-        session_obj = Pool().get('ir.session.wizard')
-        session_obj.delete(session_id)
+        Session = Pool().get('ir.session.wizard')
+        Session.delete([Session(session_id)])
 
-    def execute(self, session, data, state_name):
+    @classmethod
+    def execute(cls, session, data, state_name):
         '''
         Execute the wizard state.
 
@@ -328,22 +249,25 @@
                 - ``defaults``: a dictionary with default values
                 - ``buttons``: a list of buttons
         '''
+        wizard = cls(session)
+        for key, values in data.iteritems():
+            record = getattr(wizard, key)
+            for field, value in values.iteritems():
+                if field == 'id':
+                    continue
+                setattr(record, field, value)
+        return wizard._execute(state_name)
+
+    def _execute(self, state_name):
         if state_name == self.end_state:
             return {}
-        if isinstance(session, (int, long)):
-            session = Session(self, session)
-        for key, value in data.iteritems():
-            prev_data = session.data[key].copy()
-            session.data[key].update(value)
-            if prev_data != session.data[key]:
-                getattr(session, key).dirty = True
 
         state = self.states[state_name]
         result = {}
 
         if isinstance(state, StateView):
             view = state.get_view()
-            defaults = state.get_defaults(self, session, state_name,
+            defaults = state.get_defaults(self, state_name,
                 view['fields'].keys())
             buttons = state.get_buttons(self, state_name)
             result['view'] = {
@@ -358,13 +282,40 @@
                 action = state.get_action()
                 do = getattr(self, 'do_%s' % state_name, None)
                 if do:
-                    do_result = do(session, action)
+                    do_result = do(action)
                 else:
                     do_result = action, {}
             transition = getattr(self, 'transition_%s' % state_name, None)
             if transition:
-                result = self.execute(session, {}, transition(session))
+                result = self._execute(transition())
             if do_result:
                 result.setdefault('actions', []).append(do_result)
-        session.save()
+        self._save()
         return result
+
+    def __init__(self, session_id):
+        pool = Pool()
+        Session = pool.get('ir.session.wizard')
+        self._session_id = session_id
+        session = Session(session_id)
+        data = json.loads(session.data.encode('utf-8'),
+            object_hook=object_hook)
+        for state_name, state in self.states.iteritems():
+            if isinstance(state, StateView):
+                Target = pool.get(state.model_name)
+                data.setdefault(state_name, {})
+                setattr(self, state_name, Target(**data[state_name]))
+
+    def _save(self):
+        "Save the session in database"
+        Session = Pool().get('ir.session.wizard')
+        data = {}
+        for state_name, state in self.states.iteritems():
+            if isinstance(state, StateView):
+                data[state_name] = getattr(self, state_name)._default_values
+        session = Session(self._session_id)
+        data = json.dumps(data, cls=JSONEncoder)
+        if data != session.data.encode('utf-8'):
+            Session.write([session], {
+                    'data': data,
+                    })
