Discusión en profundidad sobre el modelo (Modelos) de la aplicación de complemento Odoo--Alanhou

Visualización y clasificación de modelos

vi models/library_book.py
# 在class内部加入如下代码
# 为模型添加更为友好的描述,更新后在Settings下可发现相应的变化(需开启调试模式Database Structure>Models)
	_description = 'Library Book' 
# 默认情况下Odoo使用内置的id进行排序,可以通过逗号分隔指定多个字段进行排序,desc表示降序,仅能使用数据库中存储的字段排序,外部计算后的字段不适用,_order有些类似SQL语句中的ORDER BY,但无法使用NULL FIRST之类的语句
	_order = 'date_release desc, name'
# _rec_name用于配置记录表示,模型记录在被其它模型引用时会有表示信息,如user_id为1时表示Administrator用户,当它在表单视图中显示时,会显示为用户名而非id,因为_rec_name的默认值是name,这也是建议在模型内添加name字段的原因,如没有name字段,则采用模型记录的标识符,类似(library.book, 1)
	_rec_name = 'short_name'
	short_name = fields.Char('Short Title', required=True)

La representación del registro también tiene un nuevo campo mágico display_name de Odoo 8.0, cuyo valor es generado por el método name_get(). De forma predeterminada, name_get() usa el atributo _rec_name para encontrar los datos. Si necesita una lógica más compleja, simplemente anule este método y devuelva una tupla que contenga la identificación del registro y la representación del registro. Para mostrar un mundo ordinario (2017-06-01), defina el siguiente método en la clase

def name_get(self):
	result = []
	for record in self:
		result.append(
		(record.id,
		"%s (%s)" % (record.name, record.date_release)
		))
	return result

Explicación de los tipos de datos en los modelos

# vi models/library_book.py
...
# Char用于字符串
    short_name = fields.Char('Short Title', required=True)
# Text用于多行字符串
    notes = fields.Text('Internal Notes')
# Selection用于选择列表,由值和描述对组成,选择的值将存储在数据库中,可以为字符串或整型,描述默认可翻译
# 虽然整型的值看似简洁,但注意Odoo会把0解析为未设置(unset),因而当存储值为0时不会显示描述
    state = fields.Selection(
        [('draft', 'Not Available'),
        ('available', 'Available'),
        ('lost', 'Lost')],
        'State')
# Html类似text字段,但一般用于存储富文本格式的HTML
    description = fields.Html('Description')
# Binary字段用于存储二进制文件,如图片或文档
    cover = fields.Binary('Book Cover')
# Boolean字段用于存储True/False布尔值
    out_of_print = fields.Boolean('Out of Print?')
# Date字段用于存储日期,ORM中以字符串格式对其进行处理,但以日期形式存放在数据库中,该格式在odoo.fileds.DATE_FORMAT中定义
    date_release = fields.Date('Release Date')
# Datetime用于存储日期时间,在数据库中以UTC无时区时间(naive)存放,ORM中以字符串和UTC时间表示,该格式在odoo.fields.DATETIME_FORMAT中定义
    date_updated = fields.Datetime('Last Updated')
# Integer即为整型
    pages = fields.Integer('Number of Pages')
# Float用于存储数值,其精度可以通过数字长度和小数长度一对值来进行指定
    reader_rating = fields.Float(
        'Reader Average Rating',
        digits=(14, 4), # Optional precision (total, decimals)
        )
# 此外Monetary用于存储货币金额
# 所有字段都有一些通用属性,如我们可以将pages字段丰富为:
pages = fields.Integer(
        string='Number of Pages',
        default=0,
        help='Total book page count',
        groups='base.group_user',
        states={
    
    'lost': [('readonly', True)]},
        copy=True,
        index=False,
        readonly=False,
        required=False,
        company_dependent=False,
        )
# Char和HTML字段分别一些特殊属性,如:
 
short_name = fields.Char(
        string='Short Title', 
        size=100, # 仅用于Char字段
        translate=False, # Text字段也可使用
        required=True)
 
description = fields.Html(
        string='Description',
        # 以下均为可选属性
        sanitize=True,
        strip_style=False,
        translate=False,
        )

