877
903
if not self._table:
878
904
self._table = self._name.replace('.', '_')
880
def browse(self, cr, uid, select, context=None, list_class=None, fields_process=None):
881
"""Fetch records as objects allowing to use dot notation to browse fields and relations
883
:param cr: database cursor
884
:param user: current user id
885
:param select: id or list of ids.
886
:param context: context arguments, like lang, time zone
887
:rtype: object or list of objects requested
890
self._list_class = list_class or browse_record_list
892
# need to accepts ints and longs because ids coming from a method
893
# launched by button in the interface have a type long...
894
if isinstance(select, (int, long)):
895
return browse_record(cr, uid, select, self, cache, context=context, list_class=self._list_class, fields_process=fields_process)
896
elif isinstance(select, list):
897
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)
906
if not hasattr(self, '_log_access'):
907
# If _log_access is not specified, it is the same value as _auto.
908
self._log_access = getattr(self, "_auto", True)
910
self._columns = self._columns.copy()
911
for store_field in self._columns:
912
f = self._columns[store_field]
913
if hasattr(f, 'digits_change'):
915
if not isinstance(f, fields.function):
919
if self._columns[store_field].store is True:
920
sm = {self._name: (lambda self, cr, uid, ids, c={}: ids, None, 10, None)}
922
sm = self._columns[store_field].store
923
for object, aa in sm.items():
925
(fnct, fields2, order, length) = aa
927
(fnct, fields2, order) = aa
930
raise except_orm('Error',
931
('Invalid function definition %s in object %s !\nYou must use the definition: store={object:(fnct, fields, priority, time length)}.' % (store_field, self._name)))
932
self.pool._store_function.setdefault(object, [])
934
for x, y, z, e, f, l in self.pool._store_function[object]:
935
if (x==self._name) and (y==store_field) and (e==fields2):
939
self.pool._store_function[object].append( (self._name, store_field, fnct, tuple(fields2) if fields2 else None, order, length))
940
self.pool._store_function[object].sort(lambda x, y: cmp(x[4], y[4]))
942
for (key, _, msg) in self._sql_constraints:
943
self.pool._sql_error[self._table+'_'+key] = msg
947
cr.execute("SELECT id FROM ir_model_fields WHERE name=%s AND model=%s", ('state', 'ir.model.fields'))
949
cr.execute('SELECT * FROM ir_model_fields WHERE model=%s AND state=%s', (self._name, 'manual'))
950
for field in cr.dictfetchall():
951
if field['name'] in self._columns:
954
'string': field['field_description'],
955
'required': bool(field['required']),
956
'readonly': bool(field['readonly']),
957
'domain': eval(field['domain']) if field['domain'] else None,
958
'size': field['size'],
959
'ondelete': field['on_delete'],
960
'translate': (field['translate']),
962
#'select': int(field['select_level'])
965
if field['ttype'] == 'selection':
966
self._columns[field['name']] = fields.selection(eval(field['selection']), **attrs)
967
elif field['ttype'] == 'reference':
968
self._columns[field['name']] = fields.reference(selection=eval(field['selection']), **attrs)
969
elif field['ttype'] == 'many2one':
970
self._columns[field['name']] = fields.many2one(field['relation'], **attrs)
971
elif field['ttype'] == 'one2many':
972
self._columns[field['name']] = fields.one2many(field['relation'], field['relation_field'], **attrs)
973
elif field['ttype'] == 'many2many':
974
_rel1 = field['relation'].replace('.', '_')
975
_rel2 = field['model'].replace('.', '_')
976
_rel_name = 'x_%s_%s_%s_rel' % (_rel1, _rel2, field['name'])
977
self._columns[field['name']] = fields.many2many(field['relation'], _rel_name, 'id1', 'id2', **attrs)
979
self._columns[field['name']] = getattr(fields, field['ttype'])(**attrs)
980
self._inherits_check()
981
self._inherits_reload()
982
if not self._sequence:
983
self._sequence = self._table + '_id_seq'
984
for k in self._defaults:
985
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,)
986
for f in self._columns:
987
self._columns[f].restart()
990
if self.is_transient():
991
self._transient_check_count = 0
992
self._transient_max_count = config.get('osv_memory_count_limit')
993
self._transient_max_hours = config.get('osv_memory_age_limit')
994
assert self._log_access, "TransientModels must have log_access turned on, "\
995
"in order to implement their access rights policy"
901
997
def __export_row(self, cr, uid, row, fields, context=None):
902
998
if context is None:
2300
2288
except AttributeError:
2303
def check_access_rule(self, cr, uid, ids, operation, context=None):
2304
"""Verifies that the operation given by ``operation`` is allowed for the user
2305
according to ir.rules.
2307
:param operation: one of ``write``, ``unlink``
2308
:raise except_orm: * if current ir.rules do not permit this operation.
2309
:return: None if the operation is allowed
2311
raise NotImplementedError(_('The check_access_rule method is not implemented on this object !'))
2313
class orm_memory(orm_template):
2315
_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']
2316
_inherit_fields = {}
2322
def createInstance(cls, pool, cr):
2323
return cls.makeInstance(pool, cr, ['_columns', '_defaults'])
2325
def __init__(self, pool, cr):
2326
super(orm_memory, self).__init__(pool, cr)
2330
self._max_count = config.get('osv_memory_count_limit')
2331
self._max_hours = config.get('osv_memory_age_limit')
2332
cr.execute('delete from wkf_instance where res_type=%s', (self._name,))
2334
def _check_access(self, uid, object_id, mode):
2335
if uid != 1 and self.datas[object_id]['internal.create_uid'] != uid:
2336
raise except_orm(_('AccessError'), '%s access is only allowed on your own records for osv_memory objects except for the super-user' % mode.capitalize())
2338
def vaccum(self, cr, uid, force=False):
2339
"""Run the vaccuum cleaning system, expiring and removing old records from the
2340
virtual osv_memory tables if the "max count" or "max age" conditions are enabled
2341
and have been reached. This method can be called very often (e.g. everytime a record
2342
is created), but will only actually trigger the cleanup process once out of
2343
"_check_time" times (by default once out of 20 calls)."""
2345
if (not force) and (self.check_id % self._check_time):
2349
# Age-based expiration
2351
max = time.time() - self._max_hours * 60 * 60
2352
for k,v in self.datas.iteritems():
2353
if v['internal.date_access'] < max:
2355
self.unlink(cr, ROOT_USER_ID, tounlink)
2357
# Count-based expiration
2358
if self._max_count and len(self.datas) > self._max_count:
2359
# sort by access time to remove only the first/oldest ones in LRU fashion
2360
records = self.datas.items()
2361
records.sort(key=lambda x:x[1]['internal.date_access'])
2362
self.unlink(cr, ROOT_USER_ID, [x[0] for x in records[:len(self.datas)-self._max_count]])
2366
def read(self, cr, user, ids, fields_to_read=None, context=None, load='_classic_read'):
2369
if not fields_to_read:
2370
fields_to_read = self._columns.keys()
2374
if isinstance(ids, (int, long)):
2378
for f in fields_to_read:
2379
record = self.datas.get(id)
2381
self._check_access(user, id, 'read')
2382
r[f] = record.get(f, False)
2383
if r[f] and isinstance(self._columns[f], fields.binary) and context.get('bin_size', False):
2386
if id in self.datas:
2387
self.datas[id]['internal.date_access'] = time.time()
2388
fields_post = filter(lambda x: x in self._columns and not getattr(self._columns[x], load), fields_to_read)
2389
for f in fields_post:
2390
res2 = self._columns[f].get_memory(cr, self, ids, f, user, context=context, values=result)
2391
for record in result:
2392
record[f] = res2[record['id']]
2393
if isinstance(ids_orig, (int, long)):
2397
def write(self, cr, user, ids, vals, context=None):
2403
if self._columns[field]._classic_write:
2404
vals2[field] = vals[field]
2406
upd_todo.append(field)
2407
for object_id in ids:
2408
self._check_access(user, object_id, mode='write')
2409
self.datas[object_id].update(vals2)
2410
self.datas[object_id]['internal.date_access'] = time.time()
2411
for field in upd_todo:
2412
self._columns[field].set_memory(cr, self, object_id, field, vals[field], user, context)
2413
self._validate(cr, user, [object_id], context)
2414
wf_service = netsvc.LocalService("workflow")
2415
wf_service.trg_write(user, self._name, object_id, cr)
2418
def create(self, cr, user, vals, context=None):
2419
self.vaccum(cr, user)
2421
id_new = self.next_id
2423
vals = self._add_missing_default_values(cr, user, vals, context)
2428
if self._columns[field]._classic_write:
2429
vals2[field] = vals[field]
2431
upd_todo.append(field)
2432
self.datas[id_new] = vals2
2433
self.datas[id_new]['internal.date_access'] = time.time()
2434
self.datas[id_new]['internal.create_uid'] = user
2436
for field in upd_todo:
2437
self._columns[field].set_memory(cr, self, id_new, field, vals[field], user, context)
2438
self._validate(cr, user, [id_new], context)
2439
if self._log_create and not (context and context.get('no_store_function', False)):
2440
message = self._description + \
2442
self.name_get(cr, user, [id_new], context=context)[0][1] + \
2444
self.log(cr, user, id_new, message, True, context=context)
2445
wf_service = netsvc.LocalService("workflow")
2446
wf_service.trg_create(user, self._name, id_new, cr)
2449
def _where_calc(self, cr, user, args, active_test=True, context=None):
2454
# if the object has a field named 'active', filter out all inactive
2455
# records unless they were explicitely asked for
2456
if 'active' in self._columns and (active_test and context.get('active_test', True)):
2458
active_in_args = False
2460
if a[0] == 'active':
2461
active_in_args = True
2462
if not active_in_args:
2463
args.insert(0, ('active', '=', 1))
2465
args = [('active', '=', 1)]
2468
e = expression.expression(cr, user, args, self, context)
2472
def _search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False, access_rights_uid=None):
2476
# implicit filter on current user except for superuser
2480
args.insert(0, ('internal.create_uid', '=', user))
2482
result = self._where_calc(cr, user, args, context=context)
2484
return self.datas.keys()
2488
#Find the value of dict
2491
for id, data in self.datas.items():
2492
counter = counter + 1
2494
if limit and (counter > int(limit)):
2499
# Amazing hack: orm_memory handles only simple domains.
2502
val = eval('data[arg[0]]'+'==' +' arg[2]', locals())
2503
elif arg[1] in ['<', '>', 'in', 'not in', '<=', '>=', '<>']:
2504
val = eval('data[arg[0]]'+arg[1] +' arg[2]', locals())
2505
elif arg[1] in ['ilike']:
2506
val = (str(data[arg[0]]).find(str(arg[2]))!=-1)
2516
def unlink(self, cr, uid, ids, context=None):
2518
self._check_access(uid, id, 'unlink')
2519
self.datas.pop(id, None)
2521
cr.execute('delete from wkf_instance where res_type=%s and res_id IN %s', (self._name, tuple(ids)))
2524
def perm_read(self, cr, user, ids, context=None, details=True):
2526
credentials = self.pool.get('res.users').name_get(cr, user, [user])[0]
2527
create_date = time.strftime('%Y-%m-%d %H:%M:%S')
2529
self._check_access(user, id, 'read')
2531
'create_uid': credentials,
2532
'create_date': create_date,
2534
'write_date': False,
2540
def _check_removed_columns(self, cr, log=False):
2541
# nothing to check in memory...
2544
def exists(self, cr, uid, ids, context=None):
2545
if isinstance(ids, (long,int)):
2547
return [id for id in ids if id in self.datas]
2549
def check_access_rule(self, cr, uid, ids, operation, context=None):
2550
# ir.rules do not currently apply for orm.memory instances,
2551
# only the implicit visibility=owner one.
2553
self._check_access(uid, id, operation)
2555
# Definition of log access columns, automatically added to models if
2556
# self._log_access is True
2557
LOG_ACCESS_COLUMNS = {
2558
'create_uid': 'INTEGER REFERENCES res_users ON DELETE SET NULL',
2559
'create_date': 'TIMESTAMP',
2560
'write_uid': 'INTEGER REFERENCES res_users ON DELETE SET NULL',
2561
'write_date': 'TIMESTAMP'
2563
# special columns automatically created by the ORM
2564
MAGIC_COLUMNS = ['id'] + LOG_ACCESS_COLUMNS.keys() + \
2565
['internal.create_uid', 'internal.date_access'] # for osv_memory only
2567
class orm(orm_template):
2568
_sql_constraints = []
2570
_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']
2571
__logger = logging.getLogger('orm')
2572
__schema = logging.getLogger('orm.schema')
2574
2291
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False):
2576
2293
Get the list of records in list view grouped by the given ``groupby`` fields
3203
2939
def _m2m_raise_or_create_relation(self, cr, f):
3204
cr.execute("SELECT relname FROM pg_class WHERE relkind IN ('r','v') AND relname=%s", (f._rel,))
2940
m2m_tbl, col1, col2 = f._sql_names(self)
2941
cr.execute("SELECT relname FROM pg_class WHERE relkind IN ('r','v') AND relname=%s", (m2m_tbl,))
3205
2942
if not cr.dictfetchall():
3206
2943
if not self.pool.get(f._obj):
3207
raise except_orm('Programming Error', ('There is no reference available for %s') % (f._obj,))
3208
ref = self.pool.get(f._obj)._table
3209
cr.execute('CREATE TABLE "%s" ("%s" INTEGER NOT NULL, "%s" INTEGER NOT NULL, UNIQUE("%s","%s")) WITH OIDS' % (f._rel, f._id1, f._id2, f._id1, f._id2))
3210
self._foreign_keys.append((f._rel, f._id1, self._table, 'CASCADE'))
3211
self._foreign_keys.append((f._rel, f._id2, ref, 'CASCADE'))
3212
cr.execute('CREATE INDEX "%s_%s_index" ON "%s" ("%s")' % (f._rel, f._id1, f._rel, f._id1))
3213
cr.execute('CREATE INDEX "%s_%s_index" ON "%s" ("%s")' % (f._rel, f._id2, f._rel, f._id2))
3214
cr.execute("COMMENT ON TABLE \"%s\" IS 'RELATION BETWEEN %s AND %s'" % (f._rel, self._table, ref))
2944
raise except_orm('Programming Error', ('Many2Many destination model does not exist: `%s`') % (f._obj,))
2945
dest_model = self.pool.get(f._obj)
2946
ref = dest_model._table
2947
cr.execute('CREATE TABLE "%s" ("%s" INTEGER NOT NULL, "%s" INTEGER NOT NULL, UNIQUE("%s","%s")) WITH OIDS' % (m2m_tbl, col1, col2, col1, col2))
2949
# create foreign key references with ondelete=cascade, unless the targets are SQL views
2950
cr.execute("SELECT relkind FROM pg_class WHERE relkind IN ('v') AND relname=%s", (ref,))
2951
if not cr.fetchall():
2952
self._m2o_add_foreign_key_unchecked(m2m_tbl, col2, dest_model, 'cascade')
2953
cr.execute("SELECT relkind FROM pg_class WHERE relkind IN ('v') AND relname=%s", (self._table,))
2954
if not cr.fetchall():
2955
self._m2o_add_foreign_key_unchecked(m2m_tbl, col1, self, 'cascade')
2957
cr.execute('CREATE INDEX "%s_%s_index" ON "%s" ("%s")' % (m2m_tbl, col1, m2m_tbl, col1))
2958
cr.execute('CREATE INDEX "%s_%s_index" ON "%s" ("%s")' % (m2m_tbl, col2, m2m_tbl, col2))
2959
cr.execute("COMMENT ON TABLE \"%s\" IS 'RELATION BETWEEN %s AND %s'" % (m2m_tbl, self._table, ref))
3216
self.__schema.debug("Create table '%s': relation between '%s' and '%s'",
3217
f._rel, self._table, ref)
2961
self.__schema.debug("Create table '%s': m2m relation between '%s' and '%s'", m2m_tbl, self._table, ref)
3220
2964
def _add_sql_constraints(self, cr):
3282
3026
cr.execute(line2)
3287
def createInstance(cls, pool, cr):
3288
return cls.makeInstance(pool, cr, ['_columns', '_defaults',
3289
'_inherits', '_constraints', '_sql_constraints'])
3291
def __init__(self, pool, cr):
3294
- copy the stored fields' functions in the osv_pool,
3295
- update the _columns with the fields found in ir_model_fields,
3296
- ensure there is a many2one for each _inherits'd parent,
3297
- update the children's _columns,
3298
- give a chance to each field to initialize itself.
3301
super(orm, self).__init__(pool, cr)
3303
if not hasattr(self, '_log_access'):
3304
# if not access is not specify, it is the same value as _auto
3305
self._log_access = getattr(self, "_auto", True)
3307
self._columns = self._columns.copy()
3308
for store_field in self._columns:
3309
f = self._columns[store_field]
3310
if hasattr(f, 'digits_change'):
3312
if not isinstance(f, fields.function):
3316
if self._columns[store_field].store is True:
3317
sm = {self._name: (lambda self, cr, uid, ids, c={}: ids, None, 10, None)}
3319
sm = self._columns[store_field].store
3320
for object, aa in sm.items():
3322
(fnct, fields2, order, length) = aa
3324
(fnct, fields2, order) = aa
3327
raise except_orm('Error',
3328
('Invalid function definition %s in object %s !\nYou must use the definition: store={object:(fnct, fields, priority, time length)}.' % (store_field, self._name)))
3329
self.pool._store_function.setdefault(object, [])
3331
for x, y, z, e, f, l in self.pool._store_function[object]:
3332
if (x==self._name) and (y==store_field) and (e==fields2):
3336
self.pool._store_function[object].append((self._name, store_field, fnct, tuple(fields2) if fields2 else None, order, length))
3337
self.pool._store_function[object].sort(lambda x, y: cmp(x[4], y[4]))
3339
for (key, _, msg) in self._sql_constraints:
3340
self.pool._sql_error[self._table+'_'+key] = msg
3342
# Load manual fields
3344
cr.execute("SELECT id FROM ir_model_fields WHERE name=%s AND model=%s", ('state', 'ir.model.fields'))
3346
cr.execute('SELECT * FROM ir_model_fields WHERE model=%s AND state=%s', (self._name, 'manual'))
3347
for field in cr.dictfetchall():
3348
if field['name'] in self._columns:
3351
'string': field['field_description'],
3352
'required': bool(field['required']),
3353
'readonly': bool(field['readonly']),
3354
'domain': eval(field['domain']) if field['domain'] else None,
3355
'size': field['size'],
3356
'ondelete': field['on_delete'],
3357
'translate': (field['translate']),
3359
#'select': int(field['select_level'])
3362
if field['ttype'] == 'selection':
3363
self._columns[field['name']] = fields.selection(eval(field['selection']), **attrs)
3364
elif field['ttype'] == 'reference':
3365
self._columns[field['name']] = fields.reference(selection=eval(field['selection']), **attrs)
3366
elif field['ttype'] == 'many2one':
3367
self._columns[field['name']] = fields.many2one(field['relation'], **attrs)
3368
elif field['ttype'] == 'one2many':
3369
self._columns[field['name']] = fields.one2many(field['relation'], field['relation_field'], **attrs)
3370
elif field['ttype'] == 'many2many':
3371
_rel1 = field['relation'].replace('.', '_')
3372
_rel2 = field['model'].replace('.', '_')
3373
_rel_name = 'x_%s_%s_%s_rel' % (_rel1, _rel2, field['name'])
3374
self._columns[field['name']] = fields.many2many(field['relation'], _rel_name, 'id1', 'id2', **attrs)
3376
self._columns[field['name']] = getattr(fields, field['ttype'])(**attrs)
3377
self._inherits_check()
3378
self._inherits_reload()
3379
if not self._sequence:
3380
self._sequence = self._table + '_id_seq'
3381
for k in self._defaults:
3382
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,)
3383
for f in self._columns:
3384
self._columns[f].restart()
3386
__init__.__doc__ = orm_template.__init__.__doc__ + __init__.__doc__
3389
3030
# Update objects that uses this one to update their _inherits fields
3475
3119
:raise AccessError: * if user has no create/write rights on the requested object
3478
3125
ira = self.pool.get('ir.model.access')
3479
3126
write_access = ira.check(cr, user, self._name, 'write', False) or \
3480
3127
ira.check(cr, user, self._name, 'create', False)
3481
return super(orm, self).fields_get(cr, user, fields, context, write_access)
3131
translation_obj = self.pool.get('ir.translation')
3132
for parent in self._inherits:
3133
res.update(self.pool.get(parent).fields_get(cr, user, allfields, context))
3135
for f, field in self._columns.iteritems():
3136
if allfields and f not in allfields:
3139
res[f] = fields.field_to_dict(self, cr, user, context, field)
3141
if not write_access:
3142
res[f]['readonly'] = True
3143
res[f]['states'] = {}
3145
if 'string' in res[f]:
3146
res_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'field', context.get('lang', False) or 'en_US')
3148
res[f]['string'] = res_trans
3149
if 'help' in res[f]:
3150
help_trans = translation_obj._get_source(cr, user, self._name + ',' + f, 'help', context.get('lang', False) or 'en_US')
3152
res[f]['help'] = help_trans
3153
if 'selection' in res[f]:
3154
if isinstance(field.selection, (tuple, list)):
3155
sel = field.selection
3157
for key, val in sel:
3160
val2 = translation_obj._get_source(cr, user, self._name + ',' + f, 'selection', context.get('lang', False) or 'en_US', val)
3161
sel2.append((key, val2 or val))
3162
res[f]['selection'] = sel2
3483
3166
def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'):
3167
""" Read records with given ids with the given fields
3169
:param cr: database cursor
3170
:param user: current user id
3171
:param ids: id or list of the ids of the records to read
3172
:param fields: optional list of field names to return (default: all fields would be returned)
3173
:type fields: list (example ['field_name_1', ...])
3174
:param context: optional context dictionary - it may contains keys for specifying certain options
3175
like ``context_lang``, ``context_tz`` to alter the results of the call.
3176
A special ``bin_size`` boolean flag may also be passed in the context to request the
3177
value of all fields.binary columns to be returned as the size of the binary instead of its
3178
contents. This can also be selectively overriden by passing a field-specific flag
3179
in the form ``bin_size_XXX: True/False`` where ``XXX`` is the name of the field.
3180
Note: The ``bin_size_XXX`` form is new in OpenERP v6.0.
3181
:return: list of dictionaries((dictionary per record asked)) with requested field values
3182
:rtype: [{‘name_of_the_field’: value, ...}, ...]
3183
:raise AccessError: * if user has no read rights on the requested object
3184
* if user tries to bypass access rules for read on the requested object
3484
3188
if not context:
3486
3190
self.pool.get('ir.model.access').check(cr, user, self._name, 'read')
3734
3439
:raise except_orm: * if current ir.rules do not permit this operation.
3735
3440
:return: None if the operation is allowed
3737
where_clause, where_params, tables = self.pool.get('ir.rule').domain_get(cr, uid, self._name, operation, context=context)
3739
where_clause = ' and ' + ' and '.join(where_clause)
3740
for sub_ids in cr.split_for_in_conditions(ids):
3741
cr.execute('SELECT ' + self._table + '.id FROM ' + ','.join(tables) +
3742
' WHERE ' + self._table + '.id IN %s' + where_clause,
3743
[sub_ids] + where_params)
3744
if cr.rowcount != len(sub_ids):
3745
raise except_orm(_('AccessError'),
3746
_('Operation prohibited by access rules, or performed on an already deleted document (Operation: %s, Document type: %s).')
3747
% (operation, self._description))
3442
if uid == SUPERUSER_ID:
3445
if self.is_transient:
3446
# Only one single implicit access rule for transient models: owner only!
3447
# This is ok to hardcode because we assert that TransientModels always
3448
# have log_access enabled and this the create_uid column is always there.
3449
# And even with _inherits, these fields are always present in the local
3450
# table too, so no need for JOINs.
3451
cr.execute("""SELECT distinct create_uid
3453
WHERE id IN %%s""" % self._table, (tuple(ids),))
3454
uids = [x[0] for x in cr.fetchall()]
3455
if len(uids) != 1 or uids[0] != uid:
3456
raise orm.except_orm(_('AccessError'), '%s access is '
3457
'restricted to your own records for transient models '
3458
'(except for the super-user).' % operation.capitalize())
3460
where_clause, where_params, tables = self.pool.get('ir.rule').domain_get(cr, uid, self._name, operation, context=context)
3462
where_clause = ' and ' + ' and '.join(where_clause)
3463
for sub_ids in cr.split_for_in_conditions(ids):
3464
cr.execute('SELECT ' + self._table + '.id FROM ' + ','.join(tables) +
3465
' WHERE ' + self._table + '.id IN %s' + where_clause,
3466
[sub_ids] + where_params)
3467
if cr.rowcount != len(sub_ids):
3468
raise except_orm(_('AccessError'),
3469
_('Operation prohibited by access rules, or performed on an already deleted document (Operation: %s, Document type: %s).')
3470
% (operation, self._description))
3749
3472
def unlink(self, cr, uid, ids, context=None):
4853
4616
results[k] = ''
4856
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
4620
def is_transient(self):
4621
""" Return whether the model is transient.
4626
return self._transient
4628
def _transient_clean_rows_older_than(self, cr, seconds):
4629
assert self._transient, "Model %s is not transient, it cannot be vacuumed!" % self._name
4630
cr.execute("SELECT id FROM " + self._table + " WHERE"
4631
" COALESCE(write_date, create_date, now())::timestamp <"
4632
" (now() - interval %s)", ("%s seconds" % seconds,))
4633
ids = [x[0] for x in cr.fetchall()]
4634
self.unlink(cr, SUPERUSER_ID, ids)
4636
def _transient_clean_old_rows(self, cr, count):
4637
assert self._transient, "Model %s is not transient, it cannot be vacuumed!" % self._name
4639
"SELECT id, COALESCE(write_date, create_date, now())::timestamp"
4640
" AS t FROM " + self._table +
4641
" ORDER BY t LIMIT %s", (count,))
4642
ids = [x[0] for x in cr.fetchall()]
4643
self.unlink(cr, SUPERUSER_ID, ids)
4645
def _transient_vacuum(self, cr, uid, force=False):
4646
"""Clean the transient records.
4648
This unlinks old records from the transient model tables whenever the
4649
"_transient_max_count" or "_max_age" conditions (if any) are reached.
4650
Actual cleaning will happen only once every "_transient_check_time" calls.
4651
This means this method can be called frequently called (e.g. whenever
4652
a new record is created).
4654
assert self._transient, "Model %s is not transient, it cannot be vacuumed!" % self._name
4655
self._transient_check_count += 1
4656
if (not force) and (self._transient_check_count % self._transient_check_time):
4657
self._transient_check_count = 0
4660
# Age-based expiration
4661
if self._transient_max_hours:
4662
self._transient_clean_rows_older_than(cr, self._transient_max_hours * 60 * 60)
4664
# Count-based expiration
4665
if self._transient_max_count:
4666
self._transient_clean_old_rows(cr, self._transient_max_count)
4670
# keep this import here, at top it will cause dependency cycle errors
4673
class Model(BaseModel):
4674
"""Main super-class for regular database-persisted OpenERP models.
4676
OpenERP models are created by inheriting from this class::
4681
The system will later instantiate the class once per database (on
4682
which the class' module is installed).
4684
_register = False # not visible in ORM registry, meant to be python-inherited only
4685
_transient = False # True in a TransientModel
4687
class TransientModel(BaseModel):
4688
"""Model super-class for transient records, meant to be temporarily
4689
persisted, and regularly vaccuum-cleaned.
4691
A TransientModel has a simplified access rights management,
4692
all users can create new records, and may only access the
4693
records they created. The super-user has unrestricted access
4694
to all TransientModel records.
4696
_register = False # not visible in ORM registry, meant to be python-inherited only
4699
class AbstractModel(BaseModel):
4700
"""Abstract Model super-class for creating an abstract class meant to be
4701
inherited by regular models (Models or TransientModels) but not meant to
4702
be usable on its own, or persisted.
4704
Technical note: we don't want to make AbstractModel the super-class of
4705
Model or BaseModel because it would not make sense to put the main
4706
definition of persistence methods such as create() in it, and still we
4707
should be able to override them within an AbstractModel.
4709
_auto = False # don't create any database backend for AbstractModels
4710
_register = False # not visible in ORM registry, meant to be python-inherited only
4713
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
b'\\ No newline at end of file'