• Odoo开发之记录集 – 使用模型数据


    Odoo 12开发之记录集 – 使用模型数据

    一·启动服务链接数据库

    # 1.启动服务,链接数据库
     	./odoo-bin shell -c debian/odoo.conf  -d library_db
        
    # 2. self   当前操作的对象
    	res.users(1,)
        
    # 3. self._name  # 当前模块名称 ,获得记录集模型名
    	'res.users'
        
    # 4. self.login  # 记录值,至于怎么用.未知
    	'__system__'
        
    # 5. self.name     #  name 值为OdooBot
    	OdooBot
    

    二·执行环境

    环境属性

    # 1. self.env , 获得当前环境
    	<odoo.api.Environment object at 0x7f78a26026a0>
        
    # 2. self.env中的执行环境属性
    	# env.cr  正在使用的数据库游标(cursor)
        	<odoo.sql_db.Cursor object at 0x7f9df38efc50>
        # env.user 是当前用户的记录
        	res.users(1,)
        # env.uid 是会话用户的id
        	1 
        # env.context 是会话上下文的不可变字典    
        	{'lang': 'en_US', 'tz': 'Europe/Brussels'}
            # 由于是 frozendict 由odoo 自行封装的字典,不可变
               env.context['test']='test'
               错误: NotImplementedError: '__setitem__' not supported on frozendic
                    
         # search([('name','like','Ad')]) 搜索查询,search和domain表达式结合使用
        	self.env['res.partner'].search([('name','like','Ad')])
         
         # browse() 也是搜索查询,browse放id列表如[1,2,3,4]
            self.env['res.partner'].browse([1,2,3,4])      
    

    环境上下文

    ### 环境上下是带有会话数据的字典. 
    ### 作用:用于客户端和服务端ORM和业务逻辑中. 可以将信息从一个视图中传递到另一个视图中.
    	# 比如:上一个视图中活跃的id,通过点击链接或者按钮.将默认值带入到下一个视图中.
        # 形式:	
    		{'lang': 'en_US', 'tz': 'Europe/Brussels', 'uid': 2}
    
    # 1. 查看上下文命令
    	self.context_get()
    	self.env.context
    	# lang :用户语言 tz:时区信息 uid:当前用户的id
    
    

    修改记录集执行环境

    ### 记录集执行环境是不可变的.不可被修改,因此需要创建一个新的环境来修改
    	# 1. env.with_context({diciionary}) # 替换原上下文为新的上下文
        # 2. env.with_context(key=value,...) # 修改当前上下文,为一些键设置值
        # 3. env.sudo(user) # 传入一条用户记录并返回该用户的环境.如果未传用户,则使用	__system__超级用户. 可绕过安全规则执行指定操作
        
    # env.ref()函数 , 传入一个外部标识符字符串并返回它的记录
    	self.env.ref('base.user_root') 
        res.users(1,)
        
    

    三· 记录集和作用域domain查询数据

    创建记录

    ### search方法 接收一个域表达式并返回符合条件的记录集. 空域[]将返回所有的记录
    
        # 关键字参数:
            # order是一个数据库查询语句种的 order by使用的字符串.通常是一个逗号分隔的字段名列表。每个字段都可接DESC关键字,用于表示倒序排列。
            # limit 设置获取记录的最大条数
            # offset 忽略钱n条记录,配合limit使用
        # 用法:
        	self.env['res.partner'].search([('name', 'like', 'Pac')])
            # 结果
            res.partner(42, 62)
     	   
        
    ### search_count()方法 返回记录条数. 先知道条数,节约内存
    	# 用法
        	self.env['res.partner'].search_count([])
            #结果
            681
    ### browse()方法接收一个ID列表或单个ID并返回这些记录的记录集
    	# 用法
        	self.env['res.partner'].browse([42, 62])
            # 结果
            res.partner(42, 62)
    

    域表达式

    ### domain用于过滤数据记录. 使用的是特殊语法来供Odoo ORM解析	
    	# 写法:
    		(‘字段名’, ‘运算符’, ‘值’) 组成的元组
        # 字段名:是一个有待过滤的字段,可用点号标记,来表示关联模型种的字段
        # 值: 分为两种运行上下文: 
        	 ① 窗口操作或字段属性等客户端,使用原生字段值,不能使用点标记符
             ② 服务端可以是一个对象
        # 运算符
        	# 比较: <,>,<=,>=,=,!=
            # =like 和 =ilike 
            # like匹配 '%value%'模式,ilike与其相似.
            # not like 和 not ilike
            # child of 支持层级关联的模型中 查找层级关系种的子级值
            # in 和 not in 在和不在
            
    	 # 多条件,即包含多个元祖  默认是 AND 运算符
        	# 例子: 此例子为编造
            self.env['res.partner'].search([('message_follower_ids', 'in', [user.partner_id.id]),('user_id', '=', user.id)])
         
         # 显式逻辑运算符 ‘&‘符号表示 AND 运算符(默认值),管道运算符’|‘表示OR运算符
        	# 例子:
                ['|',('message_follower_ids', 'in', [user.partner_id.id]),'|',('user_id', '=', user.id),('user_id', '=', False)]
     
    

    四·记录集中访问数据

    访问记录中数据

    # 1.记录集仅有一条记录时,称为单例
    	self.name  --> OdooBot
    ### 注意:
    	尝试访问有多条记录的记录集字段值会产生错误,所以在不确定操作的是否为单例数据集时就会产生问题。对于设计仅操作单例的方法,
        可在开头处使用 self.ensure_one(),如果 self 不是单例时将抛出错误。
    

    访问关联字段

    ### 包含:many-to-one, one-to-many和many-to-many
        # 对于many-to-one,其值可以是单例或空记录集
            self.company_id ---> res.company(1,)
            self.company_id.name  ---> 'YourCompany'
            self.company_id.currency_id   ---> res.currency(1,)
            self.company_id.currency_id.name   ---> 'EUR'
    
        # 空记录可像单例一样操作,访问其字段值不会返回错误而是返回 False 
        	self.company_id.parent_id --->  res.company()
            self.company_id.parent_id.name ---> False
    

    访问时间和日期值

    # 1.查询上次admin用户登录日期
    	self.browse(2).login_date ---> datetime.datetime(2019, 1, 8, 9, 2, 54, 45546)
    
    #  2. 可以利用python的datetime模块进行时间计算
    
    
    Odoo 还在odoo.tools.date_utils模块中提供额外的便利函数
        # start_of(value, granularity)是某个特定刻度时间区间的开始时间,这些刻度有year, quarter, month, week, day或hour
        #  end_of(value, granularity)是某个特定刻度时间区间的结束时间
    	#  add(value, **kwargs)为指定值加上一个时间间隔。**kwargs参数由一个relativedelta对象来定义时间间隔。这些参数可以是years, months, weeks, days, hours, minutes等等
        #  subtract(value, **kwargs)为指定值减去一个时间间隔 
        
    from odoo.tools import date_utils
    from datetime import datetime
    
    # 范围时间当前时间和指定时间参数:week,本周周一那天.如今天是2020年5月27,一周前是2020年5月25	
    # 输入: date_utils.start_of(datetime.now(), 'week')
    	    datetime.datetime(2020, 5, 25, 0, 0)
        
    # end_of 同理 末尾时间
    # 输入:date_utils.end_of(datetime.now(), 'week')
    	  datetime.datetime(2020, 5, 31, 23, 59, 59, 999999)
        
    # add 指定添加多长时间
    # 输入: date_utils.add(datetime.today(), months=2)
    		datetime.datetime(2020, 7, 27, 2, 40, 55, 424214)
        
    # subtract 指定减去多长时间
    # 输入: date_utils.subtract(datetime.today(), months=2)
    		datetime.datetime(2020, 3, 27, 2, 42, 21, 390561)
    
    odoo.fields.Date和the odoo.fields.Datetime
    # 1. fields.Date.today()   # 返回当前日期,类型是datetime
    	 datetime.date(2020, 5, 27)
    # 2. fields.Datetime.now() # 返回当前日期+时间,类型是datetime
    	 datetime.datetime(2020, 5, 27, 2, 46, 10)
    # 3. fields.Date.context_today(record, timestamp=None)
       fields.Date.context_today(self, timestamp=None)
         datetime.date(2020, 5, 27)
    
    	 # 在会话上下文中返回带有当前日期的字符串。时间从记录上下文中获取。可选项timestamp参数是一个datetime对象,如果传入将不使用当前时间,而使用传入值
     
    # 4. fields.Datetime.context_timestamp(record, timestamp)
    	 # 将原生的datetime值(无时区)转换为具体时区的datetime。时区从记录上下文中提取,因此使了前述函数名。
    

    转换文本形式的日期和时间

    # fields.Date和fields.Datetime都提供了如下函数
    # 1. to_date将字符串转换为date对象
    # 2. to_datetime(value)将字符串转换为datetime对象
    # 3. to_string(value)将 date和datetime对象转换成字符串格式
    
    # 4. Odoo 预设文本格式默认值:
        odoo.tools.DEFAULT_SERVER_DATE_FORMAT ---> %Y-%m-%d
        odoo.tools.DEFAULT_SERVER_DATETIME_FORMAT ---> Y-%m-%d %H:%M:%S
                
    from odoo import fields
    # 输入: fields.Datetime.to_datetime('2019-01-12 13:48:50')
    		datetime.datetime(2019, 1, 12, 13, 48, 50)  
    # 注意:
    	其他格式时间字符串转换成日期类型需要使用 datetime.strptime单独转换
        from datetime import datetime
    	# 输入: datetime.strptime('1/1/2019', '%d/%m/%Y')
    	datetime.datetime(2019, 1, 1, 0, 0)
    
    

    五·记录中写入

    # odoo写入分为两种模式:
    	# 1. 使用对象形式直接分配 . 简单但一次只能操作一条记录,效率较低
    	# 2. 使用write() 方法 .  写入关联字段时使用特殊语法,但每条命令可写入多个字段和记录,记录计算更为高效  
    

    对象形式分配值写入

    # 查询一条数据
    	root = self.env['res.users'].browse(1)
    # 更改root对象name值
    	root.name = 'Superuser'
    # root.name
    	Superuser
     
    ### 
    

    通过 write()方法写入

    # 用write()方法来同时更新多条记录中的多个字段,仅需一条数据库命令
    # write() 接收一个字典来进行字段和值的映射
    
    # 得到一个空对象
    	Partner = self.env['res.partner']
    # 查询值
    	recs = Partner.search( [('name', 'ilike', 'Azure')] )
    # 修改值, 修改成功为True
    	recs.write({'comment': 'Hello!'})
    
        
    ### 在写入many-to-one字段时,写入的值必须是关联记录的ID。
    	例如,我们不用self.write({‘user_id’: self.env.user}),而应使用self.write({‘user_id’: self.env.user.id})   
        
    ### 写入to-many字段时,写入的值必须使用和 XML 数据文件相同的特殊语法
    	比如,我们设置图书作者列表为author1和author2,这是两条 Partner 记录。| 管道运算符可拼接记录来创建一个记录集,因此使用对象形式的分配可以这么写
        publisher.child_ids = author1 | author2
        # 使用write()方法
        book.write( { 'child_ids': [(6, 0, [author1.id, author2.id])] } )
        ### (4, id, _)添加一条记录
        ### (6, _, [ids])替换关联记录列表为所传入的列表
    

    写入日期和时间值

    ### 可以使用文本形式值写入日期和时间:
    # 如:
    	# 搜索到一个对象
    	demo = self.search([('login', '=', 'demo')])
        # 更改demo.login_date值
    	demo.login_date = '2019-01-01 09:00:00'
        # 查看
        demo.login_date ---> datetime.datetime(2019, 1, 1, 9, 0)
    

    创建和删除记录

    #创建和删除记录 通过 create()和unlink()模型方法实现
    
    ### create方法
        # 得到一个空的对象 ,这个对象仅被加载到了内存种,没有实际保存到数据库种
            Partner = self.env['res.partner']
        # 创建一个对象,此时就被保存到数据库种
            new = Partner.create({'name': 'ACME', 'is_company': True})
            res.partner(64,)
        # 查看cutomer属性值
            print(new.customer) # customer标记默认为 True
    ### unlink()方法会删除记录
    	# 搜索到某条记录
        rec = Partner.search([('name', '=', 'ACME')])
        rec.unlink() # 删除 partner 关联字段的串联删除
    

    拷贝模型记录值

    ### copy() 方法,会自动创建一条记录.这条记录会被保存到数据库中
    
    # 查询出一条记录 
    	demo = self.env.ref('base.user_demo')
    # 拷贝
    	new = demo.copy({'name': 'Daniel', 'login': 'daniel', 'email': ''})
    

    六·重构记录集

    # 1. recordset.ids 返回记录集元素的ID列表
    # 2. recordset.ensure_one()检查是否为单条记录(单例);若不是,则抛出ValueError异常
    # 3. recordset.filtered(func)返回一个过滤了的记录集,func可以是一个函数或一个点号分隔的表达式来表示字段路径,可参见下面的示例。
    # 4. recordset.mapped(func)返回一个映射值列表。除函数外,还可使用文本字符串作为映射的字段名。
    # 5. recordset.sorted(func)返回一个排好序的记录值。除函数外,文本字符串可用作排序的字段名。reverse=True是其可选参数。
    
    >>> rs0 = self.env['res.partner'].search([])
    >>> len(rs0)
    48
    
    >>> starts_A = lambda r: r.name.startswith('A')
    >>> rs1 = rs0.filtered(starts_A)
    >>> print(rs1)
    res.partner(63, 59, 14, 35)
    
    >>> rs1.sorted(key=lambda r: r.id, reverse=True)
    res.partner(63, 59, 35, 14)
    
    >>> rs2 = rs1.filtered('is_company')
    >>> print(rs2)
    res.partner(14,)
    
    >>> rs2.mapped('name')
    ['Azure Interior']
    
    >>> rs2.mapped(lambda r: (r.id, r.name))
    [(14, 'Azure Interior')]
    
    rs1 | rs2是一个集合的并运算,会生成一个包含两个记录集所有元素的记录集
    rs1 + rs2是集合加法运算,会将两个记录集拼接为一个记录集,这可能会带来集合中有重复记录
    rs1 & rs2是集合的交集运算,会生成一个仅在两个记录集中同时出现元素组成的数据集
    rs1 – rs2是集合的差集运算,会生成在rs1中有但rs2中没有的元素组成的数据集
    
    # 还可以使用分片标记,例如:
        rs[0]和rs[-1]分别返回第一个和最后一个元素
        rs[1:]返回除第一元素外的记录集拷贝。其结果和rs – rs[0]相同,但保留了排序
        
    # 删除或添加元素来修改记录集
    	self.author_ids |= author1:如果不存在author1,它会将author1加入记录集
    	self.author_ids -= author1:如果author1存在于记录集中,会进行删除
    	self.author_ids = self.author_ids[:-1]删除最后一条记录
    

    七·底层 SQL 和数据库事务

    # 数据库引入运算在一个数据库事务上下文中执行
    # 通过数据库游标self.env.cr
    
    # 执行事务缓冲的写运算 ,提交数据
    
    	self.env.cr.commit()
        
    # 取消上次 commit之后的写运算,如果尚未 commit,则回滚所有操作
    	self.env.cr.rollback()
        
    # execute() 方法 执行SQL语法 self.env.cr.execute()
    	参数一: 运行的SQL 语句,
        可选参数:一个用作 SQL 参数值的元组或列表。这些值会用在%s占位符之处。
        
        # 用法: 
    	self.env.cr.execute("SELECT id, login FROM res_users WHERE login=%s OR id=%s", ('demo',1)
        self.env.cr.execute("SELECT id, login FROM res_users WHERE login=%s OR id=%s", ('demo',1))
    
    ### select 查询记录
    	# fetchall() 函数以元组列表的形式获取所有行
        	# 输入:self.env.cr.fetchall()
            [(1, '__system__'), (6, 'demo')]
        
        # dictfetchall()则以字典列表的形式获取
        	# 输入: self.env.cr.dictfetchall()
            [{'id': 1, 'login': '__system__'}, {'id': 6, 'login': 'demo'}]
    
    ### 修改DML操纵语言
     self.env.cache.invalidate()
    
  • 相关阅读:
    链接被点击时不出现虚线框的方法
    label 和 legend标签的用法
    了解常见的浏览器内核 Trident,Geckos,Presto,Webkit
    DOCTYPE声明的几种类型
    Angularjs基础教程
    理解angular中的module和injector,即依赖注入
    es5 api
    css3_note
    canvas 基础知识
    uglifyjs note
  • 原文地址:https://www.cnblogs.com/dengz/p/12971517.html
Copyright © 2020-2023  润新知