Odoo对象与字段映射

wen PHP项目 3

本文目录导读:

Odoo对象与字段映射

  1. 核心映射规则
  2. 字段映射详解
  3. 字段属性映射
  4. 高级映射特性
  5. 完整示例
  6. 数据库验证
  7. 迁移注意事项

在Odoo中,对象(Model)字段(Field) 的映射关系是框架的核心机制,它通过 ORM(对象关系映射) 将Python类结构自动转换为数据库表结构,以下是其核心映射规则和实现方式:

核心映射规则

模型 → 数据库表

class SaleOrder(models.Model):
    _name = 'sale.order'  # → 数据库表名: sale_order
    _description = 'Sales Order'
    # 默认字段自动添加:
    # id (主键)
    # create_uid (创建用户)
    # create_date (创建时间)
    # write_uid (最后修改用户)
    # write_date (最后修改时间)

字段 → 数据库列

字段类型 Python表示 数据库列类型
Char fields.Char() VARCHAR
Integer fields.Integer() INTEGER
Float fields.Float() NUMERIC/DOUBLE
Boolean fields.Boolean() BOOLEAN
Date fields.Date() DATE
Datetime fields.Datetime() TIMESTAMP
Text fields.Text() TEXT
Binary fields.Binary() BYTEA
Selection fields.Selection() VARCHAR
Many2one fields.Many2one() INTEGER(外键)
One2many fields.One2many() 无独立列(关联表)
Many2many fields.Many2many() 关联表

字段映射详解

基础字段映射

class ProductTemplate(models.Model):
    _name = 'product.template'
    name = fields.Char(string='Name', required=True)
    # → 列: name VARCHAR NOT NULL
    price = fields.Float(string='Price', digits=(10,2))
    # → 列: price NUMERIC(10,2)
    active = fields.Boolean(string='Active', default=True)
    # → 列: active BOOLEAN DEFAULT TRUE
    description = fields.Text(string='Description')
    # → 列: description TEXT

关系字段映射

Many2one(多对一)

class SaleOrderLine(models.Model):
    _name = 'sale.order.line'
    order_id = fields.Many2one('sale.order', string='Order', required=True)
    # → 列: order_id INTEGER REFERENCES sale_order(id)
    product_id = fields.Many2one('product.product', string='Product')
    # → 列: product_id INTEGER REFERENCES product_product(id)

One2many(一对多)

class SaleOrder(models.Model):
    _name = 'sale.order'
    order_line_ids = fields.One2many('sale.order.line', 'order_id', string='Order Lines')
    # → 无独立数据库列,通过关联字段'order_id'建立关系
    # 等效SQL: SELECT * FROM sale_order_line WHERE order_id = :current_id

Many2many(多对多)

class ProductTemplate(models.Model):
    _name = 'product.template'
    tag_ids = fields.Many2many('product.tag', string='Tags')
    # → 自动创建关联表: product_template_product_tag_rel
    #   列: product_template_id, product_tag_id
    # 等效SQL: 
    # SELECT pt.* FROM product_tag pt
    # JOIN product_template_product_tag_rel rel ON rel.product_tag_id = pt.id
    # WHERE rel.product_template_id = :current_id

字段属性映射

数据库约束

class Partner(models.Model):
    _name = 'res.partner'
    email = fields.Char(string='Email', unique=True)
    # → 列: email VARCHAR UNIQUE
    ref = fields.Char(string='Reference', index=True)
    # → 列: ref VARCHAR, 创建索引
    phone = fields.Char(string='Phone', required=True)
    # → 列: phone VARCHAR NOT NULL

默认值和计算字段

class Invoice(models.Model):
    _name = 'account.move'
    # 带默认值
    state = fields.Selection([
        ('draft', 'Draft'),
        ('posted', 'Posted'),
    ], default='draft')
    # → 列: state VARCHAR DEFAULT 'draft'
    # 计算字段(不存储)
    total_amount = fields.Float(compute='_compute_total', string='Total')
    # → 无数据库列
    # 存储的计算字段
    total_amount_stored = fields.Float(compute='_compute_total', store=True)
    # → 列: total_amount_stored NUMERIC

字段继承与重写

class ExtendedPartner(models.Model):
    _inherit = 'res.partner'
    # 重写已有字段
    name = fields.Char(string='Full Name', required=True)
    # → 修改字段属性,保留原有数据库列
    # 扩展字段
    business_type = fields.Selection(...)
    # → 新增列: business_type VARCHAR

高级映射特性

映射选项