Del ejemplo anterior, también podemos ver que los campos pueden contener atributos:

  • cadena es el título del campo, que se usa en la etiqueta de vista de la interfaz de usuario. Esto es opcional. Si no está configurado, se mostrará reemplazando el cuerpo del título subrayado con un espacio en el nombre del campo.
  • El tamaño solo se aplica al campo Char, se usa para establecer la cantidad máxima de caracteres permitidos, generalmente no se recomienda
  • El campo es traducible cuando traducir se establece en Verdadero y puede contener diferentes valores según el idioma de la interfaz de usuario
  • default se usa para establecer el valor predeterminado, y también puede ir seguido de un nombre de método, como default=_compute_default, la definición del método debe definirse antes que la definición del campo
  • ayuda Establezca el contenido del aviso explicativo que se muestra en la página de la interfaz de usuario
  • grupos Establezca los grupos de seguridad que pueden usar este campo, el valor es una cadena de ID XML de un conjunto de grupos de seguridad separados por comas, consulte el artículo de seguimiento sobre el control de permisos para obtener más información.
  • Los estados permiten la configuración dinámica de atributos invisibles, requeridos y de solo lectura en la interfaz de usuario, dependiendo de la configuración del campo de estado.
  • copy se utiliza para mostrar si se trata de una copia cuando se copia el registro. El valor predeterminado es True para campos no relacionales y Many2one, y False para One2many y campos calculados.
  • Cuando el índice se establece en Verdadero, se creará un índice para este campo en la base de datos, que reemplaza el atributo obsoleto select=1
  • readonly establece que el campo sea de solo lectura de forma predeterminada en la interfaz de usuario
  • El campo de configuración requerido es obligatorio de forma predeterminada en la interfaz de usuario
  • sanitize se usa para campos HTML, elimina etiquetas con amenazas de seguridad y desinfectará la entrada global. Para un control más preciso, algunas palabras clave están disponibles después de configurar higienizar:
  • santitize_tags =True: desinfecte las etiquetas que no están en la lista blanca (definidas en allow_tags en odoo/odoo/tools/mail.py)
  • sanitize_attributes =True: además, desinfecte los atributos que no sean la lista blanca (definidos en safe_attrs de odoo/odoo/tools/mail.py)
  • sanitize_style =True: desinfectar los atributos de estilo fuera de la lista blanca (definidos en _style_whitelist en odoo/odoo/tools/mail.py)
  • strip_style =True: quitar todos los elementos de estilo
  • strip_class =True: Borrar atributos de clase
  • company_dependent se usa para almacenar diferentes valores por empresa, reemplaza el campo Propiedad en desuso

ampliar conocimientos

Además de contener opciones como el ejemplo anterior, el campo Selección también se puede usar para referencias de métodos para obtener dinámicamente una lista de opciones, que se demostrará más adelante en este artículo.

Los campos Fecha y Fecha y hora tienen algunos métodos de herramientas para que las operaciones sean más convenientes, y Fecha tiene los siguientes métodos:

  • fields.Date.from_string(string_value) analiza la cadena en un objeto de fecha
  • fields.Date.to_string(date_value) representa el objeto Date como una cadena
  • fields.Date.today() devuelve la fecha de hoy en formato de cadena, que se puede usar para obtener el valor predeterminado
  • campos.Date.context_today(registro, marca de tiempo) devuelve una marca de tiempo en formato de cadena según la zona horaria del contexto del registro (o conjunto de registros) (devuelve una fecha cuando no se pasa el parámetro de marca de tiempo)

Datetime tiene métodos:

  • fields.Datetime.from_string(string_value) analiza la cadena en un objeto de fecha y hora
  • fields.Datetime.to_string(date_value) representa un objeto de fecha y hora como una cadena
  • fields.Datetime.now() devuelve la fecha y hora actual en formato de cadena, que se puede usar para obtener el valor predeterminado
  • fields.Datetime.context_timestamp(record, timestamp) convierte el tiempo sin zona horaria en formato de cadena de acuerdo con la zona horaria del contexto del registro. Este método no es adecuado para obtener valores predeterminados, pero se puede usar para transferir datos a sistemas externos. Además de los campos básicos, también hay
    campos de tipos relacionales: Muchos2uno, Uno2muchos y Muchos2muchos, que se analizan con mayor profundidad más adelante. Además, hay un cálculo de campo que calcula automáticamente los valores, que se presentará más adelante.

Los lectores atentos encontrarán que los modelos de Odoo también crean algunos campos de forma predeterminada, así que no utilice estos nombres al definir el modelo. Estos nombres de campo contienen el ID de identificador generado automáticamente y algunos campos de auditoría de registro:

  • create_date : la hora en que se creó el registro
  • create_uid : el usuario que creó el registro
  • write_date : El límite de tiempo cuando se editó el registro por última vez
  • write_uid : el usuario que editó el registro la última vez.
    Estos campos de registro creados automáticamente se pueden desactivar agregando el atributo _log_access=False. Otro nombre de campo especial que se puede agregar al modelo está activo, que es un campo booleano que identifica si el registro está disponible:
active = fileds.Boolean('Active', default=True)

De forma predeterminada, solo son visibles aquellos con activos establecidos en True, y para obtener registros invisibles, puede pasar el controlador de dominio [('active', '=', False)], otro método es agregar 'active_test': False al contexto ambiental, de lo contrario, el ORM no puede obtener registros no disponibles.

Poco conocimiento : A veces es imposible obtener registros tanto visibles como invisibles modificando el contexto, en este caso, utilice el ['|', ('activo', '=', Verdadero), ('activo', '=' , Falso)] campo . Tenga en cuenta que [('acitve', 'in' (Verdadero, Falso))] no funciona como cabría esperar.

Configuración de precisión para campos de punto flotante

Cuando se utiliza un campo de número de coma flotante, es posible que se le solicite al usuario final que configure la precisión. A continuación, usamos el complemento de configuración de precisión decimal para lograrlo, y luego agregamos un campo de costo al modelo del libro de la biblioteca para que el usuario lo especifique. la precisión

1. Primero haga clic en Aplicaciones en segundo plano para buscar Configuración de precisión decimal y haga clic en Instalar para instalar
inserte la descripción de la imagen aquí
2. Active el modo de desarrollador y haga clic en Configuración > Técnica > Estructura de la base de datos > Precisión decimal, agregue un precio de libro y establezca la precisión en dos lugares decimales
inserte la descripción de la imagen aquí
3. Agregar dependencias

vi local-addons/my_module/__manifest__.py
# 加入依赖
'depends': ['base', 'decimal_precision'],

4. Agregue cost_price y use la configuración de precisión mencionada anteriormente

vi local-addons/my_module/models/library_book.py
# 相入数字精度
from odoo.addons import decimal_precision as dp
# 通过get_precision方法在Decimal Accuracy中插件前面设置的精度配置
cost_price = fields.Float('Book Cost', dp.get_precision('Book Price'))

A través del método anterior, el usuario final puede controlar la precisión por sí mismo en segundo plano.

Adición de un campo Monetario al modelo

El campo Monetario requiere un campo de moneda adicional para almacenar la cantidad:

vi local-addons/my_module/models/library_book.py
# 在类中添加字段
    currency_id = fields.Many2one('res.currency', string='Currency')
    retail_price = fields.Monetary(
        'Retail Price',
        # optional: currency_field='currency_id',
        )

Después de la actualización, puede ver los cambios correspondientes en Configuración > Técnica > Estructura de la base de datos > Modelos. El campo de moneda generalmente usa id_moneda, por supuesto, también puede usar otros nombres. En este caso, debe especificarse a través del atributo opcional campo_moneda .

Sugerencia : Esto es muy útil. Podemos mantener diferentes monedas configurando varios campos. Many2one (res.currency), como una moneda de pedido de ventas y una moneda de liquidación de la empresa.

Agregar campos relacionales al modelo

Las relaciones entre modelos de Odoo están representadas por campos relacionales, y hay tres relaciones diferentes: muchos a uno (abreviado como m2o), uno a muchos (abreviado como o2m) y muchos a muchos (abreviado como m2m ). En nuestro caso del libro, cada libro tiene una editorial, por lo que la relación entre el libro y la editorial es de muchos a uno; desde la perspectiva de la edición, la relación con el libro es de uno a muchos; cada libro puede tener varios autores, y cada autor también puede tener varios libros, por lo que la relación entre autores y libros es de muchos a muchos.

Odoo usa el modelo de Socio res.partner para representar personas, instituciones y direcciones, así que aquí el autor y el editor usan res.partner:

vi local-addons/my_module/models/library_book.py 
 
# 为出版商添加many-to-one字段,many-to-one字段会在数据表中添加一列并存储相关联记录的ID,在数据库层面上会为其创建一个外键约束,以确保所存储的这些ID在关联表为有效引用 。通常应考虑添加索引,默认不会添加,需通过index=True属性来设置
    publisher_id = fields.Many2one(
        'res.partner', string='Publisher',
        # 以下为可选项
# ondelete属性用于决定many-to-one字段相关联记录删除时的操作,如本例中出版社删除后对书本如何操作?默认值为set null,将字段设为空值,还可以为restrict:防止相关联记录的删除,cascade:删除相关联记录
        ondelete='set null',
# context和domain在其它关联字段中也有效,主要用于客户端。在通过字段点击到相关联记录视图时,context为客户端上下文添加变更 ,如通过该视图创建新记录时设置默认值
        context={
    
    },
# domain为相关联记录添加过滤以控制可用记录
        domain=[]
        )
 
# 添加出版商和书的one-to-many(需要继承partner模型)以及书和作者的many-to-many,
class ResPartner(models.Model):
# _inherit在后面会进行更深入的讨论
    _inherit = 'res.partner'
# one-to-many与many-to-one相反,其在数据库中并没有体现,更多的是程序端的shortcut来让视图能表示相关联的记录
    published_book_ids = fields.One2many(
        'library.book', 'publisher_id',
        string = 'Published Books'
        )
# 书到作者的关系已通过author_ids定义,此处用于添加作者到书的关系
# many-to-many关联不在模型的表中添加列,此类关联在数据库中通过中间表实现,创建新记录时在该表中创建书与作者间的关联,Odoo自动创建关联表,默认表名使用按字母排序的两个模型名加上_rel,当然我们可以使用relation属性来覆盖该默认值
# 注意:如果两表的名称较长,会导致自动生成的名称超PostgreSQL 63个字符的上限,因此建议在两表的表名起过23个字符时使用relation属性来指定一个更短的名称
    authored_book_ids = fields.Many2many(
        'library.book',
        string='Authored Books',
        # optional: relation = 'library_book_res_partner_rel'
        )

Después de la actualización, puede ver los cambios correspondientes en Configuración > Técnica > Estructura de la base de datos > Modelos. El campo
de conocimiento extendido
Many2one tiene un atributo de unión automática que permite que ORM use la combinación de SQL en este campo, que no estará restringido por el control de permisos del usuario y permiso de registro. Esto puede mejorar el rendimiento en ciertos casos, pero generalmente no se recomienda.

El método antes mencionado de definir campos asociados es relativamente conciso, y se hacen los siguientes suplementos para completar la introducción:

Propiedades de campo One2many :

  • comodel_name : Este es el identificador del modelo de destino y es obligatorio para todos los campos asociados, pero podemos definirlo en una ubicación específica sin usar esta palabra clave

  • inverse_name : solo se usa para One2many, es el nombre de campo del modelo de destino asociado con el Many2one inverso

  • límite : solo para One2many y Many2many, establezca el límite en el número de registros de lectura en las
    propiedades de campo de la interfaz de usuario Many2many :

  • comodel_name : ver arriba

  • relación : admite nombres de tablas de datos asociados, anulando los nombres predeterminados generados automáticamente

  • column1 : el nombre de campo Many2one de la tabla de datos asociado con este modelo

  • column2 : el nombre de campo Many2one de la tabla de datos asociada con el modelo de comomodelo
    Para las asociaciones Many2many, en la mayoría de los casos, ORM manejará bien los valores predeterminados de estos atributos, incluso encontrará asociaciones Many2many inversas y tablas de relaciones existentes, y volteará adecuadamente column1 y valor de la columna2. Sin embargo, se requiere la intervención humana en dos casos. En un caso, se requiere más de una asociación Many2many entre dos modelos. En este momento, necesitamos especificar el atributo de relación para evitar conflictos; en el otro caso, la longitud de la asociación automáticamente. El nombre de la tabla de asociación generada supera el límite de 63 caracteres para los nombres de objeto de la base de datos de PostgreSQL.

El nombre de la tabla de asociación generada automáticamente es _rel , pero al mismo tiempo se crea un índice para la clave principal de la tabla de asociación y el identificador es rel id _id_key, que también está limitado por el límite superior de 63 caracteres.

Agregar una jerarquía al modelo

Una jerarquía se comporta como una asociación de modelo consigo misma, cada registro tiene un registro principal y algunos registros secundarios en el mismo modelo, lo que se puede lograr a través de una asociación de muchos a uno con el propio modelo. Odoo también agrega soporte para el modelo de conjuntos anidados para este tipo de campo.Después de la activación, las consultas que utilizan el operador child_of en el filtrado de dominios se acelerarán significativamente.

Vamos a crear un árbol de clasificación jerárquica para clasificar libros y agregar un archivo library_book_categ.py:

  1. Agregue código en __init__.py para cargar este archivo
vi local-addons/my_module/models/__init__.py 
 
from . import library_book_categ

2. Agregue el contenido del archivo library_book_categ.py

vi local-addons/my_module/models/library_book_categ.py
 
from odoo import models, fields, api
 
class BookCategory(models.Model):
    _name = 'library.book.category'
    name = fields.Char('Category')
# Many2one关联用于创建引用父记录字段
    parent_id = fields.Many2one(
        'library.book.category',
        string='Parent Category',
# 此处值必须为restrict或cascade
        ondelete='restrict',
# 添加索引用于快速发现子记录
        index=True)
# One2many关联不会在数据库中添加额外字段,而是在获取所有记录时快捷地把此记录作为父记录
    child_ids = fields.One2many(
        'library.book.category', 'parent_id',
        string='Child Categories')
# 为使用等级的支持,加入如下代码。通过以下三项配置可获取更快的数据读取速度,但同时写入的开销也会更大,第一项配置为True时后两项将会用于存储等级树中的搜索数据;默认使用parent_id字段作为记录的父级,也可通过_parent_name来进行指定,如_parent_name = 'parent_id'
    _parent_store = True
    parent_left = fields.Integer(index=True)
    parent_right = fields.Integer(index=True)
# 防止循环关联,添加如下方法。以下配置防止记录同时存在于上升和下降树级中,这会导致无限循环,models.Model中有一个_check_recursion工具方法可在此处复用
    @api.constrains('parent_id')
    def _check_hierarchy(self):
        if not self._check_recursion():
            raise models.ValidationError(
                'Error! You cannot create recursive categories.')

Conocimientos adicionales : el método anterior se usa en el nivel "estático", es decir, las operaciones comunes se leen y consultan, y rara vez se realizan actualizaciones. Obviamente, la clasificación de los libros es relativamente fija y los lectores suelen buscar por clasificación, por lo que es muy aplicable. La razón de esto es que el uso del modelo de conjunto anidado (modelo de conjunto anidado) requiere que parent_left y parent_right y otros índices de bases de datos relacionados deben actualizarse cuando se inserta, elimina o modifica una nueva clase, lo que traerá muchos problemas. cuando las transacciones paralelas se ejecutan al mismo tiempo Gran sobrecarga del sistema.

Sugerencia : cuando se trata de jerarquías dinámicas, la asociación estándar parent_id y child_ids mejora el rendimiento al evitar bloqueos a nivel de tabla.

Agregar validación de restricciones al modelo

Los modelos se pueden validar para evitar situaciones inesperadas.Hay dos tipos de restricciones: una es una verificación a nivel de base de datos y la otra es una verificación a nivel de servidor. La primera está limitada a las restricciones admitidas por PostgreSQL, la más utilizada es la restricción ÚNICA, y también existen las restricciones CHECK y EXCLUDE, si ninguna de estas cumple con los requisitos, puede aplicar las restricciones a nivel del servidor Odoo escribiendo Python código.

A continuación, agregaremos una restricción de base de datos para evitar títulos de libros duplicados. La restricción del modelo de Python evita que la fecha de lanzamiento sea posterior a la fecha actual:
1. Cree una restricción de base de datos

vi local-addons/my_module/models/library_book.py
... 
# _sql_constraints可通过列表来设置约束,每个约束为一个包含三个元素的元组:1.约束标识符后缀,如此处的name_uniq会形成一个library_book_name_uniq约束名;2.在PostgreSQL中修改或创建数据表的SQL指令;3.在违反约束时返回给用户的消息。前面提到过还可以使用其它数据表约束,但请注意如NOT NULL这样的列约束是无法使用的,可在官网获取更多<a href="https://www.postgresql.org/docs/current/static/ddl-constraints.html" target="_blank" rel="noopener">PostgreSQL约束知识</a>
    _sql_contraints = [
        ('name_uniq',
        'UNIQUE (name)',
        'Book title must be unique.')
        ]

2. Agregue una restricción a nivel de servidor a la clase a través del código python

vi local-addons/my_module/models/library_book.py
 
from odoo import api
...
# 此处使用@api.contrains装饰器,表示在参数列表中字段发生变化时进行检测,如果检测失败,则抛出ValidationErorr异常
    @api.constrains('date_release')
    def _check_relase_date(self):
        for record in self:
            if(record.date_release and record.date_release > fields.Date.today()):
                raise models.ValidationErorr('Release date must be the past')

Nota: el atributo de modelo _constraints todavía existe y está obsoleto después de la versión 8.0. Se recomienda usar el decorador @api.constrains

Agregar un campo calculado al modelo

A veces necesitamos calcular el valor del campo a partir de este registro o registros relacionados. La forma común es calcular el precio total en función del precio unitario y la cantidad, que se puede realizar en Odoo a través del campo de cálculo. Aprenderemos esto en este ejemplo calculando la cantidad de días en función de la fecha de lanzamiento. Los campos computables también se pueden editar y buscar.

vi local-addons/my_module/models/library_book.py
 
from datetime import timedelta
from odoo import api
from odoo.fields import Date as fDate
...
# 首先添加新字段,定义方法与普通字段相似,多了一个compute属性来指定执行计算的方法。虽然相似,但可计算字段和普通字段却大不相同,它在运行时动态计算,除非自己添加逻辑,默认是不可写、不可搜索的
    age_days = fields.Float(
        string='Days Since Release',
# 计算方法应保持为可计算字段设置值,否则会抛出错误并且很难调试,通常if条件语句会导致未能成功赋值
        compute='_compute_age',
# 写的支持可通过inverse方法来完成,它使用可计算字段的值来更新原始值
        inverse='_inverse_age',
# 通过search属性也可以让未存储计算字段可被搜索。本方法并不是完成实际搜索,而是接收字段用于搜索的操作符和值作为参数,返回替换了搜索条件的域。配合把Date Since Release字段搜索转化为等价于Release Date的搜索。
        search='_search_age',
# 可选的store=True配置将字段存入数据库,这时计算后值可像普通字段那样被获取,但借助@api.depends装饰器,ORM可以获知何时需重新计算并更新值,可以理解一个永久缓存。这样字段就可以使用搜索条件、排序、分组,而无需添加search方法了。
        store=False,
# compute_sudo=True用于需要提升权限完成计算的情况,需谨慎使用,它会忽略所有权限规则 ,包括多公司设置中的公司隔离规则 
        compute_sudo=False,
        )
# 添加计算逻辑,计算方法是在运行时动态计算的,但ORM采用缓存技术避免每次获取值时的无效运算,因而需要通过@depends装饰器来了解所依赖字段,已在需要时更新缓存中的值。
    @api.depends('date_release')
    def _compute_age(self):
        today = fDate.from_string(fDate.today())
        for book in self.filtered('date_release'):
            delta = (today - fDate.from_string(book.date_release))
            book.age_days = delta.days
# 添加方法用于在可计算字段上完成写的逻辑
    def _inverse_age(self):
        today = fDate.from_string(fDate.context_today(self))
        for book in self.filtered('date_release'):
            d = today - timedelta(days=book.age_days)
            book.date_release = fDate.to_string(d)
# 添加方法允许可计算字段的搜索
    def _search_age(self, operator, value):
        today = fDate.from_string(fDate.context_today(self))
        value_days = timedelta(days=value)
        value_date = fDate.to_string(today - value_days)
        # convert the operator
        # book with age > value have a date < value_date
        operator_map = {
    
    
            '>': '<', '>=': '<=',
            '<': '>', '<=': '>=',
        }
        new_op = operator_map.get(operator, operator)
        return [('date_release', new_op, value_date)]

Exponer campos relacionados almacenados en otros modelos

Cuando el cliente de Odoo lee datos del servidor, solo puede obtener los valores de campo en el modelo de consulta y el cliente no puede usar la notación de puntos para obtener datos como el servidor. Pero se puede obtener agregándolo como campo asociado, a continuación lo hacemos para la ciudad de la editorial.

vi local-addons/my_module/models/library_book.py 
 
...
    publisher_city = fields.Char(
        'Publisher City',
        related='publisher_id.city',
        readonly=True
        )

Los campos asociados son similares a los campos ordinarios, pero hay un atributo relacionado adicional, que adopta otro conjunto de cadenas transversales. En este ejemplo, el id_publicador se usa para obtener los registros relacionados con el editor, y luego se lee el campo de ciudad. También se puede usar una estructura de cadena más larga, como id_publicador.id_país.código_país. Arriba también usamos el atributo de solo lectura; de lo contrario, el usuario podría modificar el valor, lo que cambiaría la ciudad del editor asociado.
Conocimiento ampliado:
los campos asociados son en realidad campos calculados, solo un atajo para leer el valor en el modelo asociado. Dado que es un campo computable, el campo de almacenamiento también se puede usar aquí y también puede hacer referencia a atributos como nombre, traducible y requerido en el campo asociado. Además, hay un related_sudo similar a compute_sudo.Si se establece en True, el recorrido de la cadena de campo no realizará comprobaciones de permisos de usuario.

Sugerencia: el uso de create() en un campo relacional perjudica el rendimiento, ya que los cálculos esperan hasta que se completa la creación. Por lo tanto, si hay una asociación One2many, como los modelos sale.order y sale.order.line, y los campos del modelo de pedido se referencian en el modelo de línea, los campos del modelo de pedido deben leerse explícitamente cuando el registro se crea, en lugar de utilizar los accesos directos de campo asociados, especialmente cuando hay varios registros de línea.

Agregue asociaciones dinámicas usando el campo Referencia

Para usar campos asociados, necesitamos determinar el modelo de destino asociado (comodelo) de antemano, pero a veces esto debe ser decidido por el usuario. Solo necesitamos establecer el modelo que queremos usar y el registro al que apuntar. En Odoo , usamos campos relacionados para lograr

vi local-addons/my_module/models/library_book.py 
 
....
# 按helper方法的定义添加引用字段
    ref_doc_id = fields.Reference(
        selection='_referencable_models',
        string='Reference Document'
        )
...
# 添加helper方法来动态创建可选目录模型列表
    @api.model
    def _referencable_models(self):
        models = self.env['res.request.link'].search([])
        return [(x.object, x.name) for x in models]

Un campo de referencia es similar a un campo de muchos a uno, excepto que permite al usuario elegir a qué modelo apuntar. El modelo de destino se puede seleccionar de la lista especificada en el campo de selección. Esta lista debe contener un conjunto de tuplas con dos parámetros. El primer parámetro es el identificador interno del modelo y el segundo parámetro es la descripción del texto. Por ejemplo

[('res.usuarios', 'Usuario'), ('res.socio', 'Socio')]

En general, en lugar de usar una lista fija, podemos permitir que los usuarios finales configuren la lista de modelos. Este es también el propósito de los Modelos referenciables integrados en Configuración > Técnica > Estructura de la base de datos. El identificador integrado de este modelo es res .solicitud.enlace. Arriba, hemos utilizado un método para buscar todos los registros del modelo a los que se puede hacer referencia para crear dinámicamente la lista de atributos de selección. Aunque se puede hacer referencia directamente al método después de la selección, hemos agregado comillas aquí. Este método es más flexible y permite la método que se declarará en el campo definido más adelante. El decorador @api.model se usa para operar en el nivel del modelo en lugar del conjunto de registros.

Nota : Aunque este método funciona bien, la carga es relativamente pesada. Por ejemplo, al mostrar una gran cantidad de campos de referencia de registros en una vista de lista, ejercerá presión sobre la base de datos debido a una consulta separada para cada valor. Al mismo tiempo, tiempo, no puede ser utilizado como campos asociados ordinarios Integridad referencial a la base de datos.

Adición de funciones a los modelos mediante la herencia

Una característica importante de Odoo es la capacidad de heredar las características de otros complementos de módulo en un complemento de módulo sin editar las características originales. Los rasgos pueden agregar lógica para agregar campos o métodos, modificar campos existentes o heredar métodos existentes. Este es el método de herencia más utilizado, que se denomina herencia tradicional/clásica en la documentación oficial. Juntos heredaremos el modelo de socio incorporado y agregaremos la cantidad de libros por autor al campo computable.

vi local-addons/my_module/models/library_book.py 
 
 
class ResPartner(models.Model):
# 使用_inherit则是继承的模型进行修改而非替换,在继承类中定义的方法会替换父类中的,所以想要使用原方法时,需要使用super关键字
    _inherit = 'res.partner'
    _order = 'name'
    authored_book_ids = fields.Many2many(
        'library.book',
        string='Authored Books',
        # optional: relation = 'library_book_res_partner_rel'
        )
    count_books = fields.Integer(
        'Number of Authored Books',
        compute='_compute_count_books')
 
    @api.depends('authored_book_ids')
    def _compute_count_books(self):
        for r in self:
            r.count_books = len(r.authored_book_ids)

Conocimiento extendido :

Usando la herencia tradicional _inherit, también puede copiar las características del modelo principal al nuevo modelo agregando un identificador diferente al atributo _name en la clase, como

class LibraryMember(models.Model):
	_inherit = 'res.partner'
	_name = 'library.member'

El nuevo modelo tendrá su propia tabla de datos independiente del modelo principal res.partner, ya que hereda del modelo Partner, los cambios posteriores también se reflejarán en el nuevo modelo. Se denomina herencia prototipo en los documentos oficiales y rara vez se usa en aplicaciones prácticas, porque la herencia por delegación cumple con este requisito de una manera más eficiente, ya que no necesita copiar un conjunto de estructuras de datos, que se introducirá más adelante.

