[IMP] api: remove deprecated decorators

This commit is contained in:
Raphael Collet
2019-07-08 13:51:35 +00:00
parent fa5037650d
commit c552fb7a61
54 changed files with 7 additions and 424 deletions
@@ -17,7 +17,6 @@ class AccountAnalyticLine(models.Model):
code = fields.Char(size=8)
ref = fields.Char(string='Ref.')
@api.v8
@api.onchange('product_id', 'product_uom_id', 'unit_amount', 'currency_id')
def on_change_unit_amount(self):
if not self.product_id:
-1
View File
@@ -2779,7 +2779,6 @@ class AccountMoveLine(models.Model):
# LOW-LEVEL METHODS
# -------------------------------------------------------------------------
@api.model_cr
def init(self):
""" change index on partner_id to a multi-column index on (partner_id, ref), the new index will behave in the
same way when we search on partner_id, with the addition of being optimal when having a query that will
@@ -164,7 +164,6 @@ class AccountInvoiceReport(models.Model):
COALESCE(partner.country_id, commercial_partner.country_id)
'''
@api.model_cr
def init(self):
tools.drop_view_if_exists(self.env.cr, self._table)
self.env.cr.execute('''
@@ -231,7 +231,6 @@ class BaseAutomation(models.Model):
)
return any(differ(field.name) for field in self.trigger_field_ids)
@api.model_cr
def _register_hook(self):
""" Patch models that should trigger action rules based on creation,
modification, deletion of records and form onchanges.
@@ -45,13 +45,11 @@ class BaseGengoTranslations(models.TransientModel):
sync_limit = fields.Integer("No. of terms to sync", default=20)
authorized_credentials = fields.Boolean('The private and public keys are valid')
@api.model_cr
def init(self):
icp = self.env['ir.config_parameter'].sudo()
if not icp.get_param(self.GENGO_KEY):
icp.set_param(self.GENGO_KEY, str(uuid.uuid4()))
@api.model_cr
def get_gengo_key(self):
icp = self.env['ir.config_parameter'].sudo()
return icp.get_param(self.GENGO_KEY, default="Undefined")
-2
View File
@@ -57,7 +57,6 @@ class Lead(models.Model):
'utm.mixin', 'format.address.mixin', 'phone.validation.mixin']
_primary_email = 'email_from'
@api.model_cr_context
def _auto_init(self):
res = super(Lead, self)._auto_init()
tools.create_index(self._cr, 'crm_lead_create_date_team_id_idx',
@@ -146,7 +145,6 @@ class Lead(models.Model):
('check_probability', 'check(probability >= 0 and probability <= 100)', 'The probability of closing the deal should be between 0% and 100%!')
]
@api.model_cr_context
def _auto_init(self):
res = super(Lead, self)._auto_init()
tools.create_index(self._cr, 'crm_lead_user_id_team_id_type_index',
-1
View File
@@ -75,7 +75,6 @@ class ActivityReport(models.Model):
m.model = 'crm.lead' AND (m.mail_activity_type_id IS NOT NULL OR m.subtype_id = %s)
""" % (disccusion_subtype.id,)
@api.model_cr
def init(self):
tools.drop_view_if_exists(self._cr, self._table)
self._cr.execute("""
@@ -17,7 +17,6 @@ class CRMRevealView(models.Model):
reveal_state = fields.Selection([('to_process', 'To Process'), ('not_found', 'Not Found')], default='to_process', string="State", index=True)
create_date = fields.Datetime(index=True)
@api.model_cr
def init(self):
self._cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('crm_reveal_view_ip_rule_id',))
if not self._cr.fetchone():
-1
View File
@@ -49,7 +49,6 @@ class HrEmployeePublic(models.Model):
def _get_fields(self):
return ','.join('emp.%s' % name for name, field in self._fields.items() if field.store and field.type not in ['many2many', 'one2many'])
@api.model_cr
def init(self):
tools.drop_view_if_exists(self.env.cr, self._table)
self.env.cr.execute("""CREATE or REPLACE VIEW %s as (
-1
View File
@@ -252,7 +252,6 @@ class HolidaysRequest(models.Model):
('duration_check', "CHECK ( number_of_days >= 0 )", "If you want to change the number of days you should use the 'period' mode"),
]
@api.model_cr_context
def _auto_init(self):
res = super(HolidaysRequest, self)._auto_init()
tools.create_index(self._cr, 'hr_leave_date_to_date_from_index',
@@ -15,7 +15,6 @@ class TimesheetAttendance(models.Model):
total_attendance = fields.Float()
total_difference = fields.Float()
@api.model_cr
def init(self):
self._cr.execute("""CREATE OR REPLACE VIEW %s AS (
SELECT
@@ -38,7 +38,6 @@ class ImLivechatReportChannel(models.Model):
is_unrated = fields.Integer('Session not rated', readonly=True)
partner_id = fields.Many2one('res.partner', 'Operator', readonly=True)
@api.model_cr
def init(self):
# Note : start_date_hour must be remove when the read_group will allow grouping on the hour of a datetime. Don't forget to change the view !
tools.drop_view_if_exists(self.env.cr, 'im_livechat_report_channel')
@@ -20,7 +20,6 @@ class ImLivechatReportOperator(models.Model):
time_to_answer = fields.Float('Time to answer', digits=(16, 2), readonly=True, group_operator="avg", help="Average time to give the first answer to the visitor")
duration = fields.Float('Average duration', digits=(16, 2), readonly=True, group_operator="avg", help="Duration of the conversation (in seconds)")
@api.model_cr
def init(self):
# Note : start_date_hour must be remove when the read_group will allow grouping on the hour of a datetime. Don't forget to change the view !
tools.drop_view_if_exists(self.env.cr, 'im_livechat_report_operator')
@@ -302,7 +302,6 @@ class L10nInAccountInvoiceReport(models.Model):
"""
return group_by_str
@api.model_cr
def init(self):
tools.drop_view_if_exists(self.env.cr, self._table)
self.env.cr.execute("""CREATE or REPLACE VIEW %s AS (
@@ -101,7 +101,6 @@ class L10nInPaymentReport(models.AbstractModel):
AND tax.tax_group_id in (SELECT res_id FROM ir_model_data WHERE module='l10n_in' AND name in ('igst_group','gst_group'))
AND ac.internal_type IN ('receivable', 'payable') AND am.state = 'posted'"""
@api.model_cr
def init(self):
tools.drop_view_if_exists(self.env.cr, self._table)
self.env.cr.execute("""CREATE or REPLACE VIEW %s AS (
@@ -89,7 +89,6 @@ class L10nInExemptedReport(models.Model):
"""
return from_str
@api.model_cr
def init(self):
tools.drop_view_if_exists(self.env.cr, self._table)
self._cr.execute("""CREATE OR REPLACE VIEW %s AS (%s %s)""" % (
-1
View File
@@ -98,7 +98,6 @@ class L10nInProductHsnReport(models.Model):
"""
return from_str
@api.model_cr
def init(self):
tools.drop_view_if_exists(self.env.cr, self._table)
self.env.cr.execute("""CREATE OR REPLACE VIEW %s AS (%s %s)""" % (
-1
View File
@@ -220,7 +220,6 @@ class AliasMixin(models.AbstractModel):
aliases.unlink()
return res
@api.model_cr_context
def _init_column(self, name):
""" Create aliases for existing rows. """
super(AliasMixin, self)._init_column(name)
-1
View File
@@ -410,7 +410,6 @@ class Channel(models.Model):
return True
return super(Channel, self)._alias_check_contact(message, message_dict, alias)
@api.model_cr
def init(self):
self._cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('mail_channel_partner_seen_message_id_idx',))
if not self._cr.fetchone():
-1
View File
@@ -585,7 +585,6 @@ class Message(models.Model):
# mail_message internals
#------------------------------------------------------
@api.model_cr
def init(self):
self._cr.execute("""SELECT indexname FROM pg_indexes WHERE indexname = 'mail_message_model_res_id_idx'""")
if not self._cr.fetchone():
-1
View File
@@ -37,7 +37,6 @@ class Notification(models.Model):
], string='Failure type')
failure_reason = fields.Text('Failure reason', copy=False)
@api.model_cr
def init(self):
self._cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('mail_notification_res_partner_id_is_read_email_status_mail_message_id',))
if not self._cr.fetchone():
@@ -430,9 +430,6 @@ class MailComposer(models.TransientModel):
values['body'] = values.pop('body_html')
# This onchange should return command instead of ids for x2many field.
# ORM handle the assignation of command list on new onchange (api.v8),
# this force the complete replacement of x2many field with
# command and is compatible with onchange api.v7
values = self._convert_to_write(values)
return {'value': values}
@@ -22,7 +22,6 @@ class MassMailingReport(models.Model):
string='Status', readonly=True)
email_from = fields.Char('From', readonly=True)
@api.model_cr
def init(self):
"""Mass Mail Statistical Report: based on mail.mail.statistics that models the various
statistics collected for each mailing, and mail.mass_mailing model that models the
@@ -37,7 +37,6 @@ class ReportMembership(models.Model):
company_id = fields.Many2one('res.company', string='Company', readonly=True)
quantity = fields.Integer(readonly=True)
@api.model_cr
def init(self):
'''Create the view'''
tools.drop_view_if_exists(self._cr, self._table)
@@ -94,7 +94,6 @@ class PosOrderReport(models.Model):
SUM(l.qty * u.factor) != 0
"""
@api.model_cr
def init(self):
tools.drop_view_if_exists(self._cr, self._table)
self._cr.execute("""
-1
View File
@@ -20,7 +20,6 @@ class PurchaseBillUnion(models.Model):
vendor_bill_id = fields.Many2one('account.move', string='Vendor Bill', readonly=True)
purchase_order_id = fields.Many2one('purchase.order', string='Purchase Order', readonly=True)
@api.model_cr
def init(self):
tools.drop_view_if_exists(self.env.cr, 'purchase_bill_union')
self.env.cr.execute("""
@@ -50,7 +50,6 @@ class PurchaseReport(models.Model):
qty_billed = fields.Float('Qty Billed', readonly=True)
qty_to_be_billed = fields.Float('Qty to be Billed', readonly=True)
@api.model_cr
def init(self):
# self._table = sale_report
tools.drop_view_if_exists(self.env.cr, self._table)
@@ -82,7 +82,6 @@ class PosSaleReport(models.Model):
AS foo""" % (self._table, self._from())
return request
@api.model_cr
def init(self):
tools.drop_view_if_exists(self.env.cr, self._table)
self.env.cr.execute(self.get_main_request())
-1
View File
@@ -144,7 +144,6 @@ class SaleReport(models.Model):
return '%s (SELECT %s FROM %s WHERE l.product_id IS NOT NULL GROUP BY %s)' % (with_, select_, from_, groupby_)
@api.model_cr
def init(self):
# self._table = sale_report
tools.drop_view_if_exists(self.env.cr, self._table)
-1
View File
@@ -354,7 +354,6 @@ class StockMove(models.Model):
user_warning += _('\n\nBlocking: %s') % ' ,'.join(moves_error.mapped('name'))
raise UserError(user_warning)
@api.model_cr
def init(self):
self._cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('stock_move_product_location_index',))
if not self._cr.fetchone():
@@ -17,7 +17,6 @@ class ReportStockForecat(models.Model):
picking_id = fields.Many2one('stock.picking', string='Picking', readonly=True)
reference = fields.Char('Reference')
@api.model_cr
def init(self):
tools.drop_view_if_exists(self._cr, 'report_stock_forecast')
query = """
-1
View File
@@ -47,7 +47,6 @@ class ResUsers(models.Model):
current_website = self.env['website'].get_current_website()
return current_website.auth_signup_uninvited or super(ResUsers, self)._get_signup_invitation_scope()
@api.model_cr_context
def _auto_init(self):
result = super(ResUsers, self)._auto_init()
# Use unique index to implement unique constraint per website, even if website_id is null
@@ -30,7 +30,6 @@ class CrmPartnerReportAssign(models.Model):
'grade_id', 'parent_id', 'team_id', 'user_id'],
}
@api.model_cr
def init(self):
"""
CRM Lead Report
@@ -14,7 +14,6 @@ class ReportEventRegistrationQuestions(models.Model):
answer_id = fields.Many2one(comodel_name='event.answer', string='Answer')
event_id = fields.Many2one(comodel_name='event.event', string='Event')
@api.model_cr
def init(self):
""" Event Question main report """
tools.drop_view_if_exists(self._cr, 'event_question_report')
-1
View File
@@ -79,7 +79,6 @@ class website_form_model_fields(models.Model):
_description = 'Fields'
_inherit = 'ir.model.fields'
@api.model_cr
def init(self):
# set all existing unset website_form_blacklisted fields to ``true``
# (so that we can use it as a whitelist rather than a blacklist)
@@ -294,7 +294,6 @@ class Channel(models.Model):
# ORM Overrides
# ---------------------------------------------------------
@api.model_cr_context
def _init_column(self, column_name):
""" Initialize the value of the given column for existing rows.
Overridden here because we need to generate different access tokens
-39
View File
@@ -1205,20 +1205,6 @@ Porting from the old API to the new API
:attr:`~odoo.models.Model.write_date` fields: they are now created as
regular "legitimate" fields, and can be read and written like any other
field out-of-the-box
* when straight conversion is impossible (semantics can not be bridged) or the
"old API" version is not desirable and could be improved for the new API, it
is possible to use completely different "old API" and "new API"
implementations for the same method name using :func:`~odoo.api.v7` and
:func:`~odoo.api.v8`. The method should first be defined using the
old-API style and decorated with :func:`~odoo.api.v7`, it should then be
re-defined using the exact same name but the new-API style and decorated
with :func:`~odoo.api.v8`. Calls from an old-API context will be
dispatched to the first implementation and calls from a new-API context will
be dispatched to the second implementation. One implementation can call (and
frequently does) call the other by switching context.
.. danger:: using these decorators makes methods extremely difficult to
override and harder to understand and document
* uses of :attr:`~odoo.models.Model._columns` or
:attr:`~odoo.models.Model._all_columns` should be replaced by
:attr:`~odoo.models.Model._fields`, which provides access to instances of
@@ -1277,28 +1263,3 @@ decorating the old-style method:
* disabling it entirely, by decorating a method with
:func:`~odoo.api.noguess` there will be no bridging and methods will be
called the exact same way from the new and old API styles
* defining the bridge explicitly, this is mostly for methods which are matched
incorrectly (because parameters are named in unexpected ways):
:func:`~odoo.api.cr`
will automatically prepend the current cursor to explicitly provided
parameters, positionally
:func:`~odoo.api.cr_uid`
will automatically prepend the current cursor and user's id to explictly
provided parameters
:func:`~odoo.api.cr_uid_ids`
will automatically prepend the current cursor, user's id and recordset's
ids to explicitly provided parameters
:func:`~odoo.api.cr_uid_id`
will loop over the current recordset and call the method once for each
record, prepending the current cursor, user's id and record's id to
explicitly provided parameters.
.. danger:: the result of this wrapper is *always a list* when calling
from a new-API context
All of these methods have a ``_context``-suffixed version
(e.g. :func:`~odoo.api.cr_uid_context`) which also passes the current
context *by keyword*.
* dual implementations using :func:`~odoo.api.v7` and
:func:`~odoo.api.v8` will be ignored as they provide their own "bridging"
@@ -32,11 +32,6 @@ class DecimalPrecision(models.Model):
res = self.env.cr.fetchone()
return res[0] if res else 2
@api.model_cr
def clear_cache(self):
""" Deprecated, use `clear_caches` instead. """
self.clear_caches()
@api.model_create_multi
def create(self, vals_list):
res = super(DecimalPrecision, self).create(vals_list)
-1
View File
@@ -283,7 +283,6 @@ class IrActionsActWindowView(models.Model):
act_window_id = fields.Many2one('ir.actions.act_window', string='Action', ondelete='cascade')
multi = fields.Boolean(string='On Multiple Doc.', help="If set to true, the action will not be displayed on the right toolbar of a form view.")
@api.model_cr_context
def _auto_init(self):
res = super(IrActionsActWindowView, self)._auto_init()
tools.create_unique_index(self._cr, 'act_window_view_unique_mode_per_action',
@@ -765,7 +765,6 @@ class IrActionsReport(models.Model):
return None
return render_func(res_ids, data=data)
@api.noguess
def report_action(self, docids, data=None, config=True):
"""Return an action of type ir.actions.report.
-1
View File
@@ -300,7 +300,6 @@ class IrAttachment(models.Model):
mimetype = fields.Char('Mime Type', readonly=True)
index_content = fields.Text('Indexed Content', readonly=True, prefetch=False)
@api.model_cr_context
def _auto_init(self):
res = super(IrAttachment, self)._auto_init()
tools.create_index(self._cr, 'ir_attachment_res_idx',
@@ -39,7 +39,6 @@ class IrConfigParameter(models.Model):
('key_uniq', 'unique (key)', 'Key must be unique.')
]
@api.model_cr
@mute_logger('odoo.addons.base.models.ir_config_parameter')
def init(self, force=False):
"""
-1
View File
@@ -150,7 +150,6 @@ class IrFilters(models.Model):
('name_model_uid_unique', 'unique (name, model_id, user_id, action_id)', 'Filter names must be unique'),
]
@api.model_cr_context
def _auto_init(self):
result = super(IrFilters, self)._auto_init()
# Use unique index to implement unique constraint on the lowercase name (not possible using a constraint)
-1
View File
@@ -32,7 +32,6 @@ class IrLogging(models.Model):
func = fields.Char(string='Function', required=True)
line = fields.Char(required=True)
@api.model_cr
def init(self):
super(IrLogging, self).init()
self._cr.execute("select 1 from information_schema.constraint_column_usage where table_name = 'ir_logging' and constraint_name = 'ir_logging_write_uid_fkey'")
+2 -3
View File
@@ -1191,7 +1191,7 @@ class IrModelAccess(models.Model):
self._cr.execute(query, (model_name, tuple(group_ids)))
return bool(self._cr.rowcount)
@api.model_cr
@api.model
def group_names_with_access(self, model_name, access_mode):
""" Return the names of visible groups which have been granted
``access_mode`` on the model ``model_name``.
@@ -1288,7 +1288,7 @@ class IrModelAccess(models.Model):
def unregister_cache_clearing_method(cls, model, method):
cls.__cache_clearing_methods.discard((model, method))
@api.model_cr
@api.model
def call_cache_clearing_methods(self):
self.invalidate_cache()
self.check.clear_cache(self) # clear the cache of check function
@@ -1352,7 +1352,6 @@ class IrModelData(models.Model):
for res in self:
res.reference = "%s,%s" % (res.model, res.res_id)
@api.model_cr_context
def _auto_init(self):
res = super(IrModelData, self)._auto_init()
tools.create_unique_index(self._cr, 'ir_model_data_module_name_uniq_index',
@@ -247,7 +247,6 @@ class IrTranslation(models.Model):
''' the source term is stored on 'src' field '''
return [('src', operator, value)]
@api.model_cr_context
def _auto_init(self):
res = super(IrTranslation, self)._auto_init()
# Add separate md5 index on src (no size limit on values, and good performance).
@@ -759,7 +758,6 @@ class IrTranslation(models.Model):
""" Return a cursor-like object for fast inserting translations """
return IrTranslationImport(self)
@api.model_cr_context
def load_module_terms(self, modules, langs):
""" Load PO files of the given modules for the given languages. """
# make sure the given languages are active
-2
View File
@@ -101,7 +101,6 @@ class ViewCustom(models.Model):
return self.browse(view_ids).name_get()
return super(ViewCustom, self)._name_search(name, args=args, operator=operator, limit=limit, name_get_uid=name_get_uid)
@api.model_cr_context
def _auto_init(self):
res = super(ViewCustom, self)._auto_init()
tools.create_index(self._cr, 'ir_ui_view_custom_user_id_ref_id',
@@ -408,7 +407,6 @@ actual arch.
"Invalid key: QWeb view should have a key"),
]
@api.model_cr_context
def _auto_init(self):
res = super(View, self)._auto_init()
tools.create_index(self._cr, 'ir_ui_view_model_type_inherit_id',
-1
View File
@@ -102,7 +102,6 @@ class Company(models.Model):
]
@api.model_cr
def init(self):
for company in self.search([('paperformat_id', '=', False)]):
paperformat_euro = self.env.ref('base.paperformat_euro', False)
+1 -1
View File
@@ -695,7 +695,7 @@ class ResConfigSettings(models.TransientModel, ResConfigModuleInstallationMixin)
model_name, field_name = full_field_name.rsplit('.', 1)
return self.env[model_name].fields_get([field_name])[field_name]['string']
@api.model_cr_context
@api.model
def get_config_warning(self, msg):
"""
Helper: return a Warning exception with the given message where the %(field:xxx)s
-1
View File
@@ -82,7 +82,6 @@ class Lang(models.Model):
except Exception:
raise ValidationError(warning)
@api.model_cr
def _register_hook(self):
# check that there is at least one active language
if not self.search_count([]):
-1
View File
@@ -241,7 +241,6 @@ class Partner(models.Model):
('check_name', "CHECK( (type='contact' AND name IS NOT NULL) or (type!='contact') )", 'Contacts require a name.'),
]
@api.model_cr
def init(self):
self._cr.execute("""SELECT indexname FROM pg_indexes WHERE indexname = 'res_partner_vat_index'""")
if not self._cr.fetchone():
@@ -35,7 +35,6 @@ class LineModel(models.Model):
base_id = fields.Many2one('test_performance.base', required=True, ondelete='cascade')
value = fields.Integer()
@api.model_cr
def init(self):
# line values should be unique per "base" - useful for testing corner cases with unique lines
tools.create_unique_index(self._cr, 'test_performance_line_uniq', self._table, ['base_id', 'value'])
+3 -309
View File
@@ -33,14 +33,8 @@
__all__ = [
'Environment',
'Meta', 'guess', 'noguess',
'Meta',
'model', 'multi',
'model_cr', 'model_cr_context',
'cr', 'cr_context',
'cr_uid', 'cr_uid_context',
'cr_uid_id', 'cr_uid_id_context',
'cr_uid_ids', 'cr_uid_ids_context',
'cr_uid_records', 'cr_uid_records_context',
'constrains', 'depends', 'onchange', 'returns',
'call_kw',
]
@@ -48,7 +42,7 @@ __all__ = [
import logging
from collections import defaultdict, Mapping
from contextlib import contextmanager
from inspect import currentframe, getargspec
from inspect import getargspec
from pprint import pformat
from weakref import WeakSet
@@ -69,12 +63,8 @@ _logger = logging.getLogger(__name__)
#
# On wrapping method only:
# - method._api: decorator function, used for re-applying decorator
# - method._orig: original method
#
WRAPPED_ATTRS = ('__module__', '__name__', '__doc__', '_constrains',
'_depends', '_onchange', '_returns', 'clear_cache')
INHERITED_ATTRS = ('_returns',)
@@ -82,6 +72,7 @@ class Params(object):
def __init__(self, args, kwargs):
self.args = args
self.kwargs = kwargs
def __str__(self):
params = []
for arg in self.args:
@@ -106,13 +97,6 @@ class Meta(type):
# make the method inherit from decorators
value = propagate(getattr(parent, key, None), value)
# guess calling convention if none is given
if not hasattr(value, '_api'):
try:
value = guess(value)
except TypeError:
pass
if (getattr(value, '_api', None) or '').startswith('cr'):
_logger.warning("Deprecated method %s.%s in module %s", name, key, attrs.get('__module__'))
@@ -324,50 +308,6 @@ def multi(method):
return method
def model_cr(method):
""" Decorate a record-style method where ``self`` is a recordset, but its
contents is not relevant, only the model is. Such a method::
@api.model_cr
def method(self, args):
...
may be called in both record and traditional styles, like::
# recs = model.browse(cr, uid, ids, context)
recs.method(args)
model.method(cr, args)
Notice that no ``uid``, ``ids``, ``context`` are passed to the method in
the traditional style.
"""
method._api = 'model_cr'
return method
def model_cr_context(method):
""" Decorate a record-style method where ``self`` is a recordset, but its
contents is not relevant, only the model is. Such a method::
@api.model_cr_context
def method(self, args):
...
may be called in both record and traditional styles, like::
# recs = model.browse(cr, uid, ids, context)
recs.method(args)
model.method(cr, args, context=context)
Notice that no ``uid``, ``ids`` are passed to the method in the
traditional style.
"""
method._api = 'model_cr_context'
return method
_create_logger = logging.getLogger(__name__ + '.create')
@@ -412,252 +352,6 @@ def model_create_multi(method):
return wrapper
def cr(method):
""" Decorate a traditional-style method that takes ``cr`` as a parameter.
Such a method may be called in both record and traditional styles, like::
# recs = model.browse(cr, uid, ids, context)
recs.method(args)
model.method(cr, args)
"""
method._api = 'cr'
return method
def cr_context(method):
""" Decorate a traditional-style method that takes ``cr``, ``context`` as parameters. """
method._api = 'cr_context'
return method
def cr_uid(method):
""" Decorate a traditional-style method that takes ``cr``, ``uid`` as parameters. """
method._api = 'cr_uid'
return method
def cr_uid_context(method):
""" Decorate a traditional-style method that takes ``cr``, ``uid``, ``context`` as
parameters. Such a method may be called in both record and traditional
styles, like::
# recs = model.browse(cr, uid, ids, context)
recs.method(args)
model.method(cr, uid, args, context=context)
"""
method._api = 'cr_uid_context'
return method
def cr_uid_id(method):
""" Decorate a traditional-style method that takes ``cr``, ``uid``, ``id`` as
parameters. Such a method may be called in both record and traditional
styles. In the record style, the method automatically loops on records.
"""
method._api = 'cr_uid_id'
return method
def cr_uid_id_context(method):
""" Decorate a traditional-style method that takes ``cr``, ``uid``, ``id``,
``context`` as parameters. Such a method::
@api.cr_uid_id
def method(self, cr, uid, id, args, context=None):
...
may be called in both record and traditional styles, like::
# rec = model.browse(cr, uid, id, context)
rec.method(args)
model.method(cr, uid, id, args, context=context)
"""
method._api = 'cr_uid_id_context'
return method
def cr_uid_ids(method):
""" Decorate a traditional-style method that takes ``cr``, ``uid``, ``ids`` as
parameters. Such a method may be called in both record and traditional
styles.
"""
method._api = 'cr_uid_ids'
return method
def cr_uid_ids_context(method):
""" Decorate a traditional-style method that takes ``cr``, ``uid``, ``ids``,
``context`` as parameters. Such a method::
@api.cr_uid_ids_context
def method(self, cr, uid, ids, args, context=None):
...
may be called in both record and traditional styles, like::
# recs = model.browse(cr, uid, ids, context)
recs.method(args)
model.method(cr, uid, ids, args, context=context)
It is generally not necessary, see :func:`guess`.
"""
method._api = 'cr_uid_ids_context'
return method
def cr_uid_records(method):
""" Decorate a traditional-style method that takes ``cr``, ``uid``, a
recordset of model ``self`` as parameters. Such a method::
@api.cr_uid_records
def method(self, cr, uid, records, args):
...
may be called in both record and traditional styles, like::
# records = model.browse(cr, uid, ids, context)
records.method(args)
model.method(cr, uid, records, args)
"""
method._api = 'cr_uid_records'
return method
def cr_uid_records_context(method):
""" Decorate a traditional-style method that takes ``cr``, ``uid``, a
recordset of model ``self``, ``context`` as parameters. Such a method::
@api.cr_uid_records_context
def method(self, cr, uid, records, args, context=None):
...
may be called in both record and traditional styles, like::
# records = model.browse(cr, uid, ids, context)
records.method(args)
model.method(cr, uid, records, args, context=context)
"""
method._api = 'cr_uid_records_context'
return method
def v7(method_v7):
""" Decorate a method that supports the old-style api only. A new-style api
may be provided by redefining a method with the same name and decorated
with :func:`~.v8`::
@api.v7
def foo(self, cr, uid, ids, context=None):
...
@api.v8
def foo(self):
...
Special care must be taken if one method calls the other one, because
the method may be overridden! In that case, one should call the method
from the current class (say ``MyClass``), for instance::
@api.v7
def foo(self, cr, uid, ids, context=None):
# Beware: records.foo() may call an overriding of foo()
records = self.browse(cr, uid, ids, context)
return MyClass.foo(records)
Note that the wrapper method uses the docstring of the first method.
"""
# retrieve method_v8 from the caller's frame
frame = currentframe().f_back
return frame.f_locals.get(method_v7.__name__, method_v7)
def v8(method_v8):
""" Decorate a method that supports the new-style api only. An old-style api
may be provided by redefining a method with the same name and decorated
with :func:`~.v7`::
@api.v8
def foo(self):
...
@api.v7
def foo(self, cr, uid, ids, context=None):
...
Note that the wrapper method uses the docstring of the first method.
"""
if method_v8.__name__ == 'read':
return multi(method_v8)
method_v8._api = 'v8'
return method_v8
def noguess(method):
""" Decorate a method to prevent any effect from :func:`guess`. """
method._api = None
return method
def guess(method):
""" Decorate ``method`` to make it callable in both traditional and record
styles. This decorator is applied automatically by the model's
metaclass, and has no effect on already-decorated methods.
The API style is determined by heuristics on the parameter names: ``cr``
or ``cursor`` for the cursor, ``uid`` or ``user`` for the user id,
``id`` or ``ids`` for a list of record ids, and ``context`` for the
context dictionary. If a traditional API is recognized, one of the
decorators :func:`cr`, :func:`cr_context`, :func:`cr_uid`,
:func:`cr_uid_context`, :func:`cr_uid_id`, :func:`cr_uid_id_context`,
:func:`cr_uid_ids`, :func:`cr_uid_ids_context` is applied on the method.
Method calls are considered traditional style when their first parameter
is a database cursor.
"""
if hasattr(method, '_api'):
return method
# introspection on argument names to determine api style
args, vname, kwname, defaults = getargspec(method)
names = tuple(args) + (None,) * 4
if names[0] == 'self':
if names[1] in ('cr', 'cursor'):
if names[2] in ('uid', 'user'):
if names[3] == 'ids':
if 'context' in names or kwname:
return cr_uid_ids_context(method)
else:
return cr_uid_ids(method)
elif names[3] == 'id' or names[3] == 'res_id':
if 'context' in names or kwname:
return cr_uid_id_context(method)
else:
return cr_uid_id(method)
elif 'context' in names or kwname:
return cr_uid_context(method)
else:
return cr_uid(method)
elif 'context' in names:
return cr_context(method)
else:
return cr(method)
# no wrapping by default
return noguess(method)
def expected(decorator, func):
""" Decorate ``func`` with ``decorator`` if ``func`` is not wrapped yet. """
return decorator(func) if not hasattr(func, '_api') else func
def _call_kw_model(method, self, args, kwargs):
context, args, kwargs = split_context(method, args, kwargs)
recs = self.with_context(context or {})
+1 -13
View File
@@ -298,7 +298,6 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
"""
pass
@api.model_cr_context
def _reflect(self):
""" Reflect the model and its fields in the models 'ir.model' and
'ir.model.fields'. Also create entries in 'ir.model.data' if the key
@@ -2281,7 +2280,6 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
else:
return '"%s"."%s"' % (alias, fname)
@api.model_cr
def _parent_store_compute(self):
""" Compute parent_path field from scratch. """
if not self._parent_store:
@@ -2320,7 +2318,6 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
self.invalidate_cache(['parent_path'])
return True
@api.model_cr
def _check_removed_columns(self, log=False):
# iterate on the database columns to drop the NOT NULL constraints of
# fields which were required but have been removed (or will be added by
@@ -2343,7 +2340,6 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
if row['attnotnull']:
tools.drop_not_null(cr, self._table, row['attname'])
@api.model_cr_context
def _init_column(self, column_name):
""" Initialize the value of the given column for existing rows. """
# get the default value; ideally, we should use default_get(), but it
@@ -2375,7 +2371,6 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
self.env.cr.execute('SELECT 1 FROM "%s" LIMIT 1' % self._table)
return self.env.cr.rowcount
@api.model_cr_context
def _auto_init(self):
""" Initialize the database schema of ``self``:
- create the corresponding table,
@@ -2446,14 +2441,12 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
if parent_path_compute:
self._parent_store_compute()
@api.model_cr
def init(self):
""" This method is called after :meth:`~._auto_init`, and may be
overridden to create or modify a model's database schema.
"""
pass
@api.model_cr
def _create_parent_columns(self):
tools.create_column(self._cr, self._table, 'parent_path', 'VARCHAR')
if 'parent_path' not in self._fields:
@@ -2461,7 +2454,6 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
elif not self._fields['parent_path'].index:
_logger.error('parent_path field on model %s must be indexed! Add index=True to the field definition)', self._name)
@api.model_cr
def _add_sql_constraints(self):
"""
@@ -2489,7 +2481,6 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
else:
process(key, definition)
@api.model_cr
def _execute_sql(self):
""" Execute the SQL code from the _sql attribute (if any)."""
if hasattr(self, "_sql"):
@@ -4527,7 +4518,6 @@ Fields:
"""
return cls._transient
@api.model_cr
def _transient_clean_rows_older_than(self, seconds):
assert self._transient, "Model %s is not transient, it cannot be vacuumed!" % self._name
# Never delete rows used in last 5 minutes
@@ -4539,7 +4529,6 @@ Fields:
ids = [x[0] for x in self._cr.fetchall()]
self.sudo().browse(ids).unlink()
@api.model_cr
def _transient_clean_old_rows(self, max_count):
# Check how many rows we have in the table
self._cr.execute("SELECT count(*) AS row_count FROM " + self._table)
@@ -4682,7 +4671,6 @@ Fields:
for record in self:
record.active = not record.active
@api.model_cr
def _register_hook(self):
""" stuff to do right after the registry is built """
pass
@@ -4714,7 +4702,7 @@ Fields:
origin = getattr(cls, name)
method.origin = origin
# propagate decorators from origin to method, and apply api decorator
wrapped = api.guess(api.propagate(origin, method))
wrapped = api.propagate(origin, method)
wrapped.origin = origin
setattr(cls, name, wrapped)