class Model(models.Model):
    _name = 'example.model'
    field1 = fields.Char(
        string='Field1',
        size=100,           # VARCHAR(100)
        translate=True,     # 创建翻译表
        company_dependent=True,  # 公司级字段
        readonly=True,      # 只读
        help='帮助文本',     # 字段描述
        groups='base.group_user',  # 可见性组
    )

子类型字段

# Monetary字段(货币相关)
amount = fields.Monetary(string='Amount', currency_field='currency_id')
# → 列: amount NUMERIC, 配合货币字段使用
# HTML字段
content = fields.Html(string='Content')
# → 列: content TEXT
# Image字段
image = fields.Image(string='Image', max_width=1920, max_height=1080)
# → 列: image BYTEA
# Reference字段(动态多态)
reference = fields.Reference(selection='_referencable_models', string='Reference')
# → 列: reference VARCHAR(存储 "model,id")

性能相关的映射

class Model(models.Model):
    _name = 'example.model'
    # 索引和约束
    name = fields.Char(index=True)           # 创建普通索引
    code = fields.Char(index='btree')        # 指定索引类型
    ref = fields.Char(index=True, unique=True)  # 唯一索引
    # 分区字段(PostgreSQL特有)
    date = fields.Date(index=True)           # 日期字段索引
    # JSONB存储
    metadata = fields.Json(string='Metadata')
    # → 列: metadata JSONB

完整示例

from odoo import models, fields, api
class CustomOrder(models.Model):
    _name = 'custom.order'
    _description = 'Custom Order'
    _rec_name = 'display_name'
    _order = 'create_date desc'
    # 基础信息
    name = fields.Char(string='Order Reference', required=True, index=True, copy=False)
    customer_id = fields.Many2one('res.partner', string='Customer', required=True)
    order_date = fields.Date(string='Order Date', default=fields.Date.today)
    # 金额字段
    amount_total = fields.Monetary(string='Total', currency_field='currency_id', compute='_compute_total')
    currency_id = fields.Many2one('res.currency', string='Currency', default=lambda self: self.env.company.currency_id)
    # 状态字段
    state = fields.Selection([
        ('draft', 'Draft'),
        ('confirmed', 'Confirmed'),
        ('done', 'Done'),
        ('cancelled', 'Cancelled'),
    ], string='Status', default='draft', tracking=True)
    # 行明细
    line_ids = fields.One2many('custom.order.line', 'order_id', string='Order Lines')
    # 技术字段
    active = fields.Boolean(default=True)
    company_id = fields.Many2one('res.company', string='Company', default=lambda self: self.env.company)
    @api.depends('line_ids.subtotal')
    def _compute_total(self):
        for order in self:
            order.amount_total = sum(line.subtotal for line in order.line_ids)
class CustomOrderLine(models.Model):
    _name = 'custom.order.line'
    _description = 'Order Line'
    order_id = fields.Many2one('custom.order', string='Order', required=True, ondelete='cascade')
    product_id = fields.Many2one('product.product', string='Product', required=True)
    quantity = fields.Float(string='Quantity', default=1.0)
    price_unit = fields.Float(string='Unit Price', digits='Product Price')
    subtotal = fields.Float(string='Subtotal', compute='_compute_subtotal', store=True)
    @api.depends('quantity', 'price_unit')
    def _compute_subtotal(self):
        for line in self:
            line.subtotal = line.quantity * line.price_unit

数据库验证

-- 查看生成的表结构
\d custom_order
\d custom_order_line
-- 查看外键约束
SELECT 
    tc.constraint_name,
    tc.table_name,
    kcu.column_name,
    ccu.table_name AS foreign_table_name,
    ccu.column_name AS foreign_column_name
FROM 
    information_schema.table_constraints AS tc 
    JOIN information_schema.key_column_usage AS kcu
      ON tc.constraint_name = kcu.constraint_name
    JOIN information_schema.constraint_column_usage AS ccu
      ON ccu.constraint_name = tc.constraint_name
WHERE tc.table_name = 'custom_order_line' AND tc.constraint_type = 'FOREIGN KEY';

迁移注意事项

  1. 字段添加:Odoo自动处理ALTER TABLE添加列
  2. 字段删除:建议使用deprecated=True而非直接删除
  3. 类型更改:需要手动编写迁移脚本
  4. 约束更改:通过ORM的_sql_constraints处理

Odoo的ORM映射机制极大地简化了数据库操作,开发者只需关注Python类定义,框架会自动处理:

  • 表创建与字段映射
  • 外键关系维护
  • 索引与约束管理
  • 数据验证与类型转换

理解这些映射规则对于开发高效、健壮的Odoo模块至关重要。

抱歉,评论功能暂时关闭!