1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as
9 # published by the Free Software Foundation, either version 3 of the
10 # License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 ##############################################################################
22 """ Models registries.
25 from contextlib import contextmanager
30 import openerp.osv.orm
33 import openerp.modules.db
34 import openerp.tools.config
35 from openerp.tools import assertion_report
37 _logger = logging.getLogger(__name__)
39 class Registry(object):
40 """ Model registry for a particular database.
42 The registry is essentially a mapping between model names and model
43 instances. There is one registry instance per database.
47 def __init__(self, db_name):
48 self.models = {} # model name/model instance mapping
50 self._store_function = {}
52 self._init_parent = {}
53 self._assertion_report = assertion_report.assertion_report()
55 # modules fully loaded (maintained during init phase by `loading` module)
56 self._init_modules = set()
58 self.db_name = db_name
59 self.db = openerp.sql_db.db_connect(db_name)
62 has_unaccent = openerp.modules.db.has_unaccent(cr)
63 if openerp.tools.config['unaccent'] and not has_unaccent:
64 _logger.warning("The option --unaccent was given but no unaccent() function was found in database.")
65 self.has_unaccent = openerp.tools.config['unaccent'] and has_unaccent
68 def do_parent_store(self, cr):
69 for o in self._init_parent:
70 self.get(o)._parent_store_compute(cr)
74 """ Return the list of model names in this registry."""
75 return self.models.keys()
77 def add(self, model_name, model):
78 """ Add or replace a model in the registry."""
79 self.models[model_name] = model
81 def get(self, model_name):
82 """ Return a model for a given name or None if it doesn't exist."""
83 return self.models.get(model_name)
85 def __getitem__(self, model_name):
86 """ Return a model for a given name or raise KeyError if it doesn't exist."""
87 return self.models[model_name]
89 def load(self, cr, module):
90 """ Load a given module in the registry.
92 At the Python level, the modules are already loaded, but not yet on a
93 per-registry level. This method populates a registry with the given
94 modules, i.e. it instanciates all the classes of a the given module
95 and registers them in the registry.
101 # Instantiate registered classes (via the MetaModel automatic discovery
102 # or via explicit constructor call), and add them to the pool.
103 for cls in openerp.osv.orm.MetaModel.module_to_models.get(module.name, []):
104 res.append(cls.create_instance(self, cr))
108 def schedule_cron_jobs(self):
109 """ Make the cron thread care about this registry/database jobs.
110 This will initiate the cron thread to check for any pending jobs for
111 this registry/database as soon as possible. Then it will continuously
112 monitor the ir.cron model for future jobs. See openerp.cron for
115 openerp.cron.schedule_wakeup(openerp.cron.WAKE_UP_NOW, self.db.dbname)
117 def clear_caches(self):
119 This clears the caches associated to methods decorated with
120 ``tools.ormcache`` or ``tools.ormcache_multi`` for all the models.
122 for model in self.models.itervalues():
126 def cursor(self, auto_commit=True):
127 cr = self.db.cursor()
136 class RegistryManager(object):
137 """ Model registries manager.
139 The manager is responsible for creation and deletion of model
140 registries (essentially database connection/model registry pairs).
143 # Mapping between db name and model registry.
144 # Accessed through the methods below.
146 registries_lock = threading.RLock()
149 def get(cls, db_name, force_demo=False, status=None, update_module=False,
151 """ Return a registry for a given database name."""
153 return cls.registries[db_name]
155 return cls.new(db_name, force_demo, status,
156 update_module, pooljobs)
159 def new(cls, db_name, force_demo=False, status=None,
160 update_module=False, pooljobs=True):
161 """ Create and return a new registry for a given database name.
163 The (possibly) previous registry for that database name is discarded.
166 import openerp.modules
167 with cls.registries_lock:
168 registry = Registry(db_name)
170 # Initializing a registry will call general code which will in turn
171 # call registries.get (this object) to obtain the registry being
172 # initialized. Make it available in the registries dictionary then
173 # remove it if an exception is raised.
175 cls.registries[db_name] = registry
177 # This should be a method on Registry
178 openerp.modules.load_modules(registry.db, force_demo, status, update_module)
180 del cls.registries[db_name]
183 cr = registry.db.cursor()
185 registry.do_parent_store(cr)
186 registry.get('ir.actions.report.xml').register_all(cr)
192 registry.schedule_cron_jobs()
197 def delete(cls, db_name):
198 """Delete the registry linked to a given database.
200 This also cleans the associated caches. For good measure this also
201 cancels the associated cron job. But please note that the cron job can
202 be running and take some time before ending, and that you should not
203 remove a registry if it can still be used by some thread. So it might
204 be necessary to call yourself openerp.cron.Agent.cancel(db_name) and
205 and join (i.e. wait for) the thread.
207 with cls.registries_lock:
208 if db_name in cls.registries:
209 cls.registries[db_name].clear_caches()
210 del cls.registries[db_name]
211 openerp.cron.cancel(db_name)
215 """Delete all the registries. """
216 with cls.registries_lock:
217 for db_name in cls.registries.keys():
221 def clear_caches(cls, db_name):
224 This clears the caches associated to methods decorated with
225 ``tools.ormcache`` or ``tools.ormcache_multi`` for all the models
226 of the given database name.
228 This method is given to spare you a ``RegistryManager.get(db_name)``
229 that would loads the given database if it was not already loaded.
231 with cls.registries_lock:
232 if db_name in cls.registries:
233 cls.registries[db_name].clear_caches()
236 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: