DTCloud里面的模型

与Python的其他主流框架类似, dtcloud的模型(model)也是数据持久化的主要对象. dtcloud中的模型主要分为两种, TrainsientModel和Model.
dtcloud中的三种model类型

  • TransientModel
  • AbstractModel
  • Model

TransientModel
TransientModel是指一种临时对象,它的数据将被系统定期清理,因此这就决定了它的使用场景不可以作为数据的持久化使用,只能作为临时对象使用。在dtcloud中,TransientModel最常被使用的一种场景是作为向导。
向导是dtcloud中常见的一种操作引导方式,该方式通常由一个弹出式的窗体和若干字段、按钮组成。用户通过向导可以选择指定特定的字段值,然后进行下一步的操作。向导背后的技术即用到了TransientModel,作为一种临时性的数据存储方案,向导的数据不会长期留存在数据库中,会由系统定期进行清理。
Model
与TransientModel相对的就是数据的持久化存储方案,即Model对象,dtcloud中的绝大多数对象都是由Model继承而来的。
Model是继承基础模型(BaseModel)而来, 基础模型是dtcloud所有类的基类。实例化类对象的方式即继承自Model、TransientModel或AbstractModel中的一种。每个类的实例都是一个有序的记录集合(RecordSet)。如果希望创建一个不被实例化的类,可以把_register属性设置为False。
默认情况下,Model和TransientModel的子类在初始化的过程中都会自动创建数据库表,如果不希望自动创建数据库表,可以将类的_auto属性设置为False。但创建没有数据库表的模型的更推荐的方式是使用抽象类(AbstractModel)。
AbstractModel
AbstractModel不创建数据库表.
BaseModel
BaseModel是MetaModel的子类,是AbstractModel、TransientModel和Model的父类。

class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
    ...

系统会自动初始化一个每个数据库中的已经注册的模型的实例,这些实例代表了每个数据库中可以使用的模型。每个实例都是一个有序的记录集,可以通过browse或者search等方法返回包含符合条件的记录的记录集。
如果不希望系统在初始化的时候实例化某个模型,那么可以将它的_register属性设置为False.
基础属性
BaseModel包含了一些技术性的参数:

  • _auto: 是否自动创建数据库中的数据表,默认False(Mdoel中为True)
  • _regitster: 是否注册,不注册将在ORM中不可用
  • _abstract: 是否是抽象模型
  • _transient:是否是临时模型
  • _table: 指定数据库中的表名
  • _sequence: Id字段使用的SQL中的序号
  • _sql_constraints:数据库中的限制条件
  • _date_name: 日历视图中使用的字段,默认date
  • _fold_name: 看板视图中使用的字段,默认fold
  • _rec_name: _rec_name用于指定显示在Many2one类型的搜索中的显示字段,可以简单地理解为该模型的名称。默认情况下,_rec_name取的是name字段。
    _auto指定了该模型是否自动创建数据库表,如果设置False,那么用户需要在init方法中自行创建数据库表。
    BaseModel中定义了4个魔法字段,即出现在每个数据库表中的字段:
  • create_uid:该条记录的创建人
  • create_date: 该条记录的创建时间
  • update_uid: 该条记录的更新人
  • update_date: 该条记录的更新时间
  • display_name: 显示名称

魔法字段在15.0中的定义已经移动到了MetaModel中

dtcloud中的每个注册完成的模型都会将自己的信息添加到系统记录中,具体来说:

  • ir.model: 会将自己的反射数据插入到ir_model表中
  • ir.model.fields:会将字段数据插入到ir_model_fields表中
  • ir.model.fields.selection: 将字段的可选项插入到ir_model_fields_selection表中(此特性在13.0引入)
  • ir.model.constraint: 将模型的约束插入到ir_modeLconstraint表中。
    模型的继承
    正如第一部分第四章介绍的,模型的继承是通过属性_inherit来实现的。
parents = cls._inherit
parents = [parents] if isinstance(parents, str) else (parents or [])

