1864
by pap(openerp)
Changed encoding to coding ref: PEP: 0263 |
1 |
# -*- coding: utf-8 -*-
|
1
by pinky
New trunk |
2 |
##############################################################################
|
1869.1.14
by nch at tinyerp
read_group method for group by |
3 |
#
|
1242
by hda at tinyerp
bugfix for active=false search |
4 |
# OpenERP, Open Source Management Solution
|
1903
by Harry (Open ERP)
[FIX] orm, osv: change copyright with AGPL |
5 |
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
1230
by Christophe Simonis
passing in GPL-3 |
6 |
#
|
7 |
# This program is free software: you can redistribute it and/or modify
|
|
1903
by Harry (Open ERP)
[FIX] orm, osv: change copyright with AGPL |
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.
|
|
1230
by Christophe Simonis
passing in GPL-3 |
11 |
#
|
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
|
|
1903
by Harry (Open ERP)
[FIX] orm, osv: change copyright with AGPL |
15 |
# GNU Affero General Public License for more details.
|
1230
by Christophe Simonis
passing in GPL-3 |
16 |
#
|
1903
by Harry (Open ERP)
[FIX] orm, osv: change copyright with AGPL |
17 |
# You should have received a copy of the GNU Affero General Public License
|
1869.1.14
by nch at tinyerp
read_group method for group by |
18 |
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
1230
by Christophe Simonis
passing in GPL-3 |
19 |
#
|
20 |
##############################################################################
|
|
1
by pinky
New trunk |
21 |
|
3471.2.7
by P. Christeas
API docs: settings file and titles at modules |
22 |
|
3471.2.1
by P. Christeas
orm, tools, addons: Doc strings improvements |
23 |
"""
|
24 |
Object relational mapping to database (postgresql) module
|
|
25 |
* Hierarchical structure
|
|
26 |
* Constraints consistency, validations
|
|
27 |
* Object meta Data depends on its status
|
|
28 |
* Optimised processing by complex query (multiple actions at once)
|
|
29 |
* Default fields value
|
|
30 |
* Permissions optimisation
|
|
31 |
* Persistant object: DB postgresql
|
|
32 |
* Datas conversions
|
|
33 |
* Multi-level caching system
|
|
34 |
* 2 different inheritancies
|
|
35 |
* Fields:
|
|
36 |
- classicals (varchar, integer, boolean, ...)
|
|
37 |
- relations (one2many, many2one, many2many)
|
|
38 |
- functions
|
|
3549.3.1
by tfr(OpenERP)
[FIX]:import boolean fields |
39 |
|
3471.2.1
by P. Christeas
orm, tools, addons: Doc strings improvements |
40 |
"""
|
41 |
||
4486
by Vo Minh Thu
[MERGE] babel: use babel for locale-aware read_group date formatting. |
42 |
import babel.dates |
2013
by Xavier Morel
[imp] fix imports |
43 |
import calendar |
4408.2.13
by Xavier Morel
[ADD] ability to convert postgres error messages to human-readable ones |
44 |
import collections |
2013
by Xavier Morel
[imp] fix imports |
45 |
import copy |
1845
by HDA(OpenERP)
[Merged] |
46 |
import datetime |
3594.1.11
by Xavier Morel
[IMP] fields_view_get: listcomps are still awesome |
47 |
import itertools |
2096
by Xavier Morel
[imp] migrate datas warning in browse_record to logging |
48 |
import logging |
2288
by Xavier Morel
[FIX] imports in orm, remove unneeded imports and pointlessly (and incorrectly) defensive garbage |
49 |
import operator |
2013
by Xavier Morel
[imp] fix imports |
50 |
import pickle |
51 |
import re |
|
3511.1.25
by Olivier Dony
[IMP] orm: introduce cleaner class hierarchy for models |
52 |
import simplejson |
2013
by Xavier Morel
[imp] fix imports |
53 |
import time |
4466.1.1
by Christophe Simonis
[IMP] orm: be more vebose when accessing an invalid field of browse_record objects |
54 |
import traceback |
2013
by Xavier Morel
[imp] fix imports |
55 |
import types |
4408.2.1
by Xavier Morel
[ADD] big bit on new import: pretty much everything but o2m |
56 |
|
57 |
import psycopg2 |
|
3511.1.25
by Olivier Dony
[IMP] orm: introduce cleaner class hierarchy for models |
58 |
from lxml import etree |
3790.1.1
by Xavier Morel
[IMP] Use babel's locale-aware date formatting when formatting dates in read_group titles |
59 |
|
3511.1.25
by Olivier Dony
[IMP] orm: introduce cleaner class hierarchy for models |
60 |
import fields |
61 |
import openerp |
|
62 |
import openerp.tools as tools |
|
3346.1.1
by Vo Minh Thu
[IMP] openerp python module. |
63 |
from openerp.tools.config import config |
4408.2.1
by Xavier Morel
[ADD] big bit on new import: pretty much everything but o2m |
64 |
from openerp.tools.misc import CountingStream |
4477.1.2
by Quentin (OpenERP)
[IMP] use of literal_eval from ast instead of const_eval from tools, as per Olivier recommendation |
65 |
from openerp.tools.safe_eval import safe_eval as eval |
3346.1.1
by Vo Minh Thu
[IMP] openerp python module. |
66 |
from openerp.tools.translate import _ |
3511.1.35
by Olivier Dony
[IMP] start unifying the SUPERUSER_ID constant |
67 |
from openerp import SUPERUSER_ID |
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
68 |
from query import Query |
773
by Fabien Pinckaers
Modifs |
69 |
|
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
70 |
_logger = logging.getLogger(__name__) |
3976.1.27
by Antony Lesuisse
[FIX] review according to xmo :) |
71 |
_schema = logging.getLogger(__name__ + '.schema') |
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
72 |
|
3167
by Olivier Dony
[FIX] osv,tools: break some circular dependencies |
73 |
# List of etree._Element subclasses that we choose to ignore when parsing XML.
|
3346.1.1
by Vo Minh Thu
[IMP] openerp python module. |
74 |
from openerp.tools import SKIPPED_ELEMENT_TYPES |
3167
by Olivier Dony
[FIX] osv,tools: break some circular dependencies |
75 |
|
1844.4.93
by Jay(Open ERP)
[FIX] Allowed SQL reserved keywords to be used in _order,group by ,etc.[Make sure , your _order should look like _order='to' instead of to] |
76 |
regex_order = re.compile('^(([a-z0-9_]+|"[a-z0-9_]+")( *desc| *asc)?( *, *|))+$', re.I) |
3417.6.13
by Vo Minh Thu
[IMP] orm: check for object _name validity. |
77 |
regex_object_name = re.compile(r'^[a-z0-9_.]+$') |
78 |
||
3497
by Vo Minh Thu
[REVERT] re-apply the 4 commits that were reverted just after, 4 commits ago. |
79 |
def transfer_field_to_modifiers(field, modifiers): |
3498.1.1
by niv-openerp
[imp] Improved modifiers algorithm to also take the "states" attribute into account. |
80 |
default_values = {} |
81 |
state_exceptions = {} |
|
3498.1.3
by Antony Lesuisse
[FIX] apply some merge review feeback |
82 |
for attr in ('invisible', 'readonly', 'required'): |
83 |
state_exceptions[attr] = [] |
|
84 |
default_values[attr] = bool(field.get(attr)) |
|
85 |
for state, modifs in (field.get("states",{})).items(): |
|
3498.1.1
by niv-openerp
[imp] Improved modifiers algorithm to also take the "states" attribute into account. |
86 |
for modif in modifs: |
3498.1.3
by Antony Lesuisse
[FIX] apply some merge review feeback |
87 |
if default_values[modif[0]] != modif[1]: |
3498.1.1
by niv-openerp
[imp] Improved modifiers algorithm to also take the "states" attribute into account. |
88 |
state_exceptions[modif[0]].append(state) |
3498.1.3
by Antony Lesuisse
[FIX] apply some merge review feeback |
89 |
|
3498.1.1
by niv-openerp
[imp] Improved modifiers algorithm to also take the "states" attribute into account. |
90 |
for attr, default_value in default_values.items(): |
3498.1.3
by Antony Lesuisse
[FIX] apply some merge review feeback |
91 |
if state_exceptions[attr]: |
92 |
modifiers[attr] = [("state", "not in" if default_value else "in", state_exceptions[attr])] |
|
93 |
else: |
|
94 |
modifiers[attr] = default_value |
|
3497
by Vo Minh Thu
[REVERT] re-apply the 4 commits that were reverted just after, 4 commits ago. |
95 |
|
96 |
||
97 |
# Don't deal with groups, it is done by check_group().
|
|
98 |
# Need the context to evaluate the invisible attribute on tree views.
|
|
99 |
# For non-tree views, the context shouldn't be given.
|
|
100 |
def transfer_node_to_modifiers(node, modifiers, context=None, in_tree_view=False): |
|
101 |
if node.get('attrs'): |
|
102 |
modifiers.update(eval(node.get('attrs'))) |
|
103 |
||
104 |
if node.get('states'): |
|
105 |
if 'invisible' in modifiers and isinstance(modifiers['invisible'], list): |
|
3974.2.2
by Florent Xicluna
[REF] fix weird indentation, not multiple of four. |
106 |
# TODO combine with AND or OR, use implicit AND for now.
|
107 |
modifiers['invisible'].append(('state', 'not in', node.get('states').split(','))) |
|
3497
by Vo Minh Thu
[REVERT] re-apply the 4 commits that were reverted just after, 4 commits ago. |
108 |
else: |
3974.2.2
by Florent Xicluna
[REF] fix weird indentation, not multiple of four. |
109 |
modifiers['invisible'] = [('state', 'not in', node.get('states').split(','))] |
3497
by Vo Minh Thu
[REVERT] re-apply the 4 commits that were reverted just after, 4 commits ago. |
110 |
|
111 |
for a in ('invisible', 'readonly', 'required'): |
|
112 |
if node.get(a): |
|
113 |
v = bool(eval(node.get(a), {'context': context or {}})) |
|
114 |
if in_tree_view and a == 'invisible': |
|
115 |
# Invisible in a tree view has a specific meaning, make it a
|
|
116 |
# new key in the modifiers attribute.
|
|
117 |
modifiers['tree_invisible'] = v |
|
118 |
elif v or (a not in modifiers or not isinstance(modifiers[a], list)): |
|
119 |
# Don't set the attribute to False if a dynamic value was
|
|
120 |
# provided (i.e. a domain from attrs or states).
|
|
121 |
modifiers[a] = v |
|
122 |
||
123 |
||
124 |
def simplify_modifiers(modifiers): |
|
125 |
for a in ('invisible', 'readonly', 'required'): |
|
126 |
if a in modifiers and not modifiers[a]: |
|
127 |
del modifiers[a] |
|
128 |
||
129 |
||
130 |
def transfer_modifiers_to_node(modifiers, node): |
|
131 |
if modifiers: |
|
132 |
simplify_modifiers(modifiers) |
|
133 |
node.set('modifiers', simplejson.dumps(modifiers)) |
|
134 |
||
3738
by Xavier Morel
[ADD] wrapper to the modifiers setup process to simplify its call by third parties |
135 |
def setup_modifiers(node, field=None, context=None, in_tree_view=False): |
136 |
""" Processes node attributes and field descriptors to generate
|
|
137 |
the ``modifiers`` node attribute and set it on the provided node.
|
|
138 |
||
139 |
Alters its first argument in-place.
|
|
140 |
||
141 |
:param node: ``field`` node from an OpenERP view
|
|
142 |
:type node: lxml.etree._Element
|
|
143 |
:param dict field: field descriptor corresponding to the provided node
|
|
144 |
:param dict context: execution context used to evaluate node attributes
|
|
145 |
:param bool in_tree_view: triggers the ``tree_invisible`` code
|
|
146 |
path (separate from ``invisible``): in
|
|
147 |
tree view there are two levels of
|
|
148 |
invisibility, cell content (a column is
|
|
149 |
present but the cell itself is not
|
|
150 |
displayed) with ``invisible`` and column
|
|
151 |
invisibility (the whole column is
|
|
152 |
hidden) with ``tree_invisible``.
|
|
153 |
:returns: nothing
|
|
154 |
"""
|
|
155 |
modifiers = {} |
|
156 |
if field is not None: |
|
157 |
transfer_field_to_modifiers(field, modifiers) |
|
158 |
transfer_node_to_modifiers( |
|
159 |
node, modifiers, context=context, in_tree_view=in_tree_view) |
|
160 |
transfer_modifiers_to_node(modifiers, node) |
|
3497
by Vo Minh Thu
[REVERT] re-apply the 4 commits that were reverted just after, 4 commits ago. |
161 |
|
162 |
def test_modifiers(what, expected): |
|
163 |
modifiers = {} |
|
164 |
if isinstance(what, basestring): |
|
165 |
node = etree.fromstring(what) |
|
166 |
transfer_node_to_modifiers(node, modifiers) |
|
167 |
simplify_modifiers(modifiers) |
|
168 |
json = simplejson.dumps(modifiers) |
|
169 |
assert json == expected, "%s != %s" % (json, expected) |
|
170 |
elif isinstance(what, dict): |
|
171 |
transfer_field_to_modifiers(what, modifiers) |
|
172 |
simplify_modifiers(modifiers) |
|
173 |
json = simplejson.dumps(modifiers) |
|
174 |
assert json == expected, "%s != %s" % (json, expected) |
|
175 |
||
176 |
||
177 |
# To use this test:
|
|
178 |
# import openerp
|
|
179 |
# openerp.osv.orm.modifiers_tests()
|
|
180 |
def modifiers_tests(): |
|
181 |
test_modifiers('<field name="a"/>', '{}') |
|
182 |
test_modifiers('<field name="a" invisible="1"/>', '{"invisible": true}') |
|
183 |
test_modifiers('<field name="a" readonly="1"/>', '{"readonly": true}') |
|
184 |
test_modifiers('<field name="a" required="1"/>', '{"required": true}') |
|
185 |
test_modifiers('<field name="a" invisible="0"/>', '{}') |
|
186 |
test_modifiers('<field name="a" readonly="0"/>', '{}') |
|
187 |
test_modifiers('<field name="a" required="0"/>', '{}') |
|
188 |
test_modifiers('<field name="a" invisible="1" required="1"/>', '{"invisible": true, "required": true}') # TODO order is not guaranteed |
|
189 |
test_modifiers('<field name="a" invisible="1" required="0"/>', '{"invisible": true}') |
|
190 |
test_modifiers('<field name="a" invisible="0" required="1"/>', '{"required": true}') |
|
191 |
test_modifiers("""<field name="a" attrs="{'invisible': [('b', '=', 'c')]}"/>""", '{"invisible": [["b", "=", "c"]]}') |
|
192 |
||
193 |
# The dictionary is supposed to be the result of fields_get().
|
|
194 |
test_modifiers({}, '{}') |
|
195 |
test_modifiers({"invisible": True}, '{"invisible": true}') |
|
196 |
test_modifiers({"invisible": False}, '{}') |
|
3549.3.1
by tfr(OpenERP)
[FIX]:import boolean fields |
197 |
|
3497
by Vo Minh Thu
[REVERT] re-apply the 4 commits that were reverted just after, 4 commits ago. |
198 |
|
3417.6.13
by Vo Minh Thu
[IMP] orm: check for object _name validity. |
199 |
def check_object_name(name): |
200 |
""" Check if the given name is a valid openerp object name.
|
|
201 |
||
202 |
The _name attribute in osv and osv_memory object is subject to
|
|
203 |
some restrictions. This function returns True or False whether
|
|
204 |
the given name is allowed or not.
|
|
205 |
||
206 |
TODO: this is an approximation. The goal in this approximation
|
|
207 |
is to disallow uppercase characters (in some places, we quote
|
|
208 |
table/column names and in other not, which leads to this kind
|
|
209 |
of errors:
|
|
210 |
||
211 |
psycopg2.ProgrammingError: relation "xxx" does not exist).
|
|
212 |
||
213 |
The same restriction should apply to both osv and osv_memory
|
|
214 |
objects for consistency.
|
|
215 |
||
216 |
"""
|
|
217 |
if regex_object_name.match(name) is None: |
|
218 |
return False |
|
219 |
return True |
|
220 |
||
221 |
def raise_on_invalid_object_name(name): |
|
222 |
if not check_object_name(name): |
|
223 |
msg = "The _name attribute %s is not valid." % name |
|
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
224 |
_logger.error(msg) |
3417.6.13
by Vo Minh Thu
[IMP] orm: check for object _name validity. |
225 |
raise except_orm('ValueError', msg) |
772
by Fabien Pinckaers
Bugfix: sql injection |
226 |
|
2156
by Olivier Dony
[REF] Refactored postgres' confdeltypes as a constant |
227 |
POSTGRES_CONFDELTYPES = { |
228 |
'RESTRICT': 'r', |
|
229 |
'NO ACTION': 'a', |
|
230 |
'CASCADE': 'c', |
|
231 |
'SET NULL': 'n', |
|
232 |
'SET DEFAULT': 'd', |
|
233 |
}
|
|
234 |
||
1
by pinky
New trunk |
235 |
def intersect(la, lb): |
922
by Christophe Simonis
convert tabs to 4 spaces |
236 |
return filter(lambda x: x in lb, la) |
1
by pinky
New trunk |
237 |
|
3641
by Xavier Morel
[IMP] use same fieldpath-splitting function in data_import and data_export, document it a bit |
238 |
def fix_import_export_id_paths(fieldname): |
239 |
"""
|
|
240 |
Fixes the id fields in import and exports, and splits field paths
|
|
241 |
on '/'.
|
|
242 |
||
243 |
:param str fieldname: name of the field to import/export
|
|
244 |
:return: split field name
|
|
245 |
:rtype: list of str
|
|
246 |
"""
|
|
247 |
fixed_db_id = re.sub(r'([^/])\.id', r'\1/.id', fieldname) |
|
248 |
fixed_external_id = re.sub(r'([^/]):id', r'\1/id', fixed_db_id) |
|
249 |
return fixed_external_id.split('/') |
|
250 |
||
1
by pinky
New trunk |
251 |
class except_orm(Exception): |
922
by Christophe Simonis
convert tabs to 4 spaces |
252 |
def __init__(self, name, value): |
253 |
self.name = name |
|
254 |
self.value = value |
|
255 |
self.args = (name, value) |
|
1
by pinky
New trunk |
256 |
|
1786.1.1
by Christophe Simonis
[FIX] browse_record: raise a better exception when the id doesn't exists |
257 |
class BrowseRecordError(Exception): |
258 |
pass
|
|
957
by Olivier Laurent
pep8 |
259 |
|
1
by pinky
New trunk |
260 |
class browse_null(object): |
3471.2.1
by P. Christeas
orm, tools, addons: Doc strings improvements |
261 |
""" Readonly python database object browser
|
262 |
"""
|
|
391
by ced
kernel: don't modify the args of the call |
263 |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
264 |
def __init__(self): |
957
by Olivier Laurent
pep8 |
265 |
self.id = False |
922
by Christophe Simonis
convert tabs to 4 spaces |
266 |
|
267 |
def __getitem__(self, name): |
|
1119.1.60
by P. Christeas
ORM browse: return None when sth is wrong, not False |
268 |
return None |
922
by Christophe Simonis
convert tabs to 4 spaces |
269 |
|
1200
by Christophe Simonis
browse_null objects doesn't fail anymore when accessing its attributes |
270 |
def __getattr__(self, name): |
1119.1.60
by P. Christeas
ORM browse: return None when sth is wrong, not False |
271 |
return None # XXX: return self ? |
1200
by Christophe Simonis
browse_null objects doesn't fail anymore when accessing its attributes |
272 |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
273 |
def __int__(self): |
274 |
return False |
|
275 |
||
276 |
def __str__(self): |
|
277 |
return '' |
|
278 |
||
279 |
def __nonzero__(self): |
|
280 |
return False |
|
1727
by mra (Open ERP)
fix : 329208 : trans_obj is not defined |
281 |
|
1521
by Jay (Open ERP)
Bugfix on report :Added unicode method on browse_null |
282 |
def __unicode__(self): |
283 |
return u'' |
|
1
by pinky
New trunk |
284 |
|
957
by Olivier Laurent
pep8 |
285 |
|
1
by pinky
New trunk |
286 |
#
|
287 |
# TODO: execute an object method on browse_record_list
|
|
288 |
#
|
|
289 |
class browse_record_list(list): |
|
3471.2.1
by P. Christeas
orm, tools, addons: Doc strings improvements |
290 |
""" Collection of browse objects
|
3549.3.1
by tfr(OpenERP)
[FIX]:import boolean fields |
291 |
|
3471.2.1
by P. Christeas
orm, tools, addons: Doc strings improvements |
292 |
Such an instance will be returned when doing a ``browse([ids..])``
|
293 |
and will be iterable, yielding browse() objects
|
|
294 |
"""
|
|
392
by ced
kernel: don't use mutable as default value in function defintion. |
295 |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
296 |
def __init__(self, lst, context=None): |
297 |
if not context: |
|
298 |
context = {} |
|
299 |
super(browse_record_list, self).__init__(lst) |
|
300 |
self.context = context |
|
1
by pinky
New trunk |
301 |
|
456
by ced
kernel: use type for browse record test instead os instance |
302 |
|
1
by pinky
New trunk |
303 |
class browse_record(object): |
3471.2.1
by P. Christeas
orm, tools, addons: Doc strings improvements |
304 |
""" An object that behaves like a row of an object's table.
|
305 |
It has attributes after the columns of the corresponding object.
|
|
3549.3.1
by tfr(OpenERP)
[FIX]:import boolean fields |
306 |
|
3471.2.1
by P. Christeas
orm, tools, addons: Doc strings improvements |
307 |
Examples::
|
3549.3.1
by tfr(OpenERP)
[FIX]:import boolean fields |
308 |
|
3471.2.1
by P. Christeas
orm, tools, addons: Doc strings improvements |
309 |
uobj = pool.get('res.users')
|
310 |
user_rec = uobj.browse(cr, uid, 104)
|
|
311 |
name = user_rec.name
|
|
312 |
"""
|
|
2012
by Xavier Morel
[fix] exception thrown by browse_record.__getitem__ when field not found |
313 |
|
3980
by Xavier Morel
[IMP] move browse_record to logging, __init__ docstring to sphinx info fields |
314 |
def __init__(self, cr, uid, id, table, cache, context=None, |
315 |
list_class=browse_record_list, fields_process=None): |
|
3471.2.1
by P. Christeas
orm, tools, addons: Doc strings improvements |
316 |
"""
|
3980
by Xavier Morel
[IMP] move browse_record to logging, __init__ docstring to sphinx info fields |
317 |
:param table: the browsed object (inherited from orm)
|
318 |
:param dict cache: a dictionary of model->field->data to be shared
|
|
319 |
across browse objects, thus reducing the SQL
|
|
320 |
read()s. It can speed up things a lot, but also be
|
|
321 |
disastrous if not discarded after write()/unlink()
|
|
322 |
operations
|
|
323 |
:param dict context: dictionary with an optional context
|
|
3471.2.1
by P. Christeas
orm, tools, addons: Doc strings improvements |
324 |
"""
|
2537.1.4
by Numerigraphe - Lionel Sausin
[REF] stricter checking of defauts in fields & orm __init__ |
325 |
if fields_process is None: |
2537.1.6
by Numerigraphe - Lionel Sausin
[FIX] typo in field name |
326 |
fields_process = {} |
2537.1.4
by Numerigraphe - Lionel Sausin
[REF] stricter checking of defauts in fields & orm __init__ |
327 |
if context is None: |
328 |
context = {} |
|
3980
by Xavier Morel
[IMP] move browse_record to logging, __init__ docstring to sphinx info fields |
329 |
self._list_class = list_class |
922
by Christophe Simonis
convert tabs to 4 spaces |
330 |
self._cr = cr |
331 |
self._uid = uid |
|
332 |
self._id = id |
|
3427.2.35
by Olivier Dony
[IMP] browse_record._table -> browse_record._model |
333 |
self._table = table # deprecated, use _model! |
334 |
self._model = table |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
335 |
self._table_name = self._table._name |
3976.1.21
by Antony Lesuisse
[MERGE] trunk |
336 |
self.__logger = logging.getLogger('openerp.osv.orm.browse_record.' + self._table_name) |
2537.1.4
by Numerigraphe - Lionel Sausin
[REF] stricter checking of defauts in fields & orm __init__ |
337 |
self._context = context |
338 |
self._fields_process = fields_process |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
339 |
|
340 |
cache.setdefault(table._name, {}) |
|
341 |
self._data = cache[table._name] |
|
1272.1.2
by Fabien Pinckaers
improve |
342 |
|
3895
by Fabien Pinckaers
[IMP] allow non integer for ID's |
343 |
# if not (id and isinstance(id, (int, long,))):
|
344 |
# raise BrowseRecordError(_('Wrong ID for the browse record, got %r, expected an integer.') % (id,))
|
|
1789.1.3
by Jay(Open ERP)
[IMP] Temporarily commented exception for browse record that broke reports from wizard |
345 |
# if not table.exists(cr, uid, id, context):
|
346 |
# raise BrowseRecordError(_('Object %s does not exists') % (self,))
|
|
1786.1.1
by Christophe Simonis
[FIX] browse_record: raise a better exception when the id doesn't exists |
347 |
|
1460
by Christophe Simonis
[IMP] "not x in l" -> "x not in l" |
348 |
if id not in self._data: |
957
by Olivier Laurent
pep8 |
349 |
self._data[id] = {'id': id} |
1272.1.2
by Fabien Pinckaers
improve |
350 |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
351 |
self._cache = cache |
352 |
||
353 |
def __getitem__(self, name): |
|
354 |
if name == 'id': |
|
355 |
return self._id |
|
2235
by Quality Team
[FIX] Rewrite the field.property engine to work as expected (value per |
356 |
|
1460
by Christophe Simonis
[IMP] "not x in l" -> "x not in l" |
357 |
if name not in self._data[self._id]: |
922
by Christophe Simonis
convert tabs to 4 spaces |
358 |
# build the list of fields we will fetch
|
359 |
||
360 |
# fetch the definition of the field which was asked for
|
|
361 |
if name in self._table._columns: |
|
362 |
col = self._table._columns[name] |
|
363 |
elif name in self._table._inherit_fields: |
|
364 |
col = self._table._inherit_fields[name][2] |
|
1119.1.18
by P. Christeas
Behave better at exceptions (l10n, __getitem__() ) |
365 |
elif hasattr(self._table, str(name)): |
2235
by Quality Team
[FIX] Rewrite the field.property engine to work as expected (value per |
366 |
attr = getattr(self._table, name) |
367 |
if isinstance(attr, (types.MethodType, types.LambdaType, types.FunctionType)): |
|
3353.1.43
by Olivier Dony
[IMP] browse_record: getattr method execution propagates context |
368 |
def function_proxy(*args, **kwargs): |
3353.1.45
by Olivier Dony
[FIX] orm: avoid passing undefined context in browse_record getattr() |
369 |
if 'context' not in kwargs and self._context: |
3353.1.43
by Olivier Dony
[IMP] browse_record: getattr method execution propagates context |
370 |
kwargs.update(context=self._context) |
371 |
return attr(self._cr, self._uid, [self._id], *args, **kwargs) |
|
372 |
return function_proxy |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
373 |
else: |
2235
by Quality Team
[FIX] Rewrite the field.property engine to work as expected (value per |
374 |
return attr |
922
by Christophe Simonis
convert tabs to 4 spaces |
375 |
else: |
4466.1.2
by Christophe Simonis
[IMP] orm: remove trailing spaces |
376 |
error_msg = "Field '%s' does not exist in object '%s'" % (name, self) |
3976.1.29
by Vo Minh Thu
[IMP] logging: _logger.warn() replaced by _logger.warning(). |
377 |
self.__logger.warning(error_msg) |
4466.1.1
by Christophe Simonis
[IMP] orm: be more vebose when accessing an invalid field of browse_record objects |
378 |
if self.__logger.isEnabledFor(logging.DEBUG): |
379 |
self.__logger.debug(''.join(traceback.format_stack())) |
|
3743
by Olivier Dony
[FIX] orm.browse_record: avoid printing unrelated traceback upon KeyError |
380 |
raise KeyError(error_msg) |
922
by Christophe Simonis
convert tabs to 4 spaces |
381 |
|
382 |
# if the field is a classic one or a many2one, we'll fetch all classic and many2one fields
|
|
1828
by Fabien Pinckaers
[IMP] Speed impprovement: 2x faster for flow: sale -> invoice -> payment |
383 |
if col._prefetch: |
922
by Christophe Simonis
convert tabs to 4 spaces |
384 |
# gen the list of "local" (ie not inherited) fields which are classic or many2one
|
2631
by Olivier Dony
[REF] orm.browse_record: renamed variables to improve readability of __getitem__ + minor cleanup |
385 |
fields_to_fetch = filter(lambda x: x[1]._classic_write, self._table._columns.items()) |
922
by Christophe Simonis
convert tabs to 4 spaces |
386 |
# gen the list of inherited fields
|
387 |
inherits = map(lambda x: (x[0], x[1][2]), self._table._inherit_fields.items()) |
|
388 |
# complete the field list with the inherited fields which are classic or many2one
|
|
2631
by Olivier Dony
[REF] orm.browse_record: renamed variables to improve readability of __getitem__ + minor cleanup |
389 |
fields_to_fetch += filter(lambda x: x[1]._classic_write, inherits) |
922
by Christophe Simonis
convert tabs to 4 spaces |
390 |
# otherwise we fetch only that field
|
391 |
else: |
|
2631
by Olivier Dony
[REF] orm.browse_record: renamed variables to improve readability of __getitem__ + minor cleanup |
392 |
fields_to_fetch = [(name, col)] |
1460
by Christophe Simonis
[IMP] "not x in l" -> "x not in l" |
393 |
ids = filter(lambda id: name not in self._data[id], self._data.keys()) |
2631
by Olivier Dony
[REF] orm.browse_record: renamed variables to improve readability of __getitem__ + minor cleanup |
394 |
# read the results
|
395 |
field_names = map(lambda x: x[0], fields_to_fetch) |
|
396 |
field_values = self._table.read(self._cr, self._uid, ids, field_names, context=self._context, load="_classic_write") |
|
3296
by Fabien Pinckaers
[IMP] comment for later use |
397 |
|
398 |
# TODO: improve this, very slow for reports
|
|
922
by Christophe Simonis
convert tabs to 4 spaces |
399 |
if self._fields_process: |
1766.1.1
by Naresh Choksy
added report engine |
400 |
lang = self._context.get('lang', 'en_US') or 'en_US' |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
401 |
lang_obj_ids = self.pool.get('res.lang').search(self._cr, self._uid, [('code', '=', lang)]) |
1849
by HDA(OpenERP)
rename html2html to Mako2html Merging improvement in orm |
402 |
if not lang_obj_ids: |
403 |
raise Exception(_('Language with code "%s" is not defined in your system !\nDefine it through the Administration menu.') % (lang,)) |
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
404 |
lang_obj = self.pool.get('res.lang').browse(self._cr, self._uid, lang_obj_ids[0]) |
1865.1.1
by mra (Open ERP)
[FIX] osv_memory object search method: now it will accept id as domain with its action |
405 |
|
2631
by Olivier Dony
[REF] orm.browse_record: renamed variables to improve readability of __getitem__ + minor cleanup |
406 |
for field_name, field_column in fields_to_fetch: |
407 |
if field_column._type in self._fields_process: |
|
408 |
for result_line in field_values: |
|
409 |
result_line[field_name] = self._fields_process[field_column._type](result_line[field_name]) |
|
410 |
if result_line[field_name]: |
|
411 |
result_line[field_name].set_value(self._cr, self._uid, result_line[field_name], self, field_column, lang_obj) |
|
412 |
||
413 |
if not field_values: |
|
2012
by Xavier Morel
[fix] exception thrown by browse_record.__getitem__ when field not found |
414 |
# Where did those ids come from? Perhaps old entries in ir_model_dat?
|
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
415 |
_logger.warning("No field_values found for ids %s in %s", ids, self) |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
416 |
raise KeyError('Field %s not found in %s'%(name, self)) |
922
by Christophe Simonis
convert tabs to 4 spaces |
417 |
# create browse records for 'remote' objects
|
2631
by Olivier Dony
[REF] orm.browse_record: renamed variables to improve readability of __getitem__ + minor cleanup |
418 |
for result_line in field_values: |
1844.4.102
by Jay(Open ERP)
[FIX] Browse with _inherits issue fixed if more than one M2O to parent objct exists |
419 |
new_data = {} |
2631
by Olivier Dony
[REF] orm.browse_record: renamed variables to improve readability of __getitem__ + minor cleanup |
420 |
for field_name, field_column in fields_to_fetch: |
4102.1.3
by Vo Minh Thu
[IMP+FIX] fields: removed references to one2one, but also |
421 |
if field_column._type == 'many2one': |
2631
by Olivier Dony
[REF] orm.browse_record: renamed variables to improve readability of __getitem__ + minor cleanup |
422 |
if result_line[field_name]: |
423 |
obj = self._table.pool.get(field_column._obj) |
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
424 |
if isinstance(result_line[field_name], (list, tuple)): |
2631
by Olivier Dony
[REF] orm.browse_record: renamed variables to improve readability of __getitem__ + minor cleanup |
425 |
value = result_line[field_name][0] |
922
by Christophe Simonis
convert tabs to 4 spaces |
426 |
else: |
2631
by Olivier Dony
[REF] orm.browse_record: renamed variables to improve readability of __getitem__ + minor cleanup |
427 |
value = result_line[field_name] |
428 |
if value: |
|
1844.4.91
by Christophe Simonis
[FIX] orm: bug when browsing _inherits object |
429 |
# FIXME: this happen when a _inherits object
|
430 |
# overwrite a field of it parent. Need
|
|
431 |
# testing to be sure we got the right
|
|
432 |
# object and not the parent one.
|
|
2631
by Olivier Dony
[REF] orm.browse_record: renamed variables to improve readability of __getitem__ + minor cleanup |
433 |
if not isinstance(value, browse_record): |
3310.1.33
by Olivier Dony
[FIX] orm.browse_record: fields prefetching should ignore m2o pointing to models not loaded yet |
434 |
if obj is None: |
435 |
# In some cases the target model is not available yet, so we must ignore it,
|
|
436 |
# which is safe in most cases, this value will just be loaded later when needed.
|
|
437 |
# This situation can be caused by custom fields that connect objects with m2o without
|
|
438 |
# respecting module dependencies, causing relationships to be connected to soon when
|
|
439 |
# the target is not loaded yet.
|
|
440 |
continue
|
|
2631
by Olivier Dony
[REF] orm.browse_record: renamed variables to improve readability of __getitem__ + minor cleanup |
441 |
new_data[field_name] = browse_record(self._cr, |
442 |
self._uid, value, obj, self._cache, |
|
1844.4.91
by Christophe Simonis
[FIX] orm: bug when browsing _inherits object |
443 |
context=self._context, |
444 |
list_class=self._list_class, |
|
445 |
fields_process=self._fields_process) |
|
2235
by Quality Team
[FIX] Rewrite the field.property engine to work as expected (value per |
446 |
else: |
2631
by Olivier Dony
[REF] orm.browse_record: renamed variables to improve readability of __getitem__ + minor cleanup |
447 |
new_data[field_name] = value |
922
by Christophe Simonis
convert tabs to 4 spaces |
448 |
else: |
2631
by Olivier Dony
[REF] orm.browse_record: renamed variables to improve readability of __getitem__ + minor cleanup |
449 |
new_data[field_name] = browse_null() |
922
by Christophe Simonis
convert tabs to 4 spaces |
450 |
else: |
2631
by Olivier Dony
[REF] orm.browse_record: renamed variables to improve readability of __getitem__ + minor cleanup |
451 |
new_data[field_name] = browse_null() |
452 |
elif field_column._type in ('one2many', 'many2many') and len(result_line[field_name]): |
|
453 |
new_data[field_name] = self._list_class([browse_record(self._cr, self._uid, id, self._table.pool.get(field_column._obj), self._cache, context=self._context, list_class=self._list_class, fields_process=self._fields_process) for id in result_line[field_name]], self._context) |
|
4682.2.23
by Raphael Collet
[FIX] orm: replace incorrect usage of 'in' by '==' |
454 |
elif field_column._type == 'reference': |
2631
by Olivier Dony
[REF] orm.browse_record: renamed variables to improve readability of __getitem__ + minor cleanup |
455 |
if result_line[field_name]: |
456 |
if isinstance(result_line[field_name], browse_record): |
|
457 |
new_data[field_name] = result_line[field_name] |
|
2144
by HDA(OpenERP)
FIX reference field support browse in property field |
458 |
else: |
2631
by Olivier Dony
[REF] orm.browse_record: renamed variables to improve readability of __getitem__ + minor cleanup |
459 |
ref_obj, ref_id = result_line[field_name].split(',') |
2144
by HDA(OpenERP)
FIX reference field support browse in property field |
460 |
ref_id = long(ref_id) |
3129.1.2
by Olivier Dony
[FIX] fields.reference: don't accept half-defined references, and avoid crashing on previous bad ones |
461 |
if ref_id: |
462 |
obj = self._table.pool.get(ref_obj) |
|
463 |
new_data[field_name] = browse_record(self._cr, self._uid, ref_id, obj, self._cache, context=self._context, list_class=self._list_class, fields_process=self._fields_process) |
|
464 |
else: |
|
465 |
new_data[field_name] = browse_null() |
|
2111.1.16
by Rga(Open ERP)
[IMP] browse() on reference fields |
466 |
else: |
2631
by Olivier Dony
[REF] orm.browse_record: renamed variables to improve readability of __getitem__ + minor cleanup |
467 |
new_data[field_name] = browse_null() |
1844.4.102
by Jay(Open ERP)
[FIX] Browse with _inherits issue fixed if more than one M2O to parent objct exists |
468 |
else: |
2631
by Olivier Dony
[REF] orm.browse_record: renamed variables to improve readability of __getitem__ + minor cleanup |
469 |
new_data[field_name] = result_line[field_name] |
470 |
self._data[result_line['id']].update(new_data) |
|
2238.1.5
by Anup(OpenERP)
[FIX] Copy() of ir_model_fields corected |
471 |
|
1884.1.4
by Harry (Open ERP)
[FIX] replaced <TAB> with four white space. |
472 |
if not name in self._data[self._id]: |
3310.1.33
by Olivier Dony
[FIX] orm.browse_record: fields prefetching should ignore m2o pointing to models not loaded yet |
473 |
# How did this happen? Could be a missing model due to custom fields used too soon, see above.
|
3980
by Xavier Morel
[IMP] move browse_record to logging, __init__ docstring to sphinx info fields |
474 |
self.__logger.error("Fields to fetch: %s, Field values: %s", field_names, field_values) |
475 |
self.__logger.error("Cached: %s, Table: %s", self._data[self._id], self._table) |
|
2012
by Xavier Morel
[fix] exception thrown by browse_record.__getitem__ when field not found |
476 |
raise KeyError(_('Unknown attribute %s in %s ') % (name, self)) |
922
by Christophe Simonis
convert tabs to 4 spaces |
477 |
return self._data[self._id][name] |
478 |
||
479 |
def __getattr__(self, name): |
|
2012
by Xavier Morel
[fix] exception thrown by browse_record.__getitem__ when field not found |
480 |
try: |
481 |
return self[name] |
|
482 |
except KeyError, e: |
|
4815.1.9
by Vo Minh Thu
[REF] orm: |
483 |
import sys |
484 |
exc_info = sys.exc_info() |
|
4815.1.17
by Vo Minh Thu
[FIX] ir_model_data: create the _inherits parent external ID prior to the child. |
485 |
raise AttributeError, "Got %r while trying to get attribute %s on a %s record." % (e, name, self._table._name), exc_info[2] |
922
by Christophe Simonis
convert tabs to 4 spaces |
486 |
|
487 |
def __contains__(self, name): |
|
488 |
return (name in self._table._columns) or (name in self._table._inherit_fields) or hasattr(self._table, name) |
|
489 |
||
3724
by Xavier Morel
[IMP] raise error when iterating over browse_record |
490 |
def __iter__(self): |
491 |
raise NotImplementedError("Iteration is not allowed on %s" % self) |
|
492 |
||
922
by Christophe Simonis
convert tabs to 4 spaces |
493 |
def __hasattr__(self, name): |
494 |
return name in self |
|
495 |
||
496 |
def __int__(self): |
|
497 |
return self._id |
|
498 |
||
499 |
def __str__(self): |
|
500 |
return "browse_record(%s, %d)" % (self._table_name, self._id) |
|
501 |
||
502 |
def __eq__(self, other): |
|
2235
by Quality Team
[FIX] Rewrite the field.property engine to work as expected (value per |
503 |
if not isinstance(other, browse_record): |
504 |
return False |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
505 |
return (self._table_name, self._id) == (other._table_name, other._id) |
506 |
||
507 |
def __ne__(self, other): |
|
2235
by Quality Team
[FIX] Rewrite the field.property engine to work as expected (value per |
508 |
if not isinstance(other, browse_record): |
509 |
return True |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
510 |
return (self._table_name, self._id) != (other._table_name, other._id) |
511 |
||
512 |
# we need to define __unicode__ even though we've already defined __str__
|
|
513 |
# because we have overridden __getattr__
|
|
514 |
def __unicode__(self): |
|
515 |
return unicode(str(self)) |
|
516 |
||
517 |
def __hash__(self): |
|
518 |
return hash((self._table_name, self._id)) |
|
519 |
||
520 |
__repr__ = __str__ |
|
1
by pinky
New trunk |
521 |
|
3353.1.14
by Olivier Dony
[IMP] browse_record: add refresh() method for discarding the cache |
522 |
def refresh(self): |
523 |
"""Force refreshing this browse_record's data and all the data of the
|
|
524 |
records that belong to the same cache, by emptying the cache completely,
|
|
525 |
preserving only the record identifiers (for prefetching optimizations).
|
|
526 |
"""
|
|
527 |
for model, model_cache in self._cache.iteritems(): |
|
528 |
# only preserve the ids of the records that were in the cache
|
|
529 |
cached_ids = dict([(i, {'id': i}) for i in model_cache.keys()]) |
|
530 |
self._cache[model].clear() |
|
531 |
self._cache[model].update(cached_ids) |
|
1
by pinky
New trunk |
532 |
|
3579.1.2
by Xavier Morel
[IMP] extract VARCHAR typing, if no size is provided (or the size is 0) don't put a limit on the varchar |
533 |
def pg_varchar(size=0): |
534 |
""" Returns the VARCHAR declaration for the provided size:
|
|
535 |
||
3579.1.15
by Vo Minh Thu
[FIX] orm: allow negative varchar size (meaning no limit) as used in some addons. |
536 |
* If no size (or an empty or negative size is provided) return an
|
537 |
'infinite' VARCHAR
|
|
3579.1.2
by Xavier Morel
[IMP] extract VARCHAR typing, if no size is provided (or the size is 0) don't put a limit on the varchar |
538 |
* Otherwise return a VARCHAR(n)
|
539 |
||
540 |
:type int size: varchar size, optional
|
|
541 |
:rtype: str
|
|
542 |
"""
|
|
543 |
if size: |
|
3579.1.11
by Xavier Morel
[IMP] add a pair of assertions on the parameter to VARCHAR, just in case |
544 |
if not isinstance(size, int): |
545 |
raise TypeError("VARCHAR parameter should be an int, got %s" |
|
546 |
% type(size)) |
|
3579.1.15
by Vo Minh Thu
[FIX] orm: allow negative varchar size (meaning no limit) as used in some addons. |
547 |
if size > 0: |
548 |
return 'VARCHAR(%d)' % size |
|
3579.1.2
by Xavier Morel
[IMP] extract VARCHAR typing, if no size is provided (or the size is 0) don't put a limit on the varchar |
549 |
return 'VARCHAR' |
1
by pinky
New trunk |
550 |
|
3579.1.6
by Xavier Morel
[IMP] lift mapping from field types to PGTYPES outside get_pg_type |
551 |
FIELDS_TO_PGTYPES = { |
552 |
fields.boolean: 'bool', |
|
553 |
fields.integer: 'int4', |
|
554 |
fields.text: 'text', |
|
4322.1.1
by niv-openerp
Added html field type |
555 |
fields.html: 'text', |
3579.1.6
by Xavier Morel
[IMP] lift mapping from field types to PGTYPES outside get_pg_type |
556 |
fields.date: 'date', |
557 |
fields.datetime: 'timestamp', |
|
558 |
fields.binary: 'bytea', |
|
559 |
fields.many2one: 'int4', |
|
3808.1.1
by Olivier Dony
[MERGE] Implementation of sparse field (to review), by Sebastien Beau, Akretion |
560 |
fields.serialized: 'text', |
3579.1.6
by Xavier Morel
[IMP] lift mapping from field types to PGTYPES outside get_pg_type |
561 |
}
|
3579.1.13
by Vo Minh Thu
[MERGE] merged trunk. |
562 |
|
3579.1.3
by Xavier Morel
[IMP] have function fields delegate their pg_type discovery to get_pg_type |
563 |
def get_pg_type(f, type_override=None): |
564 |
"""
|
|
565 |
:param fields._column f: field to get a Postgres type for
|
|
566 |
:param type type_override: use the provided type for dispatching instead of the field's own type
|
|
567 |
:returns: (postgres_identification_type, postgres_type_specification)
|
|
568 |
:rtype: (str, str)
|
|
569 |
"""
|
|
570 |
field_type = type_override or type(f) |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
571 |
|
3579.1.6
by Xavier Morel
[IMP] lift mapping from field types to PGTYPES outside get_pg_type |
572 |
if field_type in FIELDS_TO_PGTYPES: |
3579.1.12
by Vo Minh Thu
[IMP] orm: get_pg_type() does not use early return. |
573 |
pg_type = (FIELDS_TO_PGTYPES[field_type], FIELDS_TO_PGTYPES[field_type]) |
3579.1.9
by Xavier Morel
[FIX] if types can be overridden, type-checking against the overriden type instead of the non-overridden instance might be a good idea as well |
574 |
elif issubclass(field_type, fields.float): |
922
by Christophe Simonis
convert tabs to 4 spaces |
575 |
if f.digits: |
3579.1.12
by Vo Minh Thu
[IMP] orm: get_pg_type() does not use early return. |
576 |
pg_type = ('numeric', 'NUMERIC') |
577 |
else: |
|
578 |
pg_type = ('float8', 'DOUBLE PRECISION') |
|
3579.1.9
by Xavier Morel
[FIX] if types can be overridden, type-checking against the overriden type instead of the non-overridden instance might be a good idea as well |
579 |
elif issubclass(field_type, (fields.char, fields.reference)): |
3579.1.12
by Vo Minh Thu
[IMP] orm: get_pg_type() does not use early return. |
580 |
pg_type = ('varchar', pg_varchar(f.size)) |
3579.1.9
by Xavier Morel
[FIX] if types can be overridden, type-checking against the overriden type instead of the non-overridden instance might be a good idea as well |
581 |
elif issubclass(field_type, fields.selection): |
3579.1.10
by Xavier Morel
[FIX] reimplement quality selection field API |
582 |
if (isinstance(f.selection, list) and isinstance(f.selection[0][0], int))\ |
583 |
or getattr(f, 'size', None) == -1: |
|
3579.1.12
by Vo Minh Thu
[IMP] orm: get_pg_type() does not use early return. |
584 |
pg_type = ('int4', 'INTEGER') |
585 |
else: |
|
586 |
pg_type = ('varchar', pg_varchar(getattr(f, 'size', None))) |
|
3579.1.9
by Xavier Morel
[FIX] if types can be overridden, type-checking against the overriden type instead of the non-overridden instance might be a good idea as well |
587 |
elif issubclass(field_type, fields.function): |
3579.1.8
by Xavier Morel
[FIX] typo, might want to launch the software from time to time while changing stuff |
588 |
if f._type == 'selection': |
3579.1.12
by Vo Minh Thu
[IMP] orm: get_pg_type() does not use early return. |
589 |
pg_type = ('varchar', pg_varchar()) |
590 |
else: |
|
591 |
pg_type = get_pg_type(f, getattr(fields, f._type)) |
|
592 |
else: |
|
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
593 |
_logger.warning('%s type not supported!', field_type) |
3579.1.12
by Vo Minh Thu
[IMP] orm: get_pg_type() does not use early return. |
594 |
pg_type = None |
595 |
||
596 |
return pg_type |
|
1
by pinky
New trunk |
597 |
|
957
by Olivier Laurent
pep8 |
598 |
|
3453.2.6
by Vo Minh Thu
[REF] osv: use a metaclass to discover new models. |
599 |
class MetaModel(type): |
600 |
""" Metaclass for the Model.
|
|
601 |
||
602 |
This class is used as the metaclass for the Model class to discover
|
|
603 |
the models defined in a module (i.e. without instanciating them).
|
|
604 |
If the automatic discovery is not needed, it is possible to set the
|
|
605 |
model's _register attribute to False.
|
|
606 |
||
607 |
"""
|
|
608 |
||
609 |
module_to_models = {} |
|
610 |
||
611 |
def __init__(self, name, bases, attrs): |
|
612 |
if not self._register: |
|
613 |
self._register = True |
|
614 |
super(MetaModel, self).__init__(name, bases, attrs) |
|
615 |
return
|
|
616 |
||
3943.1.6
by Vo Minh Thu
[IMP] openerp.addons: openerp.addons is used instead of |
617 |
# The (OpenERP) module name can be in the `openerp.addons` namespace
|
3943.1.4
by Vo Minh Thu
[FIX] orm: correctly set the module name on the model, even when imported with |
618 |
# or not. For instance module `sale` can be imported as
|
3943.1.6
by Vo Minh Thu
[IMP] openerp.addons: openerp.addons is used instead of |
619 |
# `openerp.addons.sale` (the good way) or `sale` (for backward
|
3943.1.4
by Vo Minh Thu
[FIX] orm: correctly set the module name on the model, even when imported with |
620 |
# compatibility).
|
621 |
module_parts = self.__module__.split('.') |
|
622 |
if len(module_parts) > 2 and module_parts[0] == 'openerp' and \ |
|
3943.1.6
by Vo Minh Thu
[IMP] openerp.addons: openerp.addons is used instead of |
623 |
module_parts[1] == 'addons': |
3943.1.4
by Vo Minh Thu
[FIX] orm: correctly set the module name on the model, even when imported with |
624 |
module_name = self.__module__.split('.')[2] |
625 |
else: |
|
626 |
module_name = self.__module__.split('.')[0] |
|
3453.2.6
by Vo Minh Thu
[REF] osv: use a metaclass to discover new models. |
627 |
if not hasattr(self, '_module'): |
628 |
self._module = module_name |
|
629 |
||
630 |
# Remember which models to instanciate for this module.
|
|
4743.1.11
by Fabien Pinckaers
[IMP] custom model allowed |
631 |
if not self._custom: |
632 |
self.module_to_models.setdefault(self._module, []).append(self) |
|
3453.2.6
by Vo Minh Thu
[REF] osv: use a metaclass to discover new models. |
633 |
|
634 |
||
3511.1.32
by Olivier Dony
[MERGE] sync w/ latest trunk (+ fix import cycles) |
635 |
# Definition of log access columns, automatically added to models if
|
636 |
# self._log_access is True
|
|
637 |
LOG_ACCESS_COLUMNS = { |
|
638 |
'create_uid': 'INTEGER REFERENCES res_users ON DELETE SET NULL', |
|
639 |
'create_date': 'TIMESTAMP', |
|
640 |
'write_uid': 'INTEGER REFERENCES res_users ON DELETE SET NULL', |
|
641 |
'write_date': 'TIMESTAMP' |
|
642 |
}
|
|
643 |
# special columns automatically created by the ORM
|
|
644 |
MAGIC_COLUMNS = ['id'] + LOG_ACCESS_COLUMNS.keys() |
|
645 |
||
3511.1.25
by Olivier Dony
[IMP] orm: introduce cleaner class hierarchy for models |
646 |
class BaseModel(object): |
3432.1.1
by Vo Minh Thu
[REF] osv: osv classes merged inside orm classes. |
647 |
""" Base class for OpenERP models.
|
648 |
||
3511.1.25
by Olivier Dony
[IMP] orm: introduce cleaner class hierarchy for models |
649 |
OpenERP models are created by inheriting from this class' subclasses:
|
650 |
||
651 |
* Model: for regular database-persisted models
|
|
652 |
* TransientModel: for temporary data, stored in the database but automatically
|
|
653 |
vaccuumed every so often
|
|
654 |
* AbstractModel: for abstract super classes meant to be shared by multiple
|
|
655 |
_inheriting classes (usually Models or TransientModels)
|
|
656 |
||
657 |
The system will later instantiate the class once per database (on
|
|
3432.1.1
by Vo Minh Thu
[REF] osv: osv classes merged inside orm classes. |
658 |
which the class' module is installed).
|
659 |
||
3511.1.25
by Olivier Dony
[IMP] orm: introduce cleaner class hierarchy for models |
660 |
To create a class that should not be instantiated, the _register class attribute
|
661 |
may be set to False.
|
|
3432.1.1
by Vo Minh Thu
[REF] osv: osv classes merged inside orm classes. |
662 |
"""
|
3511.1.8
by Vo Minh Thu
[REF] osv: previous diff makes unneccessary one of makeInstance/createInstance. |
663 |
__metaclass__ = MetaModel |
4344.1.40
by Thibault Delavallée
[IMP] ir.needaction: made the model Abstract. Fixed a bug when inheriting from AbstractModels: _auto (to create a database for the model) was not correctly set. |
664 |
_auto = True # create database backend |
3511.1.8
by Vo Minh Thu
[REF] osv: previous diff makes unneccessary one of makeInstance/createInstance. |
665 |
_register = False # Set to false if the model shouldn't be automatically discovered. |
922
by Christophe Simonis
convert tabs to 4 spaces |
666 |
_name = None |
667 |
_columns = {} |
|
668 |
_constraints = [] |
|
4743.1.11
by Fabien Pinckaers
[IMP] custom model allowed |
669 |
_custom = False |
922
by Christophe Simonis
convert tabs to 4 spaces |
670 |
_defaults = {} |
4272.1.5
by Antony Lesuisse
[FIX] typos and vmt review |
671 |
_rec_name = None |
922
by Christophe Simonis
convert tabs to 4 spaces |
672 |
_parent_name = 'parent_id' |
961
by Fabien Pinckaers
Speed Improvement in recursive functions and trees: |
673 |
_parent_store = False |
1595.1.7
by Fabien Pinckaers
bugfixes |
674 |
_parent_order = False |
922
by Christophe Simonis
convert tabs to 4 spaces |
675 |
_date_name = 'date' |
676 |
_order = 'id' |
|
677 |
_sequence = None |
|
678 |
_description = None |
|
4333.1.1
by Fabien Pinckaers
[IMP] cleaning of need action |
679 |
_needaction = False |
3612
by Olivier Dony
[FIX] orm: refactoring and fix of stored functions processing by _store_get_values |
680 |
|
4399
by Fabien Pinckaers
[IMP] folded columns in read_group |
681 |
# dict of {field:method}, with method returning the (name_get of records, {id: fold})
|
3791
by Fabien Pinckaers
[IMP] _group_by_full implementation |
682 |
# to include in the _read_group, if grouped on this field
|
683 |
_group_by_full = {} |
|
684 |
||
3511.1.31
by Olivier Dony
[IMP] orm: fix Model hierarchy to avoid TransientModel leaking downstream |
685 |
# Transience
|
686 |
_transient = False # True in a TransientModel |
|
687 |
||
3612
by Olivier Dony
[FIX] orm: refactoring and fix of stored functions processing by _store_get_values |
688 |
# structure:
|
689 |
# { 'parent_model': 'm2o_field', ... }
|
|
922
by Christophe Simonis
convert tabs to 4 spaces |
690 |
_inherits = {} |
3612
by Olivier Dony
[FIX] orm: refactoring and fix of stored functions processing by _store_get_values |
691 |
|
3627
by Vo Minh Thu
[MERGE] orm: properly handle multi-level _inherits. |
692 |
# Mapping from inherits'd field name to triple (m, r, f, n) where m is the
|
693 |
# model from which it is inherits'd, r is the (local) field towards m, f
|
|
694 |
# is the _column object itself, and n is the original (i.e. top-most)
|
|
695 |
# parent model.
|
|
696 |
# Example:
|
|
697 |
# { 'field_name': ('parent_model', 'm2o_field_to_reach_parent',
|
|
698 |
# field_column_obj, origina_parent_model), ... }
|
|
3453.2.4
by Vo Minh Thu
[REF] orm: added some comments. |
699 |
_inherit_fields = {} |
3612
by Olivier Dony
[FIX] orm: refactoring and fix of stored functions processing by _store_get_values |
700 |
|
3466.1.8
by Vo Minh Thu
[REF] orm: add a column_info class to represent entries in _inherit_fields, |
701 |
# Mapping field name/column_info object
|
702 |
# This is similar to _inherit_fields but:
|
|
703 |
# 1. includes self fields,
|
|
704 |
# 2. uses column_info instead of a triple.
|
|
705 |
_all_columns = {} |
|
3612
by Olivier Dony
[FIX] orm: refactoring and fix of stored functions processing by _store_get_values |
706 |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
707 |
_table = None |
1294
by Christophe Simonis
[IMP] better get_invalid_fields function |
708 |
_invalids = set() |
2265
by Fabien Pinckaers
[FIX] Improve logging system |
709 |
_log_create = False |
3511.1.6
by Vo Minh Thu
[REF] orm: merged orm_template inside orm. |
710 |
_sql_constraints = [] |
711 |
_protected = ['read', 'write', 'create', 'default_get', 'perm_read', 'unlink', 'fields_get', 'fields_view_get', 'search', 'name_get', 'distinct_field_get', 'name_search', 'copy', 'import_data', 'search_count', 'exists'] |
|
712 |
||
1589.1.1
by Christophe Simonis
[FIX] fix concurrency problem. Use last_modified instead of delta. Thanks to Tryton team for the idea. |
713 |
CONCURRENCY_CHECK_FIELD = '__last_update' |
3647
by Olivier Dony
[FIX] orm,expression: sanity checks for domain terms, cleanup, tests |
714 |
|
2265
by Fabien Pinckaers
[FIX] Improve logging system |
715 |
def log(self, cr, uid, id, message, secondary=False, context=None): |
4071.2.26
by Thibault Delavallée
[REM] Removed res.log feature in ORM. However, the res.log table stays, because it is still used notably in publisher_warranty, that must be checked to see how it should be updated with OpenChatter. |
716 |
return _logger.warning("log() is deprecated. Please use OpenChatter notification system instead of the res.log mechanism.") |
4466.1.2
by Christophe Simonis
[IMP] orm: remove trailing spaces |
717 |
|
2537.1.2
by Numerigraphe - Lionel Sausin
[FIX] mutable default in osv |
718 |
def view_init(self, cr, uid, fields_list, context=None): |
2111.1.15
by Rvo(Open ERP)
[IMP]:allowed default_get method to throw exception for osv_memory wizards |
719 |
"""Override this method to do specific things when a view on the object is opened."""
|
720 |
pass
|
|
721 |
||
2537.1.2
by Numerigraphe - Lionel Sausin
[FIX] mutable default in osv |
722 |
def _field_create(self, cr, context=None): |
3453.2.4
by Vo Minh Thu
[REF] orm: added some comments. |
723 |
""" Create entries in ir_model_fields for all the model's fields.
|
3432.1.4
by Vo Minh Thu
[IMP] osv: removed unnecessary module arg, added comments. |
724 |
|
3453.2.4
by Vo Minh Thu
[REF] orm: added some comments. |
725 |
If necessary, also create an entry in ir_model, and if called from the
|
726 |
modules loading scheme (by receiving 'module' in the context), also
|
|
727 |
create entries in ir_model_data (for the model and the fields).
|
|
3432.1.4
by Vo Minh Thu
[IMP] osv: removed unnecessary module arg, added comments. |
728 |
|
729 |
- create an entry in ir_model (if there is not already one),
|
|
730 |
- create an entry in ir_model_data (if there is not already one, and if
|
|
731 |
'module' is in the context),
|
|
732 |
- update ir_model_fields with the fields found in _columns
|
|
733 |
(TODO there is some redundancy as _columns is updated from
|
|
734 |
ir_model_fields in __init__).
|
|
735 |
||
736 |
"""
|
|
2537.1.2
by Numerigraphe - Lionel Sausin
[FIX] mutable default in osv |
737 |
if context is None: |
738 |
context = {} |
|
1460
by Christophe Simonis
[IMP] "not x in l" -> "x not in l" |
739 |
cr.execute("SELECT id FROM ir_model WHERE model=%s", (self._name,)) |
922
by Christophe Simonis
convert tabs to 4 spaces |
740 |
if not cr.rowcount: |
741 |
cr.execute('SELECT nextval(%s)', ('ir_model_id_seq',)) |
|
1307
by Fabien Pinckaers
improved_customization |
742 |
model_id = cr.fetchone()[0] |
1460
by Christophe Simonis
[IMP] "not x in l" -> "x not in l" |
743 |
cr.execute("INSERT INTO ir_model (id,model, name, info,state) VALUES (%s, %s, %s, %s, %s)", (model_id, self._name, self._description, self.__doc__, 'base')) |
1341.4.2
by Fabien Pinckaers
modifs |
744 |
else: |
745 |
model_id = cr.fetchone()[0] |
|
746 |
if 'module' in context: |
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
747 |
name_id = 'model_'+self._name.replace('.', '_') |
3272
by Fabien Pinckaers
[IMP] logging for set null failed |
748 |
cr.execute('select * from ir_model_data where name=%s and module=%s', (name_id, context['module'])) |
1341.4.2
by Fabien Pinckaers
modifs |
749 |
if not cr.rowcount: |
4066.1.3
by Olivier Dony
[FIX] Correct remaining SQL now() calls, must use UTC |
750 |
cr.execute("INSERT INTO ir_model_data (name,date_init,date_update,module,model,res_id) VALUES (%s, (now() at time zone 'UTC'), (now() at time zone 'UTC'), %s, %s, %s)", \ |
1341.4.2
by Fabien Pinckaers
modifs |
751 |
(name_id, context['module'], 'ir.model', model_id) |
922
by Christophe Simonis
convert tabs to 4 spaces |
752 |
)
|
1341.4.2
by Fabien Pinckaers
modifs |
753 |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
754 |
cr.commit() |
755 |
||
756 |
cr.execute("SELECT * FROM ir_model_fields WHERE model=%s", (self._name,)) |
|
757 |
cols = {} |
|
758 |
for rec in cr.dictfetchall(): |
|
759 |
cols[rec['name']] = rec |
|
760 |
||
3569.1.4
by sebastien beau
[REF] ir_model_field replace the serialisation_field by serialization_field_id |
761 |
ir_model_fields_obj = self.pool.get('ir.model.fields') |
3808.1.2
by Olivier Dony
[IMP] orm,fields: some cleanup for sparse field implementation (wip) |
762 |
|
763 |
# sparse field should be created at the end, as it depends on its serialized field already existing
|
|
3808.1.5
by Olivier Dony
[FIX] orm: typos |
764 |
model_fields = sorted(self._columns.items(), key=lambda x: 1 if x[1]._type == 'sparse' else 0) |
765 |
for (k, f) in model_fields: |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
766 |
vals = { |
957
by Olivier Laurent
pep8 |
767 |
'model_id': model_id, |
768 |
'model': self._name, |
|
769 |
'name': k, |
|
4263.1.2
by Xavier Morel
[REM] orm: pointless character replacement of quotes by spaces in field strings |
770 |
'field_description': f.string, |
957
by Olivier Laurent
pep8 |
771 |
'ttype': f._type, |
2238.1.5
by Anup(OpenERP)
[FIX] Copy() of ir_model_fields corected |
772 |
'relation': f._obj or '', |
957
by Olivier Laurent
pep8 |
773 |
'view_load': (f.view_load and 1) or 0, |
1844.4.57
by RGA(OpenERP)
[FIX] Manual object and field creation with searchable |
774 |
'select_level': tools.ustr(f.select or 0), |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
775 |
'readonly': (f.readonly and 1) or 0, |
776 |
'required': (f.required and 1) or 0, |
|
777 |
'selectable': (f.selectable and 1) or 0, |
|
3310.28.1
by olt at tinyerp
[FIX] fixes launchpad bug 780584: Field definition translate=Yes is not stored in ir.model.fields |
778 |
'translate': (f.translate and 1) or 0, |
4263.1.3
by Xavier Morel
[REM] redundant code: a field of type one2many will always have _type one2many |
779 |
'relation_field': f._fields_id if isinstance(f, fields.one2many) else '', |
3569.1.4
by sebastien beau
[REF] ir_model_field replace the serialisation_field by serialization_field_id |
780 |
'serialization_field_id': None, |
922
by Christophe Simonis
convert tabs to 4 spaces |
781 |
}
|
3808.1.2
by Olivier Dony
[IMP] orm,fields: some cleanup for sparse field implementation (wip) |
782 |
if getattr(f, 'serialization_field', None): |
783 |
# resolve link to serialization_field if specified by name
|
|
4376
by Stephane Wirtel
[REF] Replace the user id 1 by openerp.SUPERUSER_ID |
784 |
serialization_field_id = ir_model_fields_obj.search(cr, SUPERUSER_ID, [('model','=',vals['model']), ('name', '=', f.serialization_field)]) |
3569.1.4
by sebastien beau
[REF] ir_model_field replace the serialisation_field by serialization_field_id |
785 |
if not serialization_field_id: |
3808.1.2
by Olivier Dony
[IMP] orm,fields: some cleanup for sparse field implementation (wip) |
786 |
raise except_orm(_('Error'), _("Serialization field `%s` not found for sparse field `%s`!") % (f.serialization_field, k)) |
3569.1.4
by sebastien beau
[REF] ir_model_field replace the serialisation_field by serialization_field_id |
787 |
vals['serialization_field_id'] = serialization_field_id[0] |
3808.1.2
by Olivier Dony
[IMP] orm,fields: some cleanup for sparse field implementation (wip) |
788 |
|
1844.4.57
by RGA(OpenERP)
[FIX] Manual object and field creation with searchable |
789 |
# When its a custom field,it does not contain f.select
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
790 |
if context.get('field_state', 'base') == 'manual': |
791 |
if context.get('field_name', '') == k: |
|
792 |
vals['select_level'] = context.get('select', '0') |
|
1844.4.57
by RGA(OpenERP)
[FIX] Manual object and field creation with searchable |
793 |
#setting value to let the problem NOT occur next time
|
2700
by Jay(OpenERP)
[FIX] Custom field may not be found on the first go |
794 |
elif k in cols: |
1844.4.57
by RGA(OpenERP)
[FIX] Manual object and field creation with searchable |
795 |
vals['select_level'] = cols[k]['select_level'] |
1869.1.24
by nch at tinyerp
[MERGE]:from parent |
796 |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
797 |
if k not in cols: |
798 |
cr.execute('select nextval(%s)', ('ir_model_fields_id_seq',)) |
|
799 |
id = cr.fetchone()[0] |
|
800 |
vals['id'] = id |
|
801 |
cr.execute("""INSERT INTO ir_model_fields ( |
|
957
by Olivier Laurent
pep8 |
802 |
id, model_id, model, name, field_description, ttype,
|
3569.1.4
by sebastien beau
[REF] ir_model_field replace the serialisation_field by serialization_field_id |
803 |
relation,view_load,state,select_level,relation_field, translate, serialization_field_id
|
922
by Christophe Simonis
convert tabs to 4 spaces |
804 |
) VALUES (
|
3569.1.3
by sebastien beau
[IMP] add the posibility to create sparse field dynamically |
805 |
%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s |
922
by Christophe Simonis
convert tabs to 4 spaces |
806 |
)""", ( |
807 |
id, vals['model_id'], vals['model'], vals['name'], vals['field_description'], vals['ttype'], |
|
1192
by Fabien Pinckaers
improve |
808 |
vals['relation'], bool(vals['view_load']), 'base', |
3569.1.4
by sebastien beau
[REF] ir_model_field replace the serialisation_field by serialization_field_id |
809 |
vals['select_level'], vals['relation_field'], bool(vals['translate']), vals['serialization_field_id'] |
922
by Christophe Simonis
convert tabs to 4 spaces |
810 |
))
|
811 |
if 'module' in context: |
|
1803.1.23
by ACH(OpenERP)
[FIX] ir_model_data : SQL_Constraint failure covered |
812 |
name1 = 'field_' + self._table + '_' + k |
1803.1.34
by Christophe Simonis
[FIX] increase size of field "name" of ir.model.data |
813 |
cr.execute("select name from ir_model_data where name=%s", (name1,)) |
1803.1.23
by ACH(OpenERP)
[FIX] ir_model_data : SQL_Constraint failure covered |
814 |
if cr.fetchone(): |
815 |
name1 = name1 + "_" + str(id) |
|
4066.1.3
by Olivier Dony
[FIX] Correct remaining SQL now() calls, must use UTC |
816 |
cr.execute("INSERT INTO ir_model_data (name,date_init,date_update,module,model,res_id) VALUES (%s, (now() at time zone 'UTC'), (now() at time zone 'UTC'), %s, %s, %s)", \ |
1803.1.34
by Christophe Simonis
[FIX] increase size of field "name" of ir.model.data |
817 |
(name1, context['module'], 'ir.model.fields', id) |
922
by Christophe Simonis
convert tabs to 4 spaces |
818 |
)
|
819 |
else: |
|
957
by Olivier Laurent
pep8 |
820 |
for key, val in vals.items(): |
821 |
if cols[k][key] != vals[key]: |
|
822 |
cr.execute('update ir_model_fields set field_description=%s where model=%s and name=%s', (vals['field_description'], vals['model'], vals['name'])) |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
823 |
cr.commit() |
824 |
cr.execute("""UPDATE ir_model_fields SET |
|
1192
by Fabien Pinckaers
improve |
825 |
model_id=%s, field_description=%s, ttype=%s, relation=%s, |
3569.1.4
by sebastien beau
[REF] ir_model_field replace the serialisation_field by serialization_field_id |
826 |
view_load=%s, select_level=%s, readonly=%s ,required=%s, selectable=%s, relation_field=%s, translate=%s, serialization_field_id=%s |
922
by Christophe Simonis
convert tabs to 4 spaces |
827 |
WHERE
|
828 |
model=%s AND name=%s""", ( |
|
1242
by hda at tinyerp
bugfix for active=false search |
829 |
vals['model_id'], vals['field_description'], vals['ttype'], |
1009.1.1
by mga at tinyerp
Bug-Fix |
830 |
vals['relation'], bool(vals['view_load']), |
3569.1.4
by sebastien beau
[REF] ir_model_field replace the serialisation_field by serialization_field_id |
831 |
vals['select_level'], bool(vals['readonly']), bool(vals['required']), bool(vals['selectable']), vals['relation_field'], bool(vals['translate']), vals['serialization_field_id'], vals['model'], vals['name'] |
922
by Christophe Simonis
convert tabs to 4 spaces |
832 |
))
|
2967
by Fabien Pinckaers
[IMP] Speed improvement when loading modules |
833 |
break
|
922
by Christophe Simonis
convert tabs to 4 spaces |
834 |
cr.commit() |
835 |
||
3432.1.1
by Vo Minh Thu
[REF] osv: osv classes merged inside orm classes. |
836 |
#
|
837 |
# Goal: try to apply inheritance at the instanciation level and
|
|
838 |
# put objects in the pool var
|
|
839 |
#
|
|
840 |
@classmethod
|
|
3511.1.8
by Vo Minh Thu
[REF] osv: previous diff makes unneccessary one of makeInstance/createInstance. |
841 |
def create_instance(cls, pool, cr): |
3432.1.4
by Vo Minh Thu
[IMP] osv: removed unnecessary module arg, added comments. |
842 |
""" Instanciate a given model.
|
843 |
||
844 |
This class method instanciates the class of some model (i.e. a class
|
|
845 |
deriving from osv or osv_memory). The class might be the class passed
|
|
846 |
in argument or, if it inherits from another class, a class constructed
|
|
847 |
by combining the two classes.
|
|
848 |
||
849 |
The ``attributes`` argument specifies which parent class attributes
|
|
850 |
have to be combined.
|
|
851 |
||
852 |
TODO: the creation of the combined class is repeated at each call of
|
|
853 |
this method. This is probably unnecessary.
|
|
854 |
||
855 |
"""
|
|
3511.1.8
by Vo Minh Thu
[REF] osv: previous diff makes unneccessary one of makeInstance/createInstance. |
856 |
attributes = ['_columns', '_defaults', '_inherits', '_constraints', |
857 |
'_sql_constraints'] |
|
858 |
||
3432.1.1
by Vo Minh Thu
[REF] osv: osv classes merged inside orm classes. |
859 |
parent_names = getattr(cls, '_inherit', None) |
860 |
if parent_names: |
|
861 |
if isinstance(parent_names, (str, unicode)): |
|
862 |
name = cls._name or parent_names |
|
863 |
parent_names = [parent_names] |
|
864 |
else: |
|
865 |
name = cls._name |
|
866 |
if not name: |
|
867 |
raise TypeError('_name is mandatory in case of multiple inheritance') |
|
868 |
||
869 |
for parent_name in ((type(parent_names)==list) and parent_names or [parent_names]): |
|
3427.2.41
by Olivier Dony
[IMP] orm: remember Model._original_module when constructing inheritance |
870 |
parent_model = pool.get(parent_name) |
871 |
if not parent_model: |
|
3432.1.4
by Vo Minh Thu
[IMP] osv: removed unnecessary module arg, added comments. |
872 |
raise TypeError('The model "%s" specifies an unexisting parent class "%s"\n' |
873 |
'You may need to add a dependency on the parent class\' module.' % (name, parent_name)) |
|
4066.1.29
by olt at tinyerp
[FIX] 'create_instance' method: the check for parent_model existence need to be done before accessing that object attributes or methods |
874 |
if not getattr(cls, '_original_module', None) and name == parent_model._name: |
875 |
cls._original_module = parent_model._original_module |
|
3427.2.41
by Olivier Dony
[IMP] orm: remember Model._original_module when constructing inheritance |
876 |
parent_class = parent_model.__class__ |
3432.1.1
by Vo Minh Thu
[REF] osv: osv classes merged inside orm classes. |
877 |
nattr = {} |
878 |
for s in attributes: |
|
3427.2.41
by Olivier Dony
[IMP] orm: remember Model._original_module when constructing inheritance |
879 |
new = copy.copy(getattr(parent_model, s, {})) |
3432.1.1
by Vo Minh Thu
[REF] osv: osv classes merged inside orm classes. |
880 |
if s == '_columns': |
881 |
# Don't _inherit custom fields.
|
|
882 |
for c in new.keys(): |
|
883 |
if new[c].manual: |
|
884 |
del new[c] |
|
4062
by Vo Minh Thu
[FIX] fields: duplicate float fields per registry (because they are stateful). |
885 |
# Duplicate float fields because they have a .digits
|
886 |
# cache (which must be per-registry, not server-wide).
|
|
887 |
for c in new.keys(): |
|
888 |
if new[c]._type == 'float': |
|
889 |
new[c] = copy.copy(new[c]) |
|
3432.1.1
by Vo Minh Thu
[REF] osv: osv classes merged inside orm classes. |
890 |
if hasattr(new, 'update'): |
891 |
new.update(cls.__dict__.get(s, {})) |
|
892 |
elif s=='_constraints': |
|
893 |
for c in cls.__dict__.get(s, []): |
|
894 |
exist = False |
|
895 |
for c2 in range(len(new)): |
|
3974.2.2
by Florent Xicluna
[REF] fix weird indentation, not multiple of four. |
896 |
#For _constraints, we should check field and methods as well
|
897 |
if new[c2][2]==c[2] and (new[c2][0] == c[0] \ |
|
3432.1.1
by Vo Minh Thu
[REF] osv: osv classes merged inside orm classes. |
898 |
or getattr(new[c2][0],'__name__', True) == \ |
899 |
getattr(c[0],'__name__', False)): |
|
900 |
# If new class defines a constraint with
|
|
901 |
# same function name, we let it override
|
|
902 |
# the old one.
|
|
4466.1.2
by Christophe Simonis
[IMP] orm: remove trailing spaces |
903 |
|
3432.1.1
by Vo Minh Thu
[REF] osv: osv classes merged inside orm classes. |
904 |
new[c2] = c |
905 |
exist = True |
|
906 |
break
|
|
907 |
if not exist: |
|
908 |
new.append(c) |
|
909 |
else: |
|
910 |
new.extend(cls.__dict__.get(s, [])) |
|
911 |
nattr[s] = new |
|
4633
by Olivier Dony
[FIX] translate,orm: remember local Model [_sql]_constraints, and keep only those when exporting translations |
912 |
|
913 |
# Keep links to non-inherited constraints, e.g. useful when exporting translations
|
|
914 |
nattr['_local_constraints'] = cls.__dict__.get('_constraints', []) |
|
915 |
nattr['_local_sql_constraints'] = cls.__dict__.get('_sql_constraints', []) |
|
916 |
||
3453.2.6
by Vo Minh Thu
[REF] osv: use a metaclass to discover new models. |
917 |
cls = type(name, (cls, parent_class), dict(nattr, _register=False)) |
4633
by Olivier Dony
[FIX] translate,orm: remember local Model [_sql]_constraints, and keep only those when exporting translations |
918 |
else: |
919 |
cls._local_constraints = getattr(cls, '_constraints', []) |
|
920 |
cls._local_sql_constraints = getattr(cls, '_sql_constraints', []) |
|
921 |
||
3427.2.41
by Olivier Dony
[IMP] orm: remember Model._original_module when constructing inheritance |
922 |
if not getattr(cls, '_original_module', None): |
923 |
cls._original_module = cls._module |
|
3432.1.1
by Vo Minh Thu
[REF] osv: osv classes merged inside orm classes. |
924 |
obj = object.__new__(cls) |
925 |
obj.__init__(pool, cr) |
|
926 |
return obj |
|
927 |
||
928 |
def __new__(cls): |
|
3555
by Olivier Dony
[FIX] registry: use a unique list of models to load per module |
929 |
"""Register this model.
|
3432.1.1
by Vo Minh Thu
[REF] osv: osv classes merged inside orm classes. |
930 |
|
931 |
This doesn't create an instance but simply register the model
|
|
932 |
as being part of the module where it is defined.
|
|
933 |
||
934 |
"""
|
|
3511.1.25
by Olivier Dony
[IMP] orm: introduce cleaner class hierarchy for models |
935 |
|
936 |
||
3432.1.1
by Vo Minh Thu
[REF] osv: osv classes merged inside orm classes. |
937 |
# Set the module name (e.g. base, sale, accounting, ...) on the class.
|
938 |
module = cls.__module__.split('.')[0] |
|
939 |
if not hasattr(cls, '_module'): |
|
940 |
cls._module = module |
|
941 |
||
3555
by Olivier Dony
[FIX] registry: use a unique list of models to load per module |
942 |
# Record this class in the list of models to instantiate for this module,
|
943 |
# managed by the metaclass.
|
|
944 |
module_model_list = MetaModel.module_to_models.setdefault(cls._module, []) |
|
945 |
if cls not in module_model_list: |
|
4743.1.11
by Fabien Pinckaers
[IMP] custom model allowed |
946 |
if not cls._custom: |
947 |
module_model_list.append(cls) |
|
3432.1.1
by Vo Minh Thu
[REF] osv: osv classes merged inside orm classes. |
948 |
|
949 |
# Since we don't return an instance here, the __init__
|
|
950 |
# method won't be called.
|
|
951 |
return None |
|
952 |
||
953 |
def __init__(self, pool, cr): |
|
3511.1.6
by Vo Minh Thu
[REF] orm: merged orm_template inside orm. |
954 |
""" Initialize a model and make it part of the given registry.
|
955 |
||
956 |
- copy the stored fields' functions in the osv_pool,
|
|
957 |
- update the _columns with the fields found in ir_model_fields,
|
|
958 |
- ensure there is a many2one for each _inherits'd parent,
|
|
959 |
- update the children's _columns,
|
|
960 |
- give a chance to each field to initialize itself.
|
|
961 |
||
962 |
"""
|
|
3432.1.3
by Vo Minh Thu
[FIX] osv_pool: it appears it is necessary to have the model in the pool earlier on. |
963 |
pool.add(self._name, self) |
3432.1.1
by Vo Minh Thu
[REF] osv: osv classes merged inside orm classes. |
964 |
self.pool = pool |
965 |
||
1179
by Stephane Wirtel
Show an error and raise an exception if the _name is not present in an object |
966 |
if not self._name and not hasattr(self, '_inherit'): |
967 |
name = type(self).__name__.split('.')[0] |
|
968 |
msg = "The class %s has to have a _name attribute" % name |
|
969 |
||
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
970 |
_logger.error(msg) |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
971 |
raise except_orm('ValueError', msg) |
1179
by Stephane Wirtel
Show an error and raise an exception if the _name is not present in an object |
972 |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
973 |
if not self._description: |
974 |
self._description = self._name |
|
975 |
if not self._table: |
|
957
by Olivier Laurent
pep8 |
976 |
self._table = self._name.replace('.', '_') |
922
by Christophe Simonis
convert tabs to 4 spaces |
977 |
|
3511.1.6
by Vo Minh Thu
[REF] orm: merged orm_template inside orm. |
978 |
if not hasattr(self, '_log_access'): |
979 |
# If _log_access is not specified, it is the same value as _auto.
|
|
980 |
self._log_access = getattr(self, "_auto", True) |
|
981 |
||
982 |
self._columns = self._columns.copy() |
|
983 |
for store_field in self._columns: |
|
984 |
f = self._columns[store_field] |
|
985 |
if hasattr(f, 'digits_change'): |
|
986 |
f.digits_change(cr) |
|
3663.1.2
by Vo Minh Thu
[MERGE] merged trunk. |
987 |
def not_this_field(stored_func): |
988 |
x, y, z, e, f, l = stored_func |
|
989 |
return x != self._name or y != store_field |
|
990 |
self.pool._store_function[self._name] = filter(not_this_field, self.pool._store_function.get(self._name, [])) |
|
3511.1.6
by Vo Minh Thu
[REF] orm: merged orm_template inside orm. |
991 |
if not isinstance(f, fields.function): |
992 |
continue
|
|
993 |
if not f.store: |
|
994 |
continue
|
|
3663.1.2
by Vo Minh Thu
[MERGE] merged trunk. |
995 |
sm = f.store |
996 |
if sm is True: |
|
3511.1.6
by Vo Minh Thu
[REF] orm: merged orm_template inside orm. |
997 |
sm = {self._name: (lambda self, cr, uid, ids, c={}: ids, None, 10, None)} |
998 |
for object, aa in sm.items(): |
|
999 |
if len(aa) == 4: |
|
1000 |
(fnct, fields2, order, length) = aa |
|
1001 |
elif len(aa) == 3: |
|
1002 |
(fnct, fields2, order) = aa |
|
1003 |
length = None |
|
1004 |
else: |
|
1005 |
raise except_orm('Error', |
|
1006 |
('Invalid function definition %s in object %s !\nYou must use the definition: store={object:(fnct, fields, priority, time length)}.' % (store_field, self._name))) |
|
1007 |
self.pool._store_function.setdefault(object, []) |
|
3663.1.2
by Vo Minh Thu
[MERGE] merged trunk. |
1008 |
self.pool._store_function[object].append((self._name, store_field, fnct, tuple(fields2) if fields2 else None, order, length)) |
1009 |
self.pool._store_function[object].sort(lambda x, y: cmp(x[4], y[4])) |
|
3511.1.6
by Vo Minh Thu
[REF] orm: merged orm_template inside orm. |
1010 |
|
1011 |
for (key, _, msg) in self._sql_constraints: |
|
1012 |
self.pool._sql_error[self._table+'_'+key] = msg |
|
1013 |
||
1014 |
# Load manual fields
|
|
1015 |
||
4672.3.2
by Vo Minh Thu
[IMP] module loading: removed unnecessary indentation, added comments. |
1016 |
# Check the query is already done for all modules of if we need to
|
1017 |
# do it ourselves.
|
|
4672.3.3
by Vo Minh Thu
[FIX] registry: Set the fields_by_model attribute in __init__(), use None to flag non-existing fields-per-model cache. |
1018 |
if self.pool.fields_by_model is not None: |
4672.3.2
by Vo Minh Thu
[IMP] module loading: removed unnecessary indentation, added comments. |
1019 |
manual_fields = self.pool.fields_by_model.get(self._name, []) |
1020 |
else: |
|
1021 |
cr.execute('SELECT * FROM ir_model_fields WHERE model=%s AND state=%s', (self._name, 'manual')) |
|
1022 |
manual_fields = cr.dictfetchall() |
|
1023 |
for field in manual_fields: |
|
1024 |
if field['name'] in self._columns: |
|
1025 |
continue
|
|
1026 |
attrs = { |
|
1027 |
'string': field['field_description'], |
|
1028 |
'required': bool(field['required']), |
|
1029 |
'readonly': bool(field['readonly']), |
|
1030 |
'domain': eval(field['domain']) if field['domain'] else None, |
|
4743.21.1
by Chris Biersbach
[FIX] Removes the limit of 64 characters from additional text fields added via the interface |
1031 |
'size': field['size'] or None, |
4672.3.2
by Vo Minh Thu
[IMP] module loading: removed unnecessary indentation, added comments. |
1032 |
'ondelete': field['on_delete'], |
1033 |
'translate': (field['translate']), |
|
1034 |
'manual': True, |
|
1035 |
#'select': int(field['select_level'])
|
|
1036 |
}
|
|
1037 |
||
1038 |
if field['serialization_field_id']: |
|
1039 |
cr.execute('SELECT name FROM ir_model_fields WHERE id=%s', (field['serialization_field_id'],)) |
|
1040 |
attrs.update({'serialization_field': cr.fetchone()[0], 'type': field['ttype']}) |
|
1041 |
if field['ttype'] in ['many2one', 'one2many', 'many2many']: |
|
1042 |
attrs.update({'relation': field['relation']}) |
|
1043 |
self._columns[field['name']] = fields.sparse(**attrs) |
|
1044 |
elif field['ttype'] == 'selection': |
|
1045 |
self._columns[field['name']] = fields.selection(eval(field['selection']), **attrs) |
|
1046 |
elif field['ttype'] == 'reference': |
|
1047 |
self._columns[field['name']] = fields.reference(selection=eval(field['selection']), **attrs) |
|
1048 |
elif field['ttype'] == 'many2one': |
|
1049 |
self._columns[field['name']] = fields.many2one(field['relation'], **attrs) |
|
1050 |
elif field['ttype'] == 'one2many': |
|
1051 |
self._columns[field['name']] = fields.one2many(field['relation'], field['relation_field'], **attrs) |
|
1052 |
elif field['ttype'] == 'many2many': |
|
1053 |
_rel1 = field['relation'].replace('.', '_') |
|
1054 |
_rel2 = field['model'].replace('.', '_') |
|
1055 |
_rel_name = 'x_%s_%s_%s_rel' % (_rel1, _rel2, field['name']) |
|
1056 |
self._columns[field['name']] = fields.many2many(field['relation'], _rel_name, 'id1', 'id2', **attrs) |
|
4672.3.1
by Vo Minh Thu
[IMP] Reduce considerably the loading time of a new registry. |
1057 |
else: |
4672.3.2
by Vo Minh Thu
[IMP] module loading: removed unnecessary indentation, added comments. |
1058 |
self._columns[field['name']] = getattr(fields, field['ttype'])(**attrs) |
3511.1.6
by Vo Minh Thu
[REF] orm: merged orm_template inside orm. |
1059 |
|
1060 |
self._inherits_check() |
|
1061 |
self._inherits_reload() |
|
1062 |
if not self._sequence: |
|
1063 |
self._sequence = self._table + '_id_seq' |
|
1064 |
for k in self._defaults: |
|
1065 |
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,) |
|
1066 |
for f in self._columns: |
|
1067 |
self._columns[f].restart() |
|
1068 |
||
3511.1.31
by Olivier Dony
[IMP] orm: fix Model hierarchy to avoid TransientModel leaking downstream |
1069 |
# Transience
|
1070 |
if self.is_transient(): |
|
1071 |
self._transient_check_count = 0 |
|
1072 |
self._transient_max_count = config.get('osv_memory_count_limit') |
|
1073 |
self._transient_max_hours = config.get('osv_memory_age_limit') |
|
1074 |
assert self._log_access, "TransientModels must have log_access turned on, "\ |
|
1075 |
"in order to implement their access rights policy"
|
|
922
by Christophe Simonis
convert tabs to 4 spaces |
1076 |
|
4272.1.1
by Antony Lesuisse
[IMP] rec_name assertion and fallback |
1077 |
# Validate rec_name
|
4272.1.6
by Vo Minh Thu
[MAOW] lolcat ninja fix in the branch, yaoh! |
1078 |
if self._rec_name is not None: |
4629.1.1
by ajay javiya (OpenERP)
[FIX]:Issue of add custome field on object |
1079 |
assert self._rec_name in self._all_columns.keys() + ['id'], "Invalid rec_name %s for model %s" % (self._rec_name, self._name) |
4272.1.1
by Antony Lesuisse
[IMP] rec_name assertion and fallback |
1080 |
else: |
1081 |
self._rec_name = 'name' |
|
1082 |
||
1083 |
||
922
by Christophe Simonis
convert tabs to 4 spaces |
1084 |
def __export_row(self, cr, uid, row, fields, context=None): |
2238.1.41
by Jay(Open ERP)
[FIX] Export should honor the current locale(language) settings of the user. |
1085 |
if context is None: |
1086 |
context = {} |
|
1865.1.1
by mra (Open ERP)
[FIX] osv_memory object search method: now it will accept id as domain with its action |
1087 |
|
1803.1.1
by Harry (Open ERP)
[IMP] import/export : added Database ID, ID |
1088 |
def check_type(field_type): |
1089 |
if field_type == 'float': |
|
1800.2.7
by ACH,JVO
[FIX] Export will consider float values as float and char as char |
1090 |
return 0.0 |
1803.1.1
by Harry (Open ERP)
[IMP] import/export : added Database ID, ID |
1091 |
elif field_type == 'integer': |
1800.2.7
by ACH,JVO
[FIX] Export will consider float values as float and char as char |
1092 |
return 0 |
1865.1.1
by mra (Open ERP)
[FIX] osv_memory object search method: now it will accept id as domain with its action |
1093 |
elif field_type == 'boolean': |
3549.3.1
by tfr(OpenERP)
[FIX]:import boolean fields |
1094 |
return 'False' |
1803.1.1
by Harry (Open ERP)
[IMP] import/export : added Database ID, ID |
1095 |
return '' |
1869.1.14
by nch at tinyerp
read_group method for group by |
1096 |
|
1844.4.5
by YSA,JVO
[FIX] Export : Selection field gets imported by its external name if export is not import compatible |
1097 |
def selection_field(in_field): |
1098 |
col_obj = self.pool.get(in_field.keys()[0]) |
|
1099 |
if f[i] in col_obj._columns.keys(): |
|
1100 |
return col_obj._columns[f[i]] |
|
1101 |
elif f[i] in col_obj._inherits.keys(): |
|
1102 |
selection_field(col_obj._inherits) |
|
1103 |
else: |
|
1869.1.14
by nch at tinyerp
read_group method for group by |
1104 |
return False |
1105 |
||
3927
by Fabien Pinckaers
[FIX] import/export fully working through web interface |
1106 |
def _get_xml_id(self, cr, uid, r): |
1107 |
model_data = self.pool.get('ir.model.data') |
|
1108 |
data_ids = model_data.search(cr, uid, [('model', '=', r._table_name), ('res_id', '=', r['id'])]) |
|
1109 |
if len(data_ids): |
|
1110 |
d = model_data.read(cr, uid, data_ids, ['name', 'module'])[0] |
|
1111 |
if d['module']: |
|
1112 |
r = '%s.%s' % (d['module'], d['name']) |
|
1113 |
else: |
|
1114 |
r = d['name'] |
|
1115 |
else: |
|
1116 |
postfix = 0 |
|
1117 |
while True: |
|
1118 |
n = self._table+'_'+str(r['id']) + (postfix and ('_'+str(postfix)) or '' ) |
|
1119 |
if not model_data.search(cr, uid, [('name', '=', n)]): |
|
1120 |
break
|
|
1121 |
postfix += 1 |
|
4066.1.134
by Olivier Dony
[FIX] export_data: avoid access error while auto-generating external IDs during export |
1122 |
model_data.create(cr, SUPERUSER_ID, { |
3927
by Fabien Pinckaers
[FIX] import/export fully working through web interface |
1123 |
'name': n, |
1124 |
'model': self._name, |
|
1125 |
'res_id': r['id'], |
|
1126 |
'module': '__export__', |
|
1127 |
})
|
|
1128 |
r = '__export__.'+n |
|
1129 |
return r |
|
1130 |
||
922
by Christophe Simonis
convert tabs to 4 spaces |
1131 |
lines = [] |
1132 |
data = map(lambda x: '', range(len(fields))) |
|
3014
by Anup(OpenERP)
[FIX](Maintenance) Exportation fixed for o2m and m2m |
1133 |
done = [] |
922
by Christophe Simonis
convert tabs to 4 spaces |
1134 |
for fpos in range(len(fields)): |
1865.1.1
by mra (Open ERP)
[FIX] osv_memory object search method: now it will accept id as domain with its action |
1135 |
f = fields[fpos] |
922
by Christophe Simonis
convert tabs to 4 spaces |
1136 |
if f: |
1137 |
r = row |
|
1138 |
i = 0 |
|
957
by Olivier Laurent
pep8 |
1139 |
while i < len(f): |
3927
by Fabien Pinckaers
[FIX] import/export fully working through web interface |
1140 |
cols = False |
3130
by Fabien Pinckaers
[IMP] import / export cleaned |
1141 |
if f[i] == '.id': |
1865.1.1
by mra (Open ERP)
[FIX] osv_memory object search method: now it will accept id as domain with its action |
1142 |
r = r['id'] |
1143 |
elif f[i] == 'id': |
|
3927
by Fabien Pinckaers
[FIX] import/export fully working through web interface |
1144 |
r = _get_xml_id(self, cr, uid, r) |
1803.1.1
by Harry (Open ERP)
[IMP] import/export : added Database ID, ID |
1145 |
else: |
1844.4.5
by YSA,JVO
[FIX] Export : Selection field gets imported by its external name if export is not import compatible |
1146 |
r = r[f[i]] |
1147 |
# To display external name of selection field when its exported
|
|
3130
by Fabien Pinckaers
[IMP] import / export cleaned |
1148 |
if f[i] in self._columns.keys(): |
1149 |
cols = self._columns[f[i]] |
|
1150 |
elif f[i] in self._inherit_fields.keys(): |
|
1151 |
cols = selection_field(self._inherits) |
|
1152 |
if cols and cols._type == 'selection': |
|
1153 |
sel_list = cols.selection |
|
1154 |
if r and type(sel_list) == type([]): |
|
1155 |
r = [x[1] for x in sel_list if r==x[0]] |
|
1156 |
r = r and r[0] or False |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
1157 |
if not r: |
1865.1.1
by mra (Open ERP)
[FIX] osv_memory object search method: now it will accept id as domain with its action |
1158 |
if f[i] in self._columns: |
1803.1.1
by Harry (Open ERP)
[IMP] import/export : added Database ID, ID |
1159 |
r = check_type(self._columns[f[i]]._type) |
1800.2.7
by ACH,JVO
[FIX] Export will consider float values as float and char as char |
1160 |
elif f[i] in self._inherit_fields: |
1865.1.1
by mra (Open ERP)
[FIX] osv_memory object search method: now it will accept id as domain with its action |
1161 |
r = check_type(self._inherit_fields[f[i]][2]._type) |
3138
by Fabien Pinckaers
[FIX] import / export |
1162 |
data[fpos] = r or False |
922
by Christophe Simonis
convert tabs to 4 spaces |
1163 |
break
|
1164 |
if isinstance(r, (browse_record_list, list)): |
|
1165 |
first = True |
|
1166 |
fields2 = map(lambda x: (x[:i+1]==f[:i+1] and x[i+1:]) \ |
|
1167 |
or [], fields) |
|
1168 |
if fields2 in done: |
|
3014
by Anup(OpenERP)
[FIX](Maintenance) Exportation fixed for o2m and m2m |
1169 |
if [x for x in fields2 if x]: |
1170 |
break
|
|
1865.1.1
by mra (Open ERP)
[FIX] osv_memory object search method: now it will accept id as domain with its action |
1171 |
done.append(fields2) |
3927
by Fabien Pinckaers
[FIX] import/export fully working through web interface |
1172 |
if cols and cols._type=='many2many' and len(fields[fpos])>(i+1) and (fields[fpos][i+1]=='id'): |
1173 |
data[fpos] = ','.join([_get_xml_id(self, cr, uid, x) for x in r]) |
|
1174 |
break
|
|
1175 |
||
922
by Christophe Simonis
convert tabs to 4 spaces |
1176 |
for row2 in r: |
3927
by Fabien Pinckaers
[FIX] import/export fully working through web interface |
1177 |
lines2 = row2._model.__export_row(cr, uid, row2, fields2, |
1865.1.1
by mra (Open ERP)
[FIX] osv_memory object search method: now it will accept id as domain with its action |
1178 |
context) |
922
by Christophe Simonis
convert tabs to 4 spaces |
1179 |
if first: |
1180 |
for fpos2 in range(len(fields)): |
|
1181 |
if lines2 and lines2[0][fpos2]: |
|
1182 |
data[fpos2] = lines2[0][fpos2] |
|
1803.1.2
by Harry (Open ERP)
[FIX] export window : use context for import compatable option in pass argument instend of added new argemenent |
1183 |
if not data[fpos]: |
1184 |
dt = '' |
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
1185 |
for rr in r: |
3310.1.19
by Jay Vora(OpenERP)
[FIX] Export : _rec_name should be used instead of hardcoded name for relational records (Ref: SaaS Maintenance case 3921) |
1186 |
name_relation = self.pool.get(rr._table_name)._rec_name |
1187 |
if isinstance(rr[name_relation], browse_record): |
|
1188 |
rr = rr[name_relation] |
|
2238.1.41
by Jay(Open ERP)
[FIX] Export should honor the current locale(language) settings of the user. |
1189 |
rr_name = self.pool.get(rr._table_name).name_get(cr, uid, [rr.id], context=context) |
1844.6.2
by HDA(OpenERP)
Merge and bugfixes from jvo |
1190 |
rr_name = rr_name and rr_name[0] and rr_name[0][1] or '' |
1844.6.4
by Jay(Open ERP)
[FIX] Export : M2M field name was missing last character |
1191 |
dt += tools.ustr(rr_name or '') + ',' |
1803.1.2
by Harry (Open ERP)
[FIX] export window : use context for import compatable option in pass argument instend of added new argemenent |
1192 |
data[fpos] = dt[:-1] |
1193 |
break
|
|
957
by Olivier Laurent
pep8 |
1194 |
lines += lines2[1:] |
922
by Christophe Simonis
convert tabs to 4 spaces |
1195 |
first = False |
1196 |
else: |
|
1865.1.1
by mra (Open ERP)
[FIX] osv_memory object search method: now it will accept id as domain with its action |
1197 |
lines += lines2 |
922
by Christophe Simonis
convert tabs to 4 spaces |
1198 |
break
|
957
by Olivier Laurent
pep8 |
1199 |
i += 1 |
1200 |
if i == len(f): |
|
1803.1.1
by Harry (Open ERP)
[IMP] import/export : added Database ID, ID |
1201 |
if isinstance(r, browse_record): |
2238.1.41
by Jay(Open ERP)
[FIX] Export should honor the current locale(language) settings of the user. |
1202 |
r = self.pool.get(r._table_name).name_get(cr, uid, [r.id], context=context) |
1844.6.2
by HDA(OpenERP)
Merge and bugfixes from jvo |
1203 |
r = r and r[0] and r[0][1] or '' |
1597.1.1
by Stephane Wirtel
[FIX] Encoding error |
1204 |
data[fpos] = tools.ustr(r or '') |
922
by Christophe Simonis
convert tabs to 4 spaces |
1205 |
return [data] + lines |
1206 |
||
1803.1.2
by Harry (Open ERP)
[FIX] export window : use context for import compatable option in pass argument instend of added new argemenent |
1207 |
def export_data(self, cr, uid, ids, fields_to_export, context=None): |
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
1208 |
"""
|
1209 |
Export fields for selected objects
|
|
1210 |
||
1211 |
:param cr: database cursor
|
|
1212 |
:param uid: current user id
|
|
2111.3.21
by Rvo(Open ERP)
[IMP]:improved orm's public methods docstrings |
1213 |
:param ids: list of ids
|
1214 |
:param fields_to_export: list of fields
|
|
3130
by Fabien Pinckaers
[IMP] import / export cleaned |
1215 |
:param context: context arguments, like lang, time zone
|
2111.3.21
by Rvo(Open ERP)
[IMP]:improved orm's public methods docstrings |
1216 |
:rtype: dictionary with a *datas* matrix
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
1217 |
|
1218 |
This method is used when exporting data via client menu
|
|
1219 |
||
1220 |
"""
|
|
2605
by Jay(OpenERP)
[REF] |
1221 |
if context is None: |
957
by Olivier Laurent
pep8 |
1222 |
context = {} |
1803.1.1
by Harry (Open ERP)
[IMP] import/export : added Database ID, ID |
1223 |
cols = self._columns.copy() |
1224 |
for f in self._inherit_fields: |
|
1865.1.1
by mra (Open ERP)
[FIX] osv_memory object search method: now it will accept id as domain with its action |
1225 |
cols.update({f: self._inherit_fields[f][2]}) |
3641
by Xavier Morel
[IMP] use same fieldpath-splitting function in data_import and data_export, document it a bit |
1226 |
fields_to_export = map(fix_import_export_id_paths, fields_to_export) |
922
by Christophe Simonis
convert tabs to 4 spaces |
1227 |
datas = [] |
1228 |
for row in self.browse(cr, uid, ids, context): |
|
1803.1.1
by Harry (Open ERP)
[IMP] import/export : added Database ID, ID |
1229 |
datas += self.__export_row(cr, uid, row, fields_to_export, context) |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
1230 |
return {'datas': datas} |
922
by Christophe Simonis
convert tabs to 4 spaces |
1231 |
|
1729.1.17
by Fabien Pinckaers
bugfix_portal_access_rights |
1232 |
def import_data(self, cr, uid, fields, datas, mode='init', current_module='', noupdate=False, context=None, filename=None): |
4408.2.21
by Xavier Morel
[DOC] fixes and cleanups |
1233 |
"""
|
1234 |
.. deprecated:: 7.0
|
|
1235 |
Use :meth:`~load` instead
|
|
1236 |
||
1237 |
Import given data in given module
|
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
1238 |
|
3171
by Olivier Dony
[FIX] orm: corrected RST for docstring of import_data() |
1239 |
This method is used when importing data via client menu.
|
1240 |
||
1241 |
Example of fields to import for a sale.order::
|
|
1242 |
||
3154
by Fabien Pinckaers
[FIX] Complete recode of import_data to clean and fix export with import compatible. |
1243 |
.id, (=database_id)
|
1244 |
partner_id, (=name_search)
|
|
1245 |
order_line/.id, (=database_id)
|
|
1246 |
order_line/name,
|
|
1247 |
order_line/product_id/id, (=xml id)
|
|
1248 |
order_line/price_unit,
|
|
1249 |
order_line/product_uom_qty,
|
|
1250 |
order_line/product_uom/id (=xml_id)
|
|
3661
by Xavier Morel
[IMP] doc for import_data |
1251 |
|
3754
by Olivier Dony
[FIX] Model.import_data docstring: cleanup + clear RST warnings for autodoc |
1252 |
This method returns a 4-tuple with the following structure::
|
1253 |
||
1254 |
(return_code, errored_resource, error_message, unused)
|
|
1255 |
||
1256 |
* The first item is a return code, it is ``-1`` in case of
|
|
1257 |
import error, or the last imported row number in case of success
|
|
1258 |
* The second item contains the record data dict that failed to import
|
|
1259 |
in case of error, otherwise it's 0
|
|
1260 |
* The third item contains an error message string in case of error,
|
|
1261 |
otherwise it's 0
|
|
1262 |
* The last item is currently unused, with no specific semantics
|
|
1263 |
||
1264 |
:param fields: list of fields to import
|
|
4408.2.1
by Xavier Morel
[ADD] big bit on new import: pretty much everything but o2m |
1265 |
:param datas: data to import
|
3661
by Xavier Morel
[IMP] doc for import_data |
1266 |
:param mode: 'init' or 'update' for record creation
|
1267 |
:param current_module: module name
|
|
1268 |
:param noupdate: flag for record creation
|
|
1269 |
:param filename: optional file to store partial import state for recovery
|
|
3754
by Olivier Dony
[FIX] Model.import_data docstring: cleanup + clear RST warnings for autodoc |
1270 |
:returns: 4-tuple in the form (return_code, errored_resource, error_message, unused)
|
1271 |
:rtype: (int, dict or 0, str or 0, str or 0)
|
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
1272 |
"""
|
4408.2.20
by Xavier Morel
[IMP] reimplement BaseModel.import_data on top of BaseModel.load |
1273 |
context = dict(context) if context is not None else {} |
1274 |
context['_import_current_module'] = current_module |
|
1275 |
||
3641
by Xavier Morel
[IMP] use same fieldpath-splitting function in data_import and data_export, document it a bit |
1276 |
fields = map(fix_import_export_id_paths, fields) |
1803.1.1
by Harry (Open ERP)
[IMP] import/export : added Database ID, ID |
1277 |
ir_model_data_obj = self.pool.get('ir.model.data') |
1865.1.1
by mra (Open ERP)
[FIX] osv_memory object search method: now it will accept id as domain with its action |
1278 |
|
4408.2.20
by Xavier Morel
[IMP] reimplement BaseModel.import_data on top of BaseModel.load |
1279 |
def log(m): |
1280 |
if m['type'] == 'error': |
|
1281 |
raise Exception(m['message']) |
|
1282 |
||
3839
by Xavier Morel
[FIX] correctly restore partial import index when restarting an import |
1283 |
if config.get('import_partial') and filename: |
1284 |
with open(config.get('import_partial'), 'rb') as partial_import_file: |
|
1285 |
data = pickle.load(partial_import_file) |
|
1286 |
position = data.get(filename, 0) |
|
1287 |
||
4408.2.20
by Xavier Morel
[IMP] reimplement BaseModel.import_data on top of BaseModel.load |
1288 |
position = 0 |
1289 |
try: |
|
1290 |
for res_id, xml_id, res, info in self._convert_records(cr, uid, |
|
1291 |
self._extract_records(cr, uid, fields, datas, |
|
1292 |
context=context, log=log), |
|
1293 |
context=context, log=log): |
|
3458.1.2
by Vo Minh Thu
[LINT] removed unused variable, forgot to change two variables in last commit, corrected typo introduced at 3417.6.6. |
1294 |
ir_model_data_obj._update(cr, uid, self._name, |
3155
by Fabien Pinckaers
fix |
1295 |
current_module, res, mode=mode, xml_id=xml_id, |
1844.1.25
by Jay(Open ERP)
[FIX] Import : Context of the action/screen passed,taken into consideration while importing |
1296 |
noupdate=noupdate, res_id=res_id, context=context) |
4408.2.20
by Xavier Morel
[IMP] reimplement BaseModel.import_data on top of BaseModel.load |
1297 |
position = info.get('rows', {}).get('to', 0) + 1 |
1298 |
if config.get('import_partial') and filename and (not (position%100)): |
|
1299 |
with open(config.get('import_partial'), 'rb') as partial_import: |
|
1300 |
data = pickle.load(partial_import) |
|
1301 |
data[filename] = position |
|
1302 |
with open(config.get('import_partial'), 'wb') as partial_import: |
|
1303 |
pickle.dump(data, partial_import) |
|
1304 |
if context.get('defer_parent_store_computation'): |
|
1305 |
self._parent_store_compute(cr) |
|
1306 |
cr.commit() |
|
1307 |
except Exception, e: |
|
1308 |
cr.rollback() |
|
1309 |
return -1, {}, 'Line %d : %s' % (position + 1, tools.ustr(e)), '' |
|
992
by Fabien Pinckaers
Partial Commit |
1310 |
|
2238.1.56
by Christophe Simonis
[IMP] orm: allow import and record creation to defer the computation of parent_left/right,[IMP] orm: do not update parent_left/right if the parent haven't been changed |
1311 |
if context.get('defer_parent_store_computation'): |
1312 |
self._parent_store_compute(cr) |
|
3840
by Xavier Morel
[REM] import_data: unused variables, redundant parens and variables |
1313 |
return position, 0, 0, 0 |
922
by Christophe Simonis
convert tabs to 4 spaces |
1314 |
|
4408.2.1
by Xavier Morel
[ADD] big bit on new import: pretty much everything but o2m |
1315 |
def load(self, cr, uid, fields, data, context=None): |
1316 |
"""
|
|
4408.2.10
by Xavier Morel
[IMP] allow converters to add data to import messages, formalize message keys |
1317 |
Attempts to load the data matrix, and returns a list of ids (or
|
1318 |
``False`` if there was an error and no id could be generated) and a
|
|
1319 |
list of messages.
|
|
1320 |
||
4408.2.21
by Xavier Morel
[DOC] fixes and cleanups |
1321 |
The ids are those of the records created and saved (in database), in
|
1322 |
the same order they were extracted from the file. They can be passed
|
|
1323 |
directly to :meth:`~read`
|
|
1324 |
||
4408.2.1
by Xavier Morel
[ADD] big bit on new import: pretty much everything but o2m |
1325 |
:param fields: list of fields to import, at the same index as the corresponding data
|
1326 |
:type fields: list(str)
|
|
1327 |
:param data: row-major matrix of data to import
|
|
1328 |
:type data: list(list(str))
|
|
1329 |
:param dict context:
|
|
4408.2.11
by Xavier Morel
[IMP] return a dict from Model.load for easier future extensibility (if needed) rather than a tuple. |
1330 |
:returns: {ids: list(int)|False, messages: [Message]}
|
4408.2.1
by Xavier Morel
[ADD] big bit on new import: pretty much everything but o2m |
1331 |
"""
|
1332 |
cr.execute('SAVEPOINT model_load') |
|
1333 |
messages = [] |
|
1334 |
||
1335 |
fields = map(fix_import_export_id_paths, fields) |
|
4548
by Xavier Morel
[FIX] nuke ir.model.data caches before starting imports |
1336 |
ModelData = self.pool['ir.model.data'].clear_caches() |
1337 |
||
4408.2.13
by Xavier Morel
[ADD] ability to convert postgres error messages to human-readable ones |
1338 |
fg = self.fields_get(cr, uid, context=context) |
4408.2.1
by Xavier Morel
[ADD] big bit on new import: pretty much everything but o2m |
1339 |
|
1340 |
mode = 'init' |
|
1341 |
current_module = '' |
|
1342 |
noupdate = False |
|
1343 |
||
1344 |
ids = [] |
|
1345 |
for id, xid, record, info in self._convert_records(cr, uid, |
|
1346 |
self._extract_records(cr, uid, fields, data, |
|
1347 |
context=context, log=messages.append), |
|
1348 |
context=context, log=messages.append): |
|
4408.2.23
by Xavier Morel
[FIX] type error when providing a non-integer for a default database id |
1349 |
try: |
1350 |
cr.execute('SAVEPOINT model_load_save') |
|
1351 |
except psycopg2.InternalError, e: |
|
1352 |
# broken transaction, exit and hope the source error was
|
|
1353 |
# already logged
|
|
1354 |
if not any(message['type'] == 'error' for message in messages): |
|
1355 |
messages.append(dict(info, type='error',message= |
|
1356 |
u"Unknown database error: '%s'" % e)) |
|
1357 |
break
|
|
4408.2.1
by Xavier Morel
[ADD] big bit on new import: pretty much everything but o2m |
1358 |
try: |
1359 |
ids.append(ModelData._update(cr, uid, self._name, |
|
1360 |
current_module, record, mode=mode, xml_id=xid, |
|
1361 |
noupdate=noupdate, res_id=id, context=context)) |
|
1362 |
cr.execute('RELEASE SAVEPOINT model_load_save') |
|
4408.2.13
by Xavier Morel
[ADD] ability to convert postgres error messages to human-readable ones |
1363 |
except psycopg2.Warning, e: |
1364 |
messages.append(dict(info, type='warning', message=str(e))) |
|
4552
by Xavier Morel
[IMP] savepoint rollbacks after error transformation/logging of pg errors to still have original query on cursor |
1365 |
cr.execute('ROLLBACK TO SAVEPOINT model_load_save') |
4408.2.1
by Xavier Morel
[ADD] big bit on new import: pretty much everything but o2m |
1366 |
except psycopg2.Error, e: |
4408.2.13
by Xavier Morel
[ADD] ability to convert postgres error messages to human-readable ones |
1367 |
messages.append(dict( |
4408.2.23
by Xavier Morel
[FIX] type error when providing a non-integer for a default database id |
1368 |
info, type='error', |
4408.2.13
by Xavier Morel
[ADD] ability to convert postgres error messages to human-readable ones |
1369 |
**PGERROR_TO_OE[e.pgcode](self, fg, info, e))) |
4552
by Xavier Morel
[IMP] savepoint rollbacks after error transformation/logging of pg errors to still have original query on cursor |
1370 |
# Failed to write, log to messages, rollback savepoint (to
|
1371 |
# avoid broken transaction) and keep going
|
|
1372 |
cr.execute('ROLLBACK TO SAVEPOINT model_load_save') |
|
4408.2.1
by Xavier Morel
[ADD] big bit on new import: pretty much everything but o2m |
1373 |
if any(message['type'] == 'error' for message in messages): |
1374 |
cr.execute('ROLLBACK TO SAVEPOINT model_load') |
|
4408.2.11
by Xavier Morel
[IMP] return a dict from Model.load for easier future extensibility (if needed) rather than a tuple. |
1375 |
ids = False |
1376 |
return {'ids': ids, 'messages': messages} |
|
4408.2.1
by Xavier Morel
[ADD] big bit on new import: pretty much everything but o2m |
1377 |
def _extract_records(self, cr, uid, fields_, data, |
1378 |
context=None, log=lambda a: None): |
|
4408.2.14
by Xavier Morel
[FIX] use lists instead of iterators in BaseModel._extract_records |
1379 |
""" Generates record dicts from the data sequence.
|
4408.2.1
by Xavier Morel
[ADD] big bit on new import: pretty much everything but o2m |
1380 |
|
1381 |
The result is a generator of dicts mapping field names to raw
|
|
1382 |
(unconverted, unvalidated) values.
|
|
1383 |
||
1384 |
For relational fields, if sub-fields were provided the value will be
|
|
1385 |
a list of sub-records
|
|
1386 |
||
1387 |
The following sub-fields may be set on the record (by key):
|
|
1388 |
* None is the name_get for the record (to use with name_create/name_search)
|
|
1389 |
* "id" is the External ID for the record
|
|
1390 |
* ".id" is the Database ID for the record
|
|
1391 |
"""
|
|
1392 |
columns = dict((k, v.column) for k, v in self._all_columns.iteritems()) |
|
1393 |
# Fake columns to avoid special cases in extractor
|
|
1394 |
columns[None] = fields.char('rec_name') |
|
1395 |
columns['id'] = fields.char('External ID') |
|
1396 |
columns['.id'] = fields.integer('Database ID') |
|
1397 |
||
1398 |
# m2o fields can't be on multiple lines so exclude them from the
|
|
1399 |
# is_relational field rows filter, but special-case it later on to
|
|
1400 |
# be handled with relational fields (as it can have subfields)
|
|
1401 |
is_relational = lambda field: columns[field]._type in ('one2many', 'many2many', 'many2one') |
|
1402 |
get_o2m_values = itemgetter_tuple( |
|
1403 |
[index for index, field in enumerate(fields_) |
|
1404 |
if columns[field[0]]._type == 'one2many']) |
|
1405 |
get_nono2m_values = itemgetter_tuple( |
|
1406 |
[index for index, field in enumerate(fields_) |
|
1407 |
if columns[field[0]]._type != 'one2many']) |
|
1408 |
# Checks if the provided row has any non-empty non-relational field
|
|
1409 |
def only_o2m_values(row, f=get_nono2m_values, g=get_o2m_values): |
|
1410 |
return any(g(row)) and not any(f(row)) |
|
1411 |
||
4408.2.14
by Xavier Morel
[FIX] use lists instead of iterators in BaseModel._extract_records |
1412 |
index = 0 |
4408.2.1
by Xavier Morel
[ADD] big bit on new import: pretty much everything but o2m |
1413 |
while True: |
4408.2.14
by Xavier Morel
[FIX] use lists instead of iterators in BaseModel._extract_records |
1414 |
if index >= len(data): return |
4408.2.1
by Xavier Morel
[ADD] big bit on new import: pretty much everything but o2m |
1415 |
|
4408.2.14
by Xavier Morel
[FIX] use lists instead of iterators in BaseModel._extract_records |
1416 |
row = data[index] |
4408.2.1
by Xavier Morel
[ADD] big bit on new import: pretty much everything but o2m |
1417 |
# copy non-relational fields to record dict
|
1418 |
record = dict((field[0], value) |
|
1419 |
for field, value in itertools.izip(fields_, row) |
|
1420 |
if not is_relational(field[0])) |
|
1421 |
||
1422 |
# Get all following rows which have relational values attached to
|
|
1423 |
# the current record (no non-relational values)
|
|
4408.2.14
by Xavier Morel
[FIX] use lists instead of iterators in BaseModel._extract_records |
1424 |
record_span = itertools.takewhile( |
1425 |
only_o2m_values, itertools.islice(data, index + 1, None)) |
|
4408.2.1
by Xavier Morel
[ADD] big bit on new import: pretty much everything but o2m |
1426 |
# stitch record row back on for relational fields
|
4408.2.14
by Xavier Morel
[FIX] use lists instead of iterators in BaseModel._extract_records |
1427 |
record_span = list(itertools.chain([row], record_span)) |
4408.2.1
by Xavier Morel
[ADD] big bit on new import: pretty much everything but o2m |
1428 |
for relfield in set( |
1429 |
field[0] for field in fields_ |
|
1430 |
if is_relational(field[0])): |
|
1431 |
column = columns[relfield] |
|
1432 |
# FIXME: how to not use _obj without relying on fields_get?
|
|
1433 |
Model = self.pool[column._obj] |
|
1434 |
||
1435 |
# get only cells for this sub-field, should be strictly
|
|
1436 |
# non-empty, field path [None] is for name_get column
|
|
1437 |
indices, subfields = zip(*((index, field[1:] or [None]) |
|
1438 |
for index, field in enumerate(fields_) |
|
1439 |
if field[0] == relfield)) |
|
1440 |
||
1441 |
# return all rows which have at least one value for the
|
|
1442 |
# subfields of relfield
|
|
4408.2.14
by Xavier Morel
[FIX] use lists instead of iterators in BaseModel._extract_records |
1443 |
relfield_data = filter(any, map(itemgetter_tuple(indices), record_span)) |
4408.2.1
by Xavier Morel
[ADD] big bit on new import: pretty much everything but o2m |
1444 |
record[relfield] = [subrecord |
1445 |
for subrecord, _subinfo in Model._extract_records( |
|
1446 |
cr, uid, subfields, relfield_data, |
|
1447 |
context=context, log=log)] |
|
1448 |
||
4408.2.14
by Xavier Morel
[FIX] use lists instead of iterators in BaseModel._extract_records |
1449 |
yield record, {'rows': { |
1450 |
'from': index, |
|
1451 |
'to': index + len(record_span) - 1 |
|
1452 |
}}
|
|
1453 |
index += len(record_span) |
|
4408.2.1
by Xavier Morel
[ADD] big bit on new import: pretty much everything but o2m |
1454 |
def _convert_records(self, cr, uid, records, |
1455 |
context=None, log=lambda a: None): |
|
1456 |
""" Converts records from the source iterable (recursive dicts of
|
|
1457 |
strings) into forms which can be written to the database (via
|
|
1458 |
self.create or (ir.model.data)._update)
|
|
1459 |
||
1460 |
:returns: a list of triplets of (id, xid, record)
|
|
1461 |
:rtype: list((int|None, str|None, dict))
|
|
1462 |
"""
|
|
4408.2.8
by Xavier Morel
[IMP] return fields_get-style translated field strings (if available) in user-readable warning and error messages from import, rather than logical field names |
1463 |
if context is None: context = {} |
4408.2.1
by Xavier Morel
[ADD] big bit on new import: pretty much everything but o2m |
1464 |
Converter = self.pool['ir.fields.converter'] |
1465 |
columns = dict((k, v.column) for k, v in self._all_columns.iteritems()) |
|
4408.2.8
by Xavier Morel
[IMP] return fields_get-style translated field strings (if available) in user-readable warning and error messages from import, rather than logical field names |
1466 |
Translation = self.pool['ir.translation'] |
1467 |
field_names = dict( |
|
1468 |
(f, (Translation._get_source(cr, uid, self._name + ',' + f, 'field', |
|
4408.2.39
by Xavier Morel
[IMP] don't pass in a default lang to _get_source if no value is provided in the context, caught by odo |
1469 |
context.get('lang')) |
4536
by Xavier Morel
[FIX] recursive conversion of o2ms in import |
1470 |
or column.string)) |
4408.2.8
by Xavier Morel
[IMP] return fields_get-style translated field strings (if available) in user-readable warning and error messages from import, rather than logical field names |
1471 |
for f, column in columns.iteritems()) |
4536
by Xavier Morel
[FIX] recursive conversion of o2ms in import |
1472 |
|
1473 |
convert = Converter.for_model(cr, uid, self, context=context) |
|
4408.2.1
by Xavier Morel
[ADD] big bit on new import: pretty much everything but o2m |
1474 |
|
4408.2.10
by Xavier Morel
[IMP] allow converters to add data to import messages, formalize message keys |
1475 |
def _log(base, field, exception): |
1476 |
type = 'warning' if isinstance(exception, Warning) else 'error' |
|
4536
by Xavier Morel
[FIX] recursive conversion of o2ms in import |
1477 |
# logs the logical (not human-readable) field name for automated
|
1478 |
# processing of response, but injects human readable in message
|
|
1479 |
record = dict(base, type=type, field=field, |
|
4408.2.10
by Xavier Morel
[IMP] allow converters to add data to import messages, formalize message keys |
1480 |
message=unicode(exception.args[0]) % base) |
1481 |
if len(exception.args) > 1 and exception.args[1]: |
|
1482 |
record.update(exception.args[1]) |
|
1483 |
log(record) |
|
1484 |
||
4408.2.1
by Xavier Morel
[ADD] big bit on new import: pretty much everything but o2m |
1485 |
stream = CountingStream(records) |
1486 |
for record, extras in stream: |
|
1487 |
dbid = False |
|
1488 |
xid = False |
|
1489 |
# name_get/name_create
|
|
1490 |
if None in record: pass |
|
1491 |
# xid
|
|
1492 |
if 'id' in record: |
|
1493 |
xid = record['id'] |
|
1494 |
# dbid
|
|
1495 |
if '.id' in record: |
|
1496 |
try: |
|
1497 |
dbid = int(record['.id']) |
|
1498 |
except ValueError: |
|
1499 |
# in case of overridden id column
|
|
1500 |
dbid = record['.id'] |
|
1501 |
if not self.search(cr, uid, [('id', '=', dbid)], context=context): |
|
1502 |
log(dict(extras, |
|
1503 |
type='error', |
|
1504 |
record=stream.index, |
|
1505 |
field='.id', |
|
1506 |
message=_(u"Unknown database identifier '%s'") % dbid)) |
|
1507 |
dbid = False |
|
1508 |
||
4536
by Xavier Morel
[FIX] recursive conversion of o2ms in import |
1509 |
converted = convert(record, lambda field, err:\ |
1510 |
_log(dict(extras, record=stream.index, field=field_names[field]), field, err)) |
|
4408.2.1
by Xavier Morel
[ADD] big bit on new import: pretty much everything but o2m |
1511 |
|
1512 |
yield dbid, xid, converted, dict(extras, record=stream.index) |
|
1513 |
||
2537.1.2
by Numerigraphe - Lionel Sausin
[FIX] mutable default in osv |
1514 |
def get_invalid_fields(self, cr, uid): |
1294
by Christophe Simonis
[IMP] better get_invalid_fields function |
1515 |
return list(self._invalids) |
1292
by Jay Vora
Colored Fields red ,those fail in constraint validation. |
1516 |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
1517 |
def _validate(self, cr, uid, ids, context=None): |
1518 |
context = context or {} |
|
4425.1.1
by Olivier Dony
[IMP] translations: attempt to optimize out _get_source calls when there is not context lang |
1519 |
lng = context.get('lang') |
922
by Christophe Simonis
convert tabs to 4 spaces |
1520 |
trans = self.pool.get('ir.translation') |
1028
by Christophe Simonis
better error message when _constraints fail |
1521 |
error_msgs = [] |
922
by Christophe Simonis
convert tabs to 4 spaces |
1522 |
for constraint in self._constraints: |
1523 |
fun, msg, fields = constraint |
|
4635
by Vo Minh Thu
[ADD] added comment in _validate() explaining why the context is not passed around when calling _constraints functions. |
1524 |
# We don't pass around the context here: validation code
|
1525 |
# must always yield the same results.
|
|
922
by Christophe Simonis
convert tabs to 4 spaces |
1526 |
if not fun(self, cr, uid, ids): |
2262
by Quentin De Paoli
[IMP] improved constraints: can now pass a callable message function instead of hardcoded message |
1527 |
# Check presence of __call__ directly instead of using
|
1528 |
# callable() because it will be deprecated as of Python 3.0
|
|
2245.1.1
by Quentin De Paoli
[IMP] improved the constraint validation in order to accept multiple message |
1529 |
if hasattr(msg, '__call__'): |
3017.1.12
by P. Christeas
ORM, ir_*: convert standard constraint messages to callables |
1530 |
tmp_msg = msg(self, cr, uid, ids, context=context) |
1531 |
if isinstance(tmp_msg, tuple): |
|
1532 |
tmp_msg, params = tmp_msg |
|
1533 |
translated_msg = tmp_msg % params |
|
1534 |
else: |
|
1535 |
translated_msg = tmp_msg |
|
2262
by Quentin De Paoli
[IMP] improved constraints: can now pass a callable message function instead of hardcoded message |
1536 |
else: |
4425.1.1
by Olivier Dony
[IMP] translations: attempt to optimize out _get_source calls when there is not context lang |
1537 |
translated_msg = trans._get_source(cr, uid, self._name, 'constraint', lng, msg) |
1028
by Christophe Simonis
better error message when _constraints fail |
1538 |
error_msgs.append( |
1657.1.4
by Jay (Open ERP)
Report_sxw modified for browse_record values in default formats |
1539 |
_("Error occurred while validating the field(s) %s: %s") % (','.join(fields), translated_msg) |
1045.1.1
by Jay Vora
Bugfix: _inherits |
1540 |
)
|
1294
by Christophe Simonis
[IMP] better get_invalid_fields function |
1541 |
self._invalids.update(fields) |
1028
by Christophe Simonis
better error message when _constraints fail |
1542 |
if error_msgs: |
1543 |
raise except_orm('ValidateError', '\n'.join(error_msgs)) |
|
1293
by Jay Vora
Colored Fields red ,those fail in constraint validation.Modification for Updation of records |
1544 |
else: |
1294
by Christophe Simonis
[IMP] better get_invalid_fields function |
1545 |
self._invalids.clear() |
922
by Christophe Simonis
convert tabs to 4 spaces |
1546 |
|
1547 |
def default_get(self, cr, uid, fields_list, context=None): |
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
1548 |
"""
|
2615
by Olivier Dony
[REF] orm.default_get: merge duplicated default_get() up into orm_template. |
1549 |
Returns default values for the fields in fields_list.
|
2624
by Olivier Dony
[FIX] default_get: fixed RST indentation in docstring |
1550 |
|
2615
by Olivier Dony
[REF] orm.default_get: merge duplicated default_get() up into orm_template. |
1551 |
:param fields_list: list of fields to get the default values for (example ['field1', 'field2',])
|
1552 |
:type fields_list: list
|
|
2976
by Olivier Dony
[ADD] fields.binary: support 'bin_size_XXX' context flags to selectively enable returning size/contents of a binary field - pending API change after 6.0 |
1553 |
:param context: optional context dictionary - it may contains keys for specifying certain options
|
1554 |
like ``context_lang`` (language) or ``context_tz`` (timezone) to alter the results of the call.
|
|
1555 |
It may contain keys in the form ``default_XXX`` (where XXX is a field name), to set
|
|
1556 |
or override a default value for a field.
|
|
1557 |
A special ``bin_size`` boolean flag may also be passed in the context to request the
|
|
1558 |
value of all fields.binary columns to be returned as the size of the binary instead of its
|
|
1559 |
contents. This can also be selectively overriden by passing a field-specific flag
|
|
1560 |
in the form ``bin_size_XXX: True/False`` where ``XXX`` is the name of the field.
|
|
1561 |
Note: The ``bin_size_XXX`` form is new in OpenERP v6.0.
|
|
2615
by Olivier Dony
[REF] orm.default_get: merge duplicated default_get() up into orm_template. |
1562 |
:return: dictionary of the default values (set on the object model class, through user preferences, or in the context)
|
2017.1.3
by Xavier Morel
[imp] add docstring to explain how default_get works/what it does |
1563 |
"""
|
2615
by Olivier Dony
[REF] orm.default_get: merge duplicated default_get() up into orm_template. |
1564 |
# trigger view init hook
|
1565 |
self.view_init(cr, uid, fields_list, context) |
|
1566 |
||
1567 |
if not context: |
|
1568 |
context = {} |
|
1569 |
defaults = {} |
|
1570 |
||
2616
by Olivier Dony
[FIX] orm.default_get: fix invalid renaming by broken PyDev refactor |
1571 |
# get the default values for the inherited fields
|
2615
by Olivier Dony
[REF] orm.default_get: merge duplicated default_get() up into orm_template. |
1572 |
for t in self._inherits.keys(): |
1573 |
defaults.update(self.pool.get(t).default_get(cr, uid, fields_list, |
|
1574 |
context)) |
|
1575 |
||
2616
by Olivier Dony
[FIX] orm.default_get: fix invalid renaming by broken PyDev refactor |
1576 |
# get the default values defined in the object
|
2615
by Olivier Dony
[REF] orm.default_get: merge duplicated default_get() up into orm_template. |
1577 |
for f in fields_list: |
1578 |
if f in self._defaults: |
|
1579 |
if callable(self._defaults[f]): |
|
1580 |
defaults[f] = self._defaults[f](self, cr, uid, context) |
|
1581 |
else: |
|
1582 |
defaults[f] = self._defaults[f] |
|
1583 |
||
1584 |
fld_def = ((f in self._columns) and self._columns[f]) \ |
|
1585 |
or ((f in self._inherit_fields) and self._inherit_fields[f][2]) \ |
|
1586 |
or False |
|
1587 |
||
1588 |
if isinstance(fld_def, fields.property): |
|
1589 |
property_obj = self.pool.get('ir.property') |
|
1590 |
prop_value = property_obj.get(cr, uid, f, self._name, context=context) |
|
1591 |
if prop_value: |
|
1592 |
if isinstance(prop_value, (browse_record, browse_null)): |
|
1593 |
defaults[f] = prop_value.id |
|
1594 |
else: |
|
1595 |
defaults[f] = prop_value |
|
1596 |
else: |
|
1597 |
if f not in defaults: |
|
1598 |
defaults[f] = False |
|
1599 |
||
2616
by Olivier Dony
[FIX] orm.default_get: fix invalid renaming by broken PyDev refactor |
1600 |
# get the default values set by the user and override the default
|
1601 |
# values defined in the object
|
|
1602 |
ir_values_obj = self.pool.get('ir.values') |
|
2615
by Olivier Dony
[REF] orm.default_get: merge duplicated default_get() up into orm_template. |
1603 |
res = ir_values_obj.get(cr, uid, 'default', False, [self._name]) |
1604 |
for id, field, field_value in res: |
|
1605 |
if field in fields_list: |
|
1606 |
fld_def = (field in self._columns) and self._columns[field] or self._inherit_fields[field][2] |
|
4102.1.3
by Vo Minh Thu
[IMP+FIX] fields: removed references to one2one, but also |
1607 |
if fld_def._type == 'many2one': |
2615
by Olivier Dony
[REF] orm.default_get: merge duplicated default_get() up into orm_template. |
1608 |
obj = self.pool.get(fld_def._obj) |
1609 |
if not obj.search(cr, uid, [('id', '=', field_value or False)]): |
|
1610 |
continue
|
|
4102.1.3
by Vo Minh Thu
[IMP+FIX] fields: removed references to one2one, but also |
1611 |
if fld_def._type == 'many2many': |
2615
by Olivier Dony
[REF] orm.default_get: merge duplicated default_get() up into orm_template. |
1612 |
obj = self.pool.get(fld_def._obj) |
1613 |
field_value2 = [] |
|
4576
by Olivier Dony
[IMP] Model.default_get: allows setting user default values of x2m to False (for consistency with write()) |
1614 |
for i in range(len(field_value or [])): |
2615
by Olivier Dony
[REF] orm.default_get: merge duplicated default_get() up into orm_template. |
1615 |
if not obj.search(cr, uid, [('id', '=', |
1616 |
field_value[i])]): |
|
1617 |
continue
|
|
1618 |
field_value2.append(field_value[i]) |
|
1619 |
field_value = field_value2 |
|
4102.1.3
by Vo Minh Thu
[IMP+FIX] fields: removed references to one2one, but also |
1620 |
if fld_def._type == 'one2many': |
2615
by Olivier Dony
[REF] orm.default_get: merge duplicated default_get() up into orm_template. |
1621 |
obj = self.pool.get(fld_def._obj) |
1622 |
field_value2 = [] |
|
4576
by Olivier Dony
[IMP] Model.default_get: allows setting user default values of x2m to False (for consistency with write()) |
1623 |
for i in range(len(field_value or [])): |
2615
by Olivier Dony
[REF] orm.default_get: merge duplicated default_get() up into orm_template. |
1624 |
field_value2.append({}) |
1625 |
for field2 in field_value[i]: |
|
4102.1.3
by Vo Minh Thu
[IMP+FIX] fields: removed references to one2one, but also |
1626 |
if field2 in obj._columns.keys() and obj._columns[field2]._type == 'many2one': |
2615
by Olivier Dony
[REF] orm.default_get: merge duplicated default_get() up into orm_template. |
1627 |
obj2 = self.pool.get(obj._columns[field2]._obj) |
1628 |
if not obj2.search(cr, uid, |
|
1629 |
[('id', '=', field_value[i][field2])]): |
|
1630 |
continue
|
|
4102.1.3
by Vo Minh Thu
[IMP+FIX] fields: removed references to one2one, but also |
1631 |
elif field2 in obj._inherit_fields.keys() and obj._inherit_fields[field2][2]._type == 'many2one': |
2615
by Olivier Dony
[REF] orm.default_get: merge duplicated default_get() up into orm_template. |
1632 |
obj2 = self.pool.get(obj._inherit_fields[field2][2]._obj) |
1633 |
if not obj2.search(cr, uid, |
|
1634 |
[('id', '=', field_value[i][field2])]): |
|
1635 |
continue
|
|
1636 |
# TODO add test for many2many and one2many
|
|
1637 |
field_value2[i][field2] = field_value[i][field2] |
|
1638 |
field_value = field_value2 |
|
1639 |
defaults[field] = field_value |
|
1640 |
||
2616
by Olivier Dony
[FIX] orm.default_get: fix invalid renaming by broken PyDev refactor |
1641 |
# get the default values from the context
|
2615
by Olivier Dony
[REF] orm.default_get: merge duplicated default_get() up into orm_template. |
1642 |
for key in context or {}: |
1643 |
if key.startswith('default_') and (key[8:] in fields_list): |
|
1644 |
defaults[key[8:]] = context[key] |
|
1645 |
return defaults |
|
1646 |
||
2498
by Olivier Dony
[IMP] orm.fields_get: renamed read_access parameter to write_access, reflecting its real usage. Also cleaned up some mess leftover by commit 1338 in orm.field_get_keys() + other cleanup |
1647 |
def fields_get_keys(self, cr, user, context=None): |
1648 |
res = self._columns.keys() |
|
3453.2.4
by Vo Minh Thu
[REF] orm: added some comments. |
1649 |
# TODO I believe this loop can be replace by
|
1650 |
# res.extend(self._inherit_fields.key())
|
|
2498
by Olivier Dony
[IMP] orm.fields_get: renamed read_access parameter to write_access, reflecting its real usage. Also cleaned up some mess leftover by commit 1338 in orm.field_get_keys() + other cleanup |
1651 |
for parent in self._inherits: |
1652 |
res.extend(self.pool.get(parent).fields_get_keys(cr, user, context)) |
|
1653 |
return res |
|
1654 |
||
4272.1.1
by Antony Lesuisse
[IMP] rec_name assertion and fallback |
1655 |
def _rec_name_fallback(self, cr, uid, context=None): |
1656 |
rec_name = self._rec_name |
|
1657 |
if rec_name not in self._columns: |
|
1658 |
rec_name = self._columns.keys()[0] if len(self._columns.keys()) > 0 else "id" |
|
1659 |
return rec_name |
|
1660 |
||
922
by Christophe Simonis
convert tabs to 4 spaces |
1661 |
#
|
1662 |
# Overload this method if you need a window title which depends on the context
|
|
1663 |
#
|
|
1664 |
def view_header_get(self, cr, user, view_id=None, view_type='form', context=None): |
|
1665 |
return False |
|
1666 |
||
4158.1.1
by Olivier Dony
[IMP] orm: support model-level @groups attribute for access restriction |
1667 |
def user_has_groups(self, cr, uid, groups, context=None): |
1668 |
"""Return true if the user is at least member of one of the groups
|
|
1669 |
in groups_str. Typically used to resolve ``groups`` attribute
|
|
4466.1.2
by Christophe Simonis
[IMP] orm: remove trailing spaces |
1670 |
in view and model definitions.
|
4158.1.1
by Olivier Dony
[IMP] orm: support model-level @groups attribute for access restriction |
1671 |
|
1672 |
:param str groups: comma-separated list of fully-qualified group
|
|
1673 |
external IDs, e.g.: ``base.group_user,base.group_system``
|
|
1674 |
:return: True if the current user is a member of one of the
|
|
1675 |
given groups
|
|
1676 |
"""
|
|
1677 |
return any([self.pool.get('res.users').has_group(cr, uid, group_ext_id) |
|
1678 |
for group_ext_id in groups.split(',')]) |
|
1679 |
||
3497
by Vo Minh Thu
[REVERT] re-apply the 4 commits that were reverted just after, 4 commits ago. |
1680 |
def __view_look_dom(self, cr, user, node, view_id, in_tree_view, model_fields, context=None): |
4158.1.1
by Olivier Dony
[IMP] orm: support model-level @groups attribute for access restriction |
1681 |
"""Return the description of the fields in the node.
|
3497
by Vo Minh Thu
[REVERT] re-apply the 4 commits that were reverted just after, 4 commits ago. |
1682 |
|
1683 |
In a normal call to this method, node is a complete view architecture
|
|
1684 |
but it is actually possible to give some sub-node (this is used so
|
|
1685 |
that the method can call itself recursively).
|
|
1686 |
||
1687 |
Originally, the field descriptions are drawn from the node itself.
|
|
1688 |
But there is now some code calling fields_get() in order to merge some
|
|
1689 |
of those information in the architecture.
|
|
1690 |
||
1691 |
"""
|
|
3466.1.2
by Vo Minh Thu
[REF] fields_viw_get: trying to get something clearer... |
1692 |
if context is None: |
957
by Olivier Laurent
pep8 |
1693 |
context = {} |
922
by Christophe Simonis
convert tabs to 4 spaces |
1694 |
result = False |
1695 |
fields = {} |
|
2148.1.2
by Numerigraphe - Lionel Sausin
[REF] 'children' instead of 'childs' |
1696 |
children = True |
922
by Christophe Simonis
convert tabs to 4 spaces |
1697 |
|
3497
by Vo Minh Thu
[REVERT] re-apply the 4 commits that were reverted just after, 4 commits ago. |
1698 |
modifiers = {} |
1699 |
||
2017.2.52
by Ysa(Open ERP)
[IMP] diagram :- improve parser code(parse node and arrow field). |
1700 |
def encode(s): |
1701 |
if isinstance(s, unicode): |
|
1702 |
return s.encode('utf8') |
|
1703 |
return s |
|
2111.9.1
by Olivier Dony
[FIX] reviewed and improved some naming and formatting, plus removed attrs from field if groups disallows showing it |
1704 |
|
2111.5.20
by HDA(OpenERP)
bug with field with widget=selection + groups if user has no access right |
1705 |
def check_group(node): |
4158.1.1
by Olivier Dony
[IMP] orm: support model-level @groups attribute for access restriction |
1706 |
"""Apply group restrictions, may be set at view level or model level::
|
1707 |
* at view level this means the element should be made invisible to
|
|
1708 |
people who are not members
|
|
1709 |
* at model level (exclusively for fields, obviously), this means
|
|
1710 |
the field should be completely removed from the view, as it is
|
|
1711 |
completely unavailable for non-members
|
|
1712 |
||
4466.1.2
by Christophe Simonis
[IMP] orm: remove trailing spaces |
1713 |
:return: True if field should be included in the result of fields_view_get
|
4158.1.1
by Olivier Dony
[IMP] orm: support model-level @groups attribute for access restriction |
1714 |
"""
|
1715 |
if node.tag == 'field' and node.get('name') in self._all_columns: |
|
1716 |
column = self._all_columns[node.get('name')].column |
|
1717 |
if column.groups and not self.user_has_groups(cr, user, |
|
1718 |
groups=column.groups, |
|
1719 |
context=context): |
|
1720 |
node.getparent().remove(node) |
|
1721 |
fields.pop(node.get('name'), None) |
|
1722 |
# no point processing view-level ``groups`` anymore, return
|
|
1723 |
return False |
|
2111.5.20
by HDA(OpenERP)
bug with field with widget=selection + groups if user has no access right |
1724 |
if node.get('groups'): |
4158.1.1
by Olivier Dony
[IMP] orm: support model-level @groups attribute for access restriction |
1725 |
can_see = self.user_has_groups(cr, user, |
1726 |
groups=node.get('groups'), |
|
1727 |
context=context) |
|
2111.9.1
by Olivier Dony
[FIX] reviewed and improved some naming and formatting, plus removed attrs from field if groups disallows showing it |
1728 |
if not can_see: |
2111.5.20
by HDA(OpenERP)
bug with field with widget=selection + groups if user has no access right |
1729 |
node.set('invisible', '1') |
3497
by Vo Minh Thu
[REVERT] re-apply the 4 commits that were reverted just after, 4 commits ago. |
1730 |
modifiers['invisible'] = True |
2111.9.1
by Olivier Dony
[FIX] reviewed and improved some naming and formatting, plus removed attrs from field if groups disallows showing it |
1731 |
if 'attrs' in node.attrib: |
1732 |
del(node.attrib['attrs']) #avoid making field visible later |
|
2111.5.28
by Olivier Dony
[FIX] orm/selection widget: fixed leftover typos |
1733 |
del(node.attrib['groups']) |
4158.1.1
by Olivier Dony
[IMP] orm: support model-level @groups attribute for access restriction |
1734 |
return True |
2111.9.1
by Olivier Dony
[FIX] reviewed and improved some naming and formatting, plus removed attrs from field if groups disallows showing it |
1735 |
|
2017.2.46
by Ysa(Open ERP)
[IMP] diagram :- parse node and arrow tag field. |
1736 |
if node.tag in ('field', 'node', 'arrow'): |
1737 |
if node.get('object'): |
|
1738 |
attrs = {} |
|
1739 |
views = {} |
|
2017.2.52
by Ysa(Open ERP)
[IMP] diagram :- improve parser code(parse node and arrow field). |
1740 |
xml = "<form>" |
2017.2.46
by Ysa(Open ERP)
[IMP] diagram :- parse node and arrow tag field. |
1741 |
for f in node: |
4682.2.23
by Raphael Collet
[FIX] orm: replace incorrect usage of 'in' by '==' |
1742 |
if f.tag == 'field': |
2017.2.52
by Ysa(Open ERP)
[IMP] diagram :- improve parser code(parse node and arrow field). |
1743 |
xml += etree.tostring(f, encoding="utf-8") |
1744 |
xml += "</form>" |
|
1745 |
new_xml = etree.fromstring(encode(xml)) |
|
1746 |
ctx = context.copy() |
|
1747 |
ctx['base_model_name'] = self._name |
|
3055
by Olivier Dony
[MERGE+FIX] fixes for translations,sequence,copy,error messages - some patches courtesy of Margarita Manterola and Don Kirkby |
1748 |
xarch, xfields = self.pool.get(node.get('object')).__view_look_dom_arch(cr, user, new_xml, view_id, ctx) |
1749 |
views['form'] = { |
|
2017.2.52
by Ysa(Open ERP)
[IMP] diagram :- improve parser code(parse node and arrow field). |
1750 |
'arch': xarch, |
1751 |
'fields': xfields |
|
1752 |
}
|
|
1753 |
attrs = {'views': views} |
|
3055
by Olivier Dony
[MERGE+FIX] fixes for translations,sequence,copy,error messages - some patches courtesy of Margarita Manterola and Don Kirkby |
1754 |
fields = xfields |
1845
by HDA(OpenERP)
[Merged] |
1755 |
if node.get('name'): |
922
by Christophe Simonis
convert tabs to 4 spaces |
1756 |
attrs = {} |
1757 |
try: |
|
1845
by HDA(OpenERP)
[Merged] |
1758 |
if node.get('name') in self._columns: |
1759 |
column = self._columns[node.get('name')] |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
1760 |
else: |
1845
by HDA(OpenERP)
[Merged] |
1761 |
column = self._inherit_fields[node.get('name')][2] |
3055
by Olivier Dony
[MERGE+FIX] fixes for translations,sequence,copy,error messages - some patches courtesy of Margarita Manterola and Don Kirkby |
1762 |
except Exception: |
1766.2.5
by Christophe Simonis
[FIX] allow to use server-side domains with widget='selection' fields |
1763 |
column = False |
922
by Christophe Simonis
convert tabs to 4 spaces |
1764 |
|
1766.2.5
by Christophe Simonis
[FIX] allow to use server-side domains with widget='selection' fields |
1765 |
if column: |
2240
by Christophe Simonis
[FIX]orm: invisible fields with widget=selection must have at least the default value into the selection |
1766 |
relation = self.pool.get(column._obj) |
1767 |
||
2148.1.2
by Numerigraphe - Lionel Sausin
[REF] 'children' instead of 'childs' |
1768 |
children = False |
922
by Christophe Simonis
convert tabs to 4 spaces |
1769 |
views = {} |
1845
by HDA(OpenERP)
[Merged] |
1770 |
for f in node: |
4110.1.9
by Antony Lesuisse
embbded kanban view |
1771 |
if f.tag in ('form', 'tree', 'graph', 'kanban'): |
1845
by HDA(OpenERP)
[Merged] |
1772 |
node.remove(f) |
1321
by Christophe Simonis
[FIX] translation of some labels in one2many fields. Thanks to Dainius Malachovskis for the base patch. |
1773 |
ctx = context.copy() |
1774 |
ctx['base_model_name'] = self._name |
|
2240
by Christophe Simonis
[FIX]orm: invisible fields with widget=selection must have at least the default value into the selection |
1775 |
xarch, xfields = relation.__view_look_dom_arch(cr, user, f, view_id, ctx) |
1845
by HDA(OpenERP)
[Merged] |
1776 |
views[str(f.tag)] = { |
922
by Christophe Simonis
convert tabs to 4 spaces |
1777 |
'arch': xarch, |
1778 |
'fields': xfields |
|
1779 |
}
|
|
1780 |
attrs = {'views': views} |
|
1845
by HDA(OpenERP)
[Merged] |
1781 |
if node.get('widget') and node.get('widget') == 'selection': |
2670.1.4
by Olivier Dony
[IMP] orm.fields_view_get: return cached selection values even when field is not invisible |
1782 |
# Prepare the cached selection list for the client. This needs to be
|
1783 |
# done even when the field is invisible to the current user, because
|
|
1784 |
# other events could need to change its value to any of the selectable ones
|
|
1785 |
# (such as on_change events, refreshes, etc.)
|
|
1786 |
||
1787 |
# If domain and context are strings, we keep them for client-side, otherwise
|
|
1788 |
# we evaluate them server-side to consider them when generating the list of
|
|
1789 |
# possible values
|
|
1790 |
# TODO: find a way to remove this hack, by allow dynamic domains
|
|
1791 |
dom = [] |
|
1792 |
if column._domain and not isinstance(column._domain, basestring): |
|
4573.2.1
by Stefan Rijnhart
[FIX] Append view domain (in case of selection widget) to a copy of the field's domain |
1793 |
dom = list(column._domain) |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
1794 |
dom += eval(node.get('domain', '[]'), {'uid': user, 'time': time}) |
2670.1.4
by Olivier Dony
[IMP] orm.fields_view_get: return cached selection values even when field is not invisible |
1795 |
search_context = dict(context) |
1796 |
if column._context and not isinstance(column._context, basestring): |
|
1797 |
search_context.update(column._context) |
|
2675
by Olivier Dony
[FIX] orm: fields_view_get selection values must properly heed ir.rules: |
1798 |
attrs['selection'] = relation._name_search(cr, user, '', dom, context=search_context, limit=None, name_get_uid=1) |
2670.1.4
by Olivier Dony
[IMP] orm.fields_view_get: return cached selection values even when field is not invisible |
1799 |
if (node.get('required') and not int(node.get('required'))) or not column.required: |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
1800 |
attrs['selection'].append((False, '')) |
1845
by HDA(OpenERP)
[Merged] |
1801 |
fields[node.get('name')] = attrs |
922
by Christophe Simonis
convert tabs to 4 spaces |
1802 |
|
3523.1.1
by gpa
[FIXED] Fixed the problem keyerror id |
1803 |
field = model_fields.get(node.get('name')) |
1804 |
if field: |
|
1805 |
transfer_field_to_modifiers(field, modifiers) |
|
4484
by niv-openerp
[REVERT] removed server-side evaluation of field options |
1806 |
|
3497
by Vo Minh Thu
[REVERT] re-apply the 4 commits that were reverted just after, 4 commits ago. |
1807 |
|
1845
by HDA(OpenERP)
[Merged] |
1808 |
elif node.tag in ('form', 'tree'): |
1809 |
result = self.view_header_get(cr, user, False, node.tag, context) |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
1810 |
if result: |
1845
by HDA(OpenERP)
[Merged] |
1811 |
node.set('string', result) |
3497
by Vo Minh Thu
[REVERT] re-apply the 4 commits that were reverted just after, 4 commits ago. |
1812 |
in_tree_view = node.tag == 'tree' |
1236
by Christophe Simonis
fields_view_get return also the fields specified in the attributes of the <calendar> tag |
1813 |
|
1845
by HDA(OpenERP)
[Merged] |
1814 |
elif node.tag == 'calendar': |
1236
by Christophe Simonis
fields_view_get return also the fields specified in the attributes of the <calendar> tag |
1815 |
for additional_field in ('date_start', 'date_delay', 'date_stop', 'color'): |
1845
by HDA(OpenERP)
[Merged] |
1816 |
if node.get(additional_field): |
1817 |
fields[node.get(additional_field)] = {} |
|
1236
by Christophe Simonis
fields_view_get return also the fields specified in the attributes of the <calendar> tag |
1818 |
|
4158.1.1
by Olivier Dony
[IMP] orm: support model-level @groups attribute for access restriction |
1819 |
if not check_group(node): |
1820 |
# node must be removed, no need to proceed further with its children
|
|
1821 |
return fields |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
1822 |
|
3497
by Vo Minh Thu
[REVERT] re-apply the 4 commits that were reverted just after, 4 commits ago. |
1823 |
# The view architeture overrides the python model.
|
1824 |
# Get the attrs before they are (possibly) deleted by check_group below
|
|
1825 |
transfer_node_to_modifiers(node, modifiers, context, in_tree_view) |
|
1826 |
||
1827 |
# TODO remove attrs couterpart in modifiers when invisible is true ?
|
|
1828 |
||
1845
by HDA(OpenERP)
[Merged] |
1829 |
# translate view
|
3466.1.5
by Vo Minh Thu
[FIX] orm/fields_view_get: translate correctly confirm/sum/help: |
1830 |
if 'lang' in context: |
4216
by Fabien Pinckaers
[IMP] Translations of static terms in views |
1831 |
if node.text and node.text.strip(): |
1832 |
trans = self.pool.get('ir.translation')._get_source(cr, user, self._name, 'view', context['lang'], node.text.strip()) |
|
1833 |
if trans: |
|
4223
by Fabien Pinckaers
[FIX] whitespace lost in translation |
1834 |
node.text = node.text.replace(node.text.strip(), trans) |
4216
by Fabien Pinckaers
[IMP] Translations of static terms in views |
1835 |
if node.tail and node.tail.strip(): |
1836 |
trans = self.pool.get('ir.translation')._get_source(cr, user, self._name, 'view', context['lang'], node.tail.strip()) |
|
1837 |
if trans: |
|
4223
by Fabien Pinckaers
[FIX] whitespace lost in translation |
1838 |
node.tail = node.tail.replace(node.tail.strip(), trans) |
4216
by Fabien Pinckaers
[IMP] Translations of static terms in views |
1839 |
|
3466.1.5
by Vo Minh Thu
[FIX] orm/fields_view_get: translate correctly confirm/sum/help: |
1840 |
if node.get('string') and not result: |
2713
by Raphael Valyi
[FIX] orm: properly handle unicode values returned by ir.translation |
1841 |
trans = self.pool.get('ir.translation')._get_source(cr, user, self._name, 'view', context['lang'], node.get('string')) |
3110.1.1
by OpenERP
[FIX] orm: fixed translations for inherited views when the parent view uses a different model. |
1842 |
if trans == node.get('string') and ('base_model_name' in context): |
3118
by Vo Minh Thu (OpenERP)
[MERGE] orm: fixed translations for inherited views when the parent view uses a different model |
1843 |
# If translation is same as source, perhaps we'd have more luck with the alternative model name
|
1844 |
# (in case we are in a mixed situation, such as an inherited view where parent_view.model != model
|
|
2713
by Raphael Valyi
[FIX] orm: properly handle unicode values returned by ir.translation |
1845 |
trans = self.pool.get('ir.translation')._get_source(cr, user, context['base_model_name'], 'view', context['lang'], node.get('string')) |
1845
by HDA(OpenERP)
[Merged] |
1846 |
if trans: |
1847 |
node.set('string', trans) |
|
4219
by Xavier Morel
[IMP] massage all translatable attributes in a loop instead of copy/pasting a billion versions |
1848 |
|
4594.1.4
by Olivier Dony
[MERGE] Forward-port 6.1 bugfixes up to rev. 4307 |
1849 |
for attr_name in ('confirm', 'sum', 'avg', 'help', 'placeholder'): |
4222
by Raphael Collet
[IMP] orm translations: improve variable names |
1850 |
attr_value = node.get(attr_name) |
1851 |
if attr_value: |
|
1852 |
trans = self.pool.get('ir.translation')._get_source(cr, user, self._name, 'view', context['lang'], attr_value) |
|
1853 |
if trans: |
|
1854 |
node.set(attr_name, trans) |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
1855 |
|
2823
by nch at tinyerp
[FIX]:search_view 'groups=' attribute in the child of a field tag was never evaluted: |
1856 |
for f in node: |
2148.1.3
by Numerigraphe - Lionel Sausin
[MERGE] merge from 6.0.0-RC1 |
1857 |
if children or (node.tag == 'field' and f.tag in ('filter','separator')): |
3497
by Vo Minh Thu
[REVERT] re-apply the 4 commits that were reverted just after, 4 commits ago. |
1858 |
fields.update(self.__view_look_dom(cr, user, f, view_id, in_tree_view, model_fields, context)) |
1341.1.2
by Christophe Simonis
[IMP] views don't need to include the state field anymore to be state aware |
1859 |
|
3497
by Vo Minh Thu
[REVERT] re-apply the 4 commits that were reverted just after, 4 commits ago. |
1860 |
transfer_modifiers_to_node(modifiers, node) |
922
by Christophe Simonis
convert tabs to 4 spaces |
1861 |
return fields |
1862 |
||
2870
by Olivier Dony
[REM] base, workflow: removed res.roles object, replaced by dedicated res.groups |
1863 |
def _disable_workflow_buttons(self, cr, user, node): |
3466.1.2
by Vo Minh Thu
[REF] fields_viw_get: trying to get something clearer... |
1864 |
""" Set the buttons in node to readonly if the user can't activate them. """
|
2870
by Olivier Dony
[REM] base, workflow: removed res.roles object, replaced by dedicated res.groups |
1865 |
if user == 1: |
1866 |
# admin user can always activate workflow buttons
|
|
1867 |
return node |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
1868 |
|
2870
by Olivier Dony
[REM] base, workflow: removed res.roles object, replaced by dedicated res.groups |
1869 |
# TODO handle the case of more than one workflow for a model or multiple
|
1870 |
# transitions with different groups and same signal
|
|
1548
by Christophe Simonis
[IMP] use the pooler directly instead of the 'object_proxy' |
1871 |
usersobj = self.pool.get('res.users') |
1845
by HDA(OpenERP)
[Merged] |
1872 |
buttons = (n for n in node.getiterator('button') if n.get('type') != 'object') |
1608
by Stephane Wirtel
[REF] Use the xpath with condition |
1873 |
for button in buttons: |
2870
by Olivier Dony
[REM] base, workflow: removed res.roles object, replaced by dedicated res.groups |
1874 |
user_groups = usersobj.read(cr, user, [user], ['groups_id'])[0]['groups_id'] |
1875 |
cr.execute("""SELECT DISTINCT t.group_id |
|
1876 |
FROM wkf
|
|
1877 |
INNER JOIN wkf_activity a ON a.wkf_id = wkf.id
|
|
1878 |
INNER JOIN wkf_transition t ON (t.act_to = a.id)
|
|
1879 |
WHERE wkf.osv = %s |
|
1880 |
AND t.signal = %s |
|
2882
by Olivier Dony
[FIX] orm, workflow: do not disable wkf buttons unless explicit group is set on transitions |
1881 |
AND t.group_id is NOT NULL
|
2870
by Olivier Dony
[REM] base, workflow: removed res.roles object, replaced by dedicated res.groups |
1882 |
""", (self._name, button.get('name'))) |
2882
by Olivier Dony
[FIX] orm, workflow: do not disable wkf buttons unless explicit group is set on transitions |
1883 |
group_ids = [x[0] for x in cr.fetchall() if x[0]] |
2870
by Olivier Dony
[REM] base, workflow: removed res.roles object, replaced by dedicated res.groups |
1884 |
can_click = not group_ids or bool(set(user_groups).intersection(group_ids)) |
1857
by HDA(OpenERP)
etree conversion changes |
1885 |
button.set('readonly', str(int(not can_click))) |
2870
by Olivier Dony
[REM] base, workflow: removed res.roles object, replaced by dedicated res.groups |
1886 |
return node |
922
by Christophe Simonis
convert tabs to 4 spaces |
1887 |
|
2870
by Olivier Dony
[REM] base, workflow: removed res.roles object, replaced by dedicated res.groups |
1888 |
def __view_look_dom_arch(self, cr, user, node, view_id, context=None): |
3497
by Vo Minh Thu
[REVERT] re-apply the 4 commits that were reverted just after, 4 commits ago. |
1889 |
""" Return an architecture and a description of all the fields.
|
1890 |
||
1891 |
The field description combines the result of fields_get() and
|
|
1892 |
__view_look_dom().
|
|
1893 |
||
1894 |
:param node: the architecture as as an etree
|
|
1895 |
:return: a tuple (arch, fields) where arch is the given node as a
|
|
1896 |
string and fields is the description of all the fields.
|
|
1897 |
||
1898 |
"""
|
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
1899 |
fields = {} |
1900 |
if node.tag == 'diagram': |
|
1901 |
if node.getchildren()[0].tag == 'node': |
|
4344.1.43
by Olivier Dony
[MERGE] sync with trunk |
1902 |
node_model = self.pool.get(node.getchildren()[0].get('object')) |
1903 |
node_fields = node_model.fields_get(cr, user, None, context) |
|
3466.1.4
by Vo Minh Thu
[REF] orm/fields_view_get: replaced simple loop by dict.update(). |
1904 |
fields.update(node_fields) |
4344.1.43
by Olivier Dony
[MERGE] sync with trunk |
1905 |
if not node.get("create") and not node_model.check_access_rights(cr, user, 'create', raise_exception=False): |
1906 |
node.set("create", 'false') |
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
1907 |
if node.getchildren()[1].tag == 'arrow': |
3497
by Vo Minh Thu
[REVERT] re-apply the 4 commits that were reverted just after, 4 commits ago. |
1908 |
arrow_fields = self.pool.get(node.getchildren()[1].get('object')).fields_get(cr, user, None, context) |
3466.1.4
by Vo Minh Thu
[REF] orm/fields_view_get: replaced simple loop by dict.update(). |
1909 |
fields.update(arrow_fields) |
2017.2.52
by Ysa(Open ERP)
[IMP] diagram :- improve parser code(parse node and arrow field). |
1910 |
else: |
3497
by Vo Minh Thu
[REVERT] re-apply the 4 commits that were reverted just after, 4 commits ago. |
1911 |
fields = self.fields_get(cr, user, None, context) |
1912 |
fields_def = self.__view_look_dom(cr, user, node, view_id, False, fields, context=context) |
|
1913 |
node = self._disable_workflow_buttons(cr, user, node) |
|
4318.2.4
by Jigar Amin - OpenERP
[REF/IMP] refectorec the varibale names and added the gantt view, added create ndoe button diable condition |
1914 |
if node.tag in ('kanban', 'tree', 'form', 'gantt'): |
4344.1.43
by Olivier Dony
[MERGE] sync with trunk |
1915 |
for action, operation in (('create', 'create'), ('delete', 'unlink'), ('edit', 'write')): |
1916 |
if not node.get(action) and not self.check_access_rights(cr, user, operation, raise_exception=False): |
|
4318.2.4
by Jigar Amin - OpenERP
[REF/IMP] refectorec the varibale names and added the gantt view, added create ndoe button diable condition |
1917 |
node.set(action, 'false') |
3497
by Vo Minh Thu
[REVERT] re-apply the 4 commits that were reverted just after, 4 commits ago. |
1918 |
arch = etree.tostring(node, encoding="utf-8").replace('\t', '') |
1919 |
for k in fields.keys(): |
|
1920 |
if k not in fields_def: |
|
1921 |
del fields[k] |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
1922 |
for field in fields_def: |
1760.1.4
by Christophe Simonis
[FIX] allow to use the field 'id' in views (i.e., needed for domain) |
1923 |
if field == 'id': |
2148.1.1
by Numerigraphe - Lionel Sausin
[IMP] wording: "customize" intead of "customise" |
1924 |
# sometime, the view may contain the (invisible) field 'id' needed for a domain (when 2 objects have cross references)
|
1760.1.4
by Christophe Simonis
[FIX] allow to use the field 'id' in views (i.e., needed for domain) |
1925 |
fields['id'] = {'readonly': True, 'type': 'integer', 'string': 'ID'} |
1926 |
elif field in fields: |
|
1119.1.68
by P. Christeas
[Under-the-carpet-FIX] Silently ignore missing fields in views. |
1927 |
fields[field].update(fields_def[field]) |
1341.6.1
by Raphaël Valyi
view debbugging improvement: cf branche definition |
1928 |
else: |
1602
by Christophe Simonis
[MERGE] merge smile branch. |
1929 |
cr.execute('select name, model from ir_ui_view where (id=%s or inherit_id=%s) and arch like %s', (view_id, view_id, '%%%s%%' % field)) |
1930 |
res = cr.fetchall()[:] |
|
1931 |
model = res[0][1] |
|
1932 |
res.insert(0, ("Can't find field '%s' in the following view parts composing the view of object model '%s':" % (field, model), None)) |
|
1933 |
msg = "\n * ".join([r[0] for r in res]) |
|
2148.1.1
by Numerigraphe - Lionel Sausin
[IMP] wording: "customize" intead of "customise" |
1934 |
msg += "\n\nEither you wrongly customized this view, or some modules bringing those views are not compatible with your current data model" |
3976.1.16
by Vo Minh Thu
[IMP] openerp.loglevels: removed calls to openerp.loglevels.Logger(). |
1935 |
_logger.error(msg) |
1602
by Christophe Simonis
[MERGE] merge smile branch. |
1936 |
raise except_orm('View error', msg) |
922
by Christophe Simonis
convert tabs to 4 spaces |
1937 |
return arch, fields |
1938 |
||
3594.1.3
by Xavier Morel
[IMP] regroup all __get_default_$name_view calls into a single parametric getattr |
1939 |
def _get_default_form_view(self, cr, user, context=None): |
3594.1.4
by Xavier Morel
[IMP] etree-ify default view generations |
1940 |
""" Generates a default single-line form view using all fields
|
1941 |
of the current model except the m2m and o2m ones.
|
|
1942 |
||
1943 |
:param cr: database cursor
|
|
1944 |
:param int user: user id
|
|
1945 |
:param dict context: connection context
|
|
1946 |
:returns: a form view as an lxml document
|
|
1947 |
:rtype: etree._Element
|
|
1948 |
"""
|
|
1949 |
view = etree.Element('form', string=self._description) |
|
3594.1.2
by Xavier Morel
[REF] extract the two inlined default view generators left into fields_view_get into their own methods |
1950 |
# TODO it seems fields_get can be replaced by _all_columns (no need for translation)
|
3594.1.4
by Xavier Morel
[IMP] etree-ify default view generations |
1951 |
for field, descriptor in self.fields_get(cr, user, context=context).iteritems(): |
1952 |
if descriptor['type'] in ('one2many', 'many2many'): |
|
1953 |
continue
|
|
1954 |
etree.SubElement(view, 'field', name=field) |
|
1955 |
if descriptor['type'] == 'text': |
|
1956 |
etree.SubElement(view, 'newline') |
|
1957 |
return view |
|
3594.1.2
by Xavier Morel
[REF] extract the two inlined default view generators left into fields_view_get into their own methods |
1958 |
|
4185.1.6
by Fabien Pinckaers
[IMP] removing select=? and adding version=7 |
1959 |
def _get_default_search_view(self, cr, user, context=None): |
4272.1.1
by Antony Lesuisse
[IMP] rec_name assertion and fallback |
1960 |
""" Generates a single-field search view, based on _rec_name.
|
4185.1.6
by Fabien Pinckaers
[IMP] removing select=? and adding version=7 |
1961 |
|
1962 |
:param cr: database cursor
|
|
1963 |
:param int user: user id
|
|
1964 |
:param dict context: connection context
|
|
1965 |
:returns: a tree view as an lxml document
|
|
1966 |
:rtype: etree._Element
|
|
1967 |
"""
|
|
1968 |
view = etree.Element('search', string=self._description) |
|
4272.1.7
by Vo Minh Thu
[MEOW] lolcat crazy ninja fix in the branch, take two! |
1969 |
etree.SubElement(view, 'field', name=self._rec_name_fallback(cr, user, context)) |
4185.1.6
by Fabien Pinckaers
[IMP] removing select=? and adding version=7 |
1970 |
return view |
1971 |
||
3594.1.3
by Xavier Morel
[IMP] regroup all __get_default_$name_view calls into a single parametric getattr |
1972 |
def _get_default_tree_view(self, cr, user, context=None): |
4272.1.1
by Antony Lesuisse
[IMP] rec_name assertion and fallback |
1973 |
""" Generates a single-field tree view, based on _rec_name.
|
3594.1.4
by Xavier Morel
[IMP] etree-ify default view generations |
1974 |
|
1975 |
:param cr: database cursor
|
|
1976 |
:param int user: user id
|
|
1977 |
:param dict context: connection context
|
|
1978 |
:returns: a tree view as an lxml document
|
|
1979 |
:rtype: etree._Element
|
|
1980 |
"""
|
|
1981 |
view = etree.Element('tree', string=self._description) |
|
4272.1.7
by Vo Minh Thu
[MEOW] lolcat crazy ninja fix in the branch, take two! |
1982 |
etree.SubElement(view, 'field', name=self._rec_name_fallback(cr, user, context)) |
3594.1.4
by Xavier Morel
[IMP] etree-ify default view generations |
1983 |
return view |
3594.1.2
by Xavier Morel
[REF] extract the two inlined default view generators left into fields_view_get into their own methods |
1984 |
|
3594.1.3
by Xavier Morel
[IMP] regroup all __get_default_$name_view calls into a single parametric getattr |
1985 |
def _get_default_calendar_view(self, cr, user, context=None): |
3594.1.4
by Xavier Morel
[IMP] etree-ify default view generations |
1986 |
""" Generates a default calendar view by trying to infer
|
1987 |
calendar fields from a number of pre-set attribute names
|
|
4466.1.2
by Christophe Simonis
[IMP] orm: remove trailing spaces |
1988 |
|
3594.1.4
by Xavier Morel
[IMP] etree-ify default view generations |
1989 |
:param cr: database cursor
|
1990 |
:param int user: user id
|
|
1991 |
:param dict context: connection context
|
|
1992 |
:returns: a calendar view
|
|
1993 |
:rtype: etree._Element
|
|
922
by Christophe Simonis
convert tabs to 4 spaces |
1994 |
"""
|
3594.1.7
by Xavier Morel
[ADD] small helper to calendar generation code, in order to make flow clearer |
1995 |
def set_first_of(seq, in_, to): |
3717
by Vo Minh Thu
[MERGE] orm: use directly lxml.etree to generate default views. |
1996 |
"""Sets the first value of ``seq`` also found in ``in_`` to
|
3594.1.7
by Xavier Morel
[ADD] small helper to calendar generation code, in order to make flow clearer |
1997 |
the ``to`` attribute of the view being closed over.
|
1998 |
||
1999 |
Returns whether it's found a suitable value (and set it on
|
|
2000 |
the attribute) or not
|
|
2001 |
"""
|
|
2002 |
for item in seq: |
|
2003 |
if item in in_: |
|
3717
by Vo Minh Thu
[MERGE] orm: use directly lxml.etree to generate default views. |
2004 |
view.set(to, item) |
3594.1.7
by Xavier Morel
[ADD] small helper to calendar generation code, in order to make flow clearer |
2005 |
return True |
2006 |
return False |
|
2007 |
||
3594.1.5
by Xavier Morel
[IMP] convert default calendar view generation (from fields_view_get) to lxml.etree |
2008 |
view = etree.Element('calendar', string=self._description) |
4272.1.7
by Vo Minh Thu
[MEOW] lolcat crazy ninja fix in the branch, take two! |
2009 |
etree.SubElement(view, 'field', self._rec_name_fallback(cr, user, context)) |
1845
by HDA(OpenERP)
[Merged] |
2010 |
|
4682.2.9
by Xavier Morel
[REM] unnecessary parens |
2011 |
if self._date_name not in self._columns: |
2682
by olt at tinyerp
[FIX] missing comma in sql query 'execute' call ("select count...") |
2012 |
date_found = False |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
2013 |
for dt in ['date', 'date_start', 'x_date', 'x_date_start']: |
2682
by olt at tinyerp
[FIX] missing comma in sql query 'execute' call ("select count...") |
2014 |
if dt in self._columns: |
2015 |
self._date_name = dt |
|
2016 |
date_found = True |
|
2017 |
break
|
|
1845
by HDA(OpenERP)
[Merged] |
2018 |
|
2682
by olt at tinyerp
[FIX] missing comma in sql query 'execute' call ("select count...") |
2019 |
if not date_found: |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
2020 |
raise except_orm(_('Invalid Object Architecture!'), _("Insufficient fields for Calendar View!")) |
3594.1.5
by Xavier Morel
[IMP] convert default calendar view generation (from fields_view_get) to lxml.etree |
2021 |
view.set('date_start', self._date_name) |
1845
by HDA(OpenERP)
[Merged] |
2022 |
|
3594.1.7
by Xavier Morel
[ADD] small helper to calendar generation code, in order to make flow clearer |
2023 |
set_first_of(["user_id", "partner_id", "x_user_id", "x_partner_id"], |
2024 |
self._columns, 'color') |
|
1845
by HDA(OpenERP)
[Merged] |
2025 |
|
3594.1.7
by Xavier Morel
[ADD] small helper to calendar generation code, in order to make flow clearer |
2026 |
if not set_first_of(["date_stop", "date_end", "x_date_stop", "x_date_end"], |
2027 |
self._columns, 'date_stop'): |
|
2028 |
if not set_first_of(["date_delay", "planned_hours", "x_date_delay", "x_planned_hours"], |
|
2029 |
self._columns, 'date_delay'): |
|
3594.1.6
by Xavier Morel
[ADD] error when neither date_stop nor date_delay can be generated for a default calendar view |
2030 |
raise except_orm( |
2031 |
_('Invalid Object Architecture!'), |
|
4682.2.9
by Xavier Morel
[REM] unnecessary parens |
2032 |
_("Insufficient fields to generate a Calendar View for %s, missing a date_stop or a date_delay" % self._name)) |
922
by Christophe Simonis
convert tabs to 4 spaces |
2033 |
|
3594.1.5
by Xavier Morel
[IMP] convert default calendar view generation (from fields_view_get) to lxml.etree |
2034 |
return view |
922
by Christophe Simonis
convert tabs to 4 spaces |
2035 |
|
2036 |
#
|
|
2037 |
# if view_id, view_type is not required
|
|
2038 |
#
|
|
1845
by HDA(OpenERP)
[Merged] |
2039 |
def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): |
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
2040 |
"""
|
2041 |
Get the detailed composition of the requested view like fields, model, view architecture
|
|
2042 |
||
2043 |
:param cr: database cursor
|
|
2044 |
:param user: current user id
|
|
2045 |
:param view_id: id of the view or None
|
|
2046 |
:param view_type: type of the view to return if view_id is None ('form', tree', ...)
|
|
2047 |
:param context: context arguments, like lang, time zone
|
|
2111.3.21
by Rvo(Open ERP)
[IMP]:improved orm's public methods docstrings |
2048 |
:param toolbar: true to include contextual actions
|
3538
by Antony Lesuisse
[FIX] get rid of unsued menus and submenu |
2049 |
:param submenu: deprecated
|
2111.3.21
by Rvo(Open ERP)
[IMP]:improved orm's public methods docstrings |
2050 |
:return: dictionary describing the composition of the requested view (including inherited views and extensions)
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
2051 |
:raise AttributeError:
|
2052 |
* if the inherited view has unknown position to work with other than 'before', 'after', 'inside', 'replace'
|
|
2053 |
* if some tag other than 'position' is found in parent view
|
|
2054 |
:raise Invalid ArchitectureError: if there is view type other than form, tree, calendar, search etc defined on the structure
|
|
2055 |
||
2056 |
"""
|
|
3466
by Vo Minh Thu
[REF] orm: fields_get in pieces, now with docstring. |
2057 |
if context is None: |
957
by Olivier Laurent
pep8 |
2058 |
context = {} |
1727
by mra (Open ERP)
fix : 329208 : trans_obj is not defined |
2059 |
|
1404.2.11
by Christophe Simonis
[FIX] handle the case when xml are in unicode |
2060 |
def encode(s): |
2061 |
if isinstance(s, unicode): |
|
2062 |
return s.encode('utf8') |
|
1727
by mra (Open ERP)
fix : 329208 : trans_obj is not defined |
2063 |
return s |
1404.2.11
by Christophe Simonis
[FIX] handle the case when xml are in unicode |
2064 |
|
3089
by YSA (OpenERP), Olivier Dony (OpenERP)
[IMP] orm: somewhat better error message when view inheritance fails on non-<field> element |
2065 |
def raise_view_error(error_msg, child_view_id): |
2066 |
view, child_view = self.pool.get('ir.ui.view').browse(cr, user, [view_id, child_view_id], context) |
|
4466.1.2
by Christophe Simonis
[IMP] orm: remove trailing spaces |
2067 |
error_msg = error_msg % {'parent_xml_id': view.xml_id} |
3466
by Vo Minh Thu
[REF] orm: fields_get in pieces, now with docstring. |
2068 |
raise AttributeError("View definition error for inherited view '%s' on model '%s': %s" |
2069 |
% (child_view.xml_id, self._name, error_msg)) |
|
3089
by YSA (OpenERP), Olivier Dony (OpenERP)
[IMP] orm: somewhat better error message when view inheritance fails on non-<field> element |
2070 |
|
3466.1.1
by Vo Minh Thu
[REF] orm: simpler `_find` function, renamed `locate`. |
2071 |
def locate(source, spec): |
3466.1.2
by Vo Minh Thu
[REF] fields_viw_get: trying to get something clearer... |
2072 |
""" Locate a node in a source (parent) architecture.
|
3466.1.1
by Vo Minh Thu
[REF] orm: simpler `_find` function, renamed `locate`. |
2073 |
|
3466.1.2
by Vo Minh Thu
[REF] fields_viw_get: trying to get something clearer... |
2074 |
Given a complete source (parent) architecture (i.e. the field
|
3466.1.3
by Vo Minh Thu
[FIX] orm: typo in fields_view_get. |
2075 |
`arch` in a view), and a 'spec' node (a node in an inheriting
|
3466.1.2
by Vo Minh Thu
[REF] fields_viw_get: trying to get something clearer... |
2076 |
view that specifies the location in the source view of what
|
2077 |
should be changed), return (if it exists) the node in the
|
|
2078 |
source view matching the specification.
|
|
3466.1.1
by Vo Minh Thu
[REF] orm: simpler `_find` function, renamed `locate`. |
2079 |
|
3466.1.3
by Vo Minh Thu
[FIX] orm: typo in fields_view_get. |
2080 |
:param source: a parent architecture to modify
|
2081 |
:param spec: a modifying node in an inheriting view
|
|
2082 |
:return: a node in the source matching the spec
|
|
2083 |
||
3466.1.1
by Vo Minh Thu
[REF] orm: simpler `_find` function, renamed `locate`. |
2084 |
"""
|
2085 |
if spec.tag == 'xpath': |
|
2086 |
nodes = source.xpath(spec.get('expr')) |
|
2087 |
return nodes[0] if nodes else None |
|
2088 |
elif spec.tag == 'field': |
|
2089 |
# Only compare the field name: a field can be only once in a given view
|
|
2090 |
# at a given level (and for multilevel expressions, we should use xpath
|
|
2091 |
# inheritance spec anyway).
|
|
2092 |
for node in source.getiterator('field'): |
|
2093 |
if node.get('name') == spec.get('name'): |
|
2094 |
return node |
|
2095 |
return None |
|
4194.1.2
by Olivier Dony
[IMP] orm: improved @version spec validation for inherited views |
2096 |
|
2097 |
for node in source.getiterator(spec.tag): |
|
2098 |
if isinstance(node, SKIPPED_ELEMENT_TYPES): |
|
2099 |
continue
|
|
2100 |
if all(node.get(attr) == spec.get(attr) \ |
|
2101 |
for attr in spec.attrib |
|
2102 |
if attr not in ('position','version')): |
|
4466.1.2
by Christophe Simonis
[IMP] orm: remove trailing spaces |
2103 |
# Version spec should match parent's root element's version
|
4194.1.2
by Olivier Dony
[IMP] orm: improved @version spec validation for inherited views |
2104 |
if spec.get('version') and spec.get('version') != source.get('version'): |
2105 |
return None |
|
2106 |
return node |
|
2107 |
return None |
|
3466.1.1
by Vo Minh Thu
[REF] orm: simpler `_find` function, renamed `locate`. |
2108 |
|
3466.1.3
by Vo Minh Thu
[FIX] orm: typo in fields_view_get. |
2109 |
def apply_inheritance_specs(source, specs_arch, inherit_id=None): |
3466.1.2
by Vo Minh Thu
[REF] fields_viw_get: trying to get something clearer... |
2110 |
""" Apply an inheriting view.
|
2111 |
||
2112 |
Apply to a source architecture all the spec nodes (i.e. nodes
|
|
2113 |
describing where and what changes to apply to some parent
|
|
2114 |
architecture) given by an inheriting view.
|
|
2115 |
||
3466.1.3
by Vo Minh Thu
[FIX] orm: typo in fields_view_get. |
2116 |
:param source: a parent architecture to modify
|
2117 |
:param specs_arch: a modifying architecture in an inheriting view
|
|
2118 |
:param inherit_id: the database id of the inheriting view
|
|
2119 |
:return: a modified source where the specs are applied
|
|
2120 |
||
3466.1.2
by Vo Minh Thu
[REF] fields_viw_get: trying to get something clearer... |
2121 |
"""
|
3466.1.3
by Vo Minh Thu
[FIX] orm: typo in fields_view_get. |
2122 |
specs_tree = etree.fromstring(encode(specs_arch)) |
3466.1.2
by Vo Minh Thu
[REF] fields_viw_get: trying to get something clearer... |
2123 |
# Queue of specification nodes (i.e. nodes describing where and
|
2124 |
# changes to apply to some parent architecture).
|
|
2125 |
specs = [specs_tree] |
|
2126 |
||
2127 |
while len(specs): |
|
2128 |
spec = specs.pop(0) |
|
2129 |
if isinstance(spec, SKIPPED_ELEMENT_TYPES): |
|
2130 |
continue
|
|
2131 |
if spec.tag == 'data': |
|
2132 |
specs += [ c for c in specs_tree ] |
|
2133 |
continue
|
|
2134 |
node = locate(source, spec) |
|
1845
by HDA(OpenERP)
[Merged] |
2135 |
if node is not None: |
3466.1.2
by Vo Minh Thu
[REF] fields_viw_get: trying to get something clearer... |
2136 |
pos = spec.get('position', 'inside') |
957
by Olivier Laurent
pep8 |
2137 |
if pos == 'replace': |
3466.1.2
by Vo Minh Thu
[REF] fields_viw_get: trying to get something clearer... |
2138 |
if node.getparent() is None: |
2139 |
source = copy.deepcopy(spec[0]) |
|
1861
by PSO(OpenERP)
Changed licencing |
2140 |
else: |
3466.1.2
by Vo Minh Thu
[REF] fields_viw_get: trying to get something clearer... |
2141 |
for child in spec: |
1861
by PSO(OpenERP)
Changed licencing |
2142 |
node.addprevious(child) |
2143 |
node.getparent().remove(node) |
|
1887
by Stephane Wirtel
[IMP] Add the support for the override of any attribute. |
2144 |
elif pos == 'attributes': |
3466.1.2
by Vo Minh Thu
[REF] fields_viw_get: trying to get something clearer... |
2145 |
for child in spec.getiterator('attribute'): |
1887
by Stephane Wirtel
[IMP] Add the support for the override of any attribute. |
2146 |
attribute = (child.get('name'), child.text and child.text.encode('utf8') or None) |
2147 |
if attribute[1]: |
|
2148 |
node.set(attribute[0], attribute[1]) |
|
2149 |
else: |
|
2150 |
del(node.attrib[attribute[0]]) |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
2151 |
else: |
1845
by HDA(OpenERP)
[Merged] |
2152 |
sib = node.getnext() |
3466.1.2
by Vo Minh Thu
[REF] fields_viw_get: trying to get something clearer... |
2153 |
for child in spec: |
1845
by HDA(OpenERP)
[Merged] |
2154 |
if pos == 'inside': |
2155 |
node.append(child) |
|
2156 |
elif pos == 'after': |
|
2157 |
if sib is None: |
|
2158 |
node.addnext(child) |
|
3097
by Olivier Dony
[FIX] orm: preserve elements order when inheriting views, also after a leaf element |
2159 |
node = child |
922
by Christophe Simonis
convert tabs to 4 spaces |
2160 |
else: |
1845
by HDA(OpenERP)
[Merged] |
2161 |
sib.addprevious(child) |
2162 |
elif pos == 'before': |
|
2163 |
node.addprevious(child) |
|
2164 |
else: |
|
3089
by YSA (OpenERP), Olivier Dony (OpenERP)
[IMP] orm: somewhat better error message when view inheritance fails on non-<field> element |
2165 |
raise_view_error("Invalid position value: '%s'" % pos, inherit_id) |
922
by Christophe Simonis
convert tabs to 4 spaces |
2166 |
else: |
2167 |
attrs = ''.join([ |
|
3466.1.2
by Vo Minh Thu
[REF] fields_viw_get: trying to get something clearer... |
2168 |
' %s="%s"' % (attr, spec.get(attr)) |
2169 |
for attr in spec.attrib |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
2170 |
if attr != 'position' |
2171 |
])
|
|
3466.1.2
by Vo Minh Thu
[REF] fields_viw_get: trying to get something clearer... |
2172 |
tag = "<%s%s>" % (spec.tag, attrs) |
4194.1.2
by Olivier Dony
[IMP] orm: improved @version spec validation for inherited views |
2173 |
if spec.get('version') and spec.get('version') != source.get('version'): |
2174 |
raise_view_error("Mismatching view API version for element '%s': %r vs %r in parent view '%%(parent_xml_id)s'" % \ |
|
2175 |
(tag, spec.get('version'), source.get('version')), inherit_id) |
|
3089
by YSA (OpenERP), Olivier Dony (OpenERP)
[IMP] orm: somewhat better error message when view inheritance fails on non-<field> element |
2176 |
raise_view_error("Element '%s' not found in parent view '%%(parent_xml_id)s'" % tag, inherit_id) |
4466.1.2
by Christophe Simonis
[IMP] orm: remove trailing spaces |
2177 |
|
3466.1.2
by Vo Minh Thu
[REF] fields_viw_get: trying to get something clearer... |
2178 |
return source |
2179 |
||
3611.1.1
by sebastien beau
[REF] refactor code in order to add the possibility to overwrite the way to inherit the view |
2180 |
def apply_view_inheritance(cr, user, source, inherit_id): |
3466.1.3
by Vo Minh Thu
[FIX] orm: typo in fields_view_get. |
2181 |
""" Apply all the (directly and indirectly) inheriting views.
|
2182 |
||
2183 |
:param source: a parent architecture to modify (with parent
|
|
2184 |
modifications already applied)
|
|
3617
by Olivier Dony
[MERGE] refactoring: view inheritance query moved to ir.ui.view, courtesy of Sebastien Beau (Akretion) |
2185 |
:param inherit_id: the database view_id of the parent view
|
3466.1.3
by Vo Minh Thu
[FIX] orm: typo in fields_view_get. |
2186 |
:return: a modified source where all the modifying architecture
|
2187 |
are applied
|
|
2188 |
||
2189 |
"""
|
|
3617
by Olivier Dony
[MERGE] refactoring: view inheritance query moved to ir.ui.view, courtesy of Sebastien Beau (Akretion) |
2190 |
sql_inherit = self.pool.get('ir.ui.view').get_inheriting_views_arch(cr, user, inherit_id, self._name) |
2191 |
for (view_arch, view_id) in sql_inherit: |
|
2192 |
source = apply_inheritance_specs(source, view_arch, view_id) |
|
2193 |
source = apply_view_inheritance(cr, user, source, view_id) |
|
3466.1.2
by Vo Minh Thu
[REF] fields_viw_get: trying to get something clearer... |
2194 |
return source |
922
by Christophe Simonis
convert tabs to 4 spaces |
2195 |
|
957
by Olivier Laurent
pep8 |
2196 |
result = {'type': view_type, 'model': self._name} |
922
by Christophe Simonis
convert tabs to 4 spaces |
2197 |
|
2198 |
sql_res = False |
|
3110.1.1
by OpenERP
[FIX] orm: fixed translations for inherited views when the parent view uses a different model. |
2199 |
parent_view_model = None |
3466.1.2
by Vo Minh Thu
[REF] fields_viw_get: trying to get something clearer... |
2200 |
view_ref = context.get(view_type + '_view_ref') |
2201 |
# Search for a root (i.e. without any parent) view.
|
|
2202 |
while True: |
|
2536
by nch at tinyerp
[FIX]:*_view_ref should execute only if there is no view_id provided |
2203 |
if view_ref and not view_id: |
1844.1.31
by Stephane Wirtel
[MERGE] Backport: http://bazaar.launchpad.net/~openobject-training/+junk/server/revision/1822 |
2204 |
if '.' in view_ref: |
2205 |
module, view_ref = view_ref.split('.', 1) |
|
2206 |
cr.execute("SELECT res_id FROM ir_model_data WHERE model='ir.ui.view' AND module=%s AND name=%s", (module, view_ref)) |
|
2207 |
view_ref_res = cr.fetchone() |
|
2208 |
if view_ref_res: |
|
2209 |
view_id = view_ref_res[0] |
|
2210 |
||
922
by Christophe Simonis
convert tabs to 4 spaces |
2211 |
if view_id: |
3358
by Olivier Dony
[IMP] orm.fields_view_get: allow passing a view_id from a different model for creative inheritance |
2212 |
cr.execute("""SELECT arch,name,field_parent,id,type,inherit_id,model |
2213 |
FROM ir_ui_view
|
|
2214 |
WHERE id=%s""", (view_id,)) |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
2215 |
else: |
3466.1.2
by Vo Minh Thu
[REF] fields_viw_get: trying to get something clearer... |
2216 |
cr.execute("""SELECT arch,name,field_parent,id,type,inherit_id,model |
2217 |
FROM ir_ui_view
|
|
2218 |
WHERE model=%s AND type=%s AND inherit_id IS NULL |
|
2219 |
ORDER BY priority""", (self._name, view_type)) |
|
2220 |
sql_res = cr.dictfetchone() |
|
1930
by ame (Tiny)
[IMP] improved `fields_view_get` (auto create search view) |
2221 |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
2222 |
if not sql_res: |
2223 |
break
|
|
1869.1.24
by nch at tinyerp
[MERGE]:from parent |
2224 |
|
3466.1.2
by Vo Minh Thu
[REF] fields_viw_get: trying to get something clearer... |
2225 |
view_id = sql_res['inherit_id'] or sql_res['id'] |
2226 |
parent_view_model = sql_res['model'] |
|
2227 |
if not sql_res['inherit_id']: |
|
2228 |
break
|
|
922
by Christophe Simonis
convert tabs to 4 spaces |
2229 |
|
2230 |
# if a view was found
|
|
2231 |
if sql_res: |
|
3466.1.2
by Vo Minh Thu
[REF] fields_viw_get: trying to get something clearer... |
2232 |
source = etree.fromstring(encode(sql_res['arch'])) |
3594.1.1
by Xavier Morel
[IMP] bulk-update fields_view_get result via dict.update instead of a bunch of __setitem__ |
2233 |
result.update( |
3594.1.12
by Xavier Morel
[MERGE] trunk |
2234 |
arch=apply_view_inheritance(cr, user, source, sql_res['id']), |
3594.1.1
by Xavier Morel
[IMP] bulk-update fields_view_get result via dict.update instead of a bunch of __setitem__ |
2235 |
type=sql_res['type'], |
2236 |
view_id=sql_res['id'], |
|
2237 |
name=sql_res['name'], |
|
2238 |
field_parent=sql_res['field_parent'] or False) |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
2239 |
else: |
2240 |
# otherwise, build some kind of default view
|
|
3594.1.3
by Xavier Morel
[IMP] regroup all __get_default_$name_view calls into a single parametric getattr |
2241 |
try: |
3594.1.4
by Xavier Morel
[IMP] etree-ify default view generations |
2242 |
view = getattr(self, '_get_default_%s_view' % view_type)( |
3594.1.3
by Xavier Morel
[IMP] regroup all __get_default_$name_view calls into a single parametric getattr |
2243 |
cr, user, context) |
2244 |
except AttributeError: |
|
3466.1.2
by Vo Minh Thu
[REF] fields_viw_get: trying to get something clearer... |
2245 |
# what happens here, graph case?
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
2246 |
raise except_orm(_('Invalid Architecture!'), _("There is no view of type '%s' defined for the structure!") % view_type) |
3594.1.3
by Xavier Morel
[IMP] regroup all __get_default_$name_view calls into a single parametric getattr |
2247 |
|
3594.1.1
by Xavier Morel
[IMP] bulk-update fields_view_get result via dict.update instead of a bunch of __setitem__ |
2248 |
result.update( |
3594.1.4
by Xavier Morel
[IMP] etree-ify default view generations |
2249 |
arch=view, |
3594.1.1
by Xavier Morel
[IMP] bulk-update fields_view_get result via dict.update instead of a bunch of __setitem__ |
2250 |
name='default', |
2251 |
field_parent=False, |
|
2252 |
view_id=0) |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
2253 |
|
3110.1.1
by OpenERP
[FIX] orm: fixed translations for inherited views when the parent view uses a different model. |
2254 |
if parent_view_model != self._name: |
3118
by Vo Minh Thu (OpenERP)
[MERGE] orm: fixed translations for inherited views when the parent view uses a different model |
2255 |
ctx = context.copy() |
2256 |
ctx['base_model_name'] = parent_view_model |
|
3110.1.1
by OpenERP
[FIX] orm: fixed translations for inherited views when the parent view uses a different model. |
2257 |
else: |
3118
by Vo Minh Thu (OpenERP)
[MERGE] orm: fixed translations for inherited views when the parent view uses a different model |
2258 |
ctx = context |
3110.1.1
by OpenERP
[FIX] orm: fixed translations for inherited views when the parent view uses a different model. |
2259 |
xarch, xfields = self.__view_look_dom_arch(cr, user, result['arch'], view_id, context=ctx) |
922
by Christophe Simonis
convert tabs to 4 spaces |
2260 |
result['arch'] = xarch |
2261 |
result['fields'] = xfields |
|
1845
by HDA(OpenERP)
[Merged] |
2262 |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
2263 |
if toolbar: |
2264 |
def clean(x): |
|
2265 |
x = x[2] |
|
2266 |
for key in ('report_sxw_content', 'report_rml_content', |
|
2267 |
'report_sxw', 'report_rml', |
|
2268 |
'report_sxw_content_data', 'report_rml_content_data'): |
|
2269 |
if key in x: |
|
2270 |
del x[key] |
|
2271 |
return x |
|
2272 |
ir_values_obj = self.pool.get('ir.values') |
|
2273 |
resprint = ir_values_obj.get(cr, user, 'action', |
|
2274 |
'client_print_multi', [(self._name, False)], False, |
|
2275 |
context) |
|
2276 |
resaction = ir_values_obj.get(cr, user, 'action', |
|
2277 |
'client_action_multi', [(self._name, False)], False, |
|
2278 |
context) |
|
2279 |
||
2280 |
resrelate = ir_values_obj.get(cr, user, 'action', |
|
2281 |
'client_action_relate', [(self._name, False)], False, |
|
2282 |
context) |
|
3594.1.13
by Xavier Morel
[FIX] typo syntax error |
2283 |
resaction = [clean(action) for action in resaction |
2284 |
if view_type == 'tree' or not action[2].get('multi')] |
|
2285 |
resprint = [clean(print_) for print_ in resprint |
|
2286 |
if view_type == 'tree' or not print_[2].get('multi')] |
|
4761.2.20
by Antonin Bourguignon
[IMP] fix a comment's indentation, remove a few whitespaces |
2287 |
#When multi="True" set it will display only in More of the list view
|
4495.3.1
by Mayur Maheshwari(OpenERP)
[IMP]orm : improve multi tag when multi=true action display in tree and when flase action display in form |
2288 |
resrelate = [clean(action) for action in resrelate |
2289 |
if (action[2].get('multi') and view_type == 'tree') or (not action[2].get('multi') and view_type == 'form')] |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
2290 |
|
3594.1.11
by Xavier Morel
[IMP] fields_view_get: listcomps are still awesome |
2291 |
for x in itertools.chain(resprint, resaction, resrelate): |
922
by Christophe Simonis
convert tabs to 4 spaces |
2292 |
x['string'] = x['name'] |
2293 |
||
2294 |
result['toolbar'] = { |
|
2295 |
'print': resprint, |
|
2296 |
'action': resaction, |
|
2297 |
'relate': resrelate |
|
2298 |
}
|
|
2299 |
return result |
|
2300 |
||
957
by Olivier Laurent
pep8 |
2301 |
_view_look_dom_arch = __view_look_dom_arch |
922
by Christophe Simonis
convert tabs to 4 spaces |
2302 |
|
2303 |
def search_count(self, cr, user, args, context=None): |
|
2304 |
if not context: |
|
2305 |
context = {} |
|
2306 |
res = self.search(cr, user, args, context=context, count=True) |
|
2307 |
if isinstance(res, list): |
|
2308 |
return len(res) |
|
2309 |
return res |
|
2310 |
||
2675
by Olivier Dony
[FIX] orm: fields_view_get selection values must properly heed ir.rules: |
2311 |
def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False): |
2312 |
"""
|
|
2313 |
Search for records based on a search domain.
|
|
2314 |
||
2315 |
:param cr: database cursor
|
|
2316 |
:param user: current user id
|
|
2317 |
:param args: list of tuples specifying the search domain [('field_name', 'operator', value), ...]. Pass an empty list to match all records.
|
|
2318 |
:param offset: optional number of results to skip in the returned values (default: 0)
|
|
2319 |
:param limit: optional max number of records to return (default: **None**)
|
|
2320 |
:param order: optional columns to sort by (default: self._order=id )
|
|
2321 |
:param context: optional context arguments, like lang, time zone
|
|
2322 |
:type context: dictionary
|
|
2323 |
:param count: optional (default: **False**), if **True**, returns only the number of records matching the criteria, not their ids
|
|
2324 |
:return: id or list of ids of records matching the criteria
|
|
2325 |
:rtype: integer or list of integers
|
|
2326 |
:raise AccessError: * if user tries to bypass access rules for read on the requested object.
|
|
2327 |
||
2328 |
**Expressing a search domain (args)**
|
|
2329 |
||
2330 |
Each tuple in the search domain needs to have 3 elements, in the form: **('field_name', 'operator', value)**, where:
|
|
2331 |
||
2332 |
* **field_name** must be a valid name of field of the object model, possibly following many-to-one relationships using dot-notation, e.g 'street' or 'partner_id.country' are valid values.
|
|
2333 |
* **operator** must be a string with a valid comparison operator from this list: ``=, !=, >, >=, <, <=, like, ilike, in, not in, child_of, parent_left, parent_right``
|
|
2334 |
The semantics of most of these operators are obvious.
|
|
2335 |
The ``child_of`` operator will look for records who are children or grand-children of a given record,
|
|
2336 |
according to the semantics of this model (i.e following the relationship field named by
|
|
2337 |
``self._parent_name``, by default ``parent_id``.
|
|
2338 |
* **value** must be a valid value to compare with the values of **field_name**, depending on its type.
|
|
2339 |
||
2340 |
Domain criteria can be combined using 3 logical operators than can be added between tuples: '**&**' (logical AND, default), '**|**' (logical OR), '**!**' (logical NOT).
|
|
2341 |
These are **prefix** operators and the arity of the '**&**' and '**|**' operator is 2, while the arity of the '**!**' is just 1.
|
|
2342 |
Be very careful about this when you combine them the first time.
|
|
2343 |
||
2344 |
Here is an example of searching for Partners named *ABC* from Belgium and Germany whose language is not english ::
|
|
2345 |
||
2346 |
[('name','=','ABC'),'!',('language.code','=','en_US'),'|',('country_id.code','=','be'),('country_id.code','=','de'))
|
|
2347 |
||
2348 |
The '&' is omitted as it is the default, and of course we could have used '!=' for the language, but what this domain really represents is::
|
|
2349 |
||
2350 |
(name is 'ABC' AND (language is NOT english) AND (country is Belgium OR Germany))
|
|
2351 |
||
2352 |
"""
|
|
2353 |
return self._search(cr, user, args, offset=offset, limit=limit, order=order, context=context, count=count) |
|
2354 |
||
922
by Christophe Simonis
convert tabs to 4 spaces |
2355 |
def name_get(self, cr, user, ids, context=None): |
3353.1.42
by Olivier Dony
[IMP] orm.name*: better docstrings |
2356 |
"""Returns the preferred display value (text representation) for the records with the
|
2357 |
given ``ids``. By default this will be the value of the ``name`` column, unless
|
|
2358 |
the model implements a custom behavior.
|
|
2359 |
Can sometimes be seen as the inverse function of :meth:`~.name_search`, but it is not
|
|
2360 |
guaranteed to be.
|
|
2361 |
||
2362 |
:rtype: list(tuple)
|
|
2363 |
:return: list of pairs ``(id,text_repr)`` for all records with the given ``ids``.
|
|
2364 |
"""
|
|
2676
by Olivier Dony
[IMP] orm: moved name_search + its private implementation + name_get from orm to orm_template |
2365 |
if not ids: |
2366 |
return [] |
|
2367 |
if isinstance(ids, (int, long)): |
|
2368 |
ids = [ids] |
|
4272.1.1
by Antony Lesuisse
[IMP] rec_name assertion and fallback |
2369 |
|
4284.1.1
by Olivier Dony
[IMP] name_get: support more field types and improve previous name_get changes |
2370 |
if self._rec_name in self._all_columns: |
2371 |
rec_name_column = self._all_columns[self._rec_name].column |
|
2372 |
return [(r['id'], rec_name_column.as_display_name(cr, user, self, r[self._rec_name], context=context)) |
|
2373 |
for r in self.read(cr, user, ids, [self._rec_name], |
|
2374 |
load='_classic_write', context=context)] |
|
2375 |
return [(id, "%s,%s" % (self._name, id)) for id in ids] |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
2376 |
|
1919.4.1
by Jay(Open ERP)
[IMP] Limit for name_search() improved to 100 |
2377 |
def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100): |
3353.1.42
by Olivier Dony
[IMP] orm.name*: better docstrings |
2378 |
"""Search for records that have a display name matching the given ``name`` pattern if compared
|
2379 |
with the given ``operator``, while also matching the optional search domain (``args``).
|
|
2380 |
This is used for example to provide suggestions based on a partial value for a relational
|
|
2381 |
field.
|
|
2382 |
Sometimes be seen as the inverse function of :meth:`~.name_get`, but it is not
|
|
2383 |
guaranteed to be.
|
|
2384 |
||
2385 |
This method is equivalent to calling :meth:`~.search` with a search domain based on ``name``
|
|
2386 |
and then :meth:`~.name_get` on the result of the search.
|
|
2387 |
||
2388 |
:param list args: optional search domain (see :meth:`~.search` for syntax),
|
|
2389 |
specifying further restrictions
|
|
2390 |
:param str operator: domain operator for matching the ``name`` pattern, such as ``'like'``
|
|
2391 |
or ``'='``.
|
|
2392 |
:param int limit: optional max number of records to return
|
|
2393 |
:rtype: list
|
|
4466.1.2
by Christophe Simonis
[IMP] orm: remove trailing spaces |
2394 |
:return: list of pairs ``(id,text_repr)`` for all matching records.
|
2676
by Olivier Dony
[IMP] orm: moved name_search + its private implementation + name_get from orm to orm_template |
2395 |
"""
|
2396 |
return self._name_search(cr, user, name, args, operator, context, limit) |
|
3454
by Nicolas Vanhoren
[MERGE] addition of name_create() ORM method for new m2o behavior in 6.1 |
2397 |
|
2398 |
def name_create(self, cr, uid, name, context=None): |
|
3353.1.42
by Olivier Dony
[IMP] orm.name*: better docstrings |
2399 |
"""Creates a new record by calling :meth:`~.create` with only one
|
2400 |
value provided: the name of the new record (``_rec_name`` field).
|
|
2401 |
The new record will also be initialized with any default values applicable
|
|
2402 |
to this model, or provided through the context. The usual behavior of
|
|
2403 |
:meth:`~.create` applies.
|
|
2404 |
Similarly, this method may raise an exception if the model has multiple
|
|
2405 |
required fields and some do not have default values.
|
|
2406 |
||
2407 |
:param name: name of the record to create
|
|
2408 |
||
2409 |
:rtype: tuple
|
|
2410 |
:return: the :meth:`~.name_get` pair value for the newly-created record.
|
|
3454
by Nicolas Vanhoren
[MERGE] addition of name_create() ORM method for new m2o behavior in 6.1 |
2411 |
"""
|
4682.2.12
by Xavier Morel
[REM] trailing semicolons |
2412 |
rec_id = self.create(cr, uid, {self._rec_name: name}, context) |
3454
by Nicolas Vanhoren
[MERGE] addition of name_create() ORM method for new m2o behavior in 6.1 |
2413 |
return self.name_get(cr, uid, [rec_id], context)[0] |
2676
by Olivier Dony
[IMP] orm: moved name_search + its private implementation + name_get from orm to orm_template |
2414 |
|
2415 |
# private implementation of name_search, allows passing a dedicated user for the name_get part to
|
|
2416 |
# solve some access rights issues
|
|
2417 |
def _name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100, name_get_uid=None): |
|
2418 |
if args is None: |
|
2419 |
args = [] |
|
2420 |
if context is None: |
|
2421 |
context = {} |
|
2422 |
args = args[:] |
|
3753
by Olivier Dony
[FIX] expression: proper handling of empty string in m2o domain criterions |
2423 |
# optimize out the default criterion of ``ilike ''`` that matches everything
|
2424 |
if not (name == '' and operator == 'ilike'): |
|
2676
by Olivier Dony
[IMP] orm: moved name_search + its private implementation + name_get from orm to orm_template |
2425 |
args += [(self._rec_name, operator, name)] |
2426 |
access_rights_uid = name_get_uid or user |
|
2427 |
ids = self._search(cr, user, args, limit=limit, context=context, access_rights_uid=access_rights_uid) |
|
2428 |
res = self.name_get(cr, access_rights_uid, ids, context) |
|
2429 |
return res |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
2430 |
|
1314
by Stephane Wirtel
[FIX] Move the read_string and write_string methods from orm to orm_template |
2431 |
def read_string(self, cr, uid, id, langs, fields=None, context=None): |
2432 |
res = {} |
|
2433 |
res2 = {} |
|
4344.1.16
by Fabien Pinckaers
[IMP] Removing a bad practice: |
2434 |
self.pool.get('ir.translation').check_access_rights(cr, uid, 'read') |
1314
by Stephane Wirtel
[FIX] Move the read_string and write_string methods from orm to orm_template |
2435 |
if not fields: |
2436 |
fields = self._columns.keys() + self._inherit_fields.keys() |
|
1884.1.4
by Harry (Open ERP)
[FIX] replaced <TAB> with four white space. |
2437 |
#FIXME: collect all calls to _get_source into one SQL call.
|
1314
by Stephane Wirtel
[FIX] Move the read_string and write_string methods from orm to orm_template |
2438 |
for lang in langs: |
2439 |
res[lang] = {'code': lang} |
|
2440 |
for f in fields: |
|
2441 |
if f in self._columns: |
|
2442 |
res_trans = self.pool.get('ir.translation')._get_source(cr, uid, self._name+','+f, 'field', lang) |
|
2443 |
if res_trans: |
|
2444 |
res[lang][f] = res_trans |
|
2445 |
else: |
|
2446 |
res[lang][f] = self._columns[f].string |
|
2447 |
for table in self._inherits: |
|
2448 |
cols = intersect(self._inherit_fields.keys(), fields) |
|
2449 |
res2 = self.pool.get(table).read_string(cr, uid, id, langs, cols, context) |
|
2450 |
for lang in res2: |
|
2451 |
if lang in res: |
|
1559
by Stephane Wirtel
[FIX] Translation - read_string |
2452 |
res[lang]['code'] = lang |
1314
by Stephane Wirtel
[FIX] Move the read_string and write_string methods from orm to orm_template |
2453 |
for f in res2[lang]: |
2454 |
res[lang][f] = res2[lang][f] |
|
2455 |
return res |
|
2456 |
||
2457 |
def write_string(self, cr, uid, id, langs, vals, context=None): |
|
4344.1.16
by Fabien Pinckaers
[IMP] Removing a bad practice: |
2458 |
self.pool.get('ir.translation').check_access_rights(cr, uid, 'write') |
1884.1.4
by Harry (Open ERP)
[FIX] replaced <TAB> with four white space. |
2459 |
#FIXME: try to only call the translation in one SQL
|
1314
by Stephane Wirtel
[FIX] Move the read_string and write_string methods from orm to orm_template |
2460 |
for lang in langs: |
2461 |
for field in vals: |
|
2462 |
if field in self._columns: |
|
2238.1.20
by Anup(OpenERP)
[FIX] Export translations + Cache on translations + Resynchronization wizard Corrections |
2463 |
src = self._columns[field].string |
2464 |
self.pool.get('ir.translation')._set_ids(cr, uid, self._name+','+field, 'field', lang, [0], vals[field], src) |
|
1314
by Stephane Wirtel
[FIX] Move the read_string and write_string methods from orm to orm_template |
2465 |
for table in self._inherits: |
2466 |
cols = intersect(self._inherit_fields.keys(), vals) |
|
2467 |
if cols: |
|
2468 |
self.pool.get(table).write_string(cr, uid, id, langs, vals, context) |
|
2469 |
return True |
|
2470 |
||
2654
by Olivier Dony
[FIX,REF] orm: correct default values update during create() + refactor |
2471 |
def _add_missing_default_values(self, cr, uid, values, context=None): |
2472 |
missing_defaults = [] |
|
2473 |
avoid_tables = [] # avoid overriding inherited values when parent is set |
|
2474 |
for tables, parent_field in self._inherits.items(): |
|
2475 |
if parent_field in values: |
|
2476 |
avoid_tables.append(tables) |
|
2477 |
for field in self._columns.keys(): |
|
2478 |
if not field in values: |
|
2479 |
missing_defaults.append(field) |
|
2480 |
for field in self._inherit_fields.keys(): |
|
2481 |
if (field not in values) and (self._inherit_fields[field][0] not in avoid_tables): |
|
2482 |
missing_defaults.append(field) |
|
2483 |
||
2484 |
if len(missing_defaults): |
|
2485 |
# override defaults with the provided values, never allow the other way around
|
|
2486 |
defaults = self.default_get(cr, uid, missing_defaults, context) |
|
2487 |
for dv in defaults: |
|
3332
by Olivier Dony
[FIX] orm: incorrect braces when processing default values for o2m/m2m |
2488 |
if ((dv in self._columns and self._columns[dv]._type == 'many2many') \ |
2489 |
or (dv in self._inherit_fields and self._inherit_fields[dv][2]._type == 'many2many')) \ |
|
2682
by olt at tinyerp
[FIX] missing comma in sql query 'execute' call ("select count...") |
2490 |
and defaults[dv] and isinstance(defaults[dv][0], (int, long)): |
2491 |
defaults[dv] = [(6, 0, defaults[dv])] |
|
3332
by Olivier Dony
[FIX] orm: incorrect braces when processing default values for o2m/m2m |
2492 |
if (dv in self._columns and self._columns[dv]._type == 'one2many' \ |
2493 |
or (dv in self._inherit_fields and self._inherit_fields[dv][2]._type == 'one2many')) \ |
|
3310.7.1
by Jay Vora(OpenERP)
[FIX] orm: _add_missing_default_values: Allowing only valid x2m default values (Ref : Case 4364) |
2494 |
and isinstance(defaults[dv], (list, tuple)) and defaults[dv] and isinstance(defaults[dv][0], dict): |
2906.1.1
by Olivier Dony
[IMP] orm: _add_missing_default_values: handle o2m default values in the legacy form [{},{}] + inherited x2m defaults |
2495 |
defaults[dv] = [(0, 0, x) for x in defaults[dv]] |
2654
by Olivier Dony
[FIX,REF] orm: correct default values update during create() + refactor |
2496 |
defaults.update(values) |
2497 |
values = defaults |
|
2498 |
return values |
|
2499 |
||
3443.1.16
by Vo Minh Thu
[IMP] tools.cache: added missing clean_caches_for_db replacement. |
2500 |
def clear_caches(self): |
2501 |
""" Clear the caches
|
|
2502 |
||
2503 |
This clears the caches associated to methods decorated with
|
|
2504 |
``tools.ormcache`` or ``tools.ormcache_multi``.
|
|
2505 |
"""
|
|
2506 |
try: |
|
2507 |
getattr(self, '_ormcache') |
|
2508 |
self._ormcache = {} |
|
4644.1.1
by Antony Lesuisse
multiprocessing signaling manually backported from 6.1 |
2509 |
self.pool._any_cache_cleared = True |
3443.1.16
by Vo Minh Thu
[IMP] tools.cache: added missing clean_caches_for_db replacement. |
2510 |
except AttributeError: |
2511 |
pass
|
|
2512 |
||
3814
by Olivier Dony
[FIX] orm.read_group: fix issues in read_group_full and rewrite/cleanup |
2513 |
|
3821
by Olivier Dony
[FIX] orm.read_group: fix merge logic for read_group_full: |
2514 |
def _read_group_fill_results(self, cr, uid, domain, groupby, groupby_list, aggregated_fields, |
2515 |
read_group_result, read_group_order=None, context=None): |
|
2516 |
"""Helper method for filling in empty groups for all possible values of
|
|
2517 |
the field being grouped by"""
|
|
2518 |
||
3814
by Olivier Dony
[FIX] orm.read_group: fix issues in read_group_full and rewrite/cleanup |
2519 |
# self._group_by_full should map groupable fields to a method that returns
|
3821
by Olivier Dony
[FIX] orm.read_group: fix merge logic for read_group_full: |
2520 |
# a list of all aggregated values that we want to display for this field,
|
2521 |
# in the form of a m2o-like pair (key,label).
|
|
3814
by Olivier Dony
[FIX] orm.read_group: fix issues in read_group_full and rewrite/cleanup |
2522 |
# This is useful to implement kanban views for instance, where all columns
|
2523 |
# should be displayed even if they don't contain any record.
|
|
3821
by Olivier Dony
[FIX] orm.read_group: fix merge logic for read_group_full: |
2524 |
|
4466.1.2
by Christophe Simonis
[IMP] orm: remove trailing spaces |
2525 |
# Grab the list of all groups that should be displayed, including all present groups
|
3814
by Olivier Dony
[FIX] orm.read_group: fix issues in read_group_full and rewrite/cleanup |
2526 |
present_group_ids = [x[groupby][0] for x in read_group_result if x[groupby]] |
4399
by Fabien Pinckaers
[IMP] folded columns in read_group |
2527 |
all_groups,folded = self._group_by_full[groupby](self, cr, uid, present_group_ids, domain, |
3826
by Olivier Dony
[IMP] read_group: pass superuser uid to group_by_full methods to avoid access right issues |
2528 |
read_group_order=read_group_order, |
2529 |
access_rights_uid=openerp.SUPERUSER_ID, |
|
2530 |
context=context) |
|
3821
by Olivier Dony
[FIX] orm.read_group: fix merge logic for read_group_full: |
2531 |
|
3816
by Olivier Dony
[MERGE] orm.read_group: partial refactoring, by xmo |
2532 |
result_template = dict.fromkeys(aggregated_fields, False) |
4263.3.25
by Xavier Morel
[FIX] weird dict.update calls |
2533 |
result_template[groupby + '_count'] = 0 |
3821
by Olivier Dony
[FIX] orm.read_group: fix merge logic for read_group_full: |
2534 |
if groupby_list and len(groupby_list) > 1: |
4263.3.25
by Xavier Morel
[FIX] weird dict.update calls |
2535 |
result_template['__context'] = {'group_by': groupby_list[1:]} |
3821
by Olivier Dony
[FIX] orm.read_group: fix merge logic for read_group_full: |
2536 |
|
2537 |
# Merge the left_side (current results as dicts) with the right_side (all
|
|
2538 |
# possible values as m2o pairs). Both lists are supposed to be using the
|
|
2539 |
# same ordering, and can be merged in one pass.
|
|
3814
by Olivier Dony
[FIX] orm.read_group: fix issues in read_group_full and rewrite/cleanup |
2540 |
result = [] |
3821
by Olivier Dony
[FIX] orm.read_group: fix merge logic for read_group_full: |
2541 |
known_values = {} |
2542 |
def append_left(left_side): |
|
2543 |
grouped_value = left_side[groupby] and left_side[groupby][0] |
|
2544 |
if not grouped_value in known_values: |
|
2545 |
result.append(left_side) |
|
2546 |
known_values[grouped_value] = left_side |
|
2547 |
else: |
|
2548 |
count_attr = groupby + '_count' |
|
2549 |
known_values[grouped_value].update({count_attr: left_side[count_attr]}) |
|
2550 |
def append_right(right_side): |
|
2551 |
grouped_value = right_side[0] |
|
2552 |
if not grouped_value in known_values: |
|
2553 |
line = dict(result_template) |
|
4263.3.25
by Xavier Morel
[FIX] weird dict.update calls |
2554 |
line[groupby] = right_side |
2555 |
line['__domain'] = [(groupby,'=',grouped_value)] + domain |
|
3821
by Olivier Dony
[FIX] orm.read_group: fix merge logic for read_group_full: |
2556 |
result.append(line) |
2557 |
known_values[grouped_value] = line |
|
3814
by Olivier Dony
[FIX] orm.read_group: fix issues in read_group_full and rewrite/cleanup |
2558 |
while read_group_result or all_groups: |
2559 |
left_side = read_group_result[0] if read_group_result else None |
|
2560 |
right_side = all_groups[0] if all_groups else None |
|
3821
by Olivier Dony
[FIX] orm.read_group: fix merge logic for read_group_full: |
2561 |
assert left_side is None or left_side[groupby] is False \ |
2562 |
or isinstance(left_side[groupby], (tuple,list)), \ |
|
2563 |
'M2O-like pair expected, got %r' % left_side[groupby] |
|
2564 |
assert right_side is None or isinstance(right_side, (tuple,list)), \ |
|
2565 |
'M2O-like pair expected, got %r' % right_side |
|
3814
by Olivier Dony
[FIX] orm.read_group: fix issues in read_group_full and rewrite/cleanup |
2566 |
if left_side is None: |
3821
by Olivier Dony
[FIX] orm.read_group: fix merge logic for read_group_full: |
2567 |
append_right(all_groups.pop(0)) |
3814
by Olivier Dony
[FIX] orm.read_group: fix issues in read_group_full and rewrite/cleanup |
2568 |
elif right_side is None: |
3821
by Olivier Dony
[FIX] orm.read_group: fix merge logic for read_group_full: |
2569 |
append_left(read_group_result.pop(0)) |
2570 |
elif left_side[groupby] == right_side: |
|
2571 |
append_left(read_group_result.pop(0)) |
|
2572 |
all_groups.pop(0) # discard right_side |
|
2573 |
elif not left_side[groupby] or not left_side[groupby][0]: |
|
2574 |
# left side == "Undefined" entry, not present on right_side
|
|
2575 |
append_left(read_group_result.pop(0)) |
|
3814
by Olivier Dony
[FIX] orm.read_group: fix issues in read_group_full and rewrite/cleanup |
2576 |
else: |
3821
by Olivier Dony
[FIX] orm.read_group: fix merge logic for read_group_full: |
2577 |
append_right(all_groups.pop(0)) |
4400
by Fabien Pinckaers
fix |
2578 |
|
4399
by Fabien Pinckaers
[IMP] folded columns in read_group |
2579 |
if folded: |
2580 |
for r in result: |
|
4400
by Fabien Pinckaers
fix |
2581 |
r['__fold'] = folded.get(r[groupby] and r[groupby][0], False) |
3814
by Olivier Dony
[FIX] orm.read_group: fix issues in read_group_full and rewrite/cleanup |
2582 |
return result |
2583 |
||
3029.1.2
by Thibault Francois
[FIX] orm : webclient pass context insted order by, arrange order of args |
2584 |
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False): |
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
2585 |
"""
|
2111.3.21
by Rvo(Open ERP)
[IMP]:improved orm's public methods docstrings |
2586 |
Get the list of records in list view grouped by the given ``groupby`` fields
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
2587 |
|
2588 |
:param cr: database cursor
|
|
2589 |
:param uid: current user id
|
|
2111.3.21
by Rvo(Open ERP)
[IMP]:improved orm's public methods docstrings |
2590 |
:param domain: list specifying search criteria [['field_name', 'operator', 'value'], ...]
|
3404
by Xavier Morel
[IMP] fix wording of read_group documentation |
2591 |
:param list fields: list of fields present in the list view specified on the object
|
2592 |
:param list groupby: fields by which the records will be grouped
|
|
2593 |
:param int offset: optional number of records to skip
|
|
2594 |
:param int limit: optional max number of records to return
|
|
2595 |
:param dict context: context arguments, like lang, time zone
|
|
3508
by Xavier Morel
[FIX] name of ordering param in read_group docstring: it's orderby not order |
2596 |
:param list orderby: optional ``order by`` specification, for
|
2597 |
overriding the natural sort ordering of the
|
|
2598 |
groups, see also :py:meth:`~osv.osv.osv.search`
|
|
2599 |
(supported only for many2one fields currently)
|
|
2111.3.21
by Rvo(Open ERP)
[IMP]:improved orm's public methods docstrings |
2600 |
:return: list of dictionaries(one dictionary for each record) containing:
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
2601 |
|
2111.3.21
by Rvo(Open ERP)
[IMP]:improved orm's public methods docstrings |
2602 |
* the values of fields grouped by the fields in ``groupby`` argument
|
2603 |
* __domain: list of tuples specifying the search criteria
|
|
2604 |
* __context: dictionary with argument like ``groupby``
|
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
2605 |
:rtype: [{'field_name_1': value, ...]
|
2606 |
:raise AccessError: * if user has no read rights on the requested object
|
|
2607 |
* if user tries to bypass access rules for read on the requested object
|
|
2608 |
||
2609 |
"""
|
|
1981
by HDA(OpenERP)
[IMP] group by functionality |
2610 |
context = context or {} |
4344.1.16
by Fabien Pinckaers
[IMP] Removing a bad practice: |
2611 |
self.check_access_rights(cr, uid, 'read') |
1981
by HDA(OpenERP)
[IMP] group by functionality |
2612 |
if not fields: |
1986
by Fabien Pinckaers
[IMP] read_group |
2613 |
fields = self._columns.keys() |
2089.1.9
by Fabien Pinckaers
replaced tab by white spaces |
2614 |
|
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
2615 |
query = self._where_calc(cr, uid, domain, context=context) |
2616 |
self._apply_ir_rules(cr, uid, query, 'read', context=context) |
|
2089.1.9
by Fabien Pinckaers
replaced tab by white spaces |
2617 |
|
2089.1.2
by Olivier Dony
[FIX] attempt at supporting inherited fields (through _inherits) as group_by in read_group() |
2618 |
# Take care of adding join(s) if groupby is an '_inherits'ed field
|
2188
by Fabien Pinckaers
[IMP] group_read |
2619 |
groupby_list = groupby |
3269
by Olivier Dony
[FIX] orm.read_group: prepend fields with table names to avoid collisions in SQL queries |
2620 |
qualified_groupby_field = groupby |
2188
by Fabien Pinckaers
[IMP] group_read |
2621 |
if groupby: |
2728.1.13
by Olivier Dony
[IMP] orm.read_group: implement simple ordering of groups |
2622 |
if isinstance(groupby, list): |
2188
by Fabien Pinckaers
[IMP] group_read |
2623 |
groupby = groupby[0] |
3269
by Olivier Dony
[FIX] orm.read_group: prepend fields with table names to avoid collisions in SQL queries |
2624 |
qualified_groupby_field = self._inherits_join_calc(groupby, query) |
2089.1.9
by Fabien Pinckaers
replaced tab by white spaces |
2625 |
|
3122
by Olivier Dony
[IMP] orm.read_group: raise if called with group_by being a non-database-stored field |
2626 |
if groupby: |
2627 |
assert not groupby or groupby in fields, "Fields in 'groupby' must appear in the list of fields to read (perhaps it's missing in the list view?)" |
|
2628 |
groupby_def = self._columns.get(groupby) or (self._inherit_fields.get(groupby) and self._inherit_fields.get(groupby)[2]) |
|
2629 |
assert groupby_def and groupby_def._classic_write, "Fields in 'groupby' must be regular database-persisted fields (no function or related fields), or function fields with store=True" |
|
2725
by Olivier Dony
[IMP] orm.read_group: remove sql injection vector by forcing group_by to be valid field + assert grouped field is in the view |
2630 |
|
3466.1.9
by Vo Minh Thu
[REF] orm: added TODOs. |
2631 |
# TODO it seems fields_get can be replaced by _all_columns (no need for translation)
|
1986
by Fabien Pinckaers
[IMP] read_group |
2632 |
fget = self.fields_get(cr, uid, fields) |
2188
by Fabien Pinckaers
[IMP] group_read |
2633 |
flist = '' |
3003.1.1
by RGA(OpenERP)
[IMP] group by header should display how many children it has |
2634 |
group_count = group_by = groupby |
2188
by Fabien Pinckaers
[IMP] group_read |
2635 |
if groupby: |
2728.1.13
by Olivier Dony
[IMP] orm.read_group: implement simple ordering of groups |
2636 |
if fget.get(groupby): |
3900
by Olivier Dony
[FIX] read_group: NULL->False for grouped boolean fields |
2637 |
groupby_type = fget[groupby]['type'] |
2638 |
if groupby_type in ('date', 'datetime'): |
|
2639 |
qualified_groupby_field = "to_char(%s,'yyyy-mm')" % qualified_groupby_field |
|
2640 |
flist = "%s as %s " % (qualified_groupby_field, groupby) |
|
2641 |
elif groupby_type == 'boolean': |
|
2642 |
qualified_groupby_field = "coalesce(%s,false)" % qualified_groupby_field |
|
2643 |
flist = "%s as %s " % (qualified_groupby_field, groupby) |
|
2725
by Olivier Dony
[IMP] orm.read_group: remove sql injection vector by forcing group_by to be valid field + assert grouped field is in the view |
2644 |
else: |
3269
by Olivier Dony
[FIX] orm.read_group: prepend fields with table names to avoid collisions in SQL queries |
2645 |
flist = qualified_groupby_field |
2188
by Fabien Pinckaers
[IMP] group_read |
2646 |
else: |
2743
by nch at tinyerp
[FIX]:default sorting in group by |
2647 |
# Don't allow arbitrary values, as this would be a SQL injection vector!
|
2725
by Olivier Dony
[IMP] orm.read_group: remove sql injection vector by forcing group_by to be valid field + assert grouped field is in the view |
2648 |
raise except_orm(_('Invalid group_by'), |
2649 |
_('Invalid group_by specification: "%s".\nA group_by specification must be a list of valid fields.')%(groupby,)) |
|
2188
by Fabien Pinckaers
[IMP] group_read |
2650 |
|
3806.1.1
by Xavier Morel
[IMP] selection of fields to aggregate in read_group |
2651 |
aggregated_fields = [ |
2652 |
f for f in fields |
|
2653 |
if f not in ('id', 'sequence') |
|
2654 |
if fget[f]['type'] in ('integer', 'float') |
|
3816
by Olivier Dony
[MERGE] orm.read_group: partial refactoring, by xmo |
2655 |
if (f in self._columns and getattr(self._columns[f], '_classic_write'))] |
3806.1.1
by Xavier Morel
[IMP] selection of fields to aggregate in read_group |
2656 |
for f in aggregated_fields: |
2657 |
group_operator = fget[f].get('group_operator', 'sum') |
|
2658 |
if flist: |
|
2659 |
flist += ', ' |
|
2660 |
qualified_field = '"%s"."%s"' % (self._table, f) |
|
2661 |
flist += "%s(%s) AS %s" % (group_operator, qualified_field, f) |
|
2089.1.9
by Fabien Pinckaers
replaced tab by white spaces |
2662 |
|
3269
by Olivier Dony
[FIX] orm.read_group: prepend fields with table names to avoid collisions in SQL queries |
2663 |
gb = groupby and (' GROUP BY ' + qualified_groupby_field) or '' |
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
2664 |
|
2665 |
from_clause, where_clause, where_clause_params = query.get_sql() |
|
2666 |
where_clause = where_clause and ' WHERE ' + where_clause |
|
2667 |
limit_str = limit and ' limit %d' % limit or '' |
|
2668 |
offset_str = offset and ' offset %d' % offset or '' |
|
3003.1.1
by RGA(OpenERP)
[IMP] group by header should display how many children it has |
2669 |
if len(groupby_list) < 2 and context.get('group_by_no_leaf'): |
2670 |
group_count = '_' |
|
3134
by Olivier Dony
[FIX] read_group: better support for empty 'fields' or 'groupby' parameters |
2671 |
cr.execute('SELECT min(%s.id) AS id, count(%s.id) AS %s_count' % (self._table, self._table, group_count) + (flist and ',') + flist + ' FROM ' + from_clause + where_clause + gb + limit_str + offset_str, where_clause_params) |
1986
by Fabien Pinckaers
[IMP] read_group |
2672 |
alldata = {} |
1869.1.50
by nch at tinyerp
[FIX]:date display format |
2673 |
groupby = group_by |
1986
by Fabien Pinckaers
[IMP] read_group |
2674 |
for r in cr.dictfetchall(): |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
2675 |
for fld, val in r.items(): |
4682.2.2
by Xavier Morel
[IMP] compare to None by identity |
2676 |
if val is None: r[fld] = False |
1986
by Fabien Pinckaers
[IMP] read_group |
2677 |
alldata[r['id']] = r |
2678 |
del r['id'] |
|
3070
by Olivier Dony
[FIX] orm.read_group: support ordering by any field (not just m2o ones) |
2679 |
|
3821
by Olivier Dony
[FIX] orm.read_group: fix merge logic for read_group_full: |
2680 |
order = orderby or groupby |
2681 |
data_ids = self.search(cr, uid, [('id', 'in', alldata.keys())], order=order, context=context) |
|
4761.2.20
by Antonin Bourguignon
[IMP] fix a comment's indentation, remove a few whitespaces |
2682 |
|
4066.1.143
by Olivier Dony
[FIX] read_group: empty `groupby` case broken by last read_group patch |
2683 |
# the IDs of records that have groupby field value = False or '' should be included too
|
2684 |
data_ids += set(alldata.keys()).difference(data_ids) |
|
4761.2.20
by Antonin Bourguignon
[IMP] fix a comment's indentation, remove a few whitespaces |
2685 |
|
2686 |
if groupby: |
|
4066.1.143
by Olivier Dony
[FIX] read_group: empty `groupby` case broken by last read_group patch |
2687 |
data = self.read(cr, uid, data_ids, [groupby], context=context) |
2688 |
# restore order of the search as read() uses the default _order (this is only for groups, so the footprint of data should be small):
|
|
2689 |
data_dict = dict((d['id'], d[groupby] ) for d in data) |
|
2690 |
result = [{'id': i, groupby: data_dict[i]} for i in data_ids] |
|
2691 |
else: |
|
4761.2.20
by Antonin Bourguignon
[IMP] fix a comment's indentation, remove a few whitespaces |
2692 |
result = [{'id': i} for i in data_ids] |
3070
by Olivier Dony
[FIX] orm.read_group: support ordering by any field (not just m2o ones) |
2693 |
|
4066.1.135
by Olivier Dony
[FIX] read_group: remove prohibitive n^2 operations, could freeze the server on large group_by results |
2694 |
for d in result: |
2188
by Fabien Pinckaers
[IMP] group_read |
2695 |
if groupby: |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
2696 |
d['__domain'] = [(groupby, '=', alldata[d['id']][groupby] or False)] + domain |
2697 |
if not isinstance(groupby_list, (str, unicode)): |
|
2205
by nch at tinyerp
[IMP]:group_by to support no_leaf in multi level group by |
2698 |
if groupby or not context.get('group_by_no_leaf', False): |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
2699 |
d['__context'] = {'group_by': groupby_list[1:]} |
2682
by olt at tinyerp
[FIX] missing comma in sql query 'execute' call ("select count...") |
2700 |
if groupby and groupby in fget: |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
2701 |
if d[groupby] and fget[groupby]['type'] in ('date', 'datetime'): |
2702 |
dt = datetime.datetime.strptime(alldata[d['id']][groupby][:7], '%Y-%m') |
|
2626
by Olivier Dony
[IMP/REF] orm: added support of ir.rules coming from _inherits parents |
2703 |
days = calendar.monthrange(dt.year, dt.month)[1] |
1869.1.50
by nch at tinyerp
[FIX]:date display format |
2704 |
|
3790.1.1
by Xavier Morel
[IMP] Use babel's locale-aware date formatting when formatting dates in read_group titles |
2705 |
date_value = datetime.datetime.strptime(d[groupby][:10], '%Y-%m-%d') |
2706 |
d[groupby] = babel.dates.format_date( |
|
2707 |
date_value, format='MMMM yyyy', locale=context.get('lang', 'en_US')) |
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
2708 |
d['__domain'] = [(groupby, '>=', alldata[d['id']][groupby] and datetime.datetime.strptime(alldata[d['id']][groupby][:7] + '-01', '%Y-%m-%d').strftime('%Y-%m-%d') or False),\ |
2709 |
(groupby, '<=', alldata[d['id']][groupby] and datetime.datetime.strptime(alldata[d['id']][groupby][:7] + '-' + str(days), '%Y-%m-%d').strftime('%Y-%m-%d') or False)] + domain |
|
2188
by Fabien Pinckaers
[IMP] group_read |
2710 |
del alldata[d['id']][groupby] |
1986
by Fabien Pinckaers
[IMP] read_group |
2711 |
d.update(alldata[d['id']]) |
2712 |
del d['id'] |
|
3791
by Fabien Pinckaers
[IMP] _group_by_full implementation |
2713 |
|
3817
by Olivier Dony
[FIX] orm.read_group: check for empty groupby list |
2714 |
if groupby and groupby in self._group_by_full: |
4066.1.135
by Olivier Dony
[FIX] read_group: remove prohibitive n^2 operations, could freeze the server on large group_by results |
2715 |
result = self._read_group_fill_results(cr, uid, domain, groupby, groupby_list, |
2716 |
aggregated_fields, result, read_group_order=order, |
|
2717 |
context=context) |
|
3816
by Olivier Dony
[MERGE] orm.read_group: partial refactoring, by xmo |
2718 |
|
4066.1.135
by Olivier Dony
[FIX] read_group: remove prohibitive n^2 operations, could freeze the server on large group_by results |
2719 |
return result |
1981
by HDA(OpenERP)
[IMP] group by functionality |
2720 |
|
4601.2.40
by Thibault Delavallée
[IMP] [WIP] order_by: now using aliases in _generate_order_by, _generate_o2m_order_by, _inherits_join_add, ... added some tests. Next comits will clean a bit the code, because currently it is a bit messy. |
2721 |
def _inherits_join_add(self, current_model, parent_model_name, query): |
2626
by Olivier Dony
[IMP/REF] orm: added support of ir.rules coming from _inherits parents |
2722 |
"""
|
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
2723 |
Add missing table SELECT and JOIN clause to ``query`` for reaching the parent table (no duplicates)
|
4601.2.40
by Thibault Delavallée
[IMP] [WIP] order_by: now using aliases in _generate_order_by, _generate_o2m_order_by, _inherits_join_add, ... added some tests. Next comits will clean a bit the code, because currently it is a bit messy. |
2724 |
:param current_model: current model object
|
2626
by Olivier Dony
[IMP/REF] orm: added support of ir.rules coming from _inherits parents |
2725 |
:param parent_model_name: name of the parent model for which the clauses should be added
|
2793.1.3
by Olivier Dony
[IMP] osv.query,orm: removed trailing whitespace introduced by previous commits |
2726 |
:param query: query object on which the JOIN should be added
|
2626
by Olivier Dony
[IMP/REF] orm: added support of ir.rules coming from _inherits parents |
2727 |
"""
|
4601.2.40
by Thibault Delavallée
[IMP] [WIP] order_by: now using aliases in _generate_order_by, _generate_o2m_order_by, _inherits_join_add, ... added some tests. Next comits will clean a bit the code, because currently it is a bit messy. |
2728 |
inherits_field = current_model._inherits[parent_model_name] |
2626
by Olivier Dony
[IMP/REF] orm: added support of ir.rules coming from _inherits parents |
2729 |
parent_model = self.pool.get(parent_model_name) |
4601.2.44
by Thibault Delavallée
[CLEAN] Query: cleaned a bit the code. All joins now goes through the same method, either implicit or explicit. Will have to be upgraded in future versions, but at least this is a bit centralized. Updated ORM accordingly. Updated tests. Added a get_alias_from_query method in expression that find the table and the alias from a 'full alias' statement. |
2730 |
parent_alias, parent_alias_statement = query.add_join((current_model._table, parent_model._table, inherits_field, 'id', inherits_field), implicit=True) |
4601.2.40
by Thibault Delavallée
[IMP] [WIP] order_by: now using aliases in _generate_order_by, _generate_o2m_order_by, _inherits_join_add, ... added some tests. Next comits will clean a bit the code, because currently it is a bit messy. |
2731 |
return parent_alias |
3549.4.1
by Naresh(OpenERP)
[FIX]:_inherits for multilevel inheritence |
2732 |
|
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
2733 |
def _inherits_join_calc(self, field, query): |
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
2734 |
"""
|
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
2735 |
Adds missing table select and join clause(s) to ``query`` for reaching
|
2626
by Olivier Dony
[IMP/REF] orm: added support of ir.rules coming from _inherits parents |
2736 |
the field coming from an '_inherits' parent table (no duplicates).
|
2737 |
||
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
2738 |
:param field: name of inherited field to reach
|
2739 |
:param query: query object on which the JOIN should be added
|
|
2740 |
:return: qualified name of field, to be used in SELECT clause
|
|
2089.1.2
by Olivier Dony
[FIX] attempt at supporting inherited fields (through _inherits) as group_by in read_group() |
2741 |
"""
|
2742 |
current_table = self |
|
4601.2.48
by Thibault Delavallée
[CLEAN] orm: added quote around a forgottent table name; cleaned a bit some code and added comments, removed dead code. |
2743 |
parent_alias = '"%s"' % current_table._table |
2089.1.2
by Olivier Dony
[FIX] attempt at supporting inherited fields (through _inherits) as group_by in read_group() |
2744 |
while field in current_table._inherit_fields and not field in current_table._columns: |
2626
by Olivier Dony
[IMP/REF] orm: added support of ir.rules coming from _inherits parents |
2745 |
parent_model_name = current_table._inherit_fields[field][0] |
2746 |
parent_table = self.pool.get(parent_model_name) |
|
4601.2.40
by Thibault Delavallée
[IMP] [WIP] order_by: now using aliases in _generate_order_by, _generate_o2m_order_by, _inherits_join_add, ... added some tests. Next comits will clean a bit the code, because currently it is a bit messy. |
2747 |
parent_alias = self._inherits_join_add(current_table, parent_model_name, query) |
2089.1.2
by Olivier Dony
[FIX] attempt at supporting inherited fields (through _inherits) as group_by in read_group() |
2748 |
current_table = parent_table |
4601.2.40
by Thibault Delavallée
[IMP] [WIP] order_by: now using aliases in _generate_order_by, _generate_o2m_order_by, _inherits_join_add, ... added some tests. Next comits will clean a bit the code, because currently it is a bit messy. |
2749 |
return '%s."%s"' % (parent_alias, field) |
2089.1.2
by Olivier Dony
[FIX] attempt at supporting inherited fields (through _inherits) as group_by in read_group() |
2750 |
|
974
by Fabien Pinckaers
Speed Improvement: |
2751 |
def _parent_store_compute(self, cr): |
2238.1.56
by Christophe Simonis
[IMP] orm: allow import and record creation to defer the computation of parent_left/right,[IMP] orm: do not update parent_left/right if the parent haven't been changed |
2752 |
if not self._parent_store: |
2753 |
return
|
|
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
2754 |
_logger.info('Computing parent left and right for table %s...', self._table) |
961
by Fabien Pinckaers
Speed Improvement in recursive functions and trees: |
2755 |
def browse_rec(root, pos=0): |
4761.2.20
by Antonin Bourguignon
[IMP] fix a comment's indentation, remove a few whitespaces |
2756 |
# TODO: set order
|
961
by Fabien Pinckaers
Speed Improvement in recursive functions and trees: |
2757 |
where = self._parent_name+'='+str(root) |
2758 |
if not root: |
|
2759 |
where = self._parent_name+' IS NULL' |
|
1595.1.7
by Fabien Pinckaers
bugfixes |
2760 |
if self._parent_order: |
2761 |
where += ' order by '+self._parent_order |
|
961
by Fabien Pinckaers
Speed Improvement in recursive functions and trees: |
2762 |
cr.execute('SELECT id FROM '+self._table+' WHERE '+where) |
2763 |
pos2 = pos + 1 |
|
3006
by Numerigraphe - Lionel Sausin
[FIX] incorrect spellings (childs -> children) |
2764 |
for id in cr.fetchall(): |
961
by Fabien Pinckaers
Speed Improvement in recursive functions and trees: |
2765 |
pos2 = browse_rec(id[0], pos2) |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
2766 |
cr.execute('update '+self._table+' set parent_left=%s, parent_right=%s where id=%s', (pos, pos2, root)) |
2767 |
return pos2 + 1 |
|
1640.2.6
by Fabien Pinckaers
bugfix_ptt |
2768 |
query = 'SELECT id FROM '+self._table+' WHERE '+self._parent_name+' IS NULL' |
2769 |
if self._parent_order: |
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
2770 |
query += ' order by ' + self._parent_order |
1640.2.6
by Fabien Pinckaers
bugfix_ptt |
2771 |
pos = 0 |
2772 |
cr.execute(query) |
|
2773 |
for (root,) in cr.fetchall(): |
|
2774 |
pos = browse_rec(root, pos) |
|
961
by Fabien Pinckaers
Speed Improvement in recursive functions and trees: |
2775 |
return True |
2776 |
||
1398
by Fabien Pinckaers
improvements_bugfixes |
2777 |
def _update_store(self, cr, f, k): |
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
2778 |
_logger.info("storing computed values of fields.function '%s'", k) |
1398
by Fabien Pinckaers
improvements_bugfixes |
2779 |
ss = self._columns[k]._symbol_set |
2780 |
update_query = 'UPDATE "%s" SET "%s"=%s WHERE id=%%s' % (self._table, k, ss[0]) |
|
2781 |
cr.execute('select id from '+self._table) |
|
2782 |
ids_lst = map(lambda x: x[0], cr.fetchall()) |
|
2783 |
while ids_lst: |
|
2784 |
iids = ids_lst[:40] |
|
2785 |
ids_lst = ids_lst[40:] |
|
3511.1.35
by Olivier Dony
[IMP] start unifying the SUPERUSER_ID constant |
2786 |
res = f.get(cr, self, iids, k, SUPERUSER_ID, {}) |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
2787 |
for key, val in res.items(): |
1398
by Fabien Pinckaers
improvements_bugfixes |
2788 |
if f._multi: |
2789 |
val = val[k] |
|
2790 |
# if val is a many2one, just write the ID
|
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
2791 |
if type(val) == tuple: |
1398
by Fabien Pinckaers
improvements_bugfixes |
2792 |
val = val[0] |
3974.2.3
by Florent Xicluna
[REF] replace deprecated <> symbol. |
2793 |
if val is not False: |
1398
by Fabien Pinckaers
improvements_bugfixes |
2794 |
cr.execute(update_query, (ss[1](val), key)) |
2795 |
||
3129.1.2
by Olivier Dony
[FIX] fields.reference: don't accept half-defined references, and avoid crashing on previous bad ones |
2796 |
def _check_selection_field_value(self, cr, uid, field, value, context=None): |
2797 |
"""Raise except_orm if value is not among the valid values for the selection field"""
|
|
2798 |
if self._columns[field]._type == 'reference': |
|
2799 |
val_model, val_id_str = value.split(',', 1) |
|
2800 |
val_id = False |
|
2801 |
try: |
|
2802 |
val_id = long(val_id_str) |
|
2803 |
except ValueError: |
|
2804 |
pass
|
|
2805 |
if not val_id: |
|
2806 |
raise except_orm(_('ValidateError'), |
|
3630
by Vo Minh Thu
[MERGE] orm: better exception messsages. |
2807 |
_('Invalid value for reference field "%s.%s" (last part must be a non-zero integer): "%s"') % (self._table, field, value)) |
3129.1.2
by Olivier Dony
[FIX] fields.reference: don't accept half-defined references, and avoid crashing on previous bad ones |
2808 |
val = val_model |
2809 |
else: |
|
2810 |
val = value |
|
2811 |
if isinstance(self._columns[field].selection, (tuple, list)): |
|
2812 |
if val in dict(self._columns[field].selection): |
|
2813 |
return
|
|
2814 |
elif val in dict(self._columns[field].selection(self, cr, uid, context=context)): |
|
2815 |
return
|
|
2816 |
raise except_orm(_('ValidateError'), |
|
3974.2.2
by Florent Xicluna
[REF] fix weird indentation, not multiple of four. |
2817 |
_('The value "%s" for the field "%s.%s" is not in the selection') % (value, self._table, field)) |
3129.1.2
by Olivier Dony
[FIX] fields.reference: don't accept half-defined references, and avoid crashing on previous bad ones |
2818 |
|
1673
by Christophe Simonis
[IMP] the module graph is updated and loaded until there are modules to load. |
2819 |
def _check_removed_columns(self, cr, log=False): |
2820 |
# iterate on the database columns to drop the NOT NULL constraints
|
|
2821 |
# of fields which were required but have been removed (or will be added by another module)
|
|
2822 |
columns = [c for c in self._columns if not (isinstance(self._columns[c], fields.function) and not self._columns[c].store)] |
|
3647
by Olivier Dony
[FIX] orm,expression: sanity checks for domain terms, cleanup, tests |
2823 |
columns += MAGIC_COLUMNS |
1673
by Christophe Simonis
[IMP] the module graph is updated and loaded until there are modules to load. |
2824 |
cr.execute("SELECT a.attname, a.attnotnull" |
2825 |
" FROM pg_class c, pg_attribute a"
|
|
2334.1.4
by Anup(OpenERP)
[IMP] removed the possible SQL injection server. |
2826 |
" WHERE c.relname=%s" |
1673
by Christophe Simonis
[IMP] the module graph is updated and loaded until there are modules to load. |
2827 |
" AND c.oid=a.attrelid"
|
2334.1.4
by Anup(OpenERP)
[IMP] removed the possible SQL injection server. |
2828 |
" AND a.attisdropped=%s" |
1673
by Christophe Simonis
[IMP] the module graph is updated and loaded until there are modules to load. |
2829 |
" AND pg_catalog.format_type(a.atttypid, a.atttypmod) NOT IN ('cid', 'tid', 'oid', 'xid')"
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
2830 |
" AND a.attname NOT IN %s", (self._table, False, tuple(columns))), |
2334.1.4
by Anup(OpenERP)
[IMP] removed the possible SQL injection server. |
2831 |
|
1673
by Christophe Simonis
[IMP] the module graph is updated and loaded until there are modules to load. |
2832 |
for column in cr.dictfetchall(): |
2833 |
if log: |
|
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
2834 |
_logger.debug("column %s is in the table %s but not in the corresponding object %s", |
2835 |
column['attname'], self._table, self._name) |
|
1673
by Christophe Simonis
[IMP] the module graph is updated and loaded until there are modules to load. |
2836 |
if column['attnotnull']: |
2837 |
cr.execute('ALTER TABLE "%s" ALTER COLUMN "%s" DROP NOT NULL' % (self._table, column['attname'])) |
|
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
2838 |
_schema.debug("Table '%s': column '%s': dropped NOT NULL constraint", |
2839 |
self._table, column['attname']) |
|
1673
by Christophe Simonis
[IMP] the module graph is updated and loaded until there are modules to load. |
2840 |
|
4178.1.1
by Vo Minh Thu
[IMP] uninstall: started test module, added new ir_model_constraint table. |
2841 |
def _save_constraint(self, cr, constraint_name, type): |
4178.1.4
by Vo Minh Thu
[IMP] uninstall: added some docstrings. |
2842 |
"""
|
2843 |
Record the creation of a constraint for this model, to make it possible
|
|
2844 |
to delete it later when the module is uninstalled. Type can be either
|
|
2845 |
'f' or 'u' depending on the constraing being a foreign key or not.
|
|
2846 |
"""
|
|
4178.1.1
by Vo Minh Thu
[IMP] uninstall: started test module, added new ir_model_constraint table. |
2847 |
assert type in ('f', 'u') |
2848 |
cr.execute(""" |
|
2849 |
SELECT 1 FROM ir_model_constraint, ir_module_module
|
|
2850 |
WHERE ir_model_constraint.module=ir_module_module.id
|
|
2851 |
AND ir_model_constraint.name=%s |
|
2852 |
AND ir_module_module.name=%s |
|
2853 |
""", (constraint_name, self._module)) |
|
2854 |
if not cr.rowcount: |
|
2855 |
cr.execute(""" |
|
2856 |
INSERT INTO ir_model_constraint
|
|
2857 |
(name, date_init, date_update, module, model, type)
|
|
2858 |
VALUES (%s, now() AT TIME ZONE 'UTC', now() AT TIME ZONE 'UTC', |
|
2859 |
(SELECT id FROM ir_module_module WHERE name=%s), |
|
2860 |
(SELECT id FROM ir_model WHERE model=%s), %s)""", |
|
2861 |
(constraint_name, self._module, self._name, type)) |
|
2862 |
||
4178.1.3
by Vo Minh Thu
[IMP] uninstall: use a dedicated table for many2many relationships instead of overlaoding ir.model.data. |
2863 |
def _save_relation_table(self, cr, relation_table): |
4178.1.4
by Vo Minh Thu
[IMP] uninstall: added some docstrings. |
2864 |
"""
|
2865 |
Record the creation of a many2many for this model, to make it possible
|
|
2866 |
to delete it later when the module is uninstalled.
|
|
2867 |
"""
|
|
4178.1.3
by Vo Minh Thu
[IMP] uninstall: use a dedicated table for many2many relationships instead of overlaoding ir.model.data. |
2868 |
cr.execute(""" |
2869 |
SELECT 1 FROM ir_model_relation, ir_module_module
|
|
2870 |
WHERE ir_model_relation.module=ir_module_module.id
|
|
2871 |
AND ir_model_relation.name=%s |
|
2872 |
AND ir_module_module.name=%s |
|
2873 |
""", (relation_table, self._module)) |
|
4074.1.27
by Olivier Dony
[IMP] orm,ir.model: improve registration and deletion of schema ext_ids |
2874 |
if not cr.rowcount: |
4178.1.3
by Vo Minh Thu
[IMP] uninstall: use a dedicated table for many2many relationships instead of overlaoding ir.model.data. |
2875 |
cr.execute("""INSERT INTO ir_model_relation (name, date_init, date_update, module, model) |
2876 |
VALUES (%s, now() AT TIME ZONE 'UTC', now() AT TIME ZONE 'UTC', |
|
2877 |
(SELECT id FROM ir_module_module WHERE name=%s), |
|
2878 |
(SELECT id FROM ir_model WHERE model=%s))""", |
|
2879 |
(relation_table, self._module, self._name)) |
|
4074.1.27
by Olivier Dony
[IMP] orm,ir.model: improve registration and deletion of schema ext_ids |
2880 |
|
3511.1.36
by Olivier Dony
[IMP] orm: enforce proper m2o ondelete rules between TransientModels and Models |
2881 |
# checked version: for direct m2o starting from `self`
|
2882 |
def _m2o_add_foreign_key_checked(self, source_field, dest_model, ondelete): |
|
2883 |
assert self.is_transient() or not dest_model.is_transient(), \ |
|
2884 |
'Many2One relationships from non-transient Model to TransientModel are forbidden'
|
|
2885 |
if self.is_transient() and not dest_model.is_transient(): |
|
2886 |
# TransientModel relationships to regular Models are annoying
|
|
2887 |
# usually because they could block deletion due to the FKs.
|
|
2888 |
# So unless stated otherwise we default them to ondelete=cascade.
|
|
2889 |
ondelete = ondelete or 'cascade' |
|
4701
by Olivier Dony
[FIX] orm,registry: properly check m2o FKs during model update + fix some models `auto_init`ed multiple times |
2890 |
fk_def = (self._table, source_field, dest_model._table, ondelete or 'set null') |
2891 |
self._foreign_keys.add(fk_def) |
|
2892 |
_schema.debug("Table '%s': added foreign key '%s' with definition=REFERENCES \"%s\" ON DELETE %s", *fk_def) |
|
3511.1.36
by Olivier Dony
[IMP] orm: enforce proper m2o ondelete rules between TransientModels and Models |
2893 |
|
2894 |
# unchecked version: for custom cases, such as m2m relationships
|
|
2895 |
def _m2o_add_foreign_key_unchecked(self, source_table, source_field, dest_model, ondelete): |
|
4701
by Olivier Dony
[FIX] orm,registry: properly check m2o FKs during model update + fix some models `auto_init`ed multiple times |
2896 |
fk_def = (source_table, source_field, dest_model._table, ondelete or 'set null') |
2897 |
self._foreign_keys.add(fk_def) |
|
2898 |
_schema.debug("Table '%s': added foreign key '%s' with definition=REFERENCES \"%s\" ON DELETE %s", *fk_def) |
|
3511.1.36
by Olivier Dony
[IMP] orm: enforce proper m2o ondelete rules between TransientModels and Models |
2899 |
|
4066.1.11
by Olivier Dony
[FIX] orm: model auto-init - better algorithm for detecting stale constraints during update |
2900 |
def _drop_constraint(self, cr, source_table, constraint_name): |
2901 |
cr.execute("ALTER TABLE %s DROP CONSTRAINT %s" % (source_table,constraint_name)) |
|
2902 |
||
2903 |
def _m2o_fix_foreign_key(self, cr, source_table, source_field, dest_model, ondelete): |
|
2904 |
# Find FK constraint(s) currently established for the m2o field,
|
|
4466.1.2
by Christophe Simonis
[IMP] orm: remove trailing spaces |
2905 |
# and see whether they are stale or not
|
4066.1.11
by Olivier Dony
[FIX] orm: model auto-init - better algorithm for detecting stale constraints during update |
2906 |
cr.execute("""SELECT confdeltype as ondelete_rule, conname as constraint_name, |
2907 |
cl2.relname as foreign_table
|
|
2908 |
FROM pg_constraint as con, pg_class as cl1, pg_class as cl2,
|
|
2909 |
pg_attribute as att1, pg_attribute as att2
|
|
4466.1.2
by Christophe Simonis
[IMP] orm: remove trailing spaces |
2910 |
WHERE con.conrelid = cl1.oid
|
2911 |
AND cl1.relname = %s |
|
2912 |
AND con.confrelid = cl2.oid
|
|
2913 |
AND array_lower(con.conkey, 1) = 1
|
|
2914 |
AND con.conkey[1] = att1.attnum
|
|
2915 |
AND att1.attrelid = cl1.oid
|
|
2916 |
AND att1.attname = %s |
|
2917 |
AND array_lower(con.confkey, 1) = 1
|
|
2918 |
AND con.confkey[1] = att2.attnum
|
|
2919 |
AND att2.attrelid = cl2.oid
|
|
2920 |
AND att2.attname = %s |
|
4066.1.11
by Olivier Dony
[FIX] orm: model auto-init - better algorithm for detecting stale constraints during update |
2921 |
AND con.contype = 'f'""", (source_table, source_field, 'id')) |
2922 |
constraints = cr.dictfetchall() |
|
2923 |
if constraints: |
|
2924 |
if len(constraints) == 1: |
|
2925 |
# Is it the right constraint?
|
|
4466.1.2
by Christophe Simonis
[IMP] orm: remove trailing spaces |
2926 |
cons, = constraints |
4066.1.11
by Olivier Dony
[FIX] orm: model auto-init - better algorithm for detecting stale constraints during update |
2927 |
if cons['ondelete_rule'] != POSTGRES_CONFDELTYPES.get((ondelete or 'set null').upper(), 'a')\ |
2928 |
or cons['foreign_table'] != dest_model._table: |
|
4701
by Olivier Dony
[FIX] orm,registry: properly check m2o FKs during model update + fix some models `auto_init`ed multiple times |
2929 |
# Wrong FK: drop it and recreate
|
4066.1.11
by Olivier Dony
[FIX] orm: model auto-init - better algorithm for detecting stale constraints during update |
2930 |
_schema.debug("Table '%s': dropping obsolete FK constraint: '%s'", |
2931 |
source_table, cons['constraint_name']) |
|
2932 |
self._drop_constraint(cr, source_table, cons['constraint_name']) |
|
4701
by Olivier Dony
[FIX] orm,registry: properly check m2o FKs during model update + fix some models `auto_init`ed multiple times |
2933 |
else: |
2934 |
# it's all good, nothing to do!
|
|
2935 |
return
|
|
4066.1.11
by Olivier Dony
[FIX] orm: model auto-init - better algorithm for detecting stale constraints during update |
2936 |
else: |
2937 |
# Multiple FKs found for the same field, drop them all, and re-create
|
|
2938 |
for cons in constraints: |
|
2939 |
_schema.debug("Table '%s': dropping duplicate FK constraints: '%s'", |
|
2940 |
source_table, cons['constraint_name']) |
|
2941 |
self._drop_constraint(cr, source_table, cons['constraint_name']) |
|
4701
by Olivier Dony
[FIX] orm,registry: properly check m2o FKs during model update + fix some models `auto_init`ed multiple times |
2942 |
|
2943 |
# (re-)create the FK
|
|
2944 |
self._m2o_add_foreign_key_checked(source_field, dest_model, ondelete) |
|
4066.1.11
by Olivier Dony
[FIX] orm: model auto-init - better algorithm for detecting stale constraints during update |
2945 |
|
2946 |
||
2947 |
||
2537.1.2
by Numerigraphe - Lionel Sausin
[FIX] mutable default in osv |
2948 |
def _auto_init(self, cr, context=None): |
3432.1.4
by Vo Minh Thu
[IMP] osv: removed unnecessary module arg, added comments. |
2949 |
"""
|
2950 |
||
2951 |
Call _field_create and, unless _auto is False:
|
|
2952 |
||
2953 |
- create the corresponding table in database for the model,
|
|
2954 |
- possibly add the parent columns in database,
|
|
2955 |
- possibly add the columns 'create_uid', 'create_date', 'write_uid',
|
|
2956 |
'write_date' in database if _log_access is True (the default),
|
|
2957 |
- report on database columns no more existing in _columns,
|
|
2958 |
- remove no more existing not null constraints,
|
|
2959 |
- alter existing database columns to match _columns,
|
|
2960 |
- create database tables to match _columns,
|
|
2961 |
- add database indices to match _columns,
|
|
3453.2.2
by Vo Minh Thu
[IMP] orm: added comment, and delete an attribute when it is no more needed. |
2962 |
- save in self._foreign_keys a list a foreign keys to create (see
|
2963 |
_auto_end).
|
|
3432.1.4
by Vo Minh Thu
[IMP] osv: removed unnecessary module arg, added comments. |
2964 |
|
2965 |
"""
|
|
4701
by Olivier Dony
[FIX] orm,registry: properly check m2o FKs during model update + fix some models `auto_init`ed multiple times |
2966 |
self._foreign_keys = set() |
3417.6.13
by Vo Minh Thu
[IMP] orm: check for object _name validity. |
2967 |
raise_on_invalid_object_name(self._name) |
2537.1.2
by Numerigraphe - Lionel Sausin
[FIX] mutable default in osv |
2968 |
if context is None: |
2969 |
context = {} |
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
2970 |
store_compute = False |
1398
by Fabien Pinckaers
improvements_bugfixes |
2971 |
todo_end = [] |
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
2972 |
update_custom_fields = context.get('update_custom_fields', False) |
922
by Christophe Simonis
convert tabs to 4 spaces |
2973 |
self._field_create(cr, context=context) |
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
2974 |
create = not self._table_exist(cr) |
1958.2.5
by Xavier Morel
[imp] replace hasattr-based attribute selection by getattr |
2975 |
if getattr(self, '_auto', True): |
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
2976 |
|
2977 |
if create: |
|
2978 |
self._create_table(cr) |
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
2979 |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
2980 |
cr.commit() |
961
by Fabien Pinckaers
Speed Improvement in recursive functions and trees: |
2981 |
if self._parent_store: |
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
2982 |
if not self._parent_columns_exist(cr): |
2983 |
self._create_parent_columns(cr) |
|
961
by Fabien Pinckaers
Speed Improvement in recursive functions and trees: |
2984 |
store_compute = True |
2985 |
||
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
2986 |
# Create the create_uid, create_date, write_uid, write_date, columns if desired.
|
922
by Christophe Simonis
convert tabs to 4 spaces |
2987 |
if self._log_access: |
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
2988 |
self._add_log_columns(cr) |
1727
by mra (Open ERP)
fix : 329208 : trans_obj is not defined |
2989 |
|
1673
by Christophe Simonis
[IMP] the module graph is updated and loaded until there are modules to load. |
2990 |
self._check_removed_columns(cr, log=False) |
922
by Christophe Simonis
convert tabs to 4 spaces |
2991 |
|
2992 |
# iterate on the "object columns"
|
|
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
2993 |
column_data = self._select_column_data(cr) |
2994 |
||
2995 |
for k, f in self._columns.iteritems(): |
|
3647
by Olivier Dony
[FIX] orm,expression: sanity checks for domain terms, cleanup, tests |
2996 |
if k in MAGIC_COLUMNS: |
922
by Christophe Simonis
convert tabs to 4 spaces |
2997 |
continue
|
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
2998 |
# Don't update custom (also called manual) fields
|
2999 |
if f.manual and not update_custom_fields: |
|
1844.4.103
by Jay(Open ERP)
[FIX] Custom fields won't disturb anything while updating server |
3000 |
continue
|
2967
by Fabien Pinckaers
[IMP] Speed improvement when loading modules |
3001 |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
3002 |
if isinstance(f, fields.one2many): |
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3003 |
self._o2m_raise_on_missing_reference(cr, f) |
3004 |
||
922
by Christophe Simonis
convert tabs to 4 spaces |
3005 |
elif isinstance(f, fields.many2many): |
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3006 |
self._m2m_raise_or_create_relation(cr, f) |
3007 |
||
922
by Christophe Simonis
convert tabs to 4 spaces |
3008 |
else: |
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3009 |
res = column_data.get(k) |
3010 |
||
3011 |
# The field is not found as-is in database, try if it
|
|
3012 |
# exists with an old name.
|
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
3013 |
if not res and hasattr(f, 'oldname'): |
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3014 |
res = column_data.get(f.oldname) |
3015 |
if res: |
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
3016 |
cr.execute('ALTER TABLE "%s" RENAME "%s" TO "%s"' % (self._table, f.oldname, k)) |
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3017 |
res['attname'] = k |
3018 |
column_data[k] = res |
|
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
3019 |
_schema.debug("Table '%s': renamed column '%s' to '%s'", |
3020 |
self._table, f.oldname, k) |
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
3021 |
|
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3022 |
# The field already exists in database. Possibly
|
3023 |
# change its type, rename it, drop it or change its
|
|
3024 |
# constraints.
|
|
3025 |
if res: |
|
3026 |
f_pg_type = res['typname'] |
|
3027 |
f_pg_size = res['size'] |
|
3028 |
f_pg_notnull = res['attnotnull'] |
|
1958.2.5
by Xavier Morel
[imp] replace hasattr-based attribute selection by getattr |
3029 |
if isinstance(f, fields.function) and not f.store and\ |
3030 |
not getattr(f, 'nodrop', False): |
|
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
3031 |
_logger.info('column %s (%s) in table %s removed: converted to a function !\n', |
3032 |
k, f.string, self._table) |
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
3033 |
cr.execute('ALTER TABLE "%s" DROP COLUMN "%s" CASCADE' % (self._table, k)) |
1400
by Fabien Pinckaers
improvement |
3034 |
cr.commit() |
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
3035 |
_schema.debug("Table '%s': dropped column '%s' with cascade", |
3036 |
self._table, k) |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
3037 |
f_obj_type = None |
3038 |
else: |
|
3039 |
f_obj_type = get_pg_type(f) and get_pg_type(f)[0] |
|
3040 |
||
3041 |
if f_obj_type: |
|
1398
by Fabien Pinckaers
improvements_bugfixes |
3042 |
ok = False |
3043 |
casts = [ |
|
3579.1.8
by Xavier Morel
[FIX] typo, might want to launch the software from time to time while changing stuff |
3044 |
('text', 'char', pg_varchar(f.size), '::%s' % pg_varchar(f.size)), |
1398
by Fabien Pinckaers
improvements_bugfixes |
3045 |
('varchar', 'text', 'TEXT', ''), |
3046 |
('int4', 'float', get_pg_type(f)[1], '::'+get_pg_type(f)[1]), |
|
3047 |
('date', 'datetime', 'TIMESTAMP', '::TIMESTAMP'), |
|
2445
by Fabien Pinckaers
[IMP] automated migration |
3048 |
('timestamp', 'date', 'date', '::date'), |
1844.1.18
by uco(OpenERP)
[FIX] Update Module : Float8 to float and numeric to float casting made possible |
3049 |
('numeric', 'float', get_pg_type(f)[1], '::'+get_pg_type(f)[1]), |
3050 |
('float8', 'float', get_pg_type(f)[1], '::'+get_pg_type(f)[1]), |
|
1398
by Fabien Pinckaers
improvements_bugfixes |
3051 |
]
|
4466.1.3
by Christophe Simonis
[FIX] orm: handle fields.char() with not size correctly |
3052 |
if f_pg_type == 'varchar' and f._type == 'char' and ((f.size is None and f_pg_size) or f_pg_size < f.size): |
1398
by Fabien Pinckaers
improvements_bugfixes |
3053 |
cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO temp_change_size' % (self._table, k)) |
3579.1.2
by Xavier Morel
[IMP] extract VARCHAR typing, if no size is provided (or the size is 0) don't put a limit on the varchar |
3054 |
cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, pg_varchar(f.size))) |
3055 |
cr.execute('UPDATE "%s" SET "%s"=temp_change_size::%s' % (self._table, k, pg_varchar(f.size))) |
|
1844.4.20
by Jay(Open ERP)
[FIX] Upgrade could have failed when its a change in name field,added cascade |
3056 |
cr.execute('ALTER TABLE "%s" DROP COLUMN temp_change_size CASCADE' % (self._table,)) |
1398
by Fabien Pinckaers
improvements_bugfixes |
3057 |
cr.commit() |
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
3058 |
_schema.debug("Table '%s': column '%s' (type varchar) changed size from %s to %s", |
4466.1.3
by Christophe Simonis
[FIX] orm: handle fields.char() with not size correctly |
3059 |
self._table, k, f_pg_size or 'unlimited', f.size or 'unlimited') |
1398
by Fabien Pinckaers
improvements_bugfixes |
3060 |
for c in casts: |
3061 |
if (f_pg_type==c[0]) and (f._type==c[1]): |
|
2106
by Fabien Pinckaers
[IMP] digits_compute to change size of float fields on the fly |
3062 |
if f_pg_type != f_obj_type: |
1844.1.18
by uco(OpenERP)
[FIX] Update Module : Float8 to float and numeric to float casting made possible |
3063 |
ok = True |
3064 |
cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO temp_change_size' % (self._table, k)) |
|
3065 |
cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, c[2])) |
|
3066 |
cr.execute(('UPDATE "%s" SET "%s"=temp_change_size'+c[3]) % (self._table, k)) |
|
3067 |
cr.execute('ALTER TABLE "%s" DROP COLUMN temp_change_size CASCADE' % (self._table,)) |
|
3068 |
cr.commit() |
|
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
3069 |
_schema.debug("Table '%s': column '%s' changed type from %s to %s", |
2707
by Xavier Morel
[IMP] logging in orm |
3070 |
self._table, k, c[0], c[1]) |
1844.1.18
by uco(OpenERP)
[FIX] Update Module : Float8 to float and numeric to float casting made possible |
3071 |
break
|
1398
by Fabien Pinckaers
improvements_bugfixes |
3072 |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
3073 |
if f_pg_type != f_obj_type: |
1398
by Fabien Pinckaers
improvements_bugfixes |
3074 |
if not ok: |
2445
by Fabien Pinckaers
[IMP] automated migration |
3075 |
i = 0 |
3076 |
while True: |
|
3017.2.12
by P. Christeas
ORM: moved columns should carry the old name, not the table |
3077 |
newname = k + '_moved' + str(i) |
2445
by Fabien Pinckaers
[IMP] automated migration |
3078 |
cr.execute("SELECT count(1) FROM pg_class c,pg_attribute a " \ |
3079 |
"WHERE c.relname=%s " \ |
|
3080 |
"AND a.attname=%s " \ |
|
3081 |
"AND c.oid=a.attrelid ", (self._table, newname)) |
|
3082 |
if not cr.fetchone()[0]: |
|
3083 |
break
|
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
3084 |
i += 1 |
2466
by Fabien Pinckaers
[IMP] removed price_accuracy options |
3085 |
if f_pg_notnull: |
3086 |
cr.execute('ALTER TABLE "%s" ALTER COLUMN "%s" DROP NOT NULL' % (self._table, k)) |
|
2445
by Fabien Pinckaers
[IMP] automated migration |
3087 |
cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO "%s"' % (self._table, k, newname)) |
3088 |
cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, get_pg_type(f)[1])) |
|
3619
by Olivier Dony
[FIX] orm: quote column names in queries to respect case and be consistent |
3089 |
cr.execute("COMMENT ON COLUMN %s.\"%s\" IS %%s" % (self._table, k), (f.string,)) |
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
3090 |
_schema.debug("Table '%s': column '%s' has changed type (DB=%s, def=%s), data moved to column %s !", |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
3091 |
self._table, k, f_pg_type, f._type, newname) |
1398
by Fabien Pinckaers
improvements_bugfixes |
3092 |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
3093 |
# if the field is required and hasn't got a NOT NULL constraint
|
3094 |
if f.required and f_pg_notnull == 0: |
|
3095 |
# set the field to the default value if any
|
|
957
by Olivier Laurent
pep8 |
3096 |
if k in self._defaults: |
1897
by Christophe Simonis
[IMP] _defaults: allow to pass directly the value instead of a lambda |
3097 |
if callable(self._defaults[k]): |
3511.1.35
by Olivier Dony
[IMP] start unifying the SUPERUSER_ID constant |
3098 |
default = self._defaults[k](self, cr, SUPERUSER_ID, context) |
1897
by Christophe Simonis
[IMP] _defaults: allow to pass directly the value instead of a lambda |
3099 |
else: |
3100 |
default = self._defaults[k] |
|
3101 |
||
4682.2.9
by Xavier Morel
[REM] unnecessary parens |
3102 |
if default is not None: |
1341.1.20
by Christophe Simonis
[FIX] psycopg2: %d -> %s |
3103 |
ss = self._columns[k]._symbol_set |
1844.1.22
by uco(Open ERP)
[FIX] Allowing sql keywords as fields(don't use them in order by clause) |
3104 |
query = 'UPDATE "%s" SET "%s"=%s WHERE "%s" is NULL' % (self._table, k, ss[0], k) |
1341.1.20
by Christophe Simonis
[FIX] psycopg2: %d -> %s |
3105 |
cr.execute(query, (ss[1](default),)) |
922
by Christophe Simonis
convert tabs to 4 spaces |
3106 |
# add the NOT NULL constraint
|
1398
by Fabien Pinckaers
improvements_bugfixes |
3107 |
cr.commit() |
922
by Christophe Simonis
convert tabs to 4 spaces |
3108 |
try: |
3272
by Fabien Pinckaers
[IMP] logging for set null failed |
3109 |
cr.execute('ALTER TABLE "%s" ALTER COLUMN "%s" SET NOT NULL' % (self._table, k), log_exceptions=False) |
922
by Christophe Simonis
convert tabs to 4 spaces |
3110 |
cr.commit() |
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
3111 |
_schema.debug("Table '%s': column '%s': added NOT NULL constraint", |
3112 |
self._table, k) |
|
2334.1.4
by Anup(OpenERP)
[IMP] removed the possible SQL injection server. |
3113 |
except Exception: |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
3114 |
msg = "Table '%s': unable to set a NOT NULL constraint on column '%s' !\n"\ |
3115 |
"If you want to have it, you should update the records and execute manually:\n"\ |
|
2707
by Xavier Morel
[IMP] logging in orm |
3116 |
"ALTER TABLE %s ALTER COLUMN %s SET NOT NULL" |
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
3117 |
_schema.warning(msg, self._table, k, self._table, k) |
922
by Christophe Simonis
convert tabs to 4 spaces |
3118 |
cr.commit() |
3119 |
elif not f.required and f_pg_notnull == 1: |
|
1341.1.20
by Christophe Simonis
[FIX] psycopg2: %d -> %s |
3120 |
cr.execute('ALTER TABLE "%s" ALTER COLUMN "%s" DROP NOT NULL' % (self._table, k)) |
922
by Christophe Simonis
convert tabs to 4 spaces |
3121 |
cr.commit() |
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
3122 |
_schema.debug("Table '%s': column '%s': dropped NOT NULL constraint", |
3123 |
self._table, k) |
|
2651
by Olivier Dony
[IMP] ir_translate, orm: btree indexes have size limit on large text values, implemented temporary workaround |
3124 |
# Verify index
|
1341.1.20
by Christophe Simonis
[FIX] psycopg2: %d -> %s |
3125 |
indexname = '%s_%s_index' % (self._table, k) |
3126 |
cr.execute("SELECT indexname FROM pg_indexes WHERE indexname = %s and tablename = %s", (indexname, self._table)) |
|
2445
by Fabien Pinckaers
[IMP] automated migration |
3127 |
res2 = cr.dictfetchall() |
3128 |
if not res2 and f.select: |
|
1341.1.20
by Christophe Simonis
[FIX] psycopg2: %d -> %s |
3129 |
cr.execute('CREATE INDEX "%s_%s_index" ON "%s" ("%s")' % (self._table, k, self._table, k)) |
922
by Christophe Simonis
convert tabs to 4 spaces |
3130 |
cr.commit() |
2651
by Olivier Dony
[IMP] ir_translate, orm: btree indexes have size limit on large text values, implemented temporary workaround |
3131 |
if f._type == 'text': |
3132 |
# FIXME: for fields.text columns we should try creating GIN indexes instead (seems most suitable for an ERP context)
|
|
4445
by olt at tinyerp
[FIX] fixed message format (wrong number of arguments) |
3133 |
msg = "Table '%s': Adding (b-tree) index for %s column '%s'."\ |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
3134 |
"This is probably useless (does not work for fulltext search) and prevents INSERTs of long texts"\
|
3135 |
" because there is a length limit for indexable btree values!\n"\ |
|
2707
by Xavier Morel
[IMP] logging in orm |
3136 |
"Use a search view instead if you simply want to make the field searchable."
|
4445
by olt at tinyerp
[FIX] fixed message format (wrong number of arguments) |
3137 |
_schema.warning(msg, self._table, f._type, k) |
2445
by Fabien Pinckaers
[IMP] automated migration |
3138 |
if res2 and not f.select: |
1341.1.20
by Christophe Simonis
[FIX] psycopg2: %d -> %s |
3139 |
cr.execute('DROP INDEX "%s_%s_index"' % (self._table, k)) |
922
by Christophe Simonis
convert tabs to 4 spaces |
3140 |
cr.commit() |
2707
by Xavier Morel
[IMP] logging in orm |
3141 |
msg = "Table '%s': dropping index for column '%s' of type '%s' as it is not required anymore" |
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
3142 |
_schema.debug(msg, self._table, k, f._type) |
2651
by Olivier Dony
[IMP] ir_translate, orm: btree indexes have size limit on large text values, implemented temporary workaround |
3143 |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
3144 |
if isinstance(f, fields.many2one): |
3511.1.36
by Olivier Dony
[IMP] orm: enforce proper m2o ondelete rules between TransientModels and Models |
3145 |
dest_model = self.pool.get(f._obj) |
4066.1.11
by Olivier Dony
[FIX] orm: model auto-init - better algorithm for detecting stale constraints during update |
3146 |
if dest_model._table != 'ir_actions': |
3147 |
self._m2o_fix_foreign_key(cr, self._table, k, dest_model, f.ondelete) |
|
3453.2.4
by Vo Minh Thu
[REF] orm: added some comments. |
3148 |
|
3149 |
# The field doesn't exist in database. Create it if necessary.
|
|
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3150 |
else: |
2445
by Fabien Pinckaers
[IMP] automated migration |
3151 |
if not isinstance(f, fields.function) or f.store: |
3152 |
# add the missing field
|
|
3153 |
cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, get_pg_type(f)[1])) |
|
3619
by Olivier Dony
[FIX] orm: quote column names in queries to respect case and be consistent |
3154 |
cr.execute("COMMENT ON COLUMN %s.\"%s\" IS %%s" % (self._table, k), (f.string,)) |
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
3155 |
_schema.debug("Table '%s': added column '%s' with definition=%s", |
2707
by Xavier Morel
[IMP] logging in orm |
3156 |
self._table, k, get_pg_type(f)[1]) |
2445
by Fabien Pinckaers
[IMP] automated migration |
3157 |
|
3158 |
# initialize it
|
|
3159 |
if not create and k in self._defaults: |
|
3160 |
if callable(self._defaults[k]): |
|
3511.1.35
by Olivier Dony
[IMP] start unifying the SUPERUSER_ID constant |
3161 |
default = self._defaults[k](self, cr, SUPERUSER_ID, context) |
2445
by Fabien Pinckaers
[IMP] automated migration |
3162 |
else: |
3163 |
default = self._defaults[k] |
|
3164 |
||
3165 |
ss = self._columns[k]._symbol_set |
|
3166 |
query = 'UPDATE "%s" SET "%s"=%s' % (self._table, k, ss[0]) |
|
3167 |
cr.execute(query, (ss[1](default),)) |
|
3168 |
cr.commit() |
|
3976.1.16
by Vo Minh Thu
[IMP] openerp.loglevels: removed calls to openerp.loglevels.Logger(). |
3169 |
_logger.debug("Table '%s': setting default value of new column %s", self._table, k) |
2445
by Fabien Pinckaers
[IMP] automated migration |
3170 |
|
3453.2.4
by Vo Minh Thu
[REF] orm: added some comments. |
3171 |
# remember the functions to call for the stored fields
|
2445
by Fabien Pinckaers
[IMP] automated migration |
3172 |
if isinstance(f, fields.function): |
3173 |
order = 10 |
|
3453.2.4
by Vo Minh Thu
[REF] orm: added some comments. |
3174 |
if f.store is not True: # i.e. if f.store is a dict |
2445
by Fabien Pinckaers
[IMP] automated migration |
3175 |
order = f.store[f.store.keys()[0]][2] |
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3176 |
todo_end.append((order, self._update_store, (f, k))) |
2445
by Fabien Pinckaers
[IMP] automated migration |
3177 |
|
3178 |
# and add constraints if needed
|
|
3179 |
if isinstance(f, fields.many2one): |
|
3180 |
if not self.pool.get(f._obj): |
|
4682.2.9
by Xavier Morel
[REM] unnecessary parens |
3181 |
raise except_orm('Programming Error', 'There is no reference available for %s' % (f._obj,)) |
3511.1.36
by Olivier Dony
[IMP] orm: enforce proper m2o ondelete rules between TransientModels and Models |
3182 |
dest_model = self.pool.get(f._obj) |
3183 |
ref = dest_model._table |
|
2445
by Fabien Pinckaers
[IMP] automated migration |
3184 |
# ir_actions is inherited so foreign key doesn't work on it
|
3185 |
if ref != 'ir_actions': |
|
3511.1.36
by Olivier Dony
[IMP] orm: enforce proper m2o ondelete rules between TransientModels and Models |
3186 |
self._m2o_add_foreign_key_checked(k, dest_model, f.ondelete) |
2445
by Fabien Pinckaers
[IMP] automated migration |
3187 |
if f.select: |
3188 |
cr.execute('CREATE INDEX "%s_%s_index" ON "%s" ("%s")' % (self._table, k, self._table, k)) |
|
3189 |
if f.required: |
|
3190 |
try: |
|
3191 |
cr.commit() |
|
3272
by Fabien Pinckaers
[IMP] logging for set null failed |
3192 |
cr.execute('ALTER TABLE "%s" ALTER COLUMN "%s" SET NOT NULL' % (self._table, k), log_exceptions=False) |
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
3193 |
_schema.debug("Table '%s': column '%s': added a NOT NULL constraint", |
2707
by Xavier Morel
[IMP] logging in orm |
3194 |
self._table, k) |
2445
by Fabien Pinckaers
[IMP] automated migration |
3195 |
except Exception: |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
3196 |
msg = "WARNING: unable to set column %s of table %s not null !\n"\ |
3359.2.9
by Vo Minh Thu
[REF] renamed openerp-server.py to openerp-server. |
3197 |
"Try to re-run: openerp-server --update=module\n"\ |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
3198 |
"If it doesn't work, update records and execute manually:\n"\ |
2707
by Xavier Morel
[IMP] logging in orm |
3199 |
"ALTER TABLE %s ALTER COLUMN %s SET NOT NULL" |
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
3200 |
_logger.warning(msg, k, self._table, self._table, k) |
2445
by Fabien Pinckaers
[IMP] automated migration |
3201 |
cr.commit() |
1341.2.4
by Fabien Pinckaers
bugfixes |
3202 |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
3203 |
else: |
2334.1.8
by Anup(OpenERP)
[IMP] SQL injection Refactored |
3204 |
cr.execute("SELECT relname FROM pg_class WHERE relkind IN ('r','v') AND relname=%s", (self._table,)) |
922
by Christophe Simonis
convert tabs to 4 spaces |
3205 |
create = not bool(cr.fetchone()) |
3206 |
||
2238.1.21
by Jay(Open ERP)
[FIX] orm: ensure the cursor is valid before computing parent_store values |
3207 |
cr.commit() # start a new transaction |
3208 |
||
4188
by Olivier Dony
[FIX] orm: remove unused `module` parameter when creating constraints |
3209 |
self._add_sql_constraints(cr) |
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3210 |
|
3211 |
if create: |
|
3212 |
self._execute_sql(cr) |
|
3213 |
||
3214 |
if store_compute: |
|
3215 |
self._parent_store_compute(cr) |
|
3216 |
cr.commit() |
|
3217 |
||
3218 |
return todo_end |
|
3219 |
||
3220 |
def _auto_end(self, cr, context=None): |
|
3221 |
""" Create the foreign keys recorded by _auto_init. """
|
|
3222 |
for t, k, r, d in self._foreign_keys: |
|
3223 |
cr.execute('ALTER TABLE "%s" ADD FOREIGN KEY ("%s") REFERENCES "%s" ON DELETE %s' % (t, k, r, d)) |
|
4178.1.1
by Vo Minh Thu
[IMP] uninstall: started test module, added new ir_model_constraint table. |
3224 |
self._save_constraint(cr, "%s_%s_fkey" % (t, k), 'f') |
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3225 |
cr.commit() |
3226 |
del self._foreign_keys |
|
3227 |
||
3228 |
||
3229 |
def _table_exist(self, cr): |
|
3230 |
cr.execute("SELECT relname FROM pg_class WHERE relkind IN ('r','v') AND relname=%s", (self._table,)) |
|
3231 |
return cr.rowcount |
|
3232 |
||
3233 |
||
3234 |
def _create_table(self, cr): |
|
4643.1.2
by Christophe CHAUVET
[IMP] Remove completly the OIDS, cause WITHOUT OIDS is activate by default |
3235 |
cr.execute('CREATE TABLE "%s" (id SERIAL NOT NULL, PRIMARY KEY(id))' % (self._table,)) |
3619
by Olivier Dony
[FIX] orm: quote column names in queries to respect case and be consistent |
3236 |
cr.execute(("COMMENT ON TABLE \"%s\" IS %%s" % self._table), (self._description,)) |
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
3237 |
_schema.debug("Table '%s': created", self._table) |
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3238 |
|
3239 |
||
3240 |
def _parent_columns_exist(self, cr): |
|
3241 |
cr.execute("""SELECT c.relname |
|
3242 |
FROM pg_class c, pg_attribute a
|
|
3243 |
WHERE c.relname=%s AND a.attname=%s AND c.oid=a.attrelid |
|
3244 |
""", (self._table, 'parent_left')) |
|
3245 |
return cr.rowcount |
|
3246 |
||
3247 |
||
3248 |
def _create_parent_columns(self, cr): |
|
3249 |
cr.execute('ALTER TABLE "%s" ADD COLUMN "parent_left" INTEGER' % (self._table,)) |
|
3250 |
cr.execute('ALTER TABLE "%s" ADD COLUMN "parent_right" INTEGER' % (self._table,)) |
|
3251 |
if 'parent_left' not in self._columns: |
|
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
3252 |
_logger.error('create a column parent_left on object %s: fields.integer(\'Left Parent\', select=1)', |
3253 |
self._table) |
|
3254 |
_schema.debug("Table '%s': added column '%s' with definition=%s", |
|
3255 |
self._table, 'parent_left', 'INTEGER') |
|
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3256 |
elif not self._columns['parent_left'].select: |
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
3257 |
_logger.error('parent_left column on object %s must be indexed! Add select=1 to the field definition)', |
3258 |
self._table) |
|
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3259 |
if 'parent_right' not in self._columns: |
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
3260 |
_logger.error('create a column parent_right on object %s: fields.integer(\'Right Parent\', select=1)', |
3261 |
self._table) |
|
3262 |
_schema.debug("Table '%s': added column '%s' with definition=%s", |
|
3263 |
self._table, 'parent_right', 'INTEGER') |
|
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3264 |
elif not self._columns['parent_right'].select: |
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
3265 |
_logger.error('parent_right column on object %s must be indexed! Add select=1 to the field definition)', |
3266 |
self._table) |
|
4681
by Olivier Dony
[IMP] orm: do not warn about missing ondelete cascade rule if ondelete is restrict - that's safe as well |
3267 |
if self._columns[self._parent_name].ondelete not in ('cascade', 'restrict'): |
3268 |
_logger.error("The column %s on object %s must be set as ondelete='cascade' or 'restrict'", |
|
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
3269 |
self._parent_name, self._name) |
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3270 |
|
3271 |
cr.commit() |
|
3272 |
||
3273 |
||
3274 |
def _add_log_columns(self, cr): |
|
3647
by Olivier Dony
[FIX] orm,expression: sanity checks for domain terms, cleanup, tests |
3275 |
for field, field_def in LOG_ACCESS_COLUMNS.iteritems(): |
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3276 |
cr.execute(""" |
3277 |
SELECT c.relname
|
|
3278 |
FROM pg_class c, pg_attribute a
|
|
3279 |
WHERE c.relname=%s AND a.attname=%s AND c.oid=a.attrelid |
|
3647
by Olivier Dony
[FIX] orm,expression: sanity checks for domain terms, cleanup, tests |
3280 |
""", (self._table, field)) |
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3281 |
if not cr.rowcount: |
3647
by Olivier Dony
[FIX] orm,expression: sanity checks for domain terms, cleanup, tests |
3282 |
cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, field, field_def)) |
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3283 |
cr.commit() |
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
3284 |
_schema.debug("Table '%s': added column '%s' with definition=%s", |
3285 |
self._table, field, field_def) |
|
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3286 |
|
3287 |
||
3288 |
def _select_column_data(self, cr): |
|
3737
by Vo Minh Thu
[IMP] orm: added small comment. |
3289 |
# attlen is the number of bytes necessary to represent the type when
|
3290 |
# the type has a fixed size. If the type has a varying size attlen is
|
|
3291 |
# -1 and atttypmod is the size limit + 4, or -1 if there is no limit.
|
|
4466.1.3
by Christophe Simonis
[FIX] orm: handle fields.char() with not size correctly |
3292 |
cr.execute("SELECT c.relname,a.attname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,t.typname,CASE WHEN a.attlen=-1 THEN (CASE WHEN a.atttypmod=-1 THEN 0 ELSE a.atttypmod-4 END) ELSE a.attlen END as size " \ |
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3293 |
"FROM pg_class c,pg_attribute a,pg_type t " \
|
3294 |
"WHERE c.relname=%s " \ |
|
3295 |
"AND c.oid=a.attrelid " \
|
|
3296 |
"AND a.atttypid=t.oid", (self._table,)) |
|
3297 |
return dict(map(lambda x: (x['attname'], x),cr.dictfetchall())) |
|
3298 |
||
3299 |
||
3300 |
def _o2m_raise_on_missing_reference(self, cr, f): |
|
3301 |
# TODO this check should be a method on fields.one2many.
|
|
4466.1.2
by Christophe Simonis
[IMP] orm: remove trailing spaces |
3302 |
|
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3303 |
other = self.pool.get(f._obj) |
3304 |
if other: |
|
3305 |
# TODO the condition could use fields_get_keys().
|
|
3306 |
if f._fields_id not in other._columns.keys(): |
|
3307 |
if f._fields_id not in other._inherit_fields.keys(): |
|
4682.2.9
by Xavier Morel
[REM] unnecessary parens |
3308 |
raise except_orm('Programming Error', "There is no reference field '%s' found for '%s'" % (f._fields_id, f._obj,)) |
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3309 |
|
3310 |
def _m2m_raise_or_create_relation(self, cr, f): |
|
3511.1.37
by Olivier Dony
[IMP] fields.m2m: support implicit generation of m2m SQL names |
3311 |
m2m_tbl, col1, col2 = f._sql_names(self) |
4178.1.3
by Vo Minh Thu
[IMP] uninstall: use a dedicated table for many2many relationships instead of overlaoding ir.model.data. |
3312 |
self._save_relation_table(cr, m2m_tbl) |
3511.1.37
by Olivier Dony
[IMP] fields.m2m: support implicit generation of m2m SQL names |
3313 |
cr.execute("SELECT relname FROM pg_class WHERE relkind IN ('r','v') AND relname=%s", (m2m_tbl,)) |
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3314 |
if not cr.dictfetchall(): |
3315 |
if not self.pool.get(f._obj): |
|
4682.2.9
by Xavier Morel
[REM] unnecessary parens |
3316 |
raise except_orm('Programming Error', 'Many2Many destination model does not exist: `%s`' % (f._obj,)) |
3511.1.36
by Olivier Dony
[IMP] orm: enforce proper m2o ondelete rules between TransientModels and Models |
3317 |
dest_model = self.pool.get(f._obj) |
3318 |
ref = dest_model._table |
|
4643.1.2
by Christophe CHAUVET
[IMP] Remove completly the OIDS, cause WITHOUT OIDS is activate by default |
3319 |
cr.execute('CREATE TABLE "%s" ("%s" INTEGER NOT NULL, "%s" INTEGER NOT NULL, UNIQUE("%s","%s"))' % (m2m_tbl, col1, col2, col1, col2)) |
3511.1.29
by Olivier Dony
[REM] orm: m2m._no_foreign_keys attribute removed, replaced by SQL check |
3320 |
# create foreign key references with ondelete=cascade, unless the targets are SQL views
|
3321 |
cr.execute("SELECT relkind FROM pg_class WHERE relkind IN ('v') AND relname=%s", (ref,)) |
|
3322 |
if not cr.fetchall(): |
|
3511.1.37
by Olivier Dony
[IMP] fields.m2m: support implicit generation of m2m SQL names |
3323 |
self._m2o_add_foreign_key_unchecked(m2m_tbl, col2, dest_model, 'cascade') |
3511.1.29
by Olivier Dony
[REM] orm: m2m._no_foreign_keys attribute removed, replaced by SQL check |
3324 |
cr.execute("SELECT relkind FROM pg_class WHERE relkind IN ('v') AND relname=%s", (self._table,)) |
3325 |
if not cr.fetchall(): |
|
3511.1.37
by Olivier Dony
[IMP] fields.m2m: support implicit generation of m2m SQL names |
3326 |
self._m2o_add_foreign_key_unchecked(m2m_tbl, col1, self, 'cascade') |
3511.1.29
by Olivier Dony
[REM] orm: m2m._no_foreign_keys attribute removed, replaced by SQL check |
3327 |
|
3511.1.37
by Olivier Dony
[IMP] fields.m2m: support implicit generation of m2m SQL names |
3328 |
cr.execute('CREATE INDEX "%s_%s_index" ON "%s" ("%s")' % (m2m_tbl, col1, m2m_tbl, col1)) |
3329 |
cr.execute('CREATE INDEX "%s_%s_index" ON "%s" ("%s")' % (m2m_tbl, col2, m2m_tbl, col2)) |
|
3330 |
cr.execute("COMMENT ON TABLE \"%s\" IS 'RELATION BETWEEN %s AND %s'" % (m2m_tbl, self._table, ref)) |
|
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3331 |
cr.commit() |
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
3332 |
_schema.debug("Create table '%s': m2m relation between '%s' and '%s'", m2m_tbl, self._table, ref) |
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3333 |
|
3334 |
||
4188
by Olivier Dony
[FIX] orm: remove unused `module` parameter when creating constraints |
3335 |
def _add_sql_constraints(self, cr): |
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3336 |
"""
|
3337 |
||
3338 |
Modify this model's database table constraints so they match the one in
|
|
3339 |
_sql_constraints.
|
|
3340 |
||
3341 |
"""
|
|
4066.1.11
by Olivier Dony
[FIX] orm: model auto-init - better algorithm for detecting stale constraints during update |
3342 |
def unify_cons_text(txt): |
3343 |
return txt.lower().replace(', ',',').replace(' (','(') |
|
3344 |
||
957
by Olivier Laurent
pep8 |
3345 |
for (key, con, _) in self._sql_constraints: |
1341.1.20
by Christophe Simonis
[FIX] psycopg2: %d -> %s |
3346 |
conname = '%s_%s' % (self._table, key) |
2687
by olt at tinyerp
[IMP] orm: now detect constraint definition changes in _auto_init (lately it only detected if the constraint name had changed) |
3347 |
|
4178.1.2
by Vo Minh Thu
[IMP] uninstall: use dedicated table instead of overloading ir.model.data. |
3348 |
self._save_constraint(cr, conname, 'u') |
2687
by olt at tinyerp
[IMP] orm: now detect constraint definition changes in _auto_init (lately it only detected if the constraint name had changed) |
3349 |
cr.execute("SELECT conname, pg_catalog.pg_get_constraintdef(oid, true) as condef FROM pg_constraint where conname=%s", (conname,)) |
3350 |
existing_constraints = cr.dictfetchall() |
|
3351 |
sql_actions = { |
|
3352 |
'drop': { |
|
3353 |
'execute': False, |
|
3354 |
'query': 'ALTER TABLE "%s" DROP CONSTRAINT "%s"' % (self._table, conname, ), |
|
3355 |
'msg_ok': "Table '%s': dropped constraint '%s'. Reason: its definition changed from '%%s' to '%s'" % ( |
|
3356 |
self._table, conname, con), |
|
3357 |
'msg_err': "Table '%s': unable to drop \'%s\' constraint !" % (self._table, con), |
|
3358 |
'order': 1, |
|
3359 |
},
|
|
3360 |
'add': { |
|
3361 |
'execute': False, |
|
3362 |
'query': 'ALTER TABLE "%s" ADD CONSTRAINT "%s" %s' % (self._table, conname, con,), |
|
3363 |
'msg_ok': "Table '%s': added constraint '%s' with definition=%s" % (self._table, conname, con), |
|
3364 |
'msg_err': "Table '%s': unable to add \'%s\' constraint !\n If you want to have it, you should update the records and execute manually:\n%%s" % ( |
|
3365 |
self._table, con), |
|
3366 |
'order': 2, |
|
3367 |
},
|
|
3368 |
}
|
|
3369 |
||
3370 |
if not existing_constraints: |
|
3371 |
# constraint does not exists:
|
|
3372 |
sql_actions['add']['execute'] = True |
|
3373 |
sql_actions['add']['msg_err'] = sql_actions['add']['msg_err'] % (sql_actions['add']['query'], ) |
|
4066.1.11
by Olivier Dony
[FIX] orm: model auto-init - better algorithm for detecting stale constraints during update |
3374 |
elif unify_cons_text(con) not in [unify_cons_text(item['condef']) for item in existing_constraints]: |
2687
by olt at tinyerp
[IMP] orm: now detect constraint definition changes in _auto_init (lately it only detected if the constraint name had changed) |
3375 |
# constraint exists but its definition has changed:
|
3376 |
sql_actions['drop']['execute'] = True |
|
3377 |
sql_actions['drop']['msg_ok'] = sql_actions['drop']['msg_ok'] % (existing_constraints[0]['condef'].lower(), ) |
|
3378 |
sql_actions['add']['execute'] = True |
|
3379 |
sql_actions['add']['msg_err'] = sql_actions['add']['msg_err'] % (sql_actions['add']['query'], ) |
|
3380 |
||
3381 |
# we need to add the constraint:
|
|
3382 |
sql_actions = [item for item in sql_actions.values()] |
|
3383 |
sql_actions.sort(key=lambda x: x['order']) |
|
3384 |
for sql_action in [action for action in sql_actions if action['execute']]: |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
3385 |
try: |
2687
by olt at tinyerp
[IMP] orm: now detect constraint definition changes in _auto_init (lately it only detected if the constraint name had changed) |
3386 |
cr.execute(sql_action['query']) |
922
by Christophe Simonis
convert tabs to 4 spaces |
3387 |
cr.commit() |
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
3388 |
_schema.debug(sql_action['msg_ok']) |
922
by Christophe Simonis
convert tabs to 4 spaces |
3389 |
except: |
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
3390 |
_schema.warning(sql_action['msg_err']) |
2238.1.21
by Jay(Open ERP)
[FIX] orm: ensure the cursor is valid before computing parent_store values |
3391 |
cr.rollback() |
922
by Christophe Simonis
convert tabs to 4 spaces |
3392 |
|
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3393 |
|
3394 |
def _execute_sql(self, cr): |
|
3395 |
""" Execute the SQL code from the _sql attribute (if any)."""
|
|
3396 |
if hasattr(self, "_sql"): |
|
3397 |
for line in self._sql.split(';'): |
|
3398 |
line2 = line.replace('\n', '').strip() |
|
3399 |
if line2: |
|
3400 |
cr.execute(line2) |
|
3401 |
cr.commit() |
|
3402 |
||
922
by Christophe Simonis
convert tabs to 4 spaces |
3403 |
#
|
3404 |
# Update objects that uses this one to update their _inherits fields
|
|
3405 |
#
|
|
2111.3.4
by RVO(OpenERP)
[IMP] security-rule-improvement |
3406 |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
3407 |
def _inherits_reload_src(self): |
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3408 |
""" Recompute the _inherit_fields mapping on each _inherits'd child model."""
|
3458.1.1
by Vo Minh Thu
[REF] osv: moved osv_pool to modules/registry. |
3409 |
for obj in self.pool.models.values(): |
922
by Christophe Simonis
convert tabs to 4 spaces |
3410 |
if self._name in obj._inherits: |
3411 |
obj._inherits_reload() |
|
3412 |
||
3466.1.8
by Vo Minh Thu
[REF] orm: add a column_info class to represent entries in _inherit_fields, |
3413 |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
3414 |
def _inherits_reload(self): |
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3415 |
""" Recompute the _inherit_fields mapping.
|
3416 |
||
3417 |
This will also call itself on each inherits'd child model.
|
|
3418 |
||
3419 |
"""
|
|
922
by Christophe Simonis
convert tabs to 4 spaces |
3420 |
res = {} |
3421 |
for table in self._inherits: |
|
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3422 |
other = self.pool.get(table) |
3423 |
for col in other._columns.keys(): |
|
3549.4.1
by Naresh(OpenERP)
[FIX]:_inherits for multilevel inheritence |
3424 |
res[col] = (table, self._inherits[table], other._columns[col], table) |
3453.2.3
by Vo Minh Thu
[REF] orm: brake _auto_init in shorter methods. |
3425 |
for col in other._inherit_fields.keys(): |
3549.4.1
by Naresh(OpenERP)
[FIX]:_inherits for multilevel inheritence |
3426 |
res[col] = (table, self._inherits[table], other._inherit_fields[col][2], other._inherit_fields[col][3]) |
957
by Olivier Laurent
pep8 |
3427 |
self._inherit_fields = res |
3466.1.8
by Vo Minh Thu
[REF] orm: add a column_info class to represent entries in _inherit_fields, |
3428 |
self._all_columns = self._get_column_infos() |
922
by Christophe Simonis
convert tabs to 4 spaces |
3429 |
self._inherits_reload_src() |
3430 |
||
3466.1.8
by Vo Minh Thu
[REF] orm: add a column_info class to represent entries in _inherit_fields, |
3431 |
|
3432 |
def _get_column_infos(self): |
|
3433 |
"""Returns a dict mapping all fields names (direct fields and
|
|
3434 |
inherited field via _inherits) to a ``column_info`` struct
|
|
3435 |
giving detailed columns """
|
|
3436 |
result = {} |
|
3549.4.1
by Naresh(OpenERP)
[FIX]:_inherits for multilevel inheritence |
3437 |
for k, (parent, m2o, col, original_parent) in self._inherit_fields.iteritems(): |
3438 |
result[k] = fields.column_info(k, col, parent, m2o, original_parent) |
|
3466.1.8
by Vo Minh Thu
[REF] orm: add a column_info class to represent entries in _inherit_fields, |
3439 |
for k, col in self._columns.iteritems(): |
3440 |
result[k] = fields.column_info(k, col) |
|
3441 |
return result |
|
3442 |
||
3443 |
||
2609
by Olivier Dony
[IMP] orm/inherits: check that m2o fields implementing _inherits are marked required with ondelete cascade |
3444 |
def _inherits_check(self): |
3445 |
for table, field_name in self._inherits.items(): |
|
3446 |
if field_name not in self._columns: |
|
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
3447 |
_logger.info('Missing many2one field definition for _inherits reference "%s" in "%s", using default one.', field_name, self._name) |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
3448 |
self._columns[field_name] = fields.many2one(table, string="Automatically created field to link to parent %s" % table, |
2609
by Olivier Dony
[IMP] orm/inherits: check that m2o fields implementing _inherits are marked required with ondelete cascade |
3449 |
required=True, ondelete="cascade") |
4681
by Olivier Dony
[IMP] orm: do not warn about missing ondelete cascade rule if ondelete is restrict - that's safe as well |
3450 |
elif not self._columns[field_name].required or self._columns[field_name].ondelete.lower() not in ("cascade", "restrict"): |
3451 |
_logger.warning('Field definition for _inherits reference "%s" in "%s" must be marked as "required" with ondelete="cascade" or "restrict", forcing it to required + cascade.', field_name, self._name) |
|
2609
by Olivier Dony
[IMP] orm/inherits: check that m2o fields implementing _inherits are marked required with ondelete cascade |
3452 |
self._columns[field_name].required = True |
3453 |
self._columns[field_name].ondelete = "cascade" |
|
3454 |
||
2416
by Fabien Pinckaers
merge |
3455 |
#def __getattr__(self, name):
|
3456 |
# """
|
|
3457 |
# Proxies attribute accesses to the `inherits` parent so we can call methods defined on the inherited parent
|
|
3458 |
# (though inherits doesn't use Python inheritance).
|
|
3459 |
# Handles translating between local ids and remote ids.
|
|
3460 |
# Known issue: doesn't work correctly when using python's own super(), don't involve inherit-based inheritance
|
|
3461 |
# when you have inherits.
|
|
3462 |
# """
|
|
3463 |
# for model, field in self._inherits.iteritems():
|
|
3464 |
# proxy = self.pool.get(model)
|
|
3465 |
# if hasattr(proxy, name):
|
|
3466 |
# attribute = getattr(proxy, name)
|
|
3467 |
# if not hasattr(attribute, '__call__'):
|
|
3468 |
# return attribute
|
|
3469 |
# break
|
|
3470 |
# else:
|
|
3471 |
# return super(orm, self).__getattr__(name)
|
|
3472 |
||
3473 |
# def _proxy(cr, uid, ids, *args, **kwargs):
|
|
3474 |
# objects = self.browse(cr, uid, ids, kwargs.get('context', None))
|
|
3475 |
# lst = [obj[field].id for obj in objects if obj[field]]
|
|
3476 |
# return getattr(proxy, name)(cr, uid, lst, *args, **kwargs)
|
|
3477 |
||
3478 |
# return _proxy
|
|
2334.1.4
by Anup(OpenERP)
[IMP] removed the possible SQL injection server. |
3479 |
|
2335.1.2
by Stephane Wirtel
[FIX] Allow to call the methods defined in the _inherits objects |
3480 |
|
3511.1.6
by Vo Minh Thu
[REF] orm: merged orm_template inside orm. |
3481 |
def fields_get(self, cr, user, allfields=None, context=None, write_access=True): |
3482 |
""" Return the definition of each field.
|
|
3483 |
||
3484 |
The returned value is a dictionary (indiced by field name) of
|
|
3485 |
dictionaries. The _inherits'd fields are included. The string, help,
|
|
3486 |
and selection (if present) attributes are translated.
|
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
3487 |
|
3488 |
:param cr: database cursor
|
|
3489 |
:param user: current user id
|
|
4682.2.15
by Xavier Morel
[FIX] incorrect docstrings or docstring param names not matching actual param names |
3490 |
:param allfields: list of fields
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
3491 |
:param context: context arguments, like lang, time zone
|
2111.3.21
by Rvo(Open ERP)
[IMP]:improved orm's public methods docstrings |
3492 |
:return: dictionary of field dictionaries, each one describing a field of the business object
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
3493 |
:raise AccessError: * if user has no create/write rights on the requested object
|
3494 |
||
3495 |
"""
|
|
3511.1.6
by Vo Minh Thu
[REF] orm: merged orm_template inside orm. |
3496 |
if context is None: |
3497 |
context = {} |
|
3498 |
||
4431
by Antonin Bourguignon
[FIX] fields_get check_access_right |
3499 |
write_access = self.check_access_rights(cr, user, 'write', raise_exception=False) \ |
3500 |
or self.check_access_rights(cr, user, 'create', raise_exception=False) |
|
3511.1.6
by Vo Minh Thu
[REF] orm: merged orm_template inside orm. |
3501 |
|
3502 |
res = {} |
|
3503 |
||
4761.2.43
by Quentin (OpenERP)
[REF] code review |
3504 |
translation_obj = self.pool.get('ir.translation') |
3511.1.6
by Vo Minh Thu
[REF] orm: merged orm_template inside orm. |
3505 |
for parent in self._inherits: |
3506 |
res.update(self.pool.get(parent).fields_get(cr, user, allfields, context)) |
|
3507 |
||
3508 |
for f, field in self._columns.iteritems(): |
|
4158.1.1
by Olivier Dony
[IMP] orm: support model-level @groups attribute for access restriction |
3509 |
if (allfields and f not in allfields) or \ |
3510 |
(field.groups and not self.user_has_groups(cr, user, groups=field.groups, context=context)): |
|
3511.1.6
by Vo Minh Thu
[REF] orm: merged orm_template inside orm. |
3511 |
continue
|
3512 |
||
3684
by Olivier Dony
[FIX] fields: m2m SQL names may be dynamic now, care for it in field_to_dict() |
3513 |
res[f] = fields.field_to_dict(self, cr, user, field, context=context) |
3511.1.6
by Vo Minh Thu
[REF] orm: merged orm_template inside orm. |
3514 |
|
3515 |
if not write_access: |
|
3516 |
res[f]['readonly'] = True |
|
3517 |
res[f]['states'] = {} |
|
3518 |
||
4425.1.1
by Olivier Dony
[IMP] translations: attempt to optimize out _get_source calls when there is not context lang |
3519 |
if 'lang' in context: |
3520 |
if 'string' in res[f]: |
|
3521 |
res_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'field', context['lang']) |
|
3522 |
if res_trans: |
|
3523 |
res[f]['string'] = res_trans |
|
3524 |
if 'help' in res[f]: |
|
3525 |
help_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'help', context['lang']) |
|
3526 |
if help_trans: |
|
3527 |
res[f]['help'] = help_trans |
|
3528 |
if 'selection' in res[f]: |
|
3529 |
if isinstance(field.selection, (tuple, list)): |
|
3530 |
sel = field.selection |
|
3531 |
sel2 = [] |
|
3532 |
for key, val in sel: |
|
3533 |
val2 = None |
|
3534 |
if val: |
|
3535 |
val2 = translation_obj._get_source(cr, user, self._name + ',' + f, 'selection', context['lang'], val) |
|
3536 |
sel2.append((key, val2 or val)) |
|
3537 |
res[f]['selection'] = sel2 |
|
3511.1.6
by Vo Minh Thu
[REF] orm: merged orm_template inside orm. |
3538 |
|
3539 |
return res |
|
3540 |
||
4672.4.3
by Vo Minh Thu
[IMP] orm: check groups-based access rights on model fields in read() and write(). |
3541 |
def check_field_access_rights(self, cr, user, operation, fields, context=None): |
3542 |
"""
|
|
3543 |
Check the user access rights on the given fields. This raises Access
|
|
3544 |
Denied if the user does not have the rights. Otherwise it returns the
|
|
3545 |
fields (as is if the fields is not falsy, or the readable/writable
|
|
3546 |
fields if fields is falsy).
|
|
3547 |
"""
|
|
3548 |
def p(field_name): |
|
3549 |
"""Predicate to test if the user has access to the given field name."""
|
|
4672.4.5
by Vo Minh Thu
[FIX] check_field_access_rights: ignore nonexisting fields |
3550 |
# Ignore requested field if it doesn't exist. This is ugly but
|
3551 |
# it seems to happen at least with 'name_alias' on res.partner.
|
|
3552 |
if field_name not in self._all_columns: |
|
3553 |
return True |
|
4672.4.3
by Vo Minh Thu
[IMP] orm: check groups-based access rights on model fields in read() and write(). |
3554 |
field = self._all_columns[field_name].column |
4743.1.46
by Christophe Simonis
[FIX] orm: SUPERUSER is not restricted by groups on fields |
3555 |
if user != SUPERUSER_ID and field.groups: |
4672.4.3
by Vo Minh Thu
[IMP] orm: check groups-based access rights on model fields in read() and write(). |
3556 |
return self.user_has_groups(cr, user, groups=field.groups, context=context) |
3557 |
else: |
|
3558 |
return True |
|
3559 |
if not fields: |
|
3560 |
fields = filter(p, self._all_columns.keys()) |
|
3561 |
else: |
|
3562 |
filtered_fields = filter(lambda a: not p(a), fields) |
|
3563 |
if filtered_fields: |
|
3564 |
_logger.warning('Access Denied by ACLs for operation: %s, uid: %s, model: %s, fields: %s', operation, user, self._name, ', '.join(filtered_fields)) |
|
4672.4.7
by Vo Minh Thu
[IMP] orm/acl: proper message instead of `TODO`. |
3565 |
raise except_orm( |
3566 |
_('Access Denied'), |
|
3567 |
_('The requested operation cannot be completed due to security restrictions. ' |
|
3568 |
'Please contact your system administrator.\n\n(Document type: %s, Operation: %s)') % \ |
|
3569 |
(self._description, operation)) |
|
4672.4.3
by Vo Minh Thu
[IMP] orm: check groups-based access rights on model fields in read() and write(). |
3570 |
return fields |
3571 |
||
922
by Christophe Simonis
convert tabs to 4 spaces |
3572 |
def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'): |
3511.1.6
by Vo Minh Thu
[REF] orm: merged orm_template inside orm. |
3573 |
""" Read records with given ids with the given fields
|
3574 |
||
3575 |
:param cr: database cursor
|
|
3576 |
:param user: current user id
|
|
3577 |
:param ids: id or list of the ids of the records to read
|
|
3578 |
:param fields: optional list of field names to return (default: all fields would be returned)
|
|
3579 |
:type fields: list (example ['field_name_1', ...])
|
|
3580 |
:param context: optional context dictionary - it may contains keys for specifying certain options
|
|
3581 |
like ``context_lang``, ``context_tz`` to alter the results of the call.
|
|
3582 |
A special ``bin_size`` boolean flag may also be passed in the context to request the
|
|
3583 |
value of all fields.binary columns to be returned as the size of the binary instead of its
|
|
3584 |
contents. This can also be selectively overriden by passing a field-specific flag
|
|
3585 |
in the form ``bin_size_XXX: True/False`` where ``XXX`` is the name of the field.
|
|
3586 |
Note: The ``bin_size_XXX`` form is new in OpenERP v6.0.
|
|
3587 |
:return: list of dictionaries((dictionary per record asked)) with requested field values
|
|
3588 |
:rtype: [{‘name_of_the_field’: value, ...}, ...]
|
|
3589 |
:raise AccessError: * if user has no read rights on the requested object
|
|
3590 |
* if user tries to bypass access rules for read on the requested object
|
|
3591 |
||
3592 |
"""
|
|
3593 |
||
4344.1.16
by Fabien Pinckaers
[IMP] Removing a bad practice: |
3594 |
self.check_access_rights(cr, user, 'read') |
4672.4.3
by Vo Minh Thu
[IMP] orm: check groups-based access rights on model fields in read() and write(). |
3595 |
fields = self.check_field_access_rights(cr, user, 'read', fields) |
922
by Christophe Simonis
convert tabs to 4 spaces |
3596 |
if isinstance(ids, (int, long)): |
3597 |
select = [ids] |
|
1119.1.191
by P. Christeas
[Fix] Tolerate ids that came as strings. |
3598 |
else: |
1919.1.9
by Fabien Pinckaers
[FIX] select current compay on users |
3599 |
select = ids |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
3600 |
select = map(lambda x: isinstance(x, dict) and x['id'] or x, select) |
957
by Olivier Laurent
pep8 |
3601 |
result = self._read_flat(cr, user, select, fields, context, load) |
2183.1.18
by Christophe Simonis
[FIX] orm: correct the cleaning of incorrect reference fields |
3602 |
|
4815.1.9
by Vo Minh Thu
[REF] orm: |
3603 |
if isinstance(ids, (int, long)): |
1341.1.10
by Christophe Simonis
[FIX] orm: searching an inexisting id (as integer) return False (patch from Activity Solutions) |
3604 |
return result and result[0] or False |
922
by Christophe Simonis
convert tabs to 4 spaces |
3605 |
return result |
3606 |
||
1029
by Christophe Simonis
binary fields: the server send the size (instead of the data) unless it's explicitly asked by setting 'get_binary_size' to False in the context |
3607 |
def _read_flat(self, cr, user, ids, fields_to_read, context=None, load='_classic_read'): |
922
by Christophe Simonis
convert tabs to 4 spaces |
3608 |
if not context: |
957
by Olivier Laurent
pep8 |
3609 |
context = {} |
922
by Christophe Simonis
convert tabs to 4 spaces |
3610 |
if not ids: |
3611 |
return [] |
|
4682.2.2
by Xavier Morel
[IMP] compare to None by identity |
3612 |
if fields_to_read is None: |
1029
by Christophe Simonis
binary fields: the server send the size (instead of the data) unless it's explicitly asked by setting 'get_binary_size' to False in the context |
3613 |
fields_to_read = self._columns.keys() |
922
by Christophe Simonis
convert tabs to 4 spaces |
3614 |
|
2507
by Olivier Dony
[IMP] orm.read(): properly support ir.rules using fields inherited from _inherits + cleanup |
3615 |
# Construct a clause for the security rules.
|
3616 |
# 'tables' hold the list of tables necessary for the SELECT including the ir.rule clauses,
|
|
3617 |
# or will at least contain self._table.
|
|
3618 |
rule_clause, rule_params, tables = self.pool.get('ir.rule').domain_get(cr, user, self._name, 'read', context=context) |
|
3619 |
||
922
by Christophe Simonis
convert tabs to 4 spaces |
3620 |
# all inherited fields + all non inherited fields for which the attribute whose name is in load is True
|
1589.1.1
by Christophe Simonis
[FIX] fix concurrency problem. Use last_modified instead of delta. Thanks to Tryton team for the idea. |
3621 |
fields_pre = [f for f in fields_to_read if |
1727
by mra (Open ERP)
fix : 329208 : trans_obj is not defined |
3622 |
f == self.CONCURRENCY_CHECK_FIELD |
1589.1.1
by Christophe Simonis
[FIX] fix concurrency problem. Use last_modified instead of delta. Thanks to Tryton team for the idea. |
3623 |
or (f in self._columns and getattr(self._columns[f], '_classic_write')) |
3624 |
] + self._inherits.values() |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
3625 |
|
3626 |
res = [] |
|
957
by Olivier Laurent
pep8 |
3627 |
if len(fields_pre): |
1029
by Christophe Simonis
binary fields: the server send the size (instead of the data) unless it's explicitly asked by setting 'get_binary_size' to False in the context |
3628 |
def convert_field(f): |
3619
by Olivier Dony
[FIX] orm: quote column names in queries to respect case and be consistent |
3629 |
f_qual = '%s."%s"' % (self._table, f) # need fully-qualified references in case len(tables) > 1 |
1029
by Christophe Simonis
binary fields: the server send the size (instead of the data) unless it's explicitly asked by setting 'get_binary_size' to False in the context |
3630 |
if f in ('create_date', 'write_date'): |
2507
by Olivier Dony
[IMP] orm.read(): properly support ir.rules using fields inherited from _inherits + cleanup |
3631 |
return "date_trunc('second', %s) as %s" % (f_qual, f) |
1589.1.1
by Christophe Simonis
[FIX] fix concurrency problem. Use last_modified instead of delta. Thanks to Tryton team for the idea. |
3632 |
if f == self.CONCURRENCY_CHECK_FIELD: |
3633 |
if self._log_access: |
|
4066.1.3
by Olivier Dony
[FIX] Correct remaining SQL now() calls, must use UTC |
3634 |
return "COALESCE(%s.write_date, %s.create_date, (now() at time zone 'UTC'))::timestamp AS %s" % (self._table, self._table, f,) |
3635 |
return "(now() at time zone 'UTC')::timestamp AS %s" % (f,) |
|
1122
by Christophe Simonis
get_binary_size is renamed bin_size and its behavior is reversed (False by default) |
3636 |
if isinstance(self._columns[f], fields.binary) and context.get('bin_size', False): |
2512
by Jay(Open ERP)
[FIX] ORM.read() : property of type binary should not crash due to quotes |
3637 |
return 'length(%s) as "%s"' % (f_qual, f) |
2507
by Olivier Dony
[IMP] orm.read(): properly support ir.rules using fields inherited from _inherits + cleanup |
3638 |
return f_qual |
3639 |
||
1029
by Christophe Simonis
binary fields: the server send the size (instead of the data) unless it's explicitly asked by setting 'get_binary_size' to False in the context |
3640 |
fields_pre2 = map(convert_field, fields_pre) |
1844.4.91
by Christophe Simonis
[FIX] orm: bug when browsing _inherits object |
3641 |
order_by = self._parent_order or self._order |
4549.1.2
by Olivier Dony
[IMP] orm: make error handling more consistent when accessing deleted/filtered records |
3642 |
select_fields = ','.join(fields_pre2 + ['%s.id' % self._table]) |
2507
by Olivier Dony
[IMP] orm.read(): properly support ir.rules using fields inherited from _inherits + cleanup |
3643 |
query = 'SELECT %s FROM %s WHERE %s.id IN %%s' % (select_fields, ','.join(tables), self._table) |
3644 |
if rule_clause: |
|
3645 |
query += " AND " + (' OR '.join(rule_clause)) |
|
2334.1.4
by Anup(OpenERP)
[IMP] removed the possible SQL injection server. |
3646 |
query += " ORDER BY " + order_by |
2159.2.4
by Olivier Dony
[IMP] refactoring of check_access_rule to avoid SQL injection and simplify code |
3647 |
for sub_ids in cr.split_for_in_conditions(ids): |
4549.1.2
by Olivier Dony
[IMP] orm: make error handling more consistent when accessing deleted/filtered records |
3648 |
cr.execute(query, [tuple(sub_ids)] + rule_params) |
3649 |
results = cr.dictfetchall() |
|
3650 |
result_ids = [x['id'] for x in results] |
|
3651 |
self._check_record_rules_result_count(cr, user, sub_ids, result_ids, 'read', context=context) |
|
3652 |
res.extend(results) |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
3653 |
else: |
3654 |
res = map(lambda x: {'id': x}, ids) |
|
3655 |
||
4672.3.1
by Vo Minh Thu
[IMP] Reduce considerably the loading time of a new registry. |
3656 |
if context.get('lang'): |
3657 |
for f in fields_pre: |
|
3658 |
if f == self.CONCURRENCY_CHECK_FIELD: |
|
3659 |
continue
|
|
3660 |
if self._columns[f].translate: |
|
3661 |
ids = [x['id'] for x in res] |
|
3662 |
#TODO: optimize out of this loop
|
|
3663 |
res_trans = self.pool.get('ir.translation')._get_ids(cr, user, self._name+','+f, 'model', context['lang'], ids) |
|
3664 |
for r in res: |
|
3665 |
r[f] = res_trans.get(r['id'], False) or r[f] |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
3666 |
|
3667 |
for table in self._inherits: |
|
3668 |
col = self._inherits[table] |
|
3142
by Fabien Pinckaers
fix_lp_673696 |
3669 |
cols = [x for x in intersect(self._inherit_fields.keys(), fields_to_read) if x not in self._columns.keys()] |
922
by Christophe Simonis
convert tabs to 4 spaces |
3670 |
if not cols: |
3671 |
continue
|
|
3672 |
res2 = self.pool.get(table).read(cr, user, [x[col] for x in res], cols, context, load) |
|
3673 |
||
3674 |
res3 = {} |
|
3675 |
for r in res2: |
|
3676 |
res3[r['id']] = r |
|
3677 |
del r['id'] |
|
3678 |
||
3679 |
for record in res: |
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
3680 |
if not record[col]: # if the record is deleted from _inherits table? |
1844.4.78
by Jay(Open ERP)
[FIX] _inherits table may delete the record which is parental to other object but not required. |
3681 |
continue
|
922
by Christophe Simonis
convert tabs to 4 spaces |
3682 |
record.update(res3[record[col]]) |
1046
by Christophe Simonis
rebugfix |
3683 |
if col not in fields_to_read: |
922
by Christophe Simonis
convert tabs to 4 spaces |
3684 |
del record[col] |
3685 |
||
3686 |
# all fields which need to be post-processed by a simple function (symbol_get)
|
|
1029
by Christophe Simonis
binary fields: the server send the size (instead of the data) unless it's explicitly asked by setting 'get_binary_size' to False in the context |
3687 |
fields_post = filter(lambda x: x in self._columns and self._columns[x]._symbol_get, fields_to_read) |
922
by Christophe Simonis
convert tabs to 4 spaces |
3688 |
if fields_post: |
3689 |
for r in res: |
|
3690 |
for f in fields_post: |
|
1341.1.20
by Christophe Simonis
[FIX] psycopg2: %d -> %s |
3691 |
r[f] = self._columns[f]._symbol_get(r[f]) |
2507
by Olivier Dony
[IMP] orm.read(): properly support ir.rules using fields inherited from _inherits + cleanup |
3692 |
ids = [x['id'] for x in res] |
922
by Christophe Simonis
convert tabs to 4 spaces |
3693 |
|
3694 |
# all non inherited fields for which the attribute whose name is in load is False
|
|
1029
by Christophe Simonis
binary fields: the server send the size (instead of the data) unless it's explicitly asked by setting 'get_binary_size' to False in the context |
3695 |
fields_post = filter(lambda x: x in self._columns and not getattr(self._columns[x], load), fields_to_read) |
974
by Fabien Pinckaers
Speed Improvement: |
3696 |
|
3697 |
# Compute POST fields
|
|
3698 |
todo = {} |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
3699 |
for f in fields_post: |
974
by Fabien Pinckaers
Speed Improvement: |
3700 |
todo.setdefault(self._columns[f]._multi, []) |
3701 |
todo[self._columns[f]._multi].append(f) |
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
3702 |
for key, val in todo.items(): |
974
by Fabien Pinckaers
Speed Improvement: |
3703 |
if key: |
3704 |
res2 = self._columns[val[0]].get(cr, self, ids, val, user, context=context, values=res) |
|
3424
by Vo Minh Thu
[IMP] orm: added an assert on the return value of function fields. |
3705 |
assert res2 is not None, \ |
3706 |
'The function field "%s" on the "%s" model returned None\n' \ |
|
3707 |
'(a dictionary was expected).' % (val[0], self._name) |
|
1056
by Fabien Pinckaers
Bugfix in Kernel |
3708 |
for pos in val: |
974
by Fabien Pinckaers
Speed Improvement: |
3709 |
for record in res: |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
3710 |
if isinstance(res2[record['id']], str): res2[record['id']] = eval(res2[record['id']]) #TOCHECK : why got string instend of dict in python2.6 |
2874.1.6
by Anup(OpenERP)
[FIX] _read_flat() method corrected for multi |
3711 |
multi_fields = res2.get(record['id'],{}) |
3712 |
if multi_fields: |
|
3713 |
record[pos] = multi_fields.get(pos,[]) |
|
974
by Fabien Pinckaers
Speed Improvement: |
3714 |
else: |
3715 |
for f in val: |
|
3716 |
res2 = self._columns[f].get(cr, self, ids, f, user, context=context, values=res) |
|
3717 |
for record in res: |
|
1845
by HDA(OpenERP)
[Merged] |
3718 |
if res2: |
3719 |
record[f] = res2[record['id']] |
|
3720 |
else: |
|
3721 |
record[f] = [] |
|
4313.1.1
by Thibault Delavallée
[IMP] orm: added a deprecated attribute on fields. If not False, is a string, and make the ORM print a warning telling the field is deprecated. |
3722 |
|
3723 |
# Warn about deprecated fields now that fields_pre and fields_post are computed
|
|
4313.1.7
by Thibault Delavallée
[FIX] orm deprecated: added a list cast because we may receive tuples (as seen on runbot). |
3724 |
# Explicitly use list() because we may receive tuples
|
3725 |
for f in list(fields_pre) + list(fields_post): |
|
4313.1.4
by Thibault Delavallée
[IMP] Removed unnecessary conditions. |
3726 |
field_column = self._all_columns.get(f) and self._all_columns.get(f).column |
4313.1.1
by Thibault Delavallée
[IMP] orm: added a deprecated attribute on fields. If not False, is a string, and make the ORM print a warning telling the field is deprecated. |
3727 |
if field_column and field_column.deprecated: |
3728 |
_logger.warning('Field %s.%s is deprecated: %s', self._name, f, field_column.deprecated) |
|
3729 |
||
922
by Christophe Simonis
convert tabs to 4 spaces |
3730 |
readonly = None |
3731 |
for vals in res: |
|
3732 |
for field in vals.copy(): |
|
3733 |
fobj = None |
|
3734 |
if field in self._columns: |
|
3735 |
fobj = self._columns[field] |
|
3736 |
||
4815.1.9
by Vo Minh Thu
[REF] orm: |
3737 |
if fobj: |
3738 |
groups = fobj.read |
|
3739 |
if groups: |
|
3740 |
edit = False |
|
3741 |
for group in groups: |
|
3742 |
module = group.split(".")[0] |
|
3743 |
grp = group.split(".")[1] |
|
3744 |
cr.execute("select count(*) from res_groups_users_rel where gid IN (select res_id from ir_model_data where name=%s and module=%s and model=%s) and uid=%s", \ |
|
3745 |
(grp, module, 'res.groups', user)) |
|
3746 |
readonly = cr.fetchall() |
|
3747 |
if readonly[0][0] >= 1: |
|
3748 |
edit = True |
|
3749 |
break
|
|
3750 |
elif readonly[0][0] == 0: |
|
3751 |
edit = False |
|
3752 |
else: |
|
3753 |
edit = False |
|
3754 |
||
3755 |
if not edit: |
|
3756 |
if type(vals[field]) == type([]): |
|
3757 |
vals[field] = [] |
|
3758 |
elif type(vals[field]) == type(0.0): |
|
3759 |
vals[field] = 0 |
|
3760 |
elif type(vals[field]) == type(''): |
|
3761 |
vals[field] = '=No Permission=' |
|
3762 |
else: |
|
3763 |
vals[field] = False |
|
3764 |
||
3765 |
if vals[field] is None: |
|
3766 |
vals[field] = False |
|
3767 |
||
922
by Christophe Simonis
convert tabs to 4 spaces |
3768 |
return res |
3769 |
||
3511.1.3
by Vo Minh Thu
[REF] osv: removed orm_memory, adapted the distinguished bits to osv_memory, |
3770 |
# TODO check READ access
|
922
by Christophe Simonis
convert tabs to 4 spaces |
3771 |
def perm_read(self, cr, user, ids, context=None, details=True): |
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
3772 |
"""
|
2849
by Olivier Dony
[ADD] osv.perm_read: added XML ID (xmlid) to returned values - naming indicates this is not a m2o. |
3773 |
Returns some metadata about the given records.
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
3774 |
|
2199
by Olivier Dony
[FIX] minor: escaped RST special char in docstring |
3775 |
:param details: if True, \*_uid fields are replaced with the name of the user
|
2111.3.21
by Rvo(Open ERP)
[IMP]:improved orm's public methods docstrings |
3776 |
:return: list of ownership dictionaries for each requested record
|
3777 |
:rtype: list of dictionaries with the following keys:
|
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
3778 |
|
3779 |
* id: object id
|
|
3780 |
* create_uid: user who created the record
|
|
3781 |
* create_date: date when the record was created
|
|
3782 |
* write_uid: last user who changed the record
|
|
3783 |
* write_date: date of the last change to the record
|
|
2849
by Olivier Dony
[ADD] osv.perm_read: added XML ID (xmlid) to returned values - naming indicates this is not a m2o. |
3784 |
* xmlid: XML ID to use to refer to this record (if there is one), in format ``module.name``
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
3785 |
"""
|
922
by Christophe Simonis
convert tabs to 4 spaces |
3786 |
if not context: |
957
by Olivier Laurent
pep8 |
3787 |
context = {} |
922
by Christophe Simonis
convert tabs to 4 spaces |
3788 |
if not ids: |
3789 |
return [] |
|
3790 |
fields = '' |
|
2334.1.4
by Anup(OpenERP)
[IMP] removed the possible SQL injection server. |
3791 |
uniq = isinstance(ids, (int, long)) |
3792 |
if uniq: |
|
3793 |
ids = [ids] |
|
2849
by Olivier Dony
[ADD] osv.perm_read: added XML ID (xmlid) to returned values - naming indicates this is not a m2o. |
3794 |
fields = ['id'] |
922
by Christophe Simonis
convert tabs to 4 spaces |
3795 |
if self._log_access: |
2849
by Olivier Dony
[ADD] osv.perm_read: added XML ID (xmlid) to returned values - naming indicates this is not a m2o. |
3796 |
fields += ['create_uid', 'create_date', 'write_uid', 'write_date'] |
3797 |
quoted_table = '"%s"' % self._table |
|
3798 |
fields_str = ",".join('%s.%s'%(quoted_table, field) for field in fields) |
|
3799 |
query = '''SELECT %s, __imd.module, __imd.name |
|
3800 |
FROM %s LEFT JOIN ir_model_data __imd |
|
3801 |
ON (__imd.model = %%s and __imd.res_id = %s.id) |
|
3802 |
WHERE %s.id IN %%s''' % (fields_str, quoted_table, quoted_table, quoted_table) |
|
3803 |
cr.execute(query, (self._name, tuple(ids))) |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
3804 |
res = cr.dictfetchall() |
3805 |
for r in res: |
|
3806 |
for key in r: |
|
3807 |
r[key] = r[key] or False |
|
3017.2.9
by P. Christeas
orm: Return numeric uid at perm_read() when no access to res.user |
3808 |
if details and key in ('write_uid', 'create_uid') and r[key]: |
3809 |
try: |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
3810 |
r[key] = self.pool.get('res.users').name_get(cr, user, [r[key]])[0] |
3017.2.9
by P. Christeas
orm: Return numeric uid at perm_read() when no access to res.user |
3811 |
except Exception: |
3812 |
pass # Leave the numeric uid there |
|
2849
by Olivier Dony
[ADD] osv.perm_read: added XML ID (xmlid) to returned values - naming indicates this is not a m2o. |
3813 |
r['xmlid'] = ("%(module)s.%(name)s" % r) if r['name'] else False |
3814 |
del r['name'], r['module'] |
|
2334.1.4
by Anup(OpenERP)
[IMP] removed the possible SQL injection server. |
3815 |
if uniq: |
3816 |
return res[ids[0]] |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
3817 |
return res |
3818 |
||
1589.1.1
by Christophe Simonis
[FIX] fix concurrency problem. Use last_modified instead of delta. Thanks to Tryton team for the idea. |
3819 |
def _check_concurrency(self, cr, ids, context): |
3820 |
if not context: |
|
3821 |
return
|
|
2940
by Fabien Pinckaers
fix bug introduced in r2938 |
3822 |
if not (context.get(self.CONCURRENCY_CHECK_FIELD) and self._log_access): |
2938
by Xavier Morel
[IMP] cleanup _check_concurrency a bit and reduce nesting |
3823 |
return
|
4066.1.3
by Olivier Dony
[FIX] Correct remaining SQL now() calls, must use UTC |
3824 |
check_clause = "(id = %s AND %s < COALESCE(write_date, create_date, (now() at time zone 'UTC'))::timestamp)" |
2946
by Olivier Dony
[FIX] orm._check_concurrency: pop values from context after check to avoid false positives (e.g. nested writes) - one check per transaction is enough |
3825 |
for sub_ids in cr.split_for_in_conditions(ids): |
3826 |
ids_to_check = [] |
|
3827 |
for id in sub_ids: |
|
3828 |
id_ref = "%s,%s" % (self._name, id) |
|
3829 |
update_date = context[self.CONCURRENCY_CHECK_FIELD].pop(id_ref, None) |
|
3830 |
if update_date: |
|
3831 |
ids_to_check.extend([id, update_date]) |
|
3832 |
if not ids_to_check: |
|
3833 |
continue
|
|
2947
by Olivier Dony
[FIX] orm._check_concurrency: mention one conflicting record in exception message |
3834 |
cr.execute("SELECT id FROM %s WHERE %s" % (self._table, " OR ".join([check_clause]*(len(ids_to_check)/2))), tuple(ids_to_check)) |
3835 |
res = cr.fetchone() |
|
3836 |
if res: |
|
3837 |
# mention the first one only to keep the error message readable
|
|
3838 |
raise except_orm('ConcurrencyException', _('A document was modified since you last viewed it (%s:%d)') % (self._description, res[0])) |
|
1589.1.1
by Christophe Simonis
[FIX] fix concurrency problem. Use last_modified instead of delta. Thanks to Tryton team for the idea. |
3839 |
|
4549.1.2
by Olivier Dony
[IMP] orm: make error handling more consistent when accessing deleted/filtered records |
3840 |
def _check_record_rules_result_count(self, cr, uid, ids, result_ids, operation, context=None): |
3841 |
"""Verify the returned rows after applying record rules matches
|
|
4199.1.2
by Olivier Dony
[IMP] access rights: improve error messages for ACLs and record rules |
3842 |
the length of `ids`, and raise an appropriate exception if it does not.
|
3843 |
"""
|
|
4549.1.2
by Olivier Dony
[IMP] orm: make error handling more consistent when accessing deleted/filtered records |
3844 |
ids, result_ids = set(ids), set(result_ids) |
3845 |
missing_ids = ids - result_ids |
|
3846 |
if missing_ids: |
|
4466.1.2
by Christophe Simonis
[IMP] orm: remove trailing spaces |
3847 |
# Attempt to distinguish record rule restriction vs deleted records,
|
4549.1.2
by Olivier Dony
[IMP] orm: make error handling more consistent when accessing deleted/filtered records |
3848 |
# to provide a more specific error message - check if the missinf
|
3849 |
cr.execute('SELECT id FROM ' + self._table + ' WHERE id IN %s', (tuple(missing_ids),)) |
|
3850 |
if cr.rowcount: |
|
3851 |
# the missing ids are (at least partially) hidden by access rules
|
|
4554
by Olivier Dony
[FIX] orm: make sure superuser mode always bypasses access rules filtering - one case was not covered after recent refactoring |
3852 |
if uid == SUPERUSER_ID: |
3853 |
return
|
|
4549.1.2
by Olivier Dony
[IMP] orm: make error handling more consistent when accessing deleted/filtered records |
3854 |
_logger.warning('Access Denied by record rules for operation: %s, uid: %s, model: %s', operation, uid, self._name) |
3855 |
raise except_orm(_('Access Denied'), |
|
3856 |
_('The requested operation cannot be completed due to security restrictions. Please contact your system administrator.\n\n(Document type: %s, Operation: %s)') % \ |
|
3857 |
(self._description, operation)) |
|
3858 |
else: |
|
3859 |
# If we get here, the missing_ids are not in the database
|
|
3860 |
if operation in ('read','unlink'): |
|
3861 |
# No need to warn about deleting an already deleted record.
|
|
3862 |
# And no error when reading a record that was deleted, to prevent spurious
|
|
4761.2.20
by Antonin Bourguignon
[IMP] fix a comment's indentation, remove a few whitespaces |
3863 |
# errors for non-transactional search/read sequences coming from clients
|
4199.1.2
by Olivier Dony
[IMP] access rights: improve error messages for ACLs and record rules |
3864 |
return
|
3865 |
_logger.warning('Failed operation on deleted record(s): %s, uid: %s, model: %s', operation, uid, self._name) |
|
3866 |
raise except_orm(_('Missing document(s)'), |
|
3867 |
_('One of the documents you are trying to access has been deleted, please try again after refreshing.')) |
|
4549.1.2
by Olivier Dony
[IMP] orm: make error handling more consistent when accessing deleted/filtered records |
3868 |
|
4199.1.2
by Olivier Dony
[IMP] access rights: improve error messages for ACLs and record rules |
3869 |
|
3668
by Vo Minh Thu
[IMP] orm: expose ir.model.access check shortcuts. |
3870 |
def check_access_rights(self, cr, uid, operation, raise_exception=True): # no context on purpose. |
3871 |
"""Verifies that the operation given by ``operation`` is allowed for the user
|
|
3872 |
according to the access rights."""
|
|
3873 |
return self.pool.get('ir.model.access').check(cr, uid, self._name, operation, raise_exception) |
|
3874 |
||
2159.2.4
by Olivier Dony
[IMP] refactoring of check_access_rule to avoid SQL injection and simplify code |
3875 |
def check_access_rule(self, cr, uid, ids, operation, context=None): |
3876 |
"""Verifies that the operation given by ``operation`` is allowed for the user
|
|
3877 |
according to ir.rules.
|
|
2111.3.21
by Rvo(Open ERP)
[IMP]:improved orm's public methods docstrings |
3878 |
|
3879 |
:param operation: one of ``write``, ``unlink``
|
|
2111.3.20
by HDA(OpenERP)
Improved docstring of orm |
3880 |
:raise except_orm: * if current ir.rules do not permit this operation.
|
3881 |
:return: None if the operation is allowed
|
|
2159.2.4
by Olivier Dony
[IMP] refactoring of check_access_rule to avoid SQL injection and simplify code |
3882 |
"""
|
3511.1.35
by Olivier Dony
[IMP] start unifying the SUPERUSER_ID constant |
3883 |
if uid == SUPERUSER_ID: |
3511.1.31
by Olivier Dony
[IMP] orm: fix Model hierarchy to avoid TransientModel leaking downstream |
3884 |
return
|
3885 |
||
3718
by Vo Minh Thu
[FIX] orm: a typo made all models treated as if they were transient in check_access_rule(). |
3886 |
if self.is_transient(): |
3511.1.31
by Olivier Dony
[IMP] orm: fix Model hierarchy to avoid TransientModel leaking downstream |
3887 |
# Only one single implicit access rule for transient models: owner only!
|
3888 |
# This is ok to hardcode because we assert that TransientModels always
|
|
4199.1.2
by Olivier Dony
[IMP] access rights: improve error messages for ACLs and record rules |
3889 |
# have log_access enabled so that the create_uid column is always there.
|
3511.1.31
by Olivier Dony
[IMP] orm: fix Model hierarchy to avoid TransientModel leaking downstream |
3890 |
# And even with _inherits, these fields are always present in the local
|
3891 |
# table too, so no need for JOINs.
|
|
3892 |
cr.execute("""SELECT distinct create_uid |
|
3893 |
FROM %s |
|
3894 |
WHERE id IN %%s""" % self._table, (tuple(ids),)) |
|
3895 |
uids = [x[0] for x in cr.fetchall()] |
|
3896 |
if len(uids) != 1 or uids[0] != uid: |
|
4199.1.2
by Olivier Dony
[IMP] access rights: improve error messages for ACLs and record rules |
3897 |
raise except_orm(_('Access Denied'), |
3898 |
_('For this kind of document, you may only access records you created yourself.\n\n(Document type: %s)') % (self._description,)) |
|
3511.1.31
by Olivier Dony
[IMP] orm: fix Model hierarchy to avoid TransientModel leaking downstream |
3899 |
else: |
3900 |
where_clause, where_params, tables = self.pool.get('ir.rule').domain_get(cr, uid, self._name, operation, context=context) |
|
3901 |
if where_clause: |
|
3902 |
where_clause = ' and ' + ' and '.join(where_clause) |
|
3903 |
for sub_ids in cr.split_for_in_conditions(ids): |
|
3904 |
cr.execute('SELECT ' + self._table + '.id FROM ' + ','.join(tables) + |
|
3905 |
' WHERE ' + self._table + '.id IN %s' + where_clause, |
|
3906 |
[sub_ids] + where_params) |
|
4549.1.2
by Olivier Dony
[IMP] orm: make error handling more consistent when accessing deleted/filtered records |
3907 |
returned_ids = [x['id'] for x in cr.dictfetchall()] |
3908 |
self._check_record_rules_result_count(cr, uid, sub_ids, returned_ids, operation, context=context) |
|
2111.3.4
by RVO(OpenERP)
[IMP] security-rule-improvement |
3909 |
|
4769.2.7
by Raphael Collet
[IMP] remove model methods _workflow_trigger and _workflow_signal, and replace calls to new workflow methods |
3910 |
def create_workflow(self, cr, uid, ids, context=None): |
4769.2.5
by Vo Minh Thu
[DOC] orm: documented workflow-related methods. |
3911 |
"""Create a workflow instance for each given record IDs."""
|
4769.2.7
by Raphael Collet
[IMP] remove model methods _workflow_trigger and _workflow_signal, and replace calls to new workflow methods |
3912 |
from openerp import workflow |
4769.2.4
by Vo Minh Thu
[IMP] added create_workflow, delete_workflow,redirect_workflow as ORM methods. |
3913 |
for res_id in ids: |
4769.2.7
by Raphael Collet
[IMP] remove model methods _workflow_trigger and _workflow_signal, and replace calls to new workflow methods |
3914 |
workflow.trg_create(uid, self._name, res_id, cr) |
4769.2.4
by Vo Minh Thu
[IMP] added create_workflow, delete_workflow,redirect_workflow as ORM methods. |
3915 |
return True |
3916 |
||
4769.2.7
by Raphael Collet
[IMP] remove model methods _workflow_trigger and _workflow_signal, and replace calls to new workflow methods |
3917 |
def delete_workflow(self, cr, uid, ids, context=None): |
4769.2.5
by Vo Minh Thu
[DOC] orm: documented workflow-related methods. |
3918 |
"""Delete the workflow instances bound to the given record IDs."""
|
4769.2.7
by Raphael Collet
[IMP] remove model methods _workflow_trigger and _workflow_signal, and replace calls to new workflow methods |
3919 |
from openerp import workflow |
3920 |
for res_id in ids: |
|
3921 |
workflow.trg_delete(uid, self._name, res_id, cr) |
|
3922 |
return True |
|
3923 |
||
4769.2.9
by Raphael Collet
[IMP] rename model method 'trigger_workflow' into 'step_workflow' (less confusing) |
3924 |
def step_workflow(self, cr, uid, ids, context=None): |
4769.2.7
by Raphael Collet
[IMP] remove model methods _workflow_trigger and _workflow_signal, and replace calls to new workflow methods |
3925 |
"""Reevaluate the workflow instances of the given record IDs."""
|
3926 |
from openerp import workflow |
|
3927 |
for res_id in ids: |
|
3928 |
workflow.trg_write(uid, self._name, res_id, cr) |
|
3929 |
return True |
|
3930 |
||
3931 |
def signal_workflow(self, cr, uid, ids, signal, context=None): |
|
3932 |
"""Send given workflow signal and return a dict mapping ids to workflow results"""
|
|
3933 |
from openerp import workflow |
|
3934 |
result = {} |
|
3935 |
for res_id in ids: |
|
3936 |
result[res_id] = workflow.trg_validate(uid, self._name, res_id, signal, cr) |
|
3937 |
return result |
|
3938 |
||
3939 |
def redirect_workflow(self, cr, uid, old_new_ids, context=None): |
|
3940 |
""" Rebind the workflow instance bound to the given 'old' record IDs to
|
|
3941 |
the given 'new' IDs. (``old_new_ids`` is a list of pairs ``(old, new)``.
|
|
3942 |
"""
|
|
3943 |
from openerp import workflow |
|
4769.2.4
by Vo Minh Thu
[IMP] added create_workflow, delete_workflow,redirect_workflow as ORM methods. |
3944 |
for old_id, new_id in old_new_ids: |
4769.2.7
by Raphael Collet
[IMP] remove model methods _workflow_trigger and _workflow_signal, and replace calls to new workflow methods |
3945 |
workflow.trg_redirect(uid, self._name, old_id, new_id, cr) |
4769.2.4
by Vo Minh Thu
[IMP] added create_workflow, delete_workflow,redirect_workflow as ORM methods. |
3946 |
return True |
3947 |
||
922
by Christophe Simonis
convert tabs to 4 spaces |
3948 |
def unlink(self, cr, uid, ids, context=None): |
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
3949 |
"""
|
3950 |
Delete records with given ids
|
|
3951 |
||
3952 |
:param cr: database cursor
|
|
3953 |
:param uid: current user id
|
|
2111.3.21
by Rvo(Open ERP)
[IMP]:improved orm's public methods docstrings |
3954 |
:param ids: id or list of ids
|
2572
by Olivier Dony
[IMP] orm: docstring improvements: fixed incorrect RST indentation + improved doc a bit + some cleanup |
3955 |
:param context: (optional) context arguments, like lang, time zone
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
3956 |
:return: True
|
3957 |
:raise AccessError: * if user has no unlink rights on the requested object
|
|
3958 |
* if user tries to bypass access rules for unlink on the requested object
|
|
3959 |
:raise UserError: if the record is default property for other records
|
|
3960 |
||
3961 |
"""
|
|
922
by Christophe Simonis
convert tabs to 4 spaces |
3962 |
if not ids: |
3963 |
return True |
|
3964 |
if isinstance(ids, (int, long)): |
|
3965 |
ids = [ids] |
|
3966 |
||
4066.26.2
by Xavier ALT
[FIX] OPW 577292: orm: force computing store values for all columns on unlink() |
3967 |
result_store = self._store_get_values(cr, uid, ids, self._all_columns.keys(), context) |
1727
by mra (Open ERP)
fix : 329208 : trans_obj is not defined |
3968 |
|
1589.1.1
by Christophe Simonis
[FIX] fix concurrency problem. Use last_modified instead of delta. Thanks to Tryton team for the idea. |
3969 |
self._check_concurrency(cr, ids, context) |
922
by Christophe Simonis
convert tabs to 4 spaces |
3970 |
|
4344.1.16
by Fabien Pinckaers
[IMP] Removing a bad practice: |
3971 |
self.check_access_rights(cr, uid, 'unlink') |
922
by Christophe Simonis
convert tabs to 4 spaces |
3972 |
|
3846.2.2
by Vo Minh Thu
[IMP] orm: cosmetics (and the runbot will build this branch again). |
3973 |
ir_property = self.pool.get('ir.property') |
4466.1.2
by Christophe Simonis
[IMP] orm: remove trailing spaces |
3974 |
|
3846.2.2
by Vo Minh Thu
[IMP] orm: cosmetics (and the runbot will build this branch again). |
3975 |
# Check if the records are used as default properties.
|
1865.1.1
by mra (Open ERP)
[FIX] osv_memory object search method: now it will accept id as domain with its action |
3976 |
domain = [('res_id', '=', False), |
2236
by Quality Team
[IMP] Add the capabilities to store a datetime, date, integer, float and char in a field.property |
3977 |
('value_reference', 'in', ['%s,%s' % (self._name, i) for i in ids]), |
1803.1.36
by Christophe Simonis
[FIX] disallow the deletion of records set as default properties |
3978 |
]
|
3846.2.2
by Vo Minh Thu
[IMP] orm: cosmetics (and the runbot will build this branch again). |
3979 |
if ir_property.search(cr, uid, domain, context=context): |
1803.1.36
by Christophe Simonis
[FIX] disallow the deletion of records set as default properties |
3980 |
raise except_orm(_('Error'), _('Unable to delete this document because it is used as a default property')) |
3981 |
||
3846.2.2
by Vo Minh Thu
[IMP] orm: cosmetics (and the runbot will build this branch again). |
3982 |
# Delete the records' properties.
|
3983 |
property_ids = ir_property.search(cr, uid, [('res_id', 'in', ['%s,%s' % (self._name, i) for i in ids])], context=context) |
|
3984 |
ir_property.unlink(cr, uid, property_ids, context=context) |
|
3985 |
||
4769.2.7
by Raphael Collet
[IMP] remove model methods _workflow_trigger and _workflow_signal, and replace calls to new workflow methods |
3986 |
self.delete_workflow(cr, uid, ids, context=context) |
922
by Christophe Simonis
convert tabs to 4 spaces |
3987 |
|
2159.2.4
by Olivier Dony
[IMP] refactoring of check_access_rule to avoid SQL injection and simplify code |
3988 |
self.check_access_rule(cr, uid, ids, 'unlink', context=context) |
3310.10.1
by Jay Vora(OpenERP)
[FIX] Deletion : Deletion of the records which are referring to any xml/csv record,should delete the relevant reference of ir_model_data and ir_values (Ref : Case 4630) |
3989 |
pool_model_data = self.pool.get('ir.model.data') |
3416
by Vo Minh Thu
[REF] consistent, grepable ir.values object naming. |
3990 |
ir_values_obj = self.pool.get('ir.values') |
2159.2.4
by Olivier Dony
[IMP] refactoring of check_access_rule to avoid SQL injection and simplify code |
3991 |
for sub_ids in cr.split_for_in_conditions(ids): |
3992 |
cr.execute('delete from ' + self._table + ' ' \ |
|
2334.1.9
by vra
[FIX][IMP] SQL-Injection ,minor improvement. |
3993 |
'where id IN %s', (sub_ids,)) |
3310.10.4
by Olivier Dony
[FIX] orm.unlink(): properly remove ir.values entries upon deletion |
3994 |
|
3995 |
# Removing the ir_model_data reference if the record being deleted is a record created by xml/csv file,
|
|
3996 |
# as these are not connected with real database foreign keys, and would be dangling references.
|
|
3480
by Olivier Dony
[FIX] orm.unlink(): cascade deletion of ir.model.data performed as admin |
3997 |
# Note: following steps performed as admin to avoid access rights restrictions, and with no context
|
3998 |
# to avoid possible side-effects during admin calls.
|
|
3999 |
# Step 1. Calling unlink of ir_model_data only for the affected IDS
|
|
3511.1.35
by Olivier Dony
[IMP] start unifying the SUPERUSER_ID constant |
4000 |
reference_ids = pool_model_data.search(cr, SUPERUSER_ID, [('res_id','in',list(sub_ids)),('model','=',self._name)]) |
3310.10.1
by Jay Vora(OpenERP)
[FIX] Deletion : Deletion of the records which are referring to any xml/csv record,should delete the relevant reference of ir_model_data and ir_values (Ref : Case 4630) |
4001 |
# Step 2. Marching towards the real deletion of referenced records
|
3407.1.4
by Olivier Dony
[IMP] orm: minor typo and improvement in handling of ir.model.data during unlink() |
4002 |
if reference_ids: |
3511.1.35
by Olivier Dony
[IMP] start unifying the SUPERUSER_ID constant |
4003 |
pool_model_data.unlink(cr, SUPERUSER_ID, reference_ids) |
3310.10.4
by Olivier Dony
[FIX] orm.unlink(): properly remove ir.values entries upon deletion |
4004 |
|
3310.10.1
by Jay Vora(OpenERP)
[FIX] Deletion : Deletion of the records which are referring to any xml/csv record,should delete the relevant reference of ir_model_data and ir_values (Ref : Case 4630) |
4005 |
# For the same reason, removing the record relevant to ir_values
|
3416
by Vo Minh Thu
[REF] consistent, grepable ir.values object naming. |
4006 |
ir_value_ids = ir_values_obj.search(cr, uid, |
3310.11.1
by Jay Vora(OpenERP)
[FIX] Deletion : Deletion of the records which are referring to any xml/csv record,should delete the relevant reference of ir_model_data and ir_values (Ref : Case 4630) |
4007 |
['|',('value','in',['%s,%s' % (self._name, sid) for sid in sub_ids]),'&',('res_id','in',list(sub_ids)),('model','=',self._name)], |
3310.10.4
by Olivier Dony
[FIX] orm.unlink(): properly remove ir.values entries upon deletion |
4008 |
context=context) |
3310.10.1
by Jay Vora(OpenERP)
[FIX] Deletion : Deletion of the records which are referring to any xml/csv record,should delete the relevant reference of ir_model_data and ir_values (Ref : Case 4630) |
4009 |
if ir_value_ids: |
3416
by Vo Minh Thu
[REF] consistent, grepable ir.values object naming. |
4010 |
ir_values_obj.unlink(cr, uid, ir_value_ids, context=context) |
3310.10.4
by Olivier Dony
[FIX] orm.unlink(): properly remove ir.values entries upon deletion |
4011 |
|
1803.1.36
by Christophe Simonis
[FIX] disallow the deletion of records set as default properties |
4012 |
for order, object, store_ids, fields in result_store: |
2159.2.4
by Olivier Dony
[IMP] refactoring of check_access_rule to avoid SQL injection and simplify code |
4013 |
if object != self._name: |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
4014 |
obj = self.pool.get(object) |
4015 |
cr.execute('select id from '+obj._table+' where id IN %s', (tuple(store_ids),)) |
|
1803.1.36
by Christophe Simonis
[FIX] disallow the deletion of records set as default properties |
4016 |
rids = map(lambda x: x[0], cr.fetchall()) |
4017 |
if rids: |
|
4018 |
obj._store_set_values(cr, uid, rids, fields, context) |
|
3310.10.4
by Olivier Dony
[FIX] orm.unlink(): properly remove ir.values entries upon deletion |
4019 |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
4020 |
return True |
4021 |
||
4022 |
#
|
|
4023 |
# TODO: Validate
|
|
4024 |
#
|
|
4025 |
def write(self, cr, user, ids, vals, context=None): |
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
4026 |
"""
|
4027 |
Update records with given ids with the given field values
|
|
4028 |
||
4029 |
:param cr: database cursor
|
|
4030 |
:param user: current user id
|
|
2572
by Olivier Dony
[IMP] orm: docstring improvements: fixed incorrect RST indentation + improved doc a bit + some cleanup |
4031 |
:type user: integer
|
4032 |
:param ids: object id or list of object ids to update according to **vals**
|
|
4033 |
:param vals: field values to update, e.g {'field_name': new_field_value, ...}
|
|
4034 |
:type vals: dictionary
|
|
4035 |
:param context: (optional) context arguments, e.g. {'lang': 'en_us', 'tz': 'UTC', ...}
|
|
4036 |
:type context: dictionary
|
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
4037 |
:return: True
|
4038 |
:raise AccessError: * if user has no write rights on the requested object
|
|
4039 |
* if user tries to bypass access rules for write on the requested object
|
|
4040 |
:raise ValidateError: if user tries to enter invalid value for a field that is not in selection
|
|
2572
by Olivier Dony
[IMP] orm: docstring improvements: fixed incorrect RST indentation + improved doc a bit + some cleanup |
4041 |
:raise UserError: if a loop would be created in a hierarchy of objects a result of the operation (such as setting an object as its own parent)
|
4042 |
||
4043 |
**Note**: The type of field values to pass in ``vals`` for relationship fields is specific:
|
|
4044 |
||
4045 |
+ For a many2many field, a list of tuples is expected.
|
|
4046 |
Here is the list of tuple that are accepted, with the corresponding semantics ::
|
|
4047 |
||
4048 |
(0, 0, { values }) link to a new record that needs to be created with the given values dictionary
|
|
4049 |
(1, ID, { values }) update the linked record with id = ID (write *values* on it)
|
|
4050 |
(2, ID) remove and delete the linked record with id = ID (calls unlink on ID, that will delete the object completely, and the link to it as well)
|
|
4051 |
(3, ID) cut the link to the linked record with id = ID (delete the relationship between the two objects but does not delete the target object itself)
|
|
4052 |
(4, ID) link to existing record with id = ID (adds a relationship)
|
|
4053 |
(5) unlink all (like using (3,ID) for all linked records)
|
|
4054 |
(6, 0, [IDs]) replace the list of linked IDs (like using (5) then (4,ID) for each ID in the list of IDs)
|
|
4055 |
||
4056 |
Example:
|
|
4057 |
[(6, 0, [8, 5, 6, 4])] sets the many2many to ids [8, 5, 6, 4]
|
|
4058 |
||
4059 |
+ For a one2many field, a lits of tuples is expected.
|
|
4060 |
Here is the list of tuple that are accepted, with the corresponding semantics ::
|
|
4061 |
||
4062 |
(0, 0, { values }) link to a new record that needs to be created with the given values dictionary
|
|
4063 |
(1, ID, { values }) update the linked record with id = ID (write *values* on it)
|
|
4064 |
(2, ID) remove and delete the linked record with id = ID (calls unlink on ID, that will delete the object completely, and the link to it as well)
|
|
4065 |
||
4066 |
Example:
|
|
4067 |
[(0, 0, {'field_name':field_value_record1, ...}), (0, 0, {'field_name':field_value_record2, ...})]
|
|
4068 |
||
4069 |
+ For a many2one field, simply use the ID of target record, which must already exist, or ``False`` to remove the link.
|
|
4070 |
+ For a reference field, use a string with the model name, a comma, and the target object id (example: ``'product.product, 5'``)
|
|
2383
by Antony Lesuisse
improve write to many2many documentation |
4071 |
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
4072 |
"""
|
922
by Christophe Simonis
convert tabs to 4 spaces |
4073 |
readonly = None |
4672.4.3
by Vo Minh Thu
[IMP] orm: check groups-based access rights on model fields in read() and write(). |
4074 |
self.check_field_access_rights(cr, user, 'write', vals.keys()) |
922
by Christophe Simonis
convert tabs to 4 spaces |
4075 |
for field in vals.copy(): |
4076 |
fobj = None |
|
4077 |
if field in self._columns: |
|
4078 |
fobj = self._columns[field] |
|
3017.1.23
by Olivier Dony
[IMP] orm.write: ignore unknown fields, more consistent with rest of API |
4079 |
elif field in self._inherit_fields: |
922
by Christophe Simonis
convert tabs to 4 spaces |
4080 |
fobj = self._inherit_fields[field][2] |
4081 |
if not fobj: |
|
4082 |
continue
|
|
4083 |
groups = fobj.write |
|
4084 |
||
4085 |
if groups: |
|
4086 |
edit = False |
|
4087 |
for group in groups: |
|
4088 |
module = group.split(".")[0] |
|
4089 |
grp = group.split(".")[1] |
|
2682
by olt at tinyerp
[FIX] missing comma in sql query 'execute' call ("select count...") |
4090 |
cr.execute("select count(*) from res_groups_users_rel where gid IN (select res_id from ir_model_data where name=%s and module=%s and model=%s) and uid=%s", \ |
922
by Christophe Simonis
convert tabs to 4 spaces |
4091 |
(grp, module, 'res.groups', user)) |
4092 |
readonly = cr.fetchall() |
|
4093 |
if readonly[0][0] >= 1: |
|
4094 |
edit = True |
|
4095 |
break
|
|
4096 |
||
4097 |
if not edit: |
|
4098 |
vals.pop(field) |
|
4099 |
||
4100 |
if not context: |
|
957
by Olivier Laurent
pep8 |
4101 |
context = {} |
922
by Christophe Simonis
convert tabs to 4 spaces |
4102 |
if not ids: |
4103 |
return True |
|
4104 |
if isinstance(ids, (int, long)): |
|
4105 |
ids = [ids] |
|
1589.1.1
by Christophe Simonis
[FIX] fix concurrency problem. Use last_modified instead of delta. Thanks to Tryton team for the idea. |
4106 |
|
4107 |
self._check_concurrency(cr, ids, context) |
|
4344.1.16
by Fabien Pinckaers
[IMP] Removing a bad practice: |
4108 |
self.check_access_rights(cr, user, 'write') |
922
by Christophe Simonis
convert tabs to 4 spaces |
4109 |
|
2343
by Fabien Pinckaers
[FIX] store fields |
4110 |
result = self._store_get_values(cr, user, ids, vals.keys(), context) or [] |
4111 |
||
2238.1.56
by Christophe Simonis
[IMP] orm: allow import and record creation to defer the computation of parent_left/right,[IMP] orm: do not update parent_left/right if the parent haven't been changed |
4112 |
# No direct update of parent_left/right
|
4113 |
vals.pop('parent_left', None) |
|
4114 |
vals.pop('parent_right', None) |
|
4115 |
||
4116 |
parents_changed = [] |
|
3347
by Olivier Dony
[FIX] orm.write: parent_store updates must be processed in correct order to avoid edge cases |
4117 |
parent_order = self._parent_order or self._order |
2238.1.56
by Christophe Simonis
[IMP] orm: allow import and record creation to defer the computation of parent_left/right,[IMP] orm: do not update parent_left/right if the parent haven't been changed |
4118 |
if self._parent_store and (self._parent_name in vals): |
4119 |
# The parent_left/right computation may take up to
|
|
4120 |
# 5 seconds. No need to recompute the values if the
|
|
3347
by Olivier Dony
[FIX] orm.write: parent_store updates must be processed in correct order to avoid edge cases |
4121 |
# parent is the same.
|
4122 |
# Note: to respect parent_order, nodes must be processed in
|
|
4123 |
# order, so ``parents_changed`` must be ordered properly.
|
|
2238.1.56
by Christophe Simonis
[IMP] orm: allow import and record creation to defer the computation of parent_left/right,[IMP] orm: do not update parent_left/right if the parent haven't been changed |
4124 |
parent_val = vals[self._parent_name] |
4125 |
if parent_val: |
|
3347
by Olivier Dony
[FIX] orm.write: parent_store updates must be processed in correct order to avoid edge cases |
4126 |
query = "SELECT id FROM %s WHERE id IN %%s AND (%s != %%s OR %s IS NULL) ORDER BY %s" % \ |
4127 |
(self._table, self._parent_name, self._parent_name, parent_order) |
|
2397
by Borja López Soilán (Pexego)
[MERGE] orm: port of 5.0 fix for parent_store computation when previous parent is NULL |
4128 |
cr.execute(query, (tuple(ids), parent_val)) |
2238.1.56
by Christophe Simonis
[IMP] orm: allow import and record creation to defer the computation of parent_left/right,[IMP] orm: do not update parent_left/right if the parent haven't been changed |
4129 |
else: |
3347
by Olivier Dony
[FIX] orm.write: parent_store updates must be processed in correct order to avoid edge cases |
4130 |
query = "SELECT id FROM %s WHERE id IN %%s AND (%s IS NOT NULL) ORDER BY %s" % \ |
4131 |
(self._table, self._parent_name, parent_order) |
|
2397
by Borja López Soilán (Pexego)
[MERGE] orm: port of 5.0 fix for parent_store computation when previous parent is NULL |
4132 |
cr.execute(query, (tuple(ids),)) |
2238.1.56
by Christophe Simonis
[IMP] orm: allow import and record creation to defer the computation of parent_left/right,[IMP] orm: do not update parent_left/right if the parent haven't been changed |
4133 |
parents_changed = map(operator.itemgetter(0), cr.fetchall()) |
1828
by Fabien Pinckaers
[IMP] Speed impprovement: 2x faster for flow: sale -> invoice -> payment |
4134 |
|
957
by Olivier Laurent
pep8 |
4135 |
upd0 = [] |
4136 |
upd1 = [] |
|
4137 |
upd_todo = [] |
|
4138 |
updend = [] |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
4139 |
direct = [] |
4140 |
totranslate = context.get('lang', False) and (context['lang'] != 'en_US') |
|
4141 |
for field in vals: |
|
4313.1.4
by Thibault Delavallée
[IMP] Removed unnecessary conditions. |
4142 |
field_column = self._all_columns.get(field) and self._all_columns.get(field).column |
4313.1.1
by Thibault Delavallée
[IMP] orm: added a deprecated attribute on fields. If not False, is a string, and make the ORM print a warning telling the field is deprecated. |
4143 |
if field_column and field_column.deprecated: |
4144 |
_logger.warning('Field %s.%s is deprecated: %s', self._name, field, field_column.deprecated) |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
4145 |
if field in self._columns: |
1541
by Fabien Pinckaers
bugfix |
4146 |
if self._columns[field]._classic_write and not (hasattr(self._columns[field], '_fnct_inv')): |
922
by Christophe Simonis
convert tabs to 4 spaces |
4147 |
if (not totranslate) or not self._columns[field].translate: |
4148 |
upd0.append('"'+field+'"='+self._columns[field]._symbol_set[0]) |
|
4149 |
upd1.append(self._columns[field]._symbol_set[1](vals[field])) |
|
4150 |
direct.append(field) |
|
4151 |
else: |
|
4152 |
upd_todo.append(field) |
|
4153 |
else: |
|
4154 |
updend.append(field) |
|
4155 |
if field in self._columns \ |
|
4156 |
and hasattr(self._columns[field], 'selection') \ |
|
4157 |
and vals[field]: |
|
3129.1.2
by Olivier Dony
[FIX] fields.reference: don't accept half-defined references, and avoid crashing on previous bad ones |
4158 |
self._check_selection_field_value(cr, user, field, vals[field], context=context) |
922
by Christophe Simonis
convert tabs to 4 spaces |
4159 |
|
4160 |
if self._log_access: |
|
1341.1.20
by Christophe Simonis
[FIX] psycopg2: %d -> %s |
4161 |
upd0.append('write_uid=%s') |
4066.1.3
by Olivier Dony
[FIX] Correct remaining SQL now() calls, must use UTC |
4162 |
upd0.append("write_date=(now() at time zone 'UTC')") |
922
by Christophe Simonis
convert tabs to 4 spaces |
4163 |
upd1.append(user) |
4164 |
||
4165 |
if len(upd0): |
|
2159.2.4
by Olivier Dony
[IMP] refactoring of check_access_rule to avoid SQL injection and simplify code |
4166 |
self.check_access_rule(cr, user, ids, 'write', context=context) |
4167 |
for sub_ids in cr.split_for_in_conditions(ids): |
|
4168 |
cr.execute('update ' + self._table + ' set ' + ','.join(upd0) + ' ' \ |
|
2334.1.9
by vra
[FIX][IMP] SQL-Injection ,minor improvement. |
4169 |
'where id IN %s', upd1 + [sub_ids]) |
2954
by Olivier Dony
[IMP] orm.write: report an error when trying to update deleted records (thanks RGA for initial fix) |
4170 |
if cr.rowcount != len(sub_ids): |
4171 |
raise except_orm(_('AccessError'), |
|
4172 |
_('One of the records you are trying to modify has already been deleted (Document type: %s).') % self._description) |
|
1865.1.1
by mra (Open ERP)
[FIX] osv_memory object search method: now it will accept id as domain with its action |
4173 |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
4174 |
if totranslate: |
1884.1.4
by Harry (Open ERP)
[FIX] replaced <TAB> with four white space. |
4175 |
# TODO: optimize
|
922
by Christophe Simonis
convert tabs to 4 spaces |
4176 |
for f in direct: |
4177 |
if self._columns[f].translate: |
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
4178 |
src_trans = self.pool.get(self._name).read(cr, user, ids, [f])[0][f] |
1844.4.65
by ACH,JVO
[FIX] Translation was not getting inserted to DB on write() |
4179 |
if not src_trans: |
4180 |
src_trans = vals[f] |
|
4181 |
# Inserting value to DB
|
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
4182 |
self.write(cr, user, ids, {f: vals[f]}) |
1844.4.65
by ACH,JVO
[FIX] Translation was not getting inserted to DB on write() |
4183 |
self.pool.get('ir.translation')._set_ids(cr, user, self._name+','+f, 'model', context['lang'], ids, vals[f], src_trans) |
922
by Christophe Simonis
convert tabs to 4 spaces |
4184 |
|
1828
by Fabien Pinckaers
[IMP] Speed impprovement: 2x faster for flow: sale -> invoice -> payment |
4185 |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
4186 |
# call the 'set' method of fields which are not classic_write
|
957
by Olivier Laurent
pep8 |
4187 |
upd_todo.sort(lambda x, y: self._columns[x].priority-self._columns[y].priority) |
1789.2.4
by Christophe Chauvet
[FIX] ORM: Defult_field name in context belongs to curent object only, not to its relationals anymore |
4188 |
|
1803.1.27
by Jay(Open ERP)
[FIX] default_xxx in context was passed to relational fields,corrected. |
4189 |
# default element in context must be removed when call a one2many or many2many
|
1794.1.1
by Jay(Open ERP)
[IMP] default_xx in context isuue |
4190 |
rel_context = context.copy() |
1789.2.4
by Christophe Chauvet
[FIX] ORM: Defult_field name in context belongs to curent object only, not to its relationals anymore |
4191 |
for c in context.items(): |
4192 |
if c[0].startswith('default_'): |
|
4193 |
del rel_context[c[0]] |
|
4194 |
||
922
by Christophe Simonis
convert tabs to 4 spaces |
4195 |
for field in upd_todo: |
4196 |
for id in ids: |
|
1844.2.2
by Fabien Pinckaers
[FIX] regression in fields.function computation |
4197 |
result += self._columns[field].set(cr, self, id, field, vals[field], user, context=rel_context) or [] |
922
by Christophe Simonis
convert tabs to 4 spaces |
4198 |
|
3778.2.1
by Stefan Rijnhart
[FIX] Raise when an unknown field is being written to |
4199 |
unknown_fields = updend[:] |
922
by Christophe Simonis
convert tabs to 4 spaces |
4200 |
for table in self._inherits: |
4201 |
col = self._inherits[table] |
|
4202 |
nids = [] |
|
2159.2.4
by Olivier Dony
[IMP] refactoring of check_access_rule to avoid SQL injection and simplify code |
4203 |
for sub_ids in cr.split_for_in_conditions(ids): |
922
by Christophe Simonis
convert tabs to 4 spaces |
4204 |
cr.execute('select distinct "'+col+'" from "'+self._table+'" ' \ |
2334.1.8
by Anup(OpenERP)
[IMP] SQL injection Refactored |
4205 |
'where id IN %s', (sub_ids,)) |
922
by Christophe Simonis
convert tabs to 4 spaces |
4206 |
nids.extend([x[0] for x in cr.fetchall()]) |
4207 |
||
4208 |
v = {} |
|
4209 |
for val in updend: |
|
957
by Olivier Laurent
pep8 |
4210 |
if self._inherit_fields[val][0] == table: |
4211 |
v[val] = vals[val] |
|
3778.2.1
by Stefan Rijnhart
[FIX] Raise when an unknown field is being written to |
4212 |
unknown_fields.remove(val) |
2861
by Sébastien BEAU - http://www.akretion.com
[FIX]Speed improvement : Call of write() should go when there is vals |
4213 |
if v: |
4214 |
self.pool.get(table).write(cr, user, nids, v, context) |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
4215 |
|
3778.2.1
by Stefan Rijnhart
[FIX] Raise when an unknown field is being written to |
4216 |
if unknown_fields: |
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
4217 |
_logger.warning( |
3778.2.4
by Stefan Rijnhart
[FIX] replace exception by warning, for friendly deprecation |
4218 |
'No such field(s) in model %s: %s.', |
4219 |
self._name, ', '.join(unknown_fields)) |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
4220 |
self._validate(cr, user, ids, context) |
2238.1.56
by Christophe Simonis
[IMP] orm: allow import and record creation to defer the computation of parent_left/right,[IMP] orm: do not update parent_left/right if the parent haven't been changed |
4221 |
|
4222 |
# TODO: use _order to set dest at the right position and not first node of parent
|
|
4223 |
# We can't defer parent_store computation because the stored function
|
|
4224 |
# fields that are computer may refer (directly or indirectly) to
|
|
4225 |
# parent_left/right (via a child_of domain)
|
|
4226 |
if parents_changed: |
|
974
by Fabien Pinckaers
Speed Improvement: |
4227 |
if self.pool._init: |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
4228 |
self.pool._init_parent[self._name] = True |
961
by Fabien Pinckaers
Speed Improvement in recursive functions and trees: |
4229 |
else: |
2238.1.56
by Christophe Simonis
[IMP] orm: allow import and record creation to defer the computation of parent_left/right,[IMP] orm: do not update parent_left/right if the parent haven't been changed |
4230 |
order = self._parent_order or self._order |
4231 |
parent_val = vals[self._parent_name] |
|
4232 |
if parent_val: |
|
4233 |
clause, params = '%s=%%s' % (self._parent_name,), (parent_val,) |
|
4234 |
else: |
|
4235 |
clause, params = '%s IS NULL' % (self._parent_name,), () |
|
4236 |
||
4237 |
for id in parents_changed: |
|
4238 |
cr.execute('SELECT parent_left, parent_right FROM %s WHERE id=%%s' % (self._table,), (id,)) |
|
4239 |
pleft, pright = cr.fetchone() |
|
4240 |
distance = pright - pleft + 1 |
|
4241 |
||
3310.1.24
by Olivier Dony
[FIX] orm.write: avoid miscalculation of parent_left/right when moving multiple records under same parent |
4242 |
# Positions of current siblings, to locate proper insertion point;
|
4243 |
# this can _not_ be fetched outside the loop, as it needs to be refreshed
|
|
4244 |
# after each update, in case several nodes are sequentially inserted one
|
|
4245 |
# next to the other (i.e computed incrementally)
|
|
3347
by Olivier Dony
[FIX] orm.write: parent_store updates must be processed in correct order to avoid edge cases |
4246 |
cr.execute('SELECT parent_right, id FROM %s WHERE %s ORDER BY %s' % (self._table, clause, parent_order), params) |
3310.1.24
by Olivier Dony
[FIX] orm.write: avoid miscalculation of parent_left/right when moving multiple records under same parent |
4247 |
parents = cr.fetchall() |
4248 |
||
1729.1.13
by Fabien Pinckaers
bugfix_lp_331245 |
4249 |
# Find Position of the element
|
4250 |
position = None |
|
2238.1.56
by Christophe Simonis
[IMP] orm: allow import and record creation to defer the computation of parent_left/right,[IMP] orm: do not update parent_left/right if the parent haven't been changed |
4251 |
for (parent_pright, parent_id) in parents: |
4252 |
if parent_id == id: |
|
1729.1.13
by Fabien Pinckaers
bugfix_lp_331245 |
4253 |
break
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
4254 |
position = parent_pright + 1 |
1729.1.13
by Fabien Pinckaers
bugfix_lp_331245 |
4255 |
|
2238.1.56
by Christophe Simonis
[IMP] orm: allow import and record creation to defer the computation of parent_left/right,[IMP] orm: do not update parent_left/right if the parent haven't been changed |
4256 |
# It's the first node of the parent
|
1729.1.13
by Fabien Pinckaers
bugfix_lp_331245 |
4257 |
if not position: |
2238.1.56
by Christophe Simonis
[IMP] orm: allow import and record creation to defer the computation of parent_left/right,[IMP] orm: do not update parent_left/right if the parent haven't been changed |
4258 |
if not parent_val: |
1729.1.13
by Fabien Pinckaers
bugfix_lp_331245 |
4259 |
position = 1 |
1692.1.2
by Fabien Pinckaers
bugfix |
4260 |
else: |
2238.1.56
by Christophe Simonis
[IMP] orm: allow import and record creation to defer the computation of parent_left/right,[IMP] orm: do not update parent_left/right if the parent haven't been changed |
4261 |
cr.execute('select parent_left from '+self._table+' where id=%s', (parent_val,)) |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
4262 |
position = cr.fetchone()[0] + 1 |
1729.1.13
by Fabien Pinckaers
bugfix_lp_331245 |
4263 |
|
2238.1.56
by Christophe Simonis
[IMP] orm: allow import and record creation to defer the computation of parent_left/right,[IMP] orm: do not update parent_left/right if the parent haven't been changed |
4264 |
if pleft < position <= pright: |
1729.1.13
by Fabien Pinckaers
bugfix_lp_331245 |
4265 |
raise except_orm(_('UserError'), _('Recursivity Detected.')) |
4266 |
||
2238.1.56
by Christophe Simonis
[IMP] orm: allow import and record creation to defer the computation of parent_left/right,[IMP] orm: do not update parent_left/right if the parent haven't been changed |
4267 |
if pleft < position: |
1729.1.13
by Fabien Pinckaers
bugfix_lp_331245 |
4268 |
cr.execute('update '+self._table+' set parent_left=parent_left+%s where parent_left>=%s', (distance, position)) |
4269 |
cr.execute('update '+self._table+' set parent_right=parent_right+%s where parent_right>=%s', (distance, position)) |
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
4270 |
cr.execute('update '+self._table+' set parent_left=parent_left+%s, parent_right=parent_right+%s where parent_left>=%s and parent_left<%s', (position-pleft, position-pleft, pleft, pright)) |
1729.1.13
by Fabien Pinckaers
bugfix_lp_331245 |
4271 |
else: |
4272 |
cr.execute('update '+self._table+' set parent_left=parent_left+%s where parent_left>=%s', (distance, position)) |
|
4273 |
cr.execute('update '+self._table+' set parent_right=parent_right+%s where parent_right>=%s', (distance, position)) |
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
4274 |
cr.execute('update '+self._table+' set parent_left=parent_left-%s, parent_right=parent_right-%s where parent_left>=%s and parent_left<%s', (pleft-position+distance, pleft-position+distance, pleft+distance, pright+distance)) |
922
by Christophe Simonis
convert tabs to 4 spaces |
4275 |
|
1844.2.2
by Fabien Pinckaers
[FIX] regression in fields.function computation |
4276 |
result += self._store_get_values(cr, user, ids, vals.keys(), context) |
2343
by Fabien Pinckaers
[FIX] store fields |
4277 |
result.sort() |
4278 |
||
4279 |
done = {} |
|
3266
by Olivier Dony
[FIX] orm.write(): avoid name collision of "ids" variable, causing side-effects + some doc |
4280 |
for order, object, ids_to_update, fields_to_recompute in result: |
4281 |
key = (object, tuple(fields_to_recompute)) |
|
2343
by Fabien Pinckaers
[FIX] store fields |
4282 |
done.setdefault(key, {}) |
4283 |
# avoid to do several times the same computation
|
|
4284 |
todo = [] |
|
3266
by Olivier Dony
[FIX] orm.write(): avoid name collision of "ids" variable, causing side-effects + some doc |
4285 |
for id in ids_to_update: |
2343
by Fabien Pinckaers
[FIX] store fields |
4286 |
if id not in done[key]: |
4287 |
done[key][id] = True |
|
4288 |
todo.append(id) |
|
3266
by Olivier Dony
[FIX] orm.write(): avoid name collision of "ids" variable, causing side-effects + some doc |
4289 |
self.pool.get(object)._store_set_values(cr, user, todo, fields_to_recompute, context) |
1390
by Fabien Pinckaers
bugfix_cache_speed_improvement_store |
4290 |
|
4769.2.9
by Raphael Collet
[IMP] rename model method 'trigger_workflow' into 'step_workflow' (less confusing) |
4291 |
self.step_workflow(cr, user, ids, context=context) |
922
by Christophe Simonis
convert tabs to 4 spaces |
4292 |
return True |
4293 |
||
4294 |
#
|
|
4295 |
# TODO: Should set perm to user.xxx
|
|
4296 |
#
|
|
4297 |
def create(self, cr, user, vals, context=None): |
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
4298 |
"""
|
3421
by Vo Minh Thu
[MERGE] res.user: fix the call to create_user: |
4299 |
Create a new record for the model.
|
4300 |
||
4301 |
The values for the new record are initialized using the ``vals``
|
|
4302 |
argument, and if necessary the result of ``default_get()``.
|
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
4303 |
|
4304 |
:param cr: database cursor
|
|
4305 |
:param user: current user id
|
|
2572
by Olivier Dony
[IMP] orm: docstring improvements: fixed incorrect RST indentation + improved doc a bit + some cleanup |
4306 |
:type user: integer
|
4307 |
:param vals: field values for new record, e.g {'field_name': field_value, ...}
|
|
4308 |
:type vals: dictionary
|
|
4309 |
:param context: optional context arguments, e.g. {'lang': 'en_us', 'tz': 'UTC', ...}
|
|
4310 |
:type context: dictionary
|
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
4311 |
:return: id of new record created
|
4312 |
:raise AccessError: * if user has no create rights on the requested object
|
|
4313 |
* if user tries to bypass access rules for create on the requested object
|
|
4314 |
:raise ValidateError: if user tries to enter invalid value for a field that is not in selection
|
|
2572
by Olivier Dony
[IMP] orm: docstring improvements: fixed incorrect RST indentation + improved doc a bit + some cleanup |
4315 |
:raise UserError: if a loop would be created in a hierarchy of objects a result of the operation (such as setting an object as its own parent)
|
4316 |
||
4317 |
**Note**: The type of field values to pass in ``vals`` for relationship fields is specific.
|
|
4318 |
Please see the description of the :py:meth:`~osv.osv.osv.write` method for details about the possible values and how
|
|
4319 |
to specify them.
|
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
4320 |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
4321 |
"""
|
4322 |
if not context: |
|
957
by Olivier Laurent
pep8 |
4323 |
context = {} |
3511.1.31
by Olivier Dony
[IMP] orm: fix Model hierarchy to avoid TransientModel leaking downstream |
4324 |
|
4325 |
if self.is_transient(): |
|
4326 |
self._transient_vacuum(cr, user) |
|
4327 |
||
4344.1.16
by Fabien Pinckaers
[IMP] Removing a bad practice: |
4328 |
self.check_access_rights(cr, user, 'create') |
922
by Christophe Simonis
convert tabs to 4 spaces |
4329 |
|
4285.1.1
by Vo Minh Thu
[FIX] orm: disallow LOG_ACCESS fields in the create() method. |
4330 |
if self._log_access: |
4331 |
for f in LOG_ACCESS_COLUMNS: |
|
4332 |
if vals.pop(f, None) is not None: |
|
4333 |
_logger.warning( |
|
4334 |
'Field `%s` is not allowed when creating the model `%s`.', |
|
4335 |
f, self._name) |
|
2654
by Olivier Dony
[FIX,REF] orm: correct default values update during create() + refactor |
4336 |
vals = self._add_missing_default_values(cr, user, vals, context) |
922
by Christophe Simonis
convert tabs to 4 spaces |
4337 |
|
4338 |
tocreate = {} |
|
4339 |
for v in self._inherits: |
|
4340 |
if self._inherits[v] not in vals: |
|
4341 |
tocreate[v] = {} |
|
1845
by HDA(OpenERP)
[Merged] |
4342 |
else: |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
4343 |
tocreate[v] = {'id': vals[self._inherits[v]]} |
922
by Christophe Simonis
convert tabs to 4 spaces |
4344 |
(upd0, upd1, upd2) = ('', '', []) |
4345 |
upd_todo = [] |
|
3778.2.4
by Stefan Rijnhart
[FIX] replace exception by warning, for friendly deprecation |
4346 |
unknown_fields = [] |
922
by Christophe Simonis
convert tabs to 4 spaces |
4347 |
for v in vals.keys(): |
3552.1.1
by Thibaut DIRLIK
[FIX] create() does not write in the _inherits parent when fields have the same name. |
4348 |
if v in self._inherit_fields and v not in self._columns: |
3549.4.3
by Naresh(OpenERP)
[FIX]:problem when coping the _inherits record the base class record was not created resulting the new record to refer the same parent that the old record is referring |
4349 |
(table, col, col_detail, original_parent) = self._inherit_fields[v] |
922
by Christophe Simonis
convert tabs to 4 spaces |
4350 |
tocreate[table][v] = vals[v] |
4351 |
del vals[v] |
|
1845
by HDA(OpenERP)
[Merged] |
4352 |
else: |
4353 |
if (v not in self._inherit_fields) and (v not in self._columns): |
|
3778.2.4
by Stefan Rijnhart
[FIX] replace exception by warning, for friendly deprecation |
4354 |
del vals[v] |
4355 |
unknown_fields.append(v) |
|
4356 |
if unknown_fields: |
|
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
4357 |
_logger.warning( |
3778.2.4
by Stefan Rijnhart
[FIX] replace exception by warning, for friendly deprecation |
4358 |
'No such field(s) in model %s: %s.', |
4359 |
self._name, ', '.join(unknown_fields)) |
|
1592
by Fabien Pinckaers
bugfixes |
4360 |
|
1538
by Jay (Open ERP)
Bugfix : Record creation on databse views(ref : views used for dashboard). |
4361 |
# Try-except added to filter the creation of those records whose filds are readonly.
|
4362 |
# Example : any dashboard which has all the fields readonly.(due to Views(database views))
|
|
4263.3.28
by Vo Minh Thu
[FIX]: orm: reverted a change in a previous commit (where a try/except was removed). |
4363 |
try: |
4364 |
cr.execute("SELECT nextval('"+self._sequence+"')") |
|
4365 |
except: |
|
4366 |
raise except_orm(_('UserError'), |
|
4367 |
_('You cannot perform this operation. New Record Creation is not allowed for this object as this object is for reporting purpose.')) |
|
1727
by mra (Open ERP)
fix : 329208 : trans_obj is not defined |
4368 |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
4369 |
id_new = cr.fetchone()[0] |
4370 |
for table in tocreate: |
|
1845
by HDA(OpenERP)
[Merged] |
4371 |
if self._inherits[table] in vals: |
4372 |
del vals[self._inherits[table]] |
|
2132
by Stephane Wirtel
[FIX] orm: Allows the inline modification of the _inherits fields |
4373 |
|
4374 |
record_id = tocreate[table].pop('id', None) |
|
4761.2.20
by Antonin Bourguignon
[IMP] fix a comment's indentation, remove a few whitespaces |
4375 |
|
4066.16.1
by Olivier Dony
[FIX] OPW 575395: orm: don't defer function field computation during creation of parent records (inheritS) |
4376 |
# When linking/creating parent records, force context without 'no_store_function' key that
|
4761.2.20
by Antonin Bourguignon
[IMP] fix a comment's indentation, remove a few whitespaces |
4377 |
# defers stored functions computing, as these won't be computed in batch at the end of create().
|
4066.16.1
by Olivier Dony
[FIX] OPW 575395: orm: don't defer function field computation during creation of parent records (inheritS) |
4378 |
parent_context = dict(context) |
4379 |
parent_context.pop('no_store_function', None) |
|
4761.2.20
by Antonin Bourguignon
[IMP] fix a comment's indentation, remove a few whitespaces |
4380 |
|
2225
by Jay(Open ERP)
[FIX] _inherits : Relational field if on the view of child object,should not raise error |
4381 |
if record_id is None or not record_id: |
4066.16.1
by Olivier Dony
[FIX] OPW 575395: orm: don't defer function field computation during creation of parent records (inheritS) |
4382 |
record_id = self.pool.get(table).create(cr, user, tocreate[table], context=parent_context) |
2132
by Stephane Wirtel
[FIX] orm: Allows the inline modification of the _inherits fields |
4383 |
else: |
4066.16.1
by Olivier Dony
[FIX] OPW 575395: orm: don't defer function field computation during creation of parent records (inheritS) |
4384 |
self.pool.get(table).write(cr, user, [record_id], tocreate[table], context=parent_context) |
2132
by Stephane Wirtel
[FIX] orm: Allows the inline modification of the _inherits fields |
4385 |
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
4386 |
upd0 += ',' + self._inherits[table] |
1341.1.20
by Christophe Simonis
[FIX] psycopg2: %d -> %s |
4387 |
upd1 += ',%s' |
2132
by Stephane Wirtel
[FIX] orm: Allows the inline modification of the _inherits fields |
4388 |
upd2.append(record_id) |
1845
by HDA(OpenERP)
[Merged] |
4389 |
|
1865.1.1
by mra (Open ERP)
[FIX] osv_memory object search method: now it will accept id as domain with its action |
4390 |
#Start : Set bool fields to be False if they are not touched(to make search more powerful)
|
1775.1.3
by Jay(Open ERP)
[FIX] DB will save False Valuesfor booleans |
4391 |
bool_fields = [x for x in self._columns.keys() if self._columns[x]._type=='boolean'] |
1865.1.1
by mra (Open ERP)
[FIX] osv_memory object search method: now it will accept id as domain with its action |
4392 |
|
1775.1.3
by Jay(Open ERP)
[FIX] DB will save False Valuesfor booleans |
4393 |
for bool_field in bool_fields: |
4394 |
if bool_field not in vals: |
|
4395 |
vals[bool_field] = False |
|
4396 |
#End
|
|
1845
by HDA(OpenERP)
[Merged] |
4397 |
for field in vals.copy(): |
4398 |
fobj = None |
|
4399 |
if field in self._columns: |
|
4400 |
fobj = self._columns[field] |
|
4401 |
else: |
|
4402 |
fobj = self._inherit_fields[field][2] |
|
4403 |
if not fobj: |
|
4404 |
continue
|
|
4405 |
groups = fobj.write |
|
4406 |
if groups: |
|
4407 |
edit = False |
|
4408 |
for group in groups: |
|
4409 |
module = group.split(".")[0] |
|
4410 |
grp = group.split(".")[1] |
|
2334.1.8
by Anup(OpenERP)
[IMP] SQL injection Refactored |
4411 |
cr.execute("select count(*) from res_groups_users_rel where gid IN (select res_id from ir_model_data where name='%s' and module='%s' and model='%s') and uid=%s" % \ |
1845
by HDA(OpenERP)
[Merged] |
4412 |
(grp, module, 'res.groups', user)) |
4413 |
readonly = cr.fetchall() |
|
4414 |
if readonly[0][0] >= 1: |
|
4415 |
edit = True |
|
4416 |
break
|
|
4417 |
elif readonly[0][0] == 0: |
|
4418 |
edit = False |
|
4419 |
else: |
|
4420 |
edit = False |
|
4421 |
||
4422 |
if not edit: |
|
4423 |
vals.pop(field) |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
4424 |
for field in vals: |
1845
by HDA(OpenERP)
[Merged] |
4425 |
if self._columns[field]._classic_write: |
4426 |
upd0 = upd0 + ',"' + field + '"' |
|
4427 |
upd1 = upd1 + ',' + self._columns[field]._symbol_set[0] |
|
4428 |
upd2.append(self._columns[field]._symbol_set[1](vals[field])) |
|
4761.2.20
by Antonin Bourguignon
[IMP] fix a comment's indentation, remove a few whitespaces |
4429 |
#for the function fields that receive a value, we set them directly in the database
|
4492.1.1
by Quentin (OpenERP)
[FIX] osv/orm: compute the _fct_inv() of stored functional fields at the record creation. Compute also the _fct_inv() of related fields (because there is no reason not to do it). That last patch revert a 'fix' of lp bug 544087 introduced in revision 4182, i don't get the purpose of it but that bug is not repdocutible anymore... |
4430 |
#(they may be required), but we also need to trigger the _fct_inv()
|
4492.1.4
by Quentin (OpenERP)
[REV] osv/orm: revert of a previous patch that was unifying the behavior for fields.related and fields.function at creation time because it's a bit dangereous to treat it right now, and may need the add of a new field attribute. "I'll be back", said the patch |
4431 |
if (hasattr(self._columns[field], '_fnct_inv')) and not isinstance(self._columns[field], fields.related): |
4432 |
#TODO: this way to special case the related fields is really creepy but it shouldn't be changed at
|
|
4433 |
#one week of the release candidate. It seems the only good way to handle correctly this is to add an
|
|
4434 |
#attribute to make a field `really readonly´ and thus totally ignored by the create()... otherwise
|
|
4435 |
#if, for example, the related has a default value (for usability) then the fct_inv is called and it
|
|
4436 |
#may raise some access rights error. Changing this is a too big change for now, and is thus postponed
|
|
4437 |
#after the release but, definitively, the behavior shouldn't be different for related and function
|
|
4438 |
#fields.
|
|
4492.1.1
by Quentin (OpenERP)
[FIX] osv/orm: compute the _fct_inv() of stored functional fields at the record creation. Compute also the _fct_inv() of related fields (because there is no reason not to do it). That last patch revert a 'fix' of lp bug 544087 introduced in revision 4182, i don't get the purpose of it but that bug is not repdocutible anymore... |
4439 |
upd_todo.append(field) |
1845
by HDA(OpenERP)
[Merged] |
4440 |
else: |
4492.1.4
by Quentin (OpenERP)
[REV] osv/orm: revert of a previous patch that was unifying the behavior for fields.related and fields.function at creation time because it's a bit dangereous to treat it right now, and may need the add of a new field attribute. "I'll be back", said the patch |
4441 |
#TODO: this `if´ statement should be removed because there is no good reason to special case the fields
|
4442 |
#related. See the above TODO comment for further explanations.
|
|
4443 |
if not isinstance(self._columns[field], fields.related): |
|
4444 |
upd_todo.append(field) |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
4445 |
if field in self._columns \ |
4446 |
and hasattr(self._columns[field], 'selection') \ |
|
4447 |
and vals[field]: |
|
3129.1.3
by Olivier Dony
[FIX] fields.reference: don't accept half-defined references - second part of fix, in create() |
4448 |
self._check_selection_field_value(cr, user, field, vals[field], context=context) |
922
by Christophe Simonis
convert tabs to 4 spaces |
4449 |
if self._log_access: |
4104.2.1
by Samus Aran
[IMP] Creating new row in db also update write_date and write_uid |
4450 |
upd0 += ',create_uid,create_date,write_uid,write_date' |
4451 |
upd1 += ",%s,(now() at time zone 'UTC'),%s,(now() at time zone 'UTC')" |
|
4452 |
upd2.extend((user, user)) |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
4453 |
cr.execute('insert into "'+self._table+'" (id'+upd0+") values ("+str(id_new)+upd1+')', tuple(upd2)) |
957
by Olivier Laurent
pep8 |
4454 |
upd_todo.sort(lambda x, y: self._columns[x].priority-self._columns[y].priority) |
922
by Christophe Simonis
convert tabs to 4 spaces |
4455 |
|
2238.1.56
by Christophe Simonis
[IMP] orm: allow import and record creation to defer the computation of parent_left/right,[IMP] orm: do not update parent_left/right if the parent haven't been changed |
4456 |
if self._parent_store and not context.get('defer_parent_store_computation'): |
974
by Fabien Pinckaers
Speed Improvement: |
4457 |
if self.pool._init: |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
4458 |
self.pool._init_parent[self._name] = True |
961
by Fabien Pinckaers
Speed Improvement in recursive functions and trees: |
4459 |
else: |
974
by Fabien Pinckaers
Speed Improvement: |
4460 |
parent = vals.get(self._parent_name, False) |
4461 |
if parent: |
|
1702.1.1
by Fabien Pinckaers
bugfix |
4462 |
cr.execute('select parent_right from '+self._table+' where '+self._parent_name+'=%s order by '+(self._parent_order or self._order), (parent,)) |
1692.1.2
by Fabien Pinckaers
bugfix |
4463 |
pleft_old = None |
4464 |
result_p = cr.fetchall() |
|
4465 |
for (pleft,) in result_p: |
|
4466 |
if not pleft: |
|
4467 |
break
|
|
4468 |
pleft_old = pleft |
|
4469 |
if not pleft_old: |
|
4470 |
cr.execute('select parent_left from '+self._table+' where id=%s', (parent,)) |
|
4471 |
pleft_old = cr.fetchone()[0] |
|
4472 |
pleft = pleft_old |
|
974
by Fabien Pinckaers
Speed Improvement: |
4473 |
else: |
4474 |
cr.execute('select max(parent_right) from '+self._table) |
|
4475 |
pleft = cr.fetchone()[0] or 0 |
|
1341.1.20
by Christophe Simonis
[FIX] psycopg2: %d -> %s |
4476 |
cr.execute('update '+self._table+' set parent_left=parent_left+2 where parent_left>%s', (pleft,)) |
4477 |
cr.execute('update '+self._table+' set parent_right=parent_right+2 where parent_right>%s', (pleft,)) |
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
4478 |
cr.execute('update '+self._table+' set parent_left=%s,parent_right=%s where id=%s', (pleft+1, pleft+2, id_new)) |
1865.1.1
by mra (Open ERP)
[FIX] osv_memory object search method: now it will accept id as domain with its action |
4479 |
|
1845
by HDA(OpenERP)
[Merged] |
4480 |
# default element in context must be remove when call a one2many or many2many
|
1794.1.1
by Jay(Open ERP)
[IMP] default_xx in context isuue |
4481 |
rel_context = context.copy() |
1789.2.4
by Christophe Chauvet
[FIX] ORM: Defult_field name in context belongs to curent object only, not to its relationals anymore |
4482 |
for c in context.items(): |
4483 |
if c[0].startswith('default_'): |
|
4484 |
del rel_context[c[0]] |
|
1845
by HDA(OpenERP)
[Merged] |
4485 |
|
1849
by HDA(OpenERP)
rename html2html to Mako2html Merging improvement in orm |
4486 |
result = [] |
1765
by Fabien Pinckaers
bugfix |
4487 |
for field in upd_todo: |
1849
by HDA(OpenERP)
rename html2html to Mako2html Merging improvement in orm |
4488 |
result += self._columns[field].set(cr, self, id_new, field, vals[field], user, rel_context) or [] |
1765
by Fabien Pinckaers
bugfix |
4489 |
self._validate(cr, user, [id_new], context) |
961
by Fabien Pinckaers
Speed Improvement in recursive functions and trees: |
4490 |
|
1849
by HDA(OpenERP)
rename html2html to Mako2html Merging improvement in orm |
4491 |
if not context.get('no_store_function', False): |
4492 |
result += self._store_get_values(cr, user, [id_new], vals.keys(), context) |
|
4493 |
result.sort() |
|
4494 |
done = [] |
|
4495 |
for order, object, ids, fields2 in result: |
|
4496 |
if not (object, ids, fields2) in done: |
|
4497 |
self.pool.get(object)._store_set_values(cr, user, ids, fields2, context) |
|
4498 |
done.append((object, ids, fields2)) |
|
1390
by Fabien Pinckaers
bugfix_cache_speed_improvement_store |
4499 |
|
2263
by Fabien Pinckaers
[IMP] logs background activities |
4500 |
if self._log_create and not (context and context.get('no_store_function', False)): |
4501 |
message = self._description + \ |
|
4502 |
" '" + \ |
|
4503 |
self.name_get(cr, user, [id_new], context=context)[0][1] + \ |
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
4504 |
"' " + _("created.") |
2265
by Fabien Pinckaers
[FIX] Improve logging system |
4505 |
self.log(cr, user, id_new, message, True, context=context) |
4743.17.1
by Chris Biersbach
[FIX] orm: moves the access_rule check to the end of the create method to avoid getting errors when trying to create and m2m values are passed, which can cause the rules to fail |
4506 |
self.check_access_rule(cr, user, [id_new], 'create', context=context) |
4769.2.7
by Raphael Collet
[IMP] remove model methods _workflow_trigger and _workflow_signal, and replace calls to new workflow methods |
4507 |
self.create_workflow(cr, user, [id_new], context=context) |
922
by Christophe Simonis
convert tabs to 4 spaces |
4508 |
return id_new |
4509 |
||
3511.1.6
by Vo Minh Thu
[REF] orm: merged orm_template inside orm. |
4510 |
def browse(self, cr, uid, select, context=None, list_class=None, fields_process=None): |
4511 |
"""Fetch records as objects allowing to use dot notation to browse fields and relations
|
|
4512 |
||
4513 |
:param cr: database cursor
|
|
3980
by Xavier Morel
[IMP] move browse_record to logging, __init__ docstring to sphinx info fields |
4514 |
:param uid: current user id
|
3511.1.6
by Vo Minh Thu
[REF] orm: merged orm_template inside orm. |
4515 |
:param select: id or list of ids.
|
4516 |
:param context: context arguments, like lang, time zone
|
|
4517 |
:rtype: object or list of objects requested
|
|
4518 |
||
4519 |
"""
|
|
4520 |
self._list_class = list_class or browse_record_list |
|
4521 |
cache = {} |
|
4522 |
# need to accepts ints and longs because ids coming from a method
|
|
4523 |
# launched by button in the interface have a type long...
|
|
4524 |
if isinstance(select, (int, long)): |
|
4525 |
return browse_record(cr, uid, select, self, cache, context=context, list_class=self._list_class, fields_process=fields_process) |
|
4526 |
elif isinstance(select, list): |
|
4527 |
return self._list_class([browse_record(cr, uid, id, self, cache, context=context, list_class=self._list_class, fields_process=fields_process) for id in select], context=context) |
|
4528 |
else: |
|
4529 |
return browse_null() |
|
4530 |
||
1390
by Fabien Pinckaers
bugfix_cache_speed_improvement_store |
4531 |
def _store_get_values(self, cr, uid, ids, fields, context): |
3266
by Olivier Dony
[FIX] orm.write(): avoid name collision of "ids" variable, causing side-effects + some doc |
4532 |
"""Returns an ordered list of fields.functions to call due to
|
4533 |
an update operation on ``fields`` of records with ``ids``,
|
|
4534 |
obtained by calling the 'store' functions of these fields,
|
|
4535 |
as setup by their 'store' attribute.
|
|
4536 |
||
4537 |
:return: [(priority, model_name, [record_ids,], [function_fields,])]
|
|
4538 |
"""
|
|
3612
by Olivier Dony
[FIX] orm: refactoring and fix of stored functions processing by _store_get_values |
4539 |
if fields is None: fields = [] |
4540 |
stored_functions = self.pool._store_function.get(self._name, []) |
|
4541 |
||
4542 |
# use indexed names for the details of the stored_functions:
|
|
4543 |
model_name_, func_field_to_compute_, id_mapping_fnct_, trigger_fields_, priority_ = range(5) |
|
4544 |
||
4545 |
# only keep functions that should be triggered for the ``fields``
|
|
4066.26.2
by Xavier ALT
[FIX] OPW 577292: orm: force computing store values for all columns on unlink() |
4546 |
# being written to.
|
3612
by Olivier Dony
[FIX] orm: refactoring and fix of stored functions processing by _store_get_values |
4547 |
to_compute = [f for f in stored_functions \ |
4066.26.2
by Xavier ALT
[FIX] OPW 577292: orm: force computing store values for all columns on unlink() |
4548 |
if ((not f[trigger_fields_]) or set(fields).intersection(f[trigger_fields_]))] |
3612
by Olivier Dony
[FIX] orm: refactoring and fix of stored functions processing by _store_get_values |
4549 |
|
4550 |
mapping = {} |
|
4551 |
for function in to_compute: |
|
3480
by Olivier Dony
[FIX] orm.unlink(): cascade deletion of ir.model.data performed as admin |
4552 |
# use admin user for accessing objects having rules defined on store fields
|
3511.1.35
by Olivier Dony
[IMP] start unifying the SUPERUSER_ID constant |
4553 |
target_ids = [id for id in function[id_mapping_fnct_](self, cr, SUPERUSER_ID, ids, context) if id] |
3612
by Olivier Dony
[FIX] orm: refactoring and fix of stored functions processing by _store_get_values |
4554 |
|
4555 |
# the compound key must consider the priority and model name
|
|
4556 |
key = (function[priority_], function[model_name_]) |
|
4557 |
for target_id in target_ids: |
|
4558 |
mapping.setdefault(key, {}).setdefault(target_id,set()).add(tuple(function)) |
|
4559 |
||
4560 |
# Here mapping looks like:
|
|
4561 |
# { (10, 'model_a') : { target_id1: [ (function_1_tuple, function_2_tuple) ], ... }
|
|
4562 |
# (20, 'model_a') : { target_id2: [ (function_3_tuple, function_4_tuple) ], ... }
|
|
4563 |
# (99, 'model_a') : { target_id1: [ (function_5_tuple, function_6_tuple) ], ... }
|
|
4564 |
# }
|
|
4565 |
||
4566 |
# Now we need to generate the batch function calls list
|
|
4567 |
# call_map =
|
|
4568 |
# { (10, 'model_a') : [(10, 'model_a', [record_ids,], [function_fields,])] }
|
|
4569 |
call_map = {} |
|
4570 |
for ((priority,model), id_map) in mapping.iteritems(): |
|
4571 |
functions_ids_maps = {} |
|
4572 |
# function_ids_maps =
|
|
4573 |
# { (function_1_tuple, function_2_tuple) : [target_id1, target_id2, ..] }
|
|
4574 |
for id, functions in id_map.iteritems(): |
|
4575 |
functions_ids_maps.setdefault(tuple(functions), []).append(id) |
|
4576 |
for functions, ids in functions_ids_maps.iteritems(): |
|
4577 |
call_map.setdefault((priority,model),[]).append((priority, model, ids, |
|
4578 |
[f[func_field_to_compute_] for f in functions])) |
|
4579 |
ordered_keys = call_map.keys() |
|
4580 |
ordered_keys.sort() |
|
4581 |
result = [] |
|
4582 |
if ordered_keys: |
|
4583 |
result = reduce(operator.add, (call_map[k] for k in ordered_keys)) |
|
4584 |
return result |
|
1390
by Fabien Pinckaers
bugfix_cache_speed_improvement_store |
4585 |
|
4586 |
def _store_set_values(self, cr, uid, ids, fields, context): |
|
3266
by Olivier Dony
[FIX] orm.write(): avoid name collision of "ids" variable, causing side-effects + some doc |
4587 |
"""Calls the fields.function's "implementation function" for all ``fields``, on records with ``ids`` (taking care of
|
4588 |
respecting ``multi`` attributes), and stores the resulting values in the database directly."""
|
|
2343
by Fabien Pinckaers
[FIX] store fields |
4589 |
if not ids: |
4590 |
return True |
|
1845
by HDA(OpenERP)
[Merged] |
4591 |
field_flag = False |
4592 |
field_dict = {} |
|
4593 |
if self._log_access: |
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
4594 |
cr.execute('select id,write_date from '+self._table+' where id IN %s', (tuple(ids),)) |
1845
by HDA(OpenERP)
[Merged] |
4595 |
res = cr.fetchall() |
4596 |
for r in res: |
|
4597 |
if r[1]: |
|
4598 |
field_dict.setdefault(r[0], []) |
|
4599 |
res_date = time.strptime((r[1])[:19], '%Y-%m-%d %H:%M:%S') |
|
4600 |
write_date = datetime.datetime.fromtimestamp(time.mktime(res_date)) |
|
4601 |
for i in self.pool._store_function.get(self._name, []): |
|
4602 |
if i[5]: |
|
4603 |
up_write_date = write_date + datetime.timedelta(hours=i[5]) |
|
4604 |
if datetime.datetime.now() < up_write_date: |
|
4605 |
if i[1] in fields: |
|
4606 |
field_dict[r[0]].append(i[1]) |
|
4607 |
if not field_flag: |
|
1865.1.1
by mra (Open ERP)
[FIX] osv_memory object search method: now it will accept id as domain with its action |
4608 |
field_flag = True |
1390
by Fabien Pinckaers
bugfix_cache_speed_improvement_store |
4609 |
todo = {} |
1398
by Fabien Pinckaers
improvements_bugfixes |
4610 |
keys = [] |
1390
by Fabien Pinckaers
bugfix_cache_speed_improvement_store |
4611 |
for f in fields: |
1398
by Fabien Pinckaers
improvements_bugfixes |
4612 |
if self._columns[f]._multi not in keys: |
4613 |
keys.append(self._columns[f]._multi) |
|
1390
by Fabien Pinckaers
bugfix_cache_speed_improvement_store |
4614 |
todo.setdefault(self._columns[f]._multi, []) |
4615 |
todo[self._columns[f]._multi].append(f) |
|
1398
by Fabien Pinckaers
improvements_bugfixes |
4616 |
for key in keys: |
4617 |
val = todo[key] |
|
1390
by Fabien Pinckaers
bugfix_cache_speed_improvement_store |
4618 |
if key: |
3480
by Olivier Dony
[FIX] orm.unlink(): cascade deletion of ir.model.data performed as admin |
4619 |
# use admin user for accessing objects having rules defined on store fields
|
3511.1.35
by Olivier Dony
[IMP] start unifying the SUPERUSER_ID constant |
4620 |
result = self._columns[val[0]].get(cr, self, ids, val, SUPERUSER_ID, context=context) |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
4621 |
for id, value in result.items(): |
1845
by HDA(OpenERP)
[Merged] |
4622 |
if field_flag: |
4623 |
for f in value.keys(): |
|
4624 |
if f in field_dict[id]: |
|
1865.1.1
by mra (Open ERP)
[FIX] osv_memory object search method: now it will accept id as domain with its action |
4625 |
value.pop(f) |
1390
by Fabien Pinckaers
bugfix_cache_speed_improvement_store |
4626 |
upd0 = [] |
4627 |
upd1 = [] |
|
4628 |
for v in value: |
|
1398
by Fabien Pinckaers
improvements_bugfixes |
4629 |
if v not in val: |
4630 |
continue
|
|
4102.1.3
by Vo Minh Thu
[IMP+FIX] fields: removed references to one2one, but also |
4631 |
if self._columns[v]._type == 'many2one': |
1390
by Fabien Pinckaers
bugfix_cache_speed_improvement_store |
4632 |
try: |
4633 |
value[v] = value[v][0] |
|
4634 |
except: |
|
4635 |
pass
|
|
4636 |
upd0.append('"'+v+'"='+self._columns[v]._symbol_set[0]) |
|
4637 |
upd1.append(self._columns[v]._symbol_set[1](value[v])) |
|
4638 |
upd1.append(id) |
|
2169
by Harry (Open ERP)
[FIX] orm: fixed problem on function field with store=True in create method |
4639 |
if upd0 and upd1: |
4640 |
cr.execute('update "' + self._table + '" set ' + \ |
|
2682
by olt at tinyerp
[FIX] missing comma in sql query 'execute' call ("select count...") |
4641 |
','.join(upd0) + ' where id = %s', upd1) |
1390
by Fabien Pinckaers
bugfix_cache_speed_improvement_store |
4642 |
|
4643 |
else: |
|
4644 |
for f in val: |
|
3480
by Olivier Dony
[FIX] orm.unlink(): cascade deletion of ir.model.data performed as admin |
4645 |
# use admin user for accessing objects having rules defined on store fields
|
3511.1.35
by Olivier Dony
[IMP] start unifying the SUPERUSER_ID constant |
4646 |
result = self._columns[f].get(cr, self, ids, f, SUPERUSER_ID, context=context) |
1845
by HDA(OpenERP)
[Merged] |
4647 |
for r in result.keys(): |
4648 |
if field_flag: |
|
4649 |
if r in field_dict.keys(): |
|
4650 |
if f in field_dict[r]: |
|
1865.1.1
by mra (Open ERP)
[FIX] osv_memory object search method: now it will accept id as domain with its action |
4651 |
result.pop(r) |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
4652 |
for id, value in result.items(): |
4102.1.3
by Vo Minh Thu
[IMP+FIX] fields: removed references to one2one, but also |
4653 |
if self._columns[f]._type == 'many2one': |
1390
by Fabien Pinckaers
bugfix_cache_speed_improvement_store |
4654 |
try: |
4655 |
value = value[0] |
|
4656 |
except: |
|
4657 |
pass
|
|
4658 |
cr.execute('update "' + self._table + '" set ' + \ |
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
4659 |
'"'+f+'"='+self._columns[f]._symbol_set[0] + ' where id = %s', (self._columns[f]._symbol_set[1](value), id)) |
922
by Christophe Simonis
convert tabs to 4 spaces |
4660 |
return True |
4661 |
||
4662 |
#
|
|
4663 |
# TODO: Validate
|
|
4664 |
#
|
|
4665 |
def perm_write(self, cr, user, ids, fields, context=None): |
|
2287
by olt at tinyerp
[FIX] orm: raise NotImplementedError instead of strings |
4666 |
raise NotImplementedError(_('This method does not exist anymore')) |
922
by Christophe Simonis
convert tabs to 4 spaces |
4667 |
|
4668 |
# TODO: ameliorer avec NULL
|
|
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4669 |
def _where_calc(self, cr, user, domain, active_test=True, context=None): |
2593
by Olivier Dony
[FIX] orm: fix search() with sorting by inherited field |
4670 |
"""Computes the WHERE clause needed to implement an OpenERP domain.
|
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4671 |
:param domain: the domain to compute
|
4672 |
:type domain: list
|
|
2593
by Olivier Dony
[FIX] orm: fix search() with sorting by inherited field |
4673 |
:param active_test: whether the default filtering of records with ``active``
|
2743
by nch at tinyerp
[FIX]:default sorting in group by |
4674 |
field set to ``False`` should be applied.
|
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4675 |
:return: the query expressing the given domain as provided in domain
|
4676 |
:rtype: osv.query.Query
|
|
2593
by Olivier Dony
[FIX] orm: fix search() with sorting by inherited field |
4677 |
"""
|
922
by Christophe Simonis
convert tabs to 4 spaces |
4678 |
if not context: |
957
by Olivier Laurent
pep8 |
4679 |
context = {} |
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4680 |
domain = domain[:] |
922
by Christophe Simonis
convert tabs to 4 spaces |
4681 |
# if the object has a field named 'active', filter out all inactive
|
4682 |
# records unless they were explicitely asked for
|
|
4066.1.10
by Olivier Dony
[FIX] orm: inherited `active` fields should be used for the implicit filtering |
4683 |
if 'active' in self._all_columns and (active_test and context.get('active_test', True)): |
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4684 |
if domain: |
4066.1.10
by Olivier Dony
[FIX] orm: inherited `active` fields should be used for the implicit filtering |
4685 |
# the item[0] trick below works for domain items and '&'/'|'/'!'
|
4686 |
# operators too
|
|
4687 |
if not any(item[0] == 'active' for item in domain): |
|
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4688 |
domain.insert(0, ('active', '=', 1)) |
966
by Christophe Simonis
modify the expression parser to use a (more) real polish notation. |
4689 |
else: |
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4690 |
domain = [('active', '=', 1)] |
941
by Christophe Simonis
move domain parsing into expression class |
4691 |
|
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4692 |
if domain: |
3515.4.2
by Vo Minh Thu
[REF] expression: instanciating an expression now "parses" it, moved some methods as functions, introduced some nice globals. |
4693 |
e = expression.expression(cr, user, domain, self, context) |
941
by Christophe Simonis
move domain parsing into expression class |
4694 |
tables = e.get_tables() |
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4695 |
where_clause, where_params = e.to_sql() |
4696 |
where_clause = where_clause and [where_clause] or [] |
|
941
by Christophe Simonis
move domain parsing into expression class |
4697 |
else: |
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4698 |
where_clause, where_params, tables = [], [], ['"%s"' % self._table] |
941
by Christophe Simonis
move domain parsing into expression class |
4699 |
|
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4700 |
return Query(tables, where_clause, where_params) |
922
by Christophe Simonis
convert tabs to 4 spaces |
4701 |
|
4702 |
def _check_qorder(self, word): |
|
4703 |
if not regex_order.match(word): |
|
2728.1.12
by Olivier Dony
[IMP] orm.search: improved support for "order" param: multiple fields, m2o fields, stored function fields, inherited fields |
4704 |
raise except_orm(_('AccessError'), _('Invalid "order" specified. A valid "order" specification is a comma-separated list of valid field names (optionally followed by asc/desc for the direction)')) |
922
by Christophe Simonis
convert tabs to 4 spaces |
4705 |
return True |
4706 |
||
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4707 |
def _apply_ir_rules(self, cr, uid, query, mode='read', context=None): |
2783.2.10
by jas
[IMP] : base : improved Warnings |
4708 |
"""Add what's missing in ``query`` to implement all appropriate ir.rules
|
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4709 |
(using the ``model_name``'s rules or the current model's rules if ``model_name`` is None)
|
4710 |
||
4711 |
:param query: the current query object
|
|
4712 |
"""
|
|
2826
by Olivier Dony
[FIX] orm.search: properly JOIN parent table when applying inherited ir.rules |
4713 |
def apply_rule(added_clause, added_params, added_tables, parent_model=None, child_object=None): |
4601.2.40
by Thibault Delavallée
[IMP] [WIP] order_by: now using aliases in _generate_order_by, _generate_o2m_order_by, _inherits_join_add, ... added some tests. Next comits will clean a bit the code, because currently it is a bit messy. |
4714 |
""" :param string parent_model: string of the parent model
|
4715 |
:param model child_object: model object, base of the rule application
|
|
4716 |
"""
|
|
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4717 |
if added_clause: |
2826
by Olivier Dony
[FIX] orm.search: properly JOIN parent table when applying inherited ir.rules |
4718 |
if parent_model and child_object: |
4719 |
# as inherited rules are being applied, we need to add the missing JOIN
|
|
4720 |
# to reach the parent table (if it was not JOINed yet in the query)
|
|
4601.2.40
by Thibault Delavallée
[IMP] [WIP] order_by: now using aliases in _generate_order_by, _generate_o2m_order_by, _inherits_join_add, ... added some tests. Next comits will clean a bit the code, because currently it is a bit messy. |
4721 |
parent_alias = child_object._inherits_join_add(child_object, parent_model, query) |
4722 |
# inherited rules are applied on the external table -> need to get the alias and replace
|
|
4723 |
parent_table = self.pool.get(parent_model)._table |
|
4724 |
added_clause = [clause.replace('"%s"' % parent_table, '"%s"' % parent_alias) for clause in added_clause] |
|
4601.2.48
by Thibault Delavallée
[CLEAN] orm: added quote around a forgottent table name; cleaned a bit some code and added comments, removed dead code. |
4725 |
# change references to parent_table to parent_alias, because we now use the alias to refer to the table
|
4601.2.40
by Thibault Delavallée
[IMP] [WIP] order_by: now using aliases in _generate_order_by, _generate_o2m_order_by, _inherits_join_add, ... added some tests. Next comits will clean a bit the code, because currently it is a bit messy. |
4726 |
new_tables = [] |
4727 |
for table in added_tables: |
|
4601.2.48
by Thibault Delavallée
[CLEAN] orm: added quote around a forgottent table name; cleaned a bit some code and added comments, removed dead code. |
4728 |
# table is just a table name -> switch to the full alias
|
4682.2.9
by Xavier Morel
[REM] unnecessary parens |
4729 |
if table == '"%s"' % parent_table: |
4601.2.48
by Thibault Delavallée
[CLEAN] orm: added quote around a forgottent table name; cleaned a bit some code and added comments, removed dead code. |
4730 |
new_tables.append('"%s" as "%s"' % (parent_table, parent_alias)) |
4731 |
# table is already a full statement -> replace reference to the table to its alias, is correct with the way aliases are generated
|
|
4601.2.40
by Thibault Delavallée
[IMP] [WIP] order_by: now using aliases in _generate_order_by, _generate_o2m_order_by, _inherits_join_add, ... added some tests. Next comits will clean a bit the code, because currently it is a bit messy. |
4732 |
else: |
4601.2.48
by Thibault Delavallée
[CLEAN] orm: added quote around a forgottent table name; cleaned a bit some code and added comments, removed dead code. |
4733 |
new_tables.append(table.replace('"%s"' % parent_table, '"%s"' % parent_alias)) |
4601.2.40
by Thibault Delavallée
[IMP] [WIP] order_by: now using aliases in _generate_order_by, _generate_o2m_order_by, _inherits_join_add, ... added some tests. Next comits will clean a bit the code, because currently it is a bit messy. |
4734 |
added_tables = new_tables |
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4735 |
query.where_clause += added_clause |
4736 |
query.where_clause_params += added_params |
|
4737 |
for table in added_tables: |
|
4601.2.40
by Thibault Delavallée
[IMP] [WIP] order_by: now using aliases in _generate_order_by, _generate_o2m_order_by, _inherits_join_add, ... added some tests. Next comits will clean a bit the code, because currently it is a bit messy. |
4738 |
if table not in query.tables: |
4739 |
query.tables.append(table) |
|
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4740 |
return True |
4741 |
return False |
|
2793.1.3
by Olivier Dony
[IMP] osv.query,orm: removed trailing whitespace introduced by previous commits |
4742 |
|
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4743 |
# apply main rules on the object
|
4744 |
rule_obj = self.pool.get('ir.rule') |
|
4601.2.40
by Thibault Delavallée
[IMP] [WIP] order_by: now using aliases in _generate_order_by, _generate_o2m_order_by, _inherits_join_add, ... added some tests. Next comits will clean a bit the code, because currently it is a bit messy. |
4745 |
rule_where_clause, rule_where_clause_params, rule_tables = rule_obj.domain_get(cr, uid, self._name, mode, context=context) |
4746 |
apply_rule(rule_where_clause, rule_where_clause_params, rule_tables) |
|
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4747 |
|
2826
by Olivier Dony
[FIX] orm.search: properly JOIN parent table when applying inherited ir.rules |
4748 |
# apply ir.rules from the parents (through _inherits)
|
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4749 |
for inherited_model in self._inherits: |
4601.2.40
by Thibault Delavallée
[IMP] [WIP] order_by: now using aliases in _generate_order_by, _generate_o2m_order_by, _inherits_join_add, ... added some tests. Next comits will clean a bit the code, because currently it is a bit messy. |
4750 |
rule_where_clause, rule_where_clause_params, rule_tables = rule_obj.domain_get(cr, uid, inherited_model, mode, context=context) |
4751 |
apply_rule(rule_where_clause, rule_where_clause_params, rule_tables, |
|
4752 |
parent_model=inherited_model, child_object=self) |
|
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4753 |
|
4754 |
def _generate_m2o_order_by(self, order_field, query): |
|
4755 |
"""
|
|
4756 |
Add possibly missing JOIN to ``query`` and generate the ORDER BY clause for m2o fields,
|
|
2728.1.12
by Olivier Dony
[IMP] orm.search: improved support for "order" param: multiple fields, m2o fields, stored function fields, inherited fields |
4757 |
either native m2o fields or function/related fields that are stored, including
|
4758 |
intermediate JOINs for inheritance if required.
|
|
2793.1.3
by Olivier Dony
[IMP] osv.query,orm: removed trailing whitespace introduced by previous commits |
4759 |
|
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4760 |
:return: the qualified field name to use in an ORDER BY clause to sort by ``order_field``
|
2728.1.12
by Olivier Dony
[IMP] orm.search: improved support for "order" param: multiple fields, m2o fields, stored function fields, inherited fields |
4761 |
"""
|
4762 |
if order_field not in self._columns and order_field in self._inherit_fields: |
|
4763 |
# also add missing joins for reaching the table containing the m2o field
|
|
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4764 |
qualified_field = self._inherits_join_calc(order_field, query) |
2728.1.12
by Olivier Dony
[IMP] orm.search: improved support for "order" param: multiple fields, m2o fields, stored function fields, inherited fields |
4765 |
order_field_column = self._inherit_fields[order_field][2] |
4766 |
else: |
|
4767 |
qualified_field = '"%s"."%s"' % (self._table, order_field) |
|
4768 |
order_field_column = self._columns[order_field] |
|
4769 |
||
4770 |
assert order_field_column._type == 'many2one', 'Invalid field passed to _generate_m2o_order_by()' |
|
3094
by Olivier Dony
[IMP] orm.search: more quietly ignore invalid m2o order spec + fallback to default _order |
4771 |
if not order_field_column._classic_write and not getattr(order_field_column, 'store', False): |
3976.1.7
by Vo Minh Thu
[IMP] openerp.{modules,osv}: _logger with fully qualified module name. |
4772 |
_logger.debug("Many2one function/related fields must be stored " \ |
4773 |
"to be used as ordering fields! Ignoring sorting for %s.%s", |
|
4774 |
self._name, order_field) |
|
3094
by Olivier Dony
[IMP] orm.search: more quietly ignore invalid m2o order spec + fallback to default _order |
4775 |
return
|
2728.1.12
by Olivier Dony
[IMP] orm.search: improved support for "order" param: multiple fields, m2o fields, stored function fields, inherited fields |
4776 |
|
4777 |
# figure out the applicable order_by for the m2o
|
|
4778 |
dest_model = self.pool.get(order_field_column._obj) |
|
4779 |
m2o_order = dest_model._order |
|
4780 |
if not regex_order.match(m2o_order): |
|
4781 |
# _order is complex, can't use it here, so we default to _rec_name
|
|
4782 |
m2o_order = dest_model._rec_name |
|
4783 |
else: |
|
3143.1.2
by Olivier Dony
[IMP] orm.search: support multiple _order clauses in destination objects when sorting on m2o fields |
4784 |
# extract the field names, to be able to qualify them and add desc/asc
|
4785 |
m2o_order_list = [] |
|
3292
by Anup(OpenERP)
[FIX] orm.search: fix ordering when multiple order columns are used |
4786 |
for order_part in m2o_order.split(","): |
4601.2.40
by Thibault Delavallée
[IMP] [WIP] order_by: now using aliases in _generate_order_by, _generate_o2m_order_by, _inherits_join_add, ... added some tests. Next comits will clean a bit the code, because currently it is a bit messy. |
4787 |
m2o_order_list.append(order_part.strip().split(" ", 1)[0].strip()) |
3143.1.2
by Olivier Dony
[IMP] orm.search: support multiple _order clauses in destination objects when sorting on m2o fields |
4788 |
m2o_order = m2o_order_list |
2728.1.12
by Olivier Dony
[IMP] orm.search: improved support for "order" param: multiple fields, m2o fields, stored function fields, inherited fields |
4789 |
|
2783.2.10
by jas
[IMP] : base : improved Warnings |
4790 |
# Join the dest m2o table if it's not joined yet. We use [LEFT] OUTER join here
|
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4791 |
# as we don't want to exclude results that have NULL values for the m2o
|
4601.2.40
by Thibault Delavallée
[IMP] [WIP] order_by: now using aliases in _generate_order_by, _generate_o2m_order_by, _inherits_join_add, ... added some tests. Next comits will clean a bit the code, because currently it is a bit messy. |
4792 |
src_table, src_field = qualified_field.replace('"', '').split('.', 1) |
4601.2.44
by Thibault Delavallée
[CLEAN] Query: cleaned a bit the code. All joins now goes through the same method, either implicit or explicit. Will have to be upgraded in future versions, but at least this is a bit centralized. Updated ORM accordingly. Updated tests. Added a get_alias_from_query method in expression that find the table and the alias from a 'full alias' statement. |
4793 |
dst_alias, dst_alias_statement = query.add_join((src_table, dest_model._table, src_field, 'id', src_field), implicit=False, outer=True) |
4601.2.40
by Thibault Delavallée
[IMP] [WIP] order_by: now using aliases in _generate_order_by, _generate_o2m_order_by, _inherits_join_add, ... added some tests. Next comits will clean a bit the code, because currently it is a bit messy. |
4794 |
qualify = lambda field: '"%s"."%s"' % (dst_alias, field) |
3143.1.2
by Olivier Dony
[IMP] orm.search: support multiple _order clauses in destination objects when sorting on m2o fields |
4795 |
return map(qualify, m2o_order) if isinstance(m2o_order, list) else qualify(m2o_order) |
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4796 |
|
4797 |
def _generate_order_by(self, order_spec, query): |
|
2728.1.12
by Olivier Dony
[IMP] orm.search: improved support for "order" param: multiple fields, m2o fields, stored function fields, inherited fields |
4798 |
"""
|
4799 |
Attempt to consruct an appropriate ORDER BY clause based on order_spec, which must be
|
|
4800 |
a comma-separated list of valid field names, optionally followed by an ASC or DESC direction.
|
|
4801 |
||
4802 |
:raise" except_orm in case order_spec is malformed
|
|
4803 |
"""
|
|
4601.2.45
by Thibault Delavallée
[MERGE] Merged branch holding a proposal to solve the generate_order_by issue (see lp:1070757). As requested by Olivier Donny, this fix has been moved into generate_order_by. Deleted previous workaround done in this branch. Patch courtesy of Paulius Sladkevičius |
4804 |
order_by_clause = '' |
4717.1.3
by Thibault Delavallée
[REV] Reverted last improvement, because this was messing with the menus. |
4805 |
order_spec = order_spec or self._order |
2728.1.12
by Olivier Dony
[IMP] orm.search: improved support for "order" param: multiple fields, m2o fields, stored function fields, inherited fields |
4806 |
if order_spec: |
4807 |
order_by_elements = [] |
|
4808 |
self._check_qorder(order_spec) |
|
4809 |
for order_part in order_spec.split(','): |
|
4810 |
order_split = order_part.strip().split(' ') |
|
4811 |
order_field = order_split[0].strip() |
|
4812 |
order_direction = order_split[1].strip() if len(order_split) == 2 else '' |
|
3094
by Olivier Dony
[IMP] orm.search: more quietly ignore invalid m2o order spec + fallback to default _order |
4813 |
inner_clause = None |
2830.3.1
by Julien Thewys
[FIX] generate_order_by now supports 'id' order_field. |
4814 |
if order_field == 'id': |
4579.1.4
by Quentin (OpenERP)
[FIX] osv/orm: fixed the use of 'order' argument given to a search function in order to allow to order by 'ID DESC' and by '<another valid order> + ID ASC/DESC' |
4815 |
order_by_elements.append('"%s"."id" %s' % (self._table, order_direction)) |
2830.3.1
by Julien Thewys
[FIX] generate_order_by now supports 'id' order_field. |
4816 |
elif order_field in self._columns: |
2728.1.12
by Olivier Dony
[IMP] orm.search: improved support for "order" param: multiple fields, m2o fields, stored function fields, inherited fields |
4817 |
order_column = self._columns[order_field] |
4818 |
if order_column._classic_read: |
|
3094
by Olivier Dony
[IMP] orm.search: more quietly ignore invalid m2o order spec + fallback to default _order |
4819 |
inner_clause = '"%s"."%s"' % (self._table, order_field) |
2728.1.12
by Olivier Dony
[IMP] orm.search: improved support for "order" param: multiple fields, m2o fields, stored function fields, inherited fields |
4820 |
elif order_column._type == 'many2one': |
3094
by Olivier Dony
[IMP] orm.search: more quietly ignore invalid m2o order spec + fallback to default _order |
4821 |
inner_clause = self._generate_m2o_order_by(order_field, query) |
2728.1.12
by Olivier Dony
[IMP] orm.search: improved support for "order" param: multiple fields, m2o fields, stored function fields, inherited fields |
4822 |
else: |
4601.2.40
by Thibault Delavallée
[IMP] [WIP] order_by: now using aliases in _generate_order_by, _generate_o2m_order_by, _inherits_join_add, ... added some tests. Next comits will clean a bit the code, because currently it is a bit messy. |
4823 |
continue # ignore non-readable or "non-joinable" fields |
2728.1.12
by Olivier Dony
[IMP] orm.search: improved support for "order" param: multiple fields, m2o fields, stored function fields, inherited fields |
4824 |
elif order_field in self._inherit_fields: |
3549.4.1
by Naresh(OpenERP)
[FIX]:_inherits for multilevel inheritence |
4825 |
parent_obj = self.pool.get(self._inherit_fields[order_field][3]) |
2728.1.12
by Olivier Dony
[IMP] orm.search: improved support for "order" param: multiple fields, m2o fields, stored function fields, inherited fields |
4826 |
order_column = parent_obj._columns[order_field] |
4827 |
if order_column._classic_read: |
|
3094
by Olivier Dony
[IMP] orm.search: more quietly ignore invalid m2o order spec + fallback to default _order |
4828 |
inner_clause = self._inherits_join_calc(order_field, query) |
2728.1.12
by Olivier Dony
[IMP] orm.search: improved support for "order" param: multiple fields, m2o fields, stored function fields, inherited fields |
4829 |
elif order_column._type == 'many2one': |
3094
by Olivier Dony
[IMP] orm.search: more quietly ignore invalid m2o order spec + fallback to default _order |
4830 |
inner_clause = self._generate_m2o_order_by(order_field, query) |
2728.1.12
by Olivier Dony
[IMP] orm.search: improved support for "order" param: multiple fields, m2o fields, stored function fields, inherited fields |
4831 |
else: |
4601.2.40
by Thibault Delavallée
[IMP] [WIP] order_by: now using aliases in _generate_order_by, _generate_o2m_order_by, _inherits_join_add, ... added some tests. Next comits will clean a bit the code, because currently it is a bit messy. |
4832 |
continue # ignore non-readable or "non-joinable" fields |
3094
by Olivier Dony
[IMP] orm.search: more quietly ignore invalid m2o order spec + fallback to default _order |
4833 |
if inner_clause: |
3143.1.2
by Olivier Dony
[IMP] orm.search: support multiple _order clauses in destination objects when sorting on m2o fields |
4834 |
if isinstance(inner_clause, list): |
4835 |
for clause in inner_clause: |
|
4836 |
order_by_elements.append("%s %s" % (clause, order_direction)) |
|
4837 |
else: |
|
4838 |
order_by_elements.append("%s %s" % (inner_clause, order_direction)) |
|
3094
by Olivier Dony
[IMP] orm.search: more quietly ignore invalid m2o order spec + fallback to default _order |
4839 |
if order_by_elements: |
4840 |
order_by_clause = ",".join(order_by_elements) |
|
2728.1.12
by Olivier Dony
[IMP] orm.search: improved support for "order" param: multiple fields, m2o fields, stored function fields, inherited fields |
4841 |
|
4842 |
return order_by_clause and (' ORDER BY %s ' % order_by_clause) or '' |
|
4843 |
||
2675
by Olivier Dony
[FIX] orm: fields_view_get selection values must properly heed ir.rules: |
4844 |
def _search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False, access_rights_uid=None): |
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
4845 |
"""
|
2743
by nch at tinyerp
[FIX]:default sorting in group by |
4846 |
Private implementation of search() method, allowing specifying the uid to use for the access right check.
|
2675
by Olivier Dony
[FIX] orm: fields_view_get selection values must properly heed ir.rules: |
4847 |
This is useful for example when filling in the selection list for a drop-down and avoiding access rights errors,
|
4848 |
by specifying ``access_rights_uid=1`` to bypass access rights check, but not ir.rules!
|
|
4849 |
This is ok at the security level because this method is private and not callable through XML-RPC.
|
|
2743
by nch at tinyerp
[FIX]:default sorting in group by |
4850 |
|
2675
by Olivier Dony
[FIX] orm: fields_view_get selection values must properly heed ir.rules: |
4851 |
:param access_rights_uid: optional user ID to use when checking access rights
|
4852 |
(not for ir.rules, this is only for ir.model.access)
|
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
4853 |
"""
|
2238.1.25
by Albert Cervera i Areny - http://www.NaN-tic.com
[FIX] Exploit Broken : Search should have checked access rights of read |
4854 |
if context is None: |
922
by Christophe Simonis
convert tabs to 4 spaces |
4855 |
context = {} |
4344.1.16
by Fabien Pinckaers
[IMP] Removing a bad practice: |
4856 |
self.check_access_rights(cr, access_rights_uid or user, 'read') |
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4857 |
|
3511.1.31
by Olivier Dony
[IMP] orm: fix Model hierarchy to avoid TransientModel leaking downstream |
4858 |
# For transient models, restrict acces to the current user, except for the super-user
|
3511.1.35
by Olivier Dony
[IMP] start unifying the SUPERUSER_ID constant |
4859 |
if self.is_transient() and self._log_access and user != SUPERUSER_ID: |
3511.1.31
by Olivier Dony
[IMP] orm: fix Model hierarchy to avoid TransientModel leaking downstream |
4860 |
args = expression.AND(([('create_uid', '=', user)], args or [])) |
4861 |
||
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4862 |
query = self._where_calc(cr, user, args, context=context) |
4863 |
self._apply_ir_rules(cr, user, query, 'read', context=context) |
|
4864 |
order_by = self._generate_order_by(order, query) |
|
4865 |
from_clause, where_clause, where_clause_params = query.get_sql() |
|
4866 |
||
922
by Christophe Simonis
convert tabs to 4 spaces |
4867 |
limit_str = limit and ' limit %d' % limit or '' |
4868 |
offset_str = offset and ' offset %d' % offset or '' |
|
2793.1.1
by Olivier Dony
[ADD] osv: initial basic implementation of Query object: |
4869 |
where_str = where_clause and (" WHERE %s" % where_clause) or '' |
4795.1.1
by Raphael Collet
[FIX] search: when count=True, execute main query as a subquery to avoid side effects with offset and limit |
4870 |
query_str = 'SELECT "%s".id FROM ' % self._table + from_clause + where_str + order_by + limit_str + offset_str |
922
by Christophe Simonis
convert tabs to 4 spaces |
4871 |
|
4872 |
if count: |
|
4795.1.1
by Raphael Collet
[FIX] search: when count=True, execute main query as a subquery to avoid side effects with offset and limit |
4873 |
# /!\ the main query must be executed as a subquery, otherwise
|
4874 |
# offset and limit apply to the result of count()!
|
|
4802
by Olivier Dony
[MERGE] search: when count=True, execute main query as a subquery to avoid side effects with offset and limit |
4875 |
cr.execute('SELECT count(*) FROM (%s) AS count' % query_str, where_clause_params) |
4795.1.1
by Raphael Collet
[FIX] search: when count=True, execute main query as a subquery to avoid side effects with offset and limit |
4876 |
res = cr.fetchone() |
4877 |
return res[0] |
|
4878 |
||
4879 |
cr.execute(query_str, where_clause_params) |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
4880 |
res = cr.fetchall() |
4717.1.1
by Thibault Delavallée
[FIX] _search not uniquifies the results, because when using _auto_join we could have duplicates in the 'left part' of the result, i.e. a lead with several unread messages. This is done using a custom method instead of a set, because sets are unordered. |
4881 |
|
4882 |
# TDE note: with auto_join, we could have several lines about the same result
|
|
4883 |
# i.e. a lead with several unread messages; we uniquify the result using
|
|
4884 |
# a fast way to do it while preserving order (http://www.peterbe.com/plog/uniqifiers-benchmark)
|
|
4885 |
def _uniquify_list(seq): |
|
4886 |
seen = set() |
|
4887 |
return [x for x in seq if x not in seen and not seen.add(x)] |
|
4888 |
||
4889 |
return _uniquify_list([x[0] for x in res]) |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
4890 |
|
4891 |
# returns the different values ever entered for one field
|
|
4892 |
# this is used, for example, in the client when the user hits enter on
|
|
4893 |
# a char field
|
|
4894 |
def distinct_field_get(self, cr, uid, field, value, args=None, offset=0, limit=None): |
|
4895 |
if not args: |
|
957
by Olivier Laurent
pep8 |
4896 |
args = [] |
922
by Christophe Simonis
convert tabs to 4 spaces |
4897 |
if field in self._inherit_fields: |
957
by Olivier Laurent
pep8 |
4898 |
return self.pool.get(self._inherit_fields[field][0]).distinct_field_get(cr, uid, field, value, args, offset, limit) |
922
by Christophe Simonis
convert tabs to 4 spaces |
4899 |
else: |
4900 |
return self._columns[field].search(cr, self, args, field, value, offset, limit, uid) |
|
4901 |
||
1691
by Fabien Pinckaers
bugfix |
4902 |
def copy_data(self, cr, uid, id, default=None, context=None): |
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
4903 |
"""
|
4904 |
Copy given record's data with all its fields values
|
|
4905 |
||
4906 |
:param cr: database cursor
|
|
4682.2.15
by Xavier Morel
[FIX] incorrect docstrings or docstring param names not matching actual param names |
4907 |
:param uid: current user id
|
2572
by Olivier Dony
[IMP] orm: docstring improvements: fixed incorrect RST indentation + improved doc a bit + some cleanup |
4908 |
:param id: id of the record to copy
|
4909 |
:param default: field values to override in the original values of the copied record
|
|
4910 |
:type default: dictionary
|
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
4911 |
:param context: context arguments, like lang, time zone
|
2572
by Olivier Dony
[IMP] orm: docstring improvements: fixed incorrect RST indentation + improved doc a bit + some cleanup |
4912 |
:type context: dictionary
|
2111.3.21
by Rvo(Open ERP)
[IMP]:improved orm's public methods docstrings |
4913 |
:return: dictionary containing all the field values
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
4914 |
"""
|
4915 |
||
2238.1.42
by Jay(Open ERP)
[FIX] fixed a bug in copy_data: it now copies the original (not translated) field value as the source value |
4916 |
if context is None: |
957
by Olivier Laurent
pep8 |
4917 |
context = {} |
3055
by Olivier Dony
[MERGE+FIX] fixes for translations,sequence,copy,error messages - some patches courtesy of Margarita Manterola and Don Kirkby |
4918 |
|
4919 |
# avoid recursion through already copied records in case of circular relationship
|
|
4920 |
seen_map = context.setdefault('__copy_data_seen',{}) |
|
4921 |
if id in seen_map.setdefault(self._name,[]): |
|
4922 |
return
|
|
4923 |
seen_map[self._name].append(id) |
|
4924 |
||
2238.1.42
by Jay(Open ERP)
[FIX] fixed a bug in copy_data: it now copies the original (not translated) field value as the source value |
4925 |
if default is None: |
922
by Christophe Simonis
convert tabs to 4 spaces |
4926 |
default = {} |
4927 |
if 'state' not in default: |
|
4928 |
if 'state' in self._defaults: |
|
1897
by Christophe Simonis
[IMP] _defaults: allow to pass directly the value instead of a lambda |
4929 |
if callable(self._defaults['state']): |
4930 |
default['state'] = self._defaults['state'](self, cr, uid, context) |
|
4931 |
else: |
|
4932 |
default['state'] = self._defaults['state'] |
|
4933 |
||
3017.1.2
by P. Christeas
ORM: fix bug at orm.copy_data(), would reset context[lang] |
4934 |
context_wo_lang = context.copy() |
2238.1.42
by Jay(Open ERP)
[FIX] fixed a bug in copy_data: it now copies the original (not translated) field value as the source value |
4935 |
if 'lang' in context: |
4936 |
del context_wo_lang['lang'] |
|
3017.1.3
by P. Christeas
ORM: Better message when copying non-existing records |
4937 |
data = self.read(cr, uid, [id,], context=context_wo_lang) |
4938 |
if data: |
|
4939 |
data = data[0] |
|
4940 |
else: |
|
4941 |
raise IndexError( _("Record #%d of %s not found, cannot copy!") %( id, self._name)) |
|
2572
by Olivier Dony
[IMP] orm: docstring improvements: fixed incorrect RST indentation + improved doc a bit + some cleanup |
4942 |
|
4457.2.1
by Raphael Collet
[FIX] orm: fix the method copy() when an 'inherits' field is given in parameter default |
4943 |
# build a black list of fields that should not be copied
|
4944 |
blacklist = set(MAGIC_COLUMNS + ['parent_left', 'parent_right']) |
|
4945 |
def blacklist_given_fields(obj): |
|
4946 |
# blacklist the fields that are given by inheritance
|
|
4947 |
for other, field_to_other in obj._inherits.items(): |
|
4948 |
blacklist.add(field_to_other) |
|
4949 |
if field_to_other in default: |
|
4950 |
# all the fields of 'other' are given by the record: default[field_to_other],
|
|
4951 |
# except the ones redefined in self
|
|
4952 |
blacklist.update(set(self.pool.get(other)._all_columns) - set(self._columns)) |
|
4953 |
else: |
|
4954 |
blacklist_given_fields(self.pool.get(other)) |
|
4955 |
blacklist_given_fields(self) |
|
4956 |
||
4957 |
res = dict(default) |
|
4958 |
for f, colinfo in self._all_columns.items(): |
|
4959 |
field = colinfo.column |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
4960 |
if f in default: |
4457.2.1
by Raphael Collet
[FIX] orm: fix the method copy() when an 'inherits' field is given in parameter default |
4961 |
pass
|
4962 |
elif f in blacklist: |
|
4963 |
pass
|
|
4964 |
elif isinstance(field, fields.function): |
|
4965 |
pass
|
|
4966 |
elif field._type == 'many2one': |
|
4967 |
res[f] = data[f] and data[f][0] |
|
4968 |
elif field._type == 'one2many': |
|
4969 |
other = self.pool.get(field._obj) |
|
4970 |
# duplicate following the order of the ids because we'll rely on
|
|
4971 |
# it later for copying translations in copy_translation()!
|
|
4972 |
lines = [other.copy_data(cr, uid, line_id, context=context) for line_id in sorted(data[f])] |
|
4973 |
# the lines are duplicated using the wrong (old) parent, but then
|
|
4974 |
# are reassigned to the correct one thanks to the (0, 0, ...)
|
|
4975 |
res[f] = [(0, 0, line) for line in lines if line] |
|
4976 |
elif field._type == 'many2many': |
|
4977 |
res[f] = [(6, 0, data[f])] |
|
4978 |
else: |
|
4979 |
res[f] = data[f] |
|
4980 |
||
4981 |
return res |
|
2402
by Olivier Dony, Jay Vora
[FIX] orm: proper recursive copy of translations through one2many relationships during copy() |
4982 |
|
4983 |
def copy_translations(self, cr, uid, old_id, new_id, context=None): |
|
3055
by Olivier Dony
[MERGE+FIX] fixes for translations,sequence,copy,error messages - some patches courtesy of Margarita Manterola and Don Kirkby |
4984 |
if context is None: |
4985 |
context = {} |
|
4986 |
||
4987 |
# avoid recursion through already copied records in case of circular relationship
|
|
4988 |
seen_map = context.setdefault('__copy_translations_seen',{}) |
|
4989 |
if old_id in seen_map.setdefault(self._name,[]): |
|
4990 |
return
|
|
4991 |
seen_map[self._name].append(old_id) |
|
4992 |
||
2402
by Olivier Dony, Jay Vora
[FIX] orm: proper recursive copy of translations through one2many relationships during copy() |
4993 |
trans_obj = self.pool.get('ir.translation') |
3466.1.9
by Vo Minh Thu
[REF] orm: added TODOs. |
4994 |
# TODO it seems fields_get can be replaced by _all_columns (no need for translation)
|
2402
by Olivier Dony, Jay Vora
[FIX] orm: proper recursive copy of translations through one2many relationships during copy() |
4995 |
fields = self.fields_get(cr, uid, context=context) |
4996 |
||
4997 |
translation_records = [] |
|
4998 |
for field_name, field_def in fields.items(): |
|
4999 |
# we must recursively copy the translations for o2o and o2m
|
|
4102.1.3
by Vo Minh Thu
[IMP+FIX] fields: removed references to one2one, but also |
5000 |
if field_def['type'] == 'one2many': |
2402
by Olivier Dony, Jay Vora
[FIX] orm: proper recursive copy of translations through one2many relationships during copy() |
5001 |
target_obj = self.pool.get(field_def['relation']) |
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
5002 |
old_record, new_record = self.read(cr, uid, [old_id, new_id], [field_name], context=context) |
2402
by Olivier Dony, Jay Vora
[FIX] orm: proper recursive copy of translations through one2many relationships during copy() |
5003 |
# here we rely on the order of the ids to match the translations
|
5004 |
# as foreseen in copy_data()
|
|
2148.1.4
by Numerigraphe - Lionel Sausin
[IMP] wording: children intead of childs (again) |
5005 |
old_children = sorted(old_record[field_name]) |
5006 |
new_children = sorted(new_record[field_name]) |
|
5007 |
for (old_child, new_child) in zip(old_children, new_children): |
|
2402
by Olivier Dony, Jay Vora
[FIX] orm: proper recursive copy of translations through one2many relationships during copy() |
5008 |
target_obj.copy_translations(cr, uid, old_child, new_child, context=context) |
5009 |
# and for translatable fields we keep them for copy
|
|
5010 |
elif field_def.get('translate'): |
|
5011 |
trans_name = '' |
|
5012 |
if field_name in self._columns: |
|
5013 |
trans_name = self._name + "," + field_name |
|
5014 |
elif field_name in self._inherit_fields: |
|
5015 |
trans_name = self._inherit_fields[field_name][0] + "," + field_name |
|
5016 |
if trans_name: |
|
5017 |
trans_ids = trans_obj.search(cr, uid, [ |
|
5018 |
('name', '=', trans_name), |
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
5019 |
('res_id', '=', old_id) |
2402
by Olivier Dony, Jay Vora
[FIX] orm: proper recursive copy of translations through one2many relationships during copy() |
5020 |
])
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
5021 |
translation_records.extend(trans_obj.read(cr, uid, trans_ids, context=context)) |
2402
by Olivier Dony, Jay Vora
[FIX] orm: proper recursive copy of translations through one2many relationships during copy() |
5022 |
|
5023 |
for record in translation_records: |
|
5024 |
del record['id'] |
|
5025 |
record['res_id'] = new_id |
|
5026 |
trans_obj.create(cr, uid, record, context=context) |
|
5027 |
||
1170
by Jay Vora
Corrected copy functionality for fields translated=True (Ref: Yajushi Yagnik) |
5028 |
|
1691
by Fabien Pinckaers
bugfix |
5029 |
def copy(self, cr, uid, id, default=None, context=None): |
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
5030 |
"""
|
5031 |
Duplicate record with given id updating it with default values
|
|
5032 |
||
5033 |
:param cr: database cursor
|
|
5034 |
:param uid: current user id
|
|
5035 |
:param id: id of the record to copy
|
|
2572
by Olivier Dony
[IMP] orm: docstring improvements: fixed incorrect RST indentation + improved doc a bit + some cleanup |
5036 |
:param default: dictionary of field values to override in the original values of the copied record, e.g: ``{'field_name': overriden_value, ...}``
|
5037 |
:type default: dictionary
|
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
5038 |
:param context: context arguments, like lang, time zone
|
2572
by Olivier Dony
[IMP] orm: docstring improvements: fixed incorrect RST indentation + improved doc a bit + some cleanup |
5039 |
:type context: dictionary
|
3755
by Vo Minh Thu
[FIX] orm.copy(): corrected return value in docstring. |
5040 |
:return: id of the newly created record
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
5041 |
|
5042 |
"""
|
|
3055
by Olivier Dony
[MERGE+FIX] fixes for translations,sequence,copy,error messages - some patches courtesy of Margarita Manterola and Don Kirkby |
5043 |
if context is None: |
5044 |
context = {} |
|
5045 |
context = context.copy() |
|
2402
by Olivier Dony, Jay Vora
[FIX] orm: proper recursive copy of translations through one2many relationships during copy() |
5046 |
data = self.copy_data(cr, uid, id, default, context) |
1803.4.1
by Jay(Open ERP)
[FIX] Context passed on copy() |
5047 |
new_id = self.create(cr, uid, data, context) |
2402
by Olivier Dony, Jay Vora
[FIX] orm: proper recursive copy of translations through one2many relationships during copy() |
5048 |
self.copy_translations(cr, uid, id, new_id, context) |
1170
by Jay Vora
Corrected copy functionality for fields translated=True (Ref: Yajushi Yagnik) |
5049 |
return new_id |
922
by Christophe Simonis
convert tabs to 4 spaces |
5050 |
|
2235
by Quality Team
[FIX] Rewrite the field.property engine to work as expected (value per |
5051 |
def exists(self, cr, uid, ids, context=None): |
3511.1.22
by Olivier Dony
[MERGE] sync with latest trunk |
5052 |
"""Checks whether the given id or ids exist in this model,
|
5053 |
and return the list of ids that do. This is simple to use for
|
|
5054 |
a truth test on a browse_record::
|
|
5055 |
||
5056 |
if record.exists():
|
|
5057 |
pass
|
|
5058 |
||
5059 |
:param ids: id or list of ids to check for existence
|
|
5060 |
:type ids: int or [int]
|
|
5061 |
:return: the list of ids that currently exist, out of
|
|
5062 |
the given `ids`
|
|
5063 |
"""
|
|
2685
by olt at tinyerp
[IMP] orm: added 'schema' and 'data' logger |
5064 |
if type(ids) in (int, long): |
2235
by Quality Team
[FIX] Rewrite the field.property engine to work as expected (value per |
5065 |
ids = [ids] |
4682.2.9
by Xavier Morel
[REM] unnecessary parens |
5066 |
query = 'SELECT id FROM "%s"' % self._table |
2235
by Quality Team
[FIX] Rewrite the field.property engine to work as expected (value per |
5067 |
cr.execute(query + "WHERE ID IN %s", (tuple(ids),)) |
3353.1.39
by Olivier Dony
[IMP] orm,orm_memory: more consistent implementations of exists() and check_access_rule() |
5068 |
return [x[0] for x in cr.fetchall()] |
1786.1.1
by Christophe Simonis
[FIX] browse_record: raise a better exception when the id doesn't exists |
5069 |
|
3084
by Olivier Dony
[IMP] orm,base: added context to _constraints functions + use generic osv.osv.check_recursion() |
5070 |
def check_recursion(self, cr, uid, ids, context=None, parent=None): |
3976.1.18
by Vo Minh Thu
[IMP] warnings: turn warnings.warn into logging.warning: |
5071 |
_logger.warning("You are using deprecated %s.check_recursion(). Please use the '_check_recursion()' instead!" % \ |
5072 |
self._name) |
|
3017.1.13
by Olivier Dony
[IMP] orm: proper test for column names in check_recursion() |
5073 |
assert parent is None or parent in self._columns or parent in self._inherit_fields,\ |
5074 |
"The 'parent' parameter passed to check_recursion() must be None or a valid field name"
|
|
3084
by Olivier Dony
[IMP] orm,base: added context to _constraints functions + use generic osv.osv.check_recursion() |
5075 |
return self._check_recursion(cr, uid, ids, context, parent) |
3017.1.4
by P. Christeas
ORM: deprecate check_recursion() in favour of _check_recursion() |
5076 |
|
3084
by Olivier Dony
[IMP] orm,base: added context to _constraints functions + use generic osv.osv.check_recursion() |
5077 |
def _check_recursion(self, cr, uid, ids, context=None, parent=None): |
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
5078 |
"""
|
2572
by Olivier Dony
[IMP] orm: docstring improvements: fixed incorrect RST indentation + improved doc a bit + some cleanup |
5079 |
Verifies that there is no loop in a hierarchical structure of records,
|
5080 |
by following the parent relationship using the **parent** field until a loop
|
|
5081 |
is detected or until a top-level record is found.
|
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
5082 |
|
5083 |
:param cr: database cursor
|
|
5084 |
:param uid: current user id
|
|
2572
by Olivier Dony
[IMP] orm: docstring improvements: fixed incorrect RST indentation + improved doc a bit + some cleanup |
5085 |
:param ids: list of ids of records to check
|
5086 |
:param parent: optional parent field name (default: ``self._parent_name = parent_id``)
|
|
5087 |
:return: **True** if the operation can proceed safely, or **False** if an infinite loop is detected.
|
|
2111.3.15
by RVO(OpenERP)
Added docstring in public methods of orm |
5088 |
"""
|
5089 |
||
922
by Christophe Simonis
convert tabs to 4 spaces |
5090 |
if not parent: |
5091 |
parent = self._parent_name |
|
5092 |
ids_parent = ids[:] |
|
2334.1.4
by Anup(OpenERP)
[IMP] removed the possible SQL injection server. |
5093 |
query = 'SELECT distinct "%s" FROM "%s" WHERE id IN %%s' % (parent, self._table) |
5094 |
while ids_parent: |
|
922
by Christophe Simonis
convert tabs to 4 spaces |
5095 |
ids_parent2 = [] |
943
by Christophe Simonis
move, rename and refactor use of ID_MAX |
5096 |
for i in range(0, len(ids), cr.IN_MAX): |
5097 |
sub_ids_parent = ids_parent[i:i+cr.IN_MAX] |
|
2334.1.4
by Anup(OpenERP)
[IMP] removed the possible SQL injection server. |
5098 |
cr.execute(query, (tuple(sub_ids_parent),)) |
922
by Christophe Simonis
convert tabs to 4 spaces |
5099 |
ids_parent2.extend(filter(None, map(lambda x: x[0], cr.fetchall()))) |
5100 |
ids_parent = ids_parent2 |
|
5101 |
for i in ids_parent: |
|
5102 |
if i in ids: |
|
5103 |
return False |
|
5104 |
return True |
|
1844.6.2
by HDA(OpenERP)
Merge and bugfixes from jvo |
5105 |
|
3427.2.34
by Olivier Dony
[IMP] orm: rename/cleanup: get_xml_id* -> get_external_id* |
5106 |
def _get_external_ids(self, cr, uid, ids, *args, **kwargs): |
5107 |
"""Retrieve the External ID(s) of any database record.
|
|
3212
by Olivier Dony
[IMP] orm: added get_xml_ids() variant to support retrieving multiple IDs |
5108 |
|
5109 |
**Synopsis**: ``_get_xml_ids(cr, uid, ids) -> { 'id': ['module.xml_id'] }``
|
|
5110 |
||
3427.2.34
by Olivier Dony
[IMP] orm: rename/cleanup: get_xml_id* -> get_external_id* |
5111 |
:return: map of ids to the list of their fully qualified External IDs
|
5112 |
in the form ``module.key``, or an empty list when there's no External
|
|
5113 |
ID for a record, e.g.::
|
|
5114 |
||
5115 |
{ 'id': ['module.ext_id', 'module.ext_id_bis'],
|
|
5116 |
'id2': [] }
|
|
3212
by Olivier Dony
[IMP] orm: added get_xml_ids() variant to support retrieving multiple IDs |
5117 |
"""
|
3427.2.34
by Olivier Dony
[IMP] orm: rename/cleanup: get_xml_id* -> get_external_id* |
5118 |
ir_model_data = self.pool.get('ir.model.data') |
5119 |
data_ids = ir_model_data.search(cr, uid, [('model', '=', self._name), ('res_id', 'in', ids)]) |
|
5120 |
data_results = ir_model_data.read(cr, uid, data_ids, ['module', 'name', 'res_id']) |
|
3212
by Olivier Dony
[IMP] orm: added get_xml_ids() variant to support retrieving multiple IDs |
5121 |
result = {} |
5122 |
for id in ids: |
|
5123 |
# can't use dict.fromkeys() as the list would be shared!
|
|
5124 |
result[id] = [] |
|
5125 |
for record in data_results: |
|
5126 |
result[record['res_id']].append('%(module)s.%(name)s' % record) |
|
5127 |
return result |
|
5128 |
||
3427.2.34
by Olivier Dony
[IMP] orm: rename/cleanup: get_xml_id* -> get_external_id* |
5129 |
def get_external_id(self, cr, uid, ids, *args, **kwargs): |
5130 |
"""Retrieve the External ID of any database record, if there
|
|
2284
by Olivier Dony
[ADD] osv.osv, base: added generic xml_id getter + added xml_id to views on ir.ui.views + minor fine-tuning of admin views |
5131 |
is one. This method works as a possible implementation
|
5132 |
for a function field, to be able to add it to any
|
|
3427.2.34
by Olivier Dony
[IMP] orm: rename/cleanup: get_xml_id* -> get_external_id* |
5133 |
model object easily, referencing it as ``Model.get_external_id``.
|
2284
by Olivier Dony
[ADD] osv.osv, base: added generic xml_id getter + added xml_id to views on ir.ui.views + minor fine-tuning of admin views |
5134 |
|
3427.2.34
by Olivier Dony
[IMP] orm: rename/cleanup: get_xml_id* -> get_external_id* |
5135 |
When multiple External IDs exist for a record, only one
|
3212
by Olivier Dony
[IMP] orm: added get_xml_ids() variant to support retrieving multiple IDs |
5136 |
of them is returned (randomly).
|
5137 |
||
5138 |
:return: map of ids to their fully qualified XML ID,
|
|
2572
by Olivier Dony
[IMP] orm: docstring improvements: fixed incorrect RST indentation + improved doc a bit + some cleanup |
5139 |
defaulting to an empty string when there's none
|
4466.1.2
by Christophe Simonis
[IMP] orm: remove trailing spaces |
5140 |
(to be usable as a function field),
|
3427.2.34
by Olivier Dony
[IMP] orm: rename/cleanup: get_xml_id* -> get_external_id* |
5141 |
e.g.::
|
5142 |
||
5143 |
{ 'id': 'module.ext_id',
|
|
5144 |
'id2': '' }
|
|
2284
by Olivier Dony
[ADD] osv.osv, base: added generic xml_id getter + added xml_id to views on ir.ui.views + minor fine-tuning of admin views |
5145 |
"""
|
3297
by J-E Baudoux
[MERGE] orm,base: added ir.model introspection OOo report, made orm._get_xml_ids() private (return type incompatible with XML-RPC) |
5146 |
results = self._get_xml_ids(cr, uid, ids) |
3427.2.34
by Olivier Dony
[IMP] orm: rename/cleanup: get_xml_id* -> get_external_id* |
5147 |
for k, v in results.iteritems(): |
3212
by Olivier Dony
[IMP] orm: added get_xml_ids() variant to support retrieving multiple IDs |
5148 |
if results[k]: |
5149 |
results[k] = v[0] |
|
5150 |
else: |
|
5151 |
results[k] = '' |
|
5152 |
return results |
|
2284
by Olivier Dony
[ADD] osv.osv, base: added generic xml_id getter + added xml_id to views on ir.ui.views + minor fine-tuning of admin views |
5153 |
|
3427.2.34
by Olivier Dony
[IMP] orm: rename/cleanup: get_xml_id* -> get_external_id* |
5154 |
# backwards compatibility
|
5155 |
get_xml_id = get_external_id |
|
5156 |
_get_xml_ids = _get_external_ids |
|
4333.1.1
by Fabien Pinckaers
[IMP] cleaning of need action |
5157 |
|
4802.3.19
by Vo Minh Thu
[IMP] orm: added a print_report() method. |
5158 |
def print_report(self, cr, uid, ids, name, data, context=None): |
5159 |
"""
|
|
5160 |
Render the report `name` for the given IDs. The report must be defined
|
|
5161 |
for this model, not another.
|
|
5162 |
"""
|
|
5163 |
report = self.pool['ir.actions.report.xml']._lookup_report(cr, name) |
|
5164 |
assert self._name == report.table |
|
5165 |
return report.create(cr, uid, ids, data, context) |
|
5166 |
||
3511.1.31
by Olivier Dony
[IMP] orm: fix Model hierarchy to avoid TransientModel leaking downstream |
5167 |
# Transience
|
3511.1.11
by Vo Minh Thu
[REF] osv: replace isinstance(m,osv_memory) by m.is_transient(). |
5168 |
def is_transient(self): |
5169 |
""" Return whether the model is transient.
|
|
5170 |
||
4408.2.21
by Xavier Morel
[DOC] fixes and cleanups |
5171 |
See :class:`TransientModel`.
|
3511.1.11
by Vo Minh Thu
[REF] osv: replace isinstance(m,osv_memory) by m.is_transient(). |
5172 |
|
5173 |
"""
|
|
5174 |
return self._transient |
|
5175 |
||
3511.1.31
by Olivier Dony
[IMP] orm: fix Model hierarchy to avoid TransientModel leaking downstream |
5176 |
def _transient_clean_rows_older_than(self, cr, seconds): |
5177 |
assert self._transient, "Model %s is not transient, it cannot be vacuumed!" % self._name |
|
4503.2.3
by Raphael Collet
[IMP] orm: fix vacuum of transient model |
5178 |
# Never delete rows used in last 5 minutes
|
4503.2.2
by Ronald Portier
[FIX] Correct both age based as count based vacuum cleaning. |
5179 |
seconds = max(seconds, 300) |
4503.2.3
by Raphael Collet
[IMP] orm: fix vacuum of transient model |
5180 |
query = ("SELECT id FROM " + self._table + " WHERE" |
5181 |
" COALESCE(write_date, create_date, (now() at time zone 'UTC'))::timestamp"
|
|
5182 |
" < ((now() at time zone 'UTC') - interval %s)") |
|
5183 |
cr.execute(query, ("%s seconds" % seconds,)) |
|
3511.1.31
by Olivier Dony
[IMP] orm: fix Model hierarchy to avoid TransientModel leaking downstream |
5184 |
ids = [x[0] for x in cr.fetchall()] |
4503.2.3
by Raphael Collet
[IMP] orm: fix vacuum of transient model |
5185 |
self.unlink(cr, SUPERUSER_ID, ids) |
3511.1.31
by Olivier Dony
[IMP] orm: fix Model hierarchy to avoid TransientModel leaking downstream |
5186 |
|
4503.2.1
by Ronald Portier
[FIX] Fix vacuum cleaning both for time and count based cleaning, |
5187 |
def _transient_clean_old_rows(self, cr, max_count): |
5188 |
# Check how many rows we have in the table
|
|
4503.2.3
by Raphael Collet
[IMP] orm: fix vacuum of transient model |
5189 |
cr.execute("SELECT count(*) AS row_count FROM " + self._table) |
4503.2.1
by Ronald Portier
[FIX] Fix vacuum cleaning both for time and count based cleaning, |
5190 |
res = cr.fetchall() |
4503.2.3
by Raphael Collet
[IMP] orm: fix vacuum of transient model |
5191 |
if res[0][0] <= max_count: |
4503.2.1
by Ronald Portier
[FIX] Fix vacuum cleaning both for time and count based cleaning, |
5192 |
return # max not reached, nothing to do |
4503.2.2
by Ronald Portier
[FIX] Correct both age based as count based vacuum cleaning. |
5193 |
self._transient_clean_rows_older_than(cr, 300) |
3511.1.31
by Olivier Dony
[IMP] orm: fix Model hierarchy to avoid TransientModel leaking downstream |
5194 |
|
5195 |
def _transient_vacuum(self, cr, uid, force=False): |
|
5196 |
"""Clean the transient records.
|
|
5197 |
||
5198 |
This unlinks old records from the transient model tables whenever the
|
|
5199 |
"_transient_max_count" or "_max_age" conditions (if any) are reached.
|
|
5200 |
Actual cleaning will happen only once every "_transient_check_time" calls.
|
|
5201 |
This means this method can be called frequently called (e.g. whenever
|
|
5202 |
a new record is created).
|
|
4503.2.2
by Ronald Portier
[FIX] Correct both age based as count based vacuum cleaning. |
5203 |
Example with both max_hours and max_count active:
|
5204 |
Suppose max_hours = 0.2 (e.g. 12 minutes), max_count = 20, there are 55 rows in the
|
|
5205 |
table, 10 created/changed in the last 5 minutes, an additional 12 created/changed between
|
|
5206 |
5 and 10 minutes ago, the rest created/changed more then 12 minutes ago.
|
|
5207 |
- age based vacuum will leave the 22 rows created/changed in the last 12 minutes
|
|
5208 |
- count based vacuum will wipe out another 12 rows. Not just 2, otherwise each addition
|
|
5209 |
would immediately cause the maximum to be reached again.
|
|
5210 |
- the 10 rows that have been created/changed the last 5 minutes will NOT be deleted
|
|
3511.1.31
by Olivier Dony
[IMP] orm: fix Model hierarchy to avoid TransientModel leaking downstream |
5211 |
"""
|
5212 |
assert self._transient, "Model %s is not transient, it cannot be vacuumed!" % self._name |
|
4503.2.3
by Raphael Collet
[IMP] orm: fix vacuum of transient model |
5213 |
_transient_check_time = 20 # arbitrary limit on vacuum executions |
3511.1.31
by Olivier Dony
[IMP] orm: fix Model hierarchy to avoid TransientModel leaking downstream |
5214 |
self._transient_check_count += 1 |
4503.2.3
by Raphael Collet
[IMP] orm: fix vacuum of transient model |
5215 |
if not force and (self._transient_check_count < _transient_check_time): |
4503.2.1
by Ronald Portier
[FIX] Fix vacuum cleaning both for time and count based cleaning, |
5216 |
return True # no vacuum cleaning this time |
5217 |
self._transient_check_count = 0 |
|
3511.1.31
by Olivier Dony
[IMP] orm: fix Model hierarchy to avoid TransientModel leaking downstream |
5218 |
|
5219 |
# Age-based expiration
|
|
5220 |
if self._transient_max_hours: |
|
5221 |
self._transient_clean_rows_older_than(cr, self._transient_max_hours * 60 * 60) |
|
5222 |
||
5223 |
# Count-based expiration
|
|
5224 |
if self._transient_max_count: |
|
5225 |
self._transient_clean_old_rows(cr, self._transient_max_count) |
|
5226 |
||
5227 |
return True |
|
5228 |
||
4341.4.37
by Raphael Collet
[IMP] orm: fix and generalize method resolve_o2m_commands_to_record_dicts (now named resolve_2many_commands) |
5229 |
def resolve_2many_commands(self, cr, uid, field_name, commands, fields=None, context=None): |
5230 |
""" Serializes one2many and many2many commands into record dictionaries
|
|
5231 |
(as if all the records came from the database via a read()). This
|
|
5232 |
method is aimed at onchange methods on one2many and many2many fields.
|
|
5233 |
||
5234 |
Because commands might be creation commands, not all record dicts
|
|
5235 |
will contain an ``id`` field. Commands matching an existing record
|
|
5236 |
will have an ``id``.
|
|
5237 |
||
5238 |
:param field_name: name of the one2many or many2many field matching the commands
|
|
5239 |
:type field_name: str
|
|
5240 |
:param commands: one2many or many2many commands to execute on ``field_name``
|
|
5241 |
:type commands: list((int|False, int|False, dict|False))
|
|
5242 |
:param fields: list of fields to read from the database, when applicable
|
|
5243 |
:type fields: list(str)
|
|
5244 |
:returns: records in a shape similar to that returned by ``read()``
|
|
5245 |
(except records may be missing the ``id`` field if they don't exist in db)
|
|
5246 |
:rtype: list(dict)
|
|
3747.1.1
by Xavier Morel
[ADD] method to correctly serialize o2m commands into a sequence of record-dicts for e.g. onchanges |
5247 |
"""
|
4341.4.37
by Raphael Collet
[IMP] orm: fix and generalize method resolve_o2m_commands_to_record_dicts (now named resolve_2many_commands) |
5248 |
result = [] # result (list of dict) |
4341.4.43
by Raphael Collet
[FIX] method resolve_2many_commands and its tests |
5249 |
record_ids = [] # ids of records to read |
4341.4.37
by Raphael Collet
[IMP] orm: fix and generalize method resolve_o2m_commands_to_record_dicts (now named resolve_2many_commands) |
5250 |
updates = {} # {id: dict} of updates on particular records |
5251 |
||
5252 |
for command in commands: |
|
5253 |
if not isinstance(command, (list, tuple)): |
|
4341.4.43
by Raphael Collet
[FIX] method resolve_2many_commands and its tests |
5254 |
record_ids.append(command) |
4341.4.37
by Raphael Collet
[IMP] orm: fix and generalize method resolve_o2m_commands_to_record_dicts (now named resolve_2many_commands) |
5255 |
elif command[0] == 0: |
5256 |
result.append(command[2]) |
|
5257 |
elif command[0] == 1: |
|
4341.4.43
by Raphael Collet
[FIX] method resolve_2many_commands and its tests |
5258 |
record_ids.append(command[1]) |
4341.4.37
by Raphael Collet
[IMP] orm: fix and generalize method resolve_o2m_commands_to_record_dicts (now named resolve_2many_commands) |
5259 |
updates.setdefault(command[1], {}).update(command[2]) |
5260 |
elif command[0] in (2, 3): |
|
4341.4.43
by Raphael Collet
[FIX] method resolve_2many_commands and its tests |
5261 |
record_ids = [id for id in record_ids if id != command[1]] |
4341.4.37
by Raphael Collet
[IMP] orm: fix and generalize method resolve_o2m_commands_to_record_dicts (now named resolve_2many_commands) |
5262 |
elif command[0] == 4: |
4341.4.43
by Raphael Collet
[FIX] method resolve_2many_commands and its tests |
5263 |
record_ids.append(command[1]) |
4341.4.37
by Raphael Collet
[IMP] orm: fix and generalize method resolve_o2m_commands_to_record_dicts (now named resolve_2many_commands) |
5264 |
elif command[0] == 5: |
5265 |
result, record_ids = [], [] |
|
5266 |
elif command[0] == 6: |
|
5267 |
result, record_ids = [], list(command[2]) |
|
5268 |
||
5269 |
# read the records and apply the updates
|
|
5270 |
other_model = self.pool.get(self._all_columns[field_name].column._obj) |
|
4341.4.43
by Raphael Collet
[FIX] method resolve_2many_commands and its tests |
5271 |
for record in other_model.read(cr, uid, record_ids, fields=fields, context=context): |
4341.4.37
by Raphael Collet
[IMP] orm: fix and generalize method resolve_o2m_commands_to_record_dicts (now named resolve_2many_commands) |
5272 |
record.update(updates.get(record['id'], {})) |
5273 |
result.append(record) |
|
5274 |
||
5275 |
return result |
|
5276 |
||
5277 |
# for backward compatibility
|
|
4341.4.47
by Raphael Collet
[IMP] orm: simplify method definition |
5278 |
resolve_o2m_commands_to_record_dicts = resolve_2many_commands |
3747.1.1
by Xavier Morel
[ADD] method to correctly serialize o2m commands into a sequence of record-dicts for e.g. onchanges |
5279 |
|
4581.3.1
by Arnaud Pineux
[FIX] Automated action rules |
5280 |
def _register_hook(self, cr): |
4632.1.3
by Raphael Collet
[IMP] register_hook: improve a bit the code |
5281 |
""" stuff to do right after the registry is built """
|
4581.3.1
by Arnaud Pineux
[FIX] Automated action rules |
5282 |
pass
|
5283 |
||
4769.2.2
by Vo Minh Thu
[IMP] orm: expose a signal_xxx() method to call workflow.trg_validate(). |
5284 |
def __getattr__(self, name): |
5285 |
if name.startswith('signal_'): |
|
5286 |
signal_name = name[len('signal_'):] |
|
5287 |
assert signal_name |
|
4769.2.7
by Raphael Collet
[IMP] remove model methods _workflow_trigger and _workflow_signal, and replace calls to new workflow methods |
5288 |
return (lambda *args, **kwargs: |
5289 |
self.signal_workflow(*args, signal=signal_name, **kwargs)) |
|
4815.1.11
by Xavier Morel
[FIX] __getattr__ implementation on BaseModel |
5290 |
get = getattr(super(BaseModel, self), '__getattr__', None) |
5291 |
if get is not None: return get(name) |
|
5292 |
raise AttributeError( |
|
5293 |
"'%s' object has no attribute '%s'" % (type(self).__name__, name)) |
|
4769.2.2
by Vo Minh Thu
[IMP] orm: expose a signal_xxx() method to call workflow.trg_validate(). |
5294 |
|
3511.1.32
by Olivier Dony
[MERGE] sync w/ latest trunk (+ fix import cycles) |
5295 |
# keep this import here, at top it will cause dependency cycle errors
|
5296 |
import expression |
|
5297 |
||
3511.1.25
by Olivier Dony
[IMP] orm: introduce cleaner class hierarchy for models |
5298 |
class Model(BaseModel): |
5299 |
"""Main super-class for regular database-persisted OpenERP models.
|
|
5300 |
||
5301 |
OpenERP models are created by inheriting from this class::
|
|
5302 |
||
5303 |
class user(Model):
|
|
5304 |
...
|
|
5305 |
||
5306 |
The system will later instantiate the class once per database (on
|
|
5307 |
which the class' module is installed).
|
|
5308 |
"""
|
|
4066.18.1
by Olivier Dony
[FIX] orm: make sure all *Model classes define _auto,_register,_transient |
5309 |
_auto = True |
3511.1.25
by Olivier Dony
[IMP] orm: introduce cleaner class hierarchy for models |
5310 |
_register = False # not visible in ORM registry, meant to be python-inherited only |
3511.1.30
by Olivier Dony
[IMP] review/cleanup + fix TransientModels inheritance |
5311 |
_transient = False # True in a TransientModel |
3511.1.25
by Olivier Dony
[IMP] orm: introduce cleaner class hierarchy for models |
5312 |
|
5313 |
class TransientModel(BaseModel): |
|
5314 |
"""Model super-class for transient records, meant to be temporarily
|
|
5315 |
persisted, and regularly vaccuum-cleaned.
|
|
3747.1.8
by Olivier Dony
[IMP] orm: resolve_o2m_commands_...: simplify assert, remove context doc following guidelines |
5316 |
|
3511.1.25
by Olivier Dony
[IMP] orm: introduce cleaner class hierarchy for models |
5317 |
A TransientModel has a simplified access rights management,
|
5318 |
all users can create new records, and may only access the
|
|
5319 |
records they created. The super-user has unrestricted access
|
|
5320 |
to all TransientModel records.
|
|
5321 |
"""
|
|
4066.18.1
by Olivier Dony
[FIX] orm: make sure all *Model classes define _auto,_register,_transient |
5322 |
_auto = True |
3511.1.25
by Olivier Dony
[IMP] orm: introduce cleaner class hierarchy for models |
5323 |
_register = False # not visible in ORM registry, meant to be python-inherited only |
5324 |
_transient = True |
|
5325 |
||
5326 |
class AbstractModel(BaseModel): |
|
5327 |
"""Abstract Model super-class for creating an abstract class meant to be
|
|
5328 |
inherited by regular models (Models or TransientModels) but not meant to
|
|
5329 |
be usable on its own, or persisted.
|
|
5330 |
||
5331 |
Technical note: we don't want to make AbstractModel the super-class of
|
|
5332 |
Model or BaseModel because it would not make sense to put the main
|
|
5333 |
definition of persistence methods such as create() in it, and still we
|
|
5334 |
should be able to override them within an AbstractModel.
|
|
5335 |
"""
|
|
5336 |
_auto = False # don't create any database backend for AbstractModels |
|
5337 |
_register = False # not visible in ORM registry, meant to be python-inherited only |
|
4066.18.1
by Olivier Dony
[FIX] orm: make sure all *Model classes define _auto,_register,_transient |
5338 |
_transient = False |
3511.1.25
by Olivier Dony
[IMP] orm: introduce cleaner class hierarchy for models |
5339 |
|
4408.2.1
by Xavier Morel
[ADD] big bit on new import: pretty much everything but o2m |
5340 |
def itemgetter_tuple(items): |
5341 |
""" Fixes itemgetter inconsistency (useful in some cases) of not returning
|
|
5342 |
a tuple if len(items) == 1: always returns an n-tuple where n = len(items)
|
|
5343 |
"""
|
|
5344 |
if len(items) == 0: |
|
5345 |
return lambda a: () |
|
5346 |
if len(items) == 1: |
|
5347 |
return lambda gettable: (gettable[items[0]],) |
|
5348 |
return operator.itemgetter(*items) |
|
5349 |
class ImportWarning(Warning): |
|
5350 |
""" Used to send warnings upwards the stack during the import process
|
|
5351 |
"""
|
|
5352 |
pass
|
|
4408.2.13
by Xavier Morel
[ADD] ability to convert postgres error messages to human-readable ones |
5353 |
|
5354 |
||
5355 |
def convert_pgerror_23502(model, fields, info, e): |
|
5356 |
m = re.match(r'^null value in column "(?P<field>\w+)" violates ' |
|
4408.2.17
by Xavier Morel
[FIX] postgres 9.2 added data at the end of 23502's error message, only use prefix matches not fullstring |
5357 |
r'not-null constraint\n', |
4408.2.13
by Xavier Morel
[ADD] ability to convert postgres error messages to human-readable ones |
5358 |
str(e)) |
4553
by Xavier Morel
[FIX] temperate error message in case of missing required field |
5359 |
field_name = m.group('field') |
5360 |
if not m or field_name not in fields: |
|
4408.2.13
by Xavier Morel
[ADD] ability to convert postgres error messages to human-readable ones |
5361 |
return {'message': unicode(e)} |
4553
by Xavier Morel
[FIX] temperate error message in case of missing required field |
5362 |
message = _(u"Missing required value for the field '%s'.") % field_name |
5363 |
field = fields.get(field_name) |
|
5364 |
if field: |
|
4805
by Fabien Pinckaers
[iMP] error message simplified for required values |
5365 |
message = _(u"Missing required value for the field '%s' (%s)") % (field['string'], field_name) |
4408.2.13
by Xavier Morel
[ADD] ability to convert postgres error messages to human-readable ones |
5366 |
return { |
4553
by Xavier Morel
[FIX] temperate error message in case of missing required field |
5367 |
'message': message, |
5368 |
'field': field_name, |
|
4408.2.13
by Xavier Morel
[ADD] ability to convert postgres error messages to human-readable ones |
5369 |
}
|
4743.1.89
by Xavier Morel
[IMP] don't log from import when converting psycopg exceptions to output messages |
5370 |
def convert_pgerror_23505(model, fields, info, e): |
5371 |
m = re.match(r'^duplicate key (?P<field>\w+) violates unique constraint', |
|
5372 |
str(e)) |
|
5373 |
field_name = m.group('field') |
|
5374 |
if not m or field_name not in fields: |
|
5375 |
return {'message': unicode(e)} |
|
5376 |
message = _(u"The value for the field '%s' already exists.") % field_name |
|
5377 |
field = fields.get(field_name) |
|
5378 |
if field: |
|
5379 |
message = _(u"%s This might be '%s' in the current model, or a field " |
|
5380 |
u"of the same name in an o2m.") % (message, field['string']) |
|
5381 |
return { |
|
5382 |
'message': message, |
|
5383 |
'field': field_name, |
|
5384 |
}
|
|
4408.2.13
by Xavier Morel
[ADD] ability to convert postgres error messages to human-readable ones |
5385 |
|
5386 |
PGERROR_TO_OE = collections.defaultdict( |
|
5387 |
# shape of mapped converters
|
|
5388 |
lambda: (lambda model, fvg, info, pgerror: {'message': unicode(pgerror)}), { |
|
5389 |
# not_null_violation
|
|
5390 |
'23502': convert_pgerror_23502, |
|
4743.1.89
by Xavier Morel
[IMP] don't log from import when converting psycopg exceptions to output messages |
5391 |
# unique constraint error
|
5392 |
'23505': convert_pgerror_23505, |
|
4408.2.13
by Xavier Morel
[ADD] ability to convert postgres error messages to human-readable ones |
5393 |
})
|
3668
by Vo Minh Thu
[IMP] orm: expose ir.model.access check shortcuts. |
5394 |
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|