Root类是 OpenERP Web客户端的WSGI应用,处理HTTP请求的接口为__call__()
def __call__(self, environ, start_response):
""" Handle a WSGI request
"""
if not self._loaded:
self._loaded = True
self.load_addons()
return self.dispatch(environ, start_response)
可见,dispatch()是处理HTTP请求的核心方法
def dispatch(self, environ, start_response):
"""
Performs the actual WSGI dispatching for the application.
"""
try:
httprequest = werkzeug.wrappers.Request(environ)
httprequest.app = self
explicit_session = self.setup_session(httprequest)
self.setup_db(httprequest)
self.setup_lang(httprequest)
request = self.get_request(httprequest)
def _dispatch_nodb():
try:
func, arguments = self.nodb_routing_map.bind_to_environ(request.httprequest.environ).match()
except werkzeug.exceptions.HTTPException, e:
return request._handle_exception(e)
request.set_handler(func, arguments, "none")
result = request.dispatch()
return result
with request:
db = request.session.db
if db:
openerp.modules.registry.RegistryManager.check_registry_signaling(db)
try:
with openerp.tools.mute_logger('openerp.sql_db'):
ir_http = request.registry['ir.http']
except (AttributeError, psycopg2.OperationalError):
# psycopg2 error or attribute error while constructing
# the registry. That means the database probably does
# not exists anymore or the code doesnt match the db.
# Log the user out and fall back to nodb
request.session.logout()
result = _dispatch_nodb()
else:
result = ir_http._dispatch()
openerp.modules.registry.RegistryManager.signal_caches_change(db)
else:
result = _dispatch_nodb()
response = self.get_response(httprequest, result, explicit_session)
return response(environ, start_response)
except werkzeug.exceptions.HTTPException, e:
return e(environ, start_response)
dispatch将通过setup_session()方法恢复或者创建session,session保存在openerp.tools.config.session_dir所指明的目录下。
在dispatch()中,通过 httprequest = werkzeug.wrappers.Request(environ)
得到WSGI的Request对象,并对werkzeug.wrappers.Request类通过HttpRequest类进行包装。
通过bind_to_environ
函数,得到当前Http请求对应的回调函数。并将回调函数通过request.set_handler(func, arguments, "none")
传递给request。紧接着通过ir_http = request.registry['ir.http']
得到一个ir_http对象
request.registry定义如下:
class WebRequest(object):
...
@property
def registry(self):
"""
The registry to the database linked to this request. Can be ``None``
if the current request uses the ``none`` authentication.
.. deprecated:: 8.0
use :attr:`.env`
"""
return openerp.modules.registry.RegistryManager.get(self.db) if self.db else None
RegistryManager.get()从指定的数据库名称中返回一个registry对象,其定义如下。
@classmethod
def get(cls, db_name, force_demo=False, status=None, update_module=False):
""" Return a registry for a given database name."""
with cls.lock():
try:
return cls.registries[db_name]
except KeyError:
return cls.new(db_name, force_demo, status,
update_module)
finally:
# set db tracker - cleaned up at the WSGI
# dispatching phase in openerp.service.wsgi_server.application
threading.current_thread().dbname = db_name
新建Registry的代码如下,此时应当为加载一个新的数据库。
@classmethod
def new(cls, db_name, force_demo=False, status=None,
update_module=False):
""" Create and return a new registry for a given database name.
The (possibly) previous registry for that database name is discarded.
"""
import openerp.modules
with cls.lock():
with openerp.api.Environment.manage():
registry = Registry(db_name)
# Initializing a registry will call general code which will in
# turn call registries.get (this object) to obtain the registry
# being initialized. Make it available in the registries
# dictionary then remove it if an exception is raised.
cls.delete(db_name)
cls.registries[db_name] = registry
try:
with registry.cursor() as cr:
seq_registry, seq_cache = Registry.setup_multi_process_signaling(cr)
registry.base_registry_signaling_sequence = seq_registry
registry.base_cache_signaling_sequence = seq_cache
# This should be a method on Registry
openerp.modules.load_modules(registry._db, force_demo, status, update_module)
except Exception:
del cls.registries[db_name]
raise
# load_modules() above can replace the registry by calling
# indirectly new() again (when modules have to be uninstalled).
# Yeah, crazy.
registry = cls.registries[db_name]
cr = registry.cursor()
try:
registry.do_parent_store(cr)
cr.commit()
finally:
cr.close()
registry.ready = True
if update_module:
# only in case of update, otherwise we'll have an infinite reload loop!
cls.signal_registry_change(db_name)
return registry
加载模块
#openerp.modules.load_modules(registry._db, force_demo, status, update_module)
def load_modules(db, force_demo=False, status=None, update_module=False):
...
# STEP 1: LOAD BASE (must be done before module dependencies can be computed for later steps)
graph = openerp.modules.graph.Graph()
graph.add_module(cr, 'base', force)
...
loaded_modules, processed_modules = load_module_graph(cr, graph, status, perform_checks=update_module, report=report)
# STEP 2: Mark other modules to be loaded/updated
...
# STEP 3: Load marked modules (skipping base which was done in STEP 1)
# IMPORTANT: this is done in two parts, first loading all installed or
# partially installed modules (i.e. installed/to upgrade), to
# offer a consistent system to the second part: installing
# newly selected modules.
# We include the modules 'to remove' in the first step, because
# they are part of the "currently installed" modules. They will
# be dropped in STEP 6 later, before restarting the loading
# process.
# IMPORTANT 2: We have to loop here until all relevant modules have been
# processed, because in some rare cases the dependencies have
# changed, and modules that depend on an uninstalled module
# will not be processed on the first pass.
# It's especially useful for migrations.
...
if update_module:
processed_modules += load_marked_modules(cr, graph,
['to install'], force, status, report,
loaded_modules, update_module)
...
# STEP 4: Finish and cleanup installations
...
# STEP 5: Cleanup menus
# Remove menu items that are not referenced by any of other
# (child) menu item, ir_values, or ir_model_data.
# TODO: This code could be a method of ir_ui_menu. Remove menu without actions of children
...
# STEP 6: Uninstall modules to remove
# Remove records referenced from ir_model_data for modules to be
# removed (and removed the references from ir_model_data).
...
# STEP 7: verify custom views on every model
...
# STEP 8: call _register_hook on every model
...
# STEP 9: Run the post-install tests
...
for index, package in enumerate(graph):
module_name = package.name
module_id = package.id
...
load_openerp_module(package.name)
new_install = package.installed_version is None
if new_install:
py_module = sys.modules['openerp.addons.%s' % (module_name,)]
pre_init = package.info.get('pre_init_hook')
if pre_init:
getattr(py_module, pre_init)(cr)
models = registry.load(cr, package)#加载模块
loaded_modules.append(package.name)
if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'):
registry.setup_models(cr, partial=True)
init_module_models(cr, package.name, models)
# Can't put this line out of the loop: ir.module.module will be
# registered by init_module_models() above.
modobj = registry['ir.module.module']
...
def load_openerp_module(module_name):
global loaded
if module_name in loaded:
return
initialize_sys_path()
try:
mod_path = get_module_path(module_name)
__import__('openerp.addons.' + module_name)
info = load_information_from_description_file(module_name)
if info['post_load']:
getattr(sys.modules['openerp.addons.' + module_name], info['post_load'])()
except Exception, e:
msg = "Couldn't load module %s" % (module_name)
_logger.critical(msg)
_logger.critical(e)
raise
else:
loaded.append(module_name)
def init_module_models(cr, module_name, obj_list):
_logger.info('module %s: creating or updating database tables', module_name)
todo = []
for obj in obj_list:
result = obj._auto_init(cr, {'module': module_name})
...
cr.commit()
通过obj._auto_end(cr, {'module': module_name})
初始化数据库表
def _auto_init(self, cr, context=None):
...
self._field_create(cr, context=context)#添加ir_model、ir_model_fields和ir_model_data数据项
create = not self._table_exist(cr)
...
self._create_table(cr)
...
def load(self, cr, module):
...
from .. import models
models_to_load = [] # need to preserve loading order
lazy_property.reset_all(self)
for cls in models.MetaModel.module_to_models.get(module.name, []):
model = cls._build_model(self, cr)#实例化模块
if model._name not in models_to_load:
models_to_load.append(model._name)
return [self.models[m] for m in models_to_load]
openerp.modules.registry将自身作为pool传递给_build_model
@classmethod
def _build_model(cls, pool, cr):
...
# instantiate the model, and initialize it
model = object.__new__(cls)
model.__init__(pool, cr)
return model