Use modelos abstractos para reutilizar las características del modelo

A veces, hay características especiales que desea usar en varios modelos. La codificación repetida no es un buen hábito de codificación, por lo que es mejor escribir una vez y reutilizarla varias veces. El modelo abstracto se puede utilizar para escribir un modelo general, y las características que contiene pueden ser heredadas y utilizadas por otros modelos comunes. A continuación, escribimos una función de archivo, que agrega el campo activo al modelo y expone el método de archivo para cambiar la bandera activa. Esto funciona porque activo es un campo mágico y, si existe de forma predeterminada en el modelo, los registros con activo=Falso se filtrarán de la consulta.

La función Archivar puede ser solo un complemento, pero en aras de la simplicidad, todavía usamos el archivo library_book.py que siempre hemos tenido:

# 抽象模型通过models.AbstractModel定义,拥有所有普通模型的属性,不同处在于ORM不会在数据库中创建一个实际表现,因而无法存储数据。它的用处就是作为模板来提供可复用特性。这个抽象模型非常简单,仅包含了一个active字段和一个切换active标识的方法。
class BaseArchive(models.AbstractModel):
    _name = 'base.archive'
    active = fields.Boolean(default=True)
 
    def do_archive(self):
        for record in self:
            record.active = not record.active
 
            
class LibraryBook(models.Model):
    _name = 'library.book'
# _inherit后可接字符串或列表,列表可用于继承多个模型类
    _inherit = ['base.archive']

Conocimiento ampliado
Vale la pena mencionar que hay un modelo de abstracción incorporado mail.thread, proporcionado por el modelo de complemento de correo, que habilita funciones de discusión en el modelo (utilizado para el muro de mensajes en la parte inferior de muchos formularios). Además de AbstractModel, también hay models.TransientModel. El rendimiento es similar a models.Model, pero los registros creados son temporales y serán limpiados por la tarea programada en el lado del servidor, y los demás son iguales al modelo normal. Puede ser útil para interacciones de usuario más complejas (llamadas asistentes), como requerir la entrada del usuario para ejecutar un proceso o informe.

Copie rasgos a otro modelo usando la herencia de delegación

La herencia tradicional usa _inherit para heredar las características del modelo para su modificación, pero todavía hay casos en los que modifica un modelo existente, solo quiere usar algunas características existentes, que se implementan en Odoo con _inherits usando la herencia delegada. La herencia tradicional es diferente del concepto de programación orientada a objetos, pero el agente delegado es más similar, puede crear un nuevo modelo que contiene las características del modelo principal y admite herencia polimórfica, es decir, heredar de dos o más modelos. .

A continuación, agreguemos la membresía del libro, que requiere los datos de identidad y dirección que existen en el modelo de socio, así como la información utilizada por los miembros, como la fecha de inicio, la fecha de finalización y el número de tarjeta.

vi local-addons/my_module/models/library_book.py 
 
class LibraryMember(models.Model):
    _name = 'library.member'
# 字典中的key为所继承的模型,值为进行关联的字段名
    _inherits = {
    
    'res.partner': 'partner_id'}
    partner_id = fields.Many2one(
    'res.partner',
    ondelete='cascade')
# 添加一些图书会员需用的字段
    date_start = fields.Date('Member Since')
    date_end = fields.Date('Termination Date')
    member_number = fields.Char()
    date_of_birth = fields.Date('Date of Birth')

Al crear un miembro, se creará un nuevo registro en la tabla res_partner en la base de datos, y se creará un nuevo registro en library_member, y el campo partner_id de library_member se establecerá en el id del registro creado en res_partner. Los registros de miembros se asocian automáticamente con nuevos registros de socios. Entonces, ¿qué sucede cuando se elimina un miembro? Esto está controlado por el valor de ondelete. La cascada utilizada aquí significa que el Miembro también se eliminará cuando se elimine el Socio. La configuración más conservadora es restringir, y eliminar el Socio no eliminará al Miembro.

Tenga en cuenta que la herencia de delegación solo se puede usar para campos, no para métodos.

ampliar conocimientos

El modelo de usuario res.users heredado por el delegado se hereda de res.partner, lo que significa que algunos campos almacenados en Usuario en realidad se almacenan en el modelo de Socio (como el campo de nombre). Al crear un usuario, un Socio también será automáticamente creado.

------------------------------------- Extracto de Alanhou

Supongo que te gusta

Origin blog.csdn.net/iuv_li/article/details/126094730
Recomendado
Clasificación