# determine the model's name
name = cls._name or (len(parents) == 1 and parents[0]) or cls.__name__

从源码中可以看出_inherit希望传入的是一个模型的列表,假如只有一个父类模型,那么也可以传入文本,且指定了_inherit时可以不声明_name,系统会自己使用模型的父类的_name。
常用方法
BaseModel中定义了一些常用的方法:
user_has_groups
判断用户是否在某个用户组中。

@api.model
def user_has_groups(self, groups):
    ....

groups是用户组的xmlid列表,用,分隔,例如:user.user_has_groups(‘`base.group_user,base.group_system’)
with_context
with_context方法用来给当前记录集对象扩展新的上下文对象。

# 当前context对象内容{'key1':True}
r2 = records.with_context({},key2=True)
# r2._context 内容:{'key2':True}
r2 = records.with_context(key2=True)
# r2._context内容:{'key1':True,'key2':True}

如果当前上下文中对象_context中包含allowed_company_ids 那么,with_context方法在返回的结果中也会包含allowed_company_ids。
flush方法
BaseModel中还定义了一个flush方法,其作用是将所有挂起的计算更新到数据库中。

@api.model
def flush(self, fnames=None, records=None):
    """ Process all the pending computations (on all models), and flush all
    the pending updates to the database.

    :param fnames (list<str>): list of field names to flush.  If given,
        limit the processing to the given fields of the current model.
    :param records (Model): if given (together with ``fnames``), limit the
    ....

flush方法接收两个参数:

  • fnames: 需要被更新的字段名称列表
  • records: 需要被更新的记录集
    如果没有传入任何参数,将更新所有挂起的计算。如果传入了fnames,将更新当前模型中需要被更新的字段列表。如果传入了records,将更新指定的记录集的值。
    new方法
    产生一个使用传入的值并附加在当前环境的新记录值。此值只存在内存中,尚未保存到数据库中。
@api.model
def new(self, values={}, origin=None, ref=None):
    """ new([values], [origin], [ref]) -> record

    Return a new record instance attached to the current environment and
    initialized with the provided ``value``. The record is *not* created
    in database, it only exists in memory.

    One can pass an ``origin`` record, which is the actual record behind the
    result. It is retrieved as ``record._origin``. Two new records with the
    same origin record are considered equal.

    One can also pass a ``ref`` value to identify the record among other new
    records. The reference is encapsulated in the ``id`` of the record.
    """
    if origin is not None:
        origin = origin.id
    record = self.browse([NewId(origin, ref)])
    record._update_cache(values, validate=False)

    return record

从函数定义中,我们可以看到new方法返回一个记录值,该记录值的ID是我们前面提到过的NewId对象。
recompute
重新计算所有或指定的字段值。

@api.model
def recompute(self, fnames=None, records=None):
    """ Recompute all function fields (or the given ``fnames`` if present).
        The fields and records to recompute have been determined by method
        :meth:`modified`.
    """
    ...

recompute接收两个参数:

  • fnames: 需要重新计算值的字段名列表
  • records: 需要重新计算值的记录集
    如果不传fnames,则所有需要重新计算的字段都将重新计算。
    BaseModel的重载
    在第一部分,我们知道了模型可以被继承,方法可以被重载,这都是对常规的类来说的,如果我们有种需求要对基类的某些方法进行重载,该怎么做呢?可能有同学想到了,直接在源码的修改,但这是我们极为不推荐的一种方式。在比较早的版本,实现这个需求比较绕,要用到_register_hook方法,所幸的是从10.0开始,官方给我们提供了一种修改BaseModel的途径。
from dtcloud import api, fields, models, _

class BaseModel(models.AbstractModel):

    _inherit = "base"

    ...

我们只需要新建一个抽象类,然后继承自"base"模块,就可以对BaseModel中的方法进行重载了。
中亿丰——何双新

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

中亿丰数字科技集团有限公司

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值