odoo实现跨库读写

odoo实现跨库读写

本文不是更换框架的数据源,只是通过代码的方式简单实现。本次实验使用MySQL数据库。


首先,你需要下载一个库: pymysql

pip install pymysql

接下来做个简单的分析,不想看的可以直接拉到最后,我提供了完整的代码,可以直接下载参考,本文使用的odoo13,其它版本举一反三。
你们可能不知道,odoo的 model有个布尔值属性 _auto,默认是 True,作用就是,安装model的时候默认在 postgresql 创建一张对应的表,我们不使用postgresql, 所以第一步就是将它设成 False
在这里插入图片描述
然后看看常规的模块是怎么加载数据的:

在这里插入图片描述
而通过浏览器调试可以看到,加载model时会调用 search_read(),所以,可以确定它需要重写, 而通过阅读odoo/models.py源码的search_read(),还发现了它还会调用 search():
在这里插入图片描述
那再来看看 search():
在这里插入图片描述
它返回的是什么?我尝试在返回之前加个 print(), 输出它将要返回的结果:
在这里插入图片描述
可以看到,其实 search()最终返回的是一个装着字典数据的列表。
那OK,回到read_search(),它调用了search()之后,最终返回的是什么?我们在返回之前加个print看看:
在这里插入图片描述
好吧,还是一个跟前面一样的数据集:
在这里插入图片描述
那么,相关的基本上就分析得差不多啦,还有,form视图的读取需要调用(要实现主题我们也需要重写) read()方法哦,这里就不再做详细分析了,坑我给你们填,直接上代码吧。
先看看我的模块结构:
在这里插入图片描述
先写一个公共的model public.py,目的是封装好能够通用(增、删、改)的代码:

# -*- coding: utf-8 -*-

from odoo import models, fields, api
from ..tools.connection import mysql_client as client


class Public(models.Model):
    _name = 'mysql.public'
    _description = '其它model需要继承此类'
    _auto = False

    @api.model
    def search_read(self, domain=None, fields=None, offset=0, limit=None, order=None):
        records = self.search(domain or [], offset=offset, limit=limit, order=order)
        if not records:
            return []
        return records

    @api.model
    def name_search(self, name='', args=None, operator='ilike', limit=100):
        return

    @api.model
    def create(self, values):
        key_list = []
        val_list = []
        for k, v in values.items():
            key_list.append(k)
            val_list.append(v)
        str1 = """
            INSERT INTO {} (
        """.format(self._tb_name) + (",{}" * len(values)).format(*key_list) + ") VALUES ("
        str2 = (",'{}'" * len(values)).format(*val_list) + ")"
        str1 = str1.replace(',', '', 1)
        str2 = str2.replace(',', '', 1)
        sql = str1 + str2
        client.insert(sql)
        return self

    def unlink(self):
        if len(self) > 1:
            tb_name = ''
            for item in self:
                tb_name = item._tb_name
                break
            for current_id in self.ids:
                client.delete_by_id(tb_name, current_id)
        else:
            client.delete_by_id(self._tb_name, self.id)
        return True

    def write(self, values):
        result_list = []
        for k, v in values.items():
            result_list.append(k)
            result_list.append(v)
        sql = """
            UPDATE {} SET
        """.format(self._tb_name) + ((",{}" + "=" + "'{}'") * len(values)).format(
            *result_list) + " WHERE id = {}".format(
            self.id)
        sql = sql.replace(',', '', 1)
        client.eitd_by_id(sql)
        return True

models.py , 已经重写了必要的方法,如下:

# -*- coding: utf-8 -*-

from odoo import models, fields, api
from .public import Public
from ..tools.connection import mysql_client as client


class MySQL(Public, models.Model):
    _name = 'mysql.user'
    _description = '用户'
    _auto = False  # 禁止自动创建psql数据库
    _tb_name = 'users'  # 此model对应MySQL的表名, 务必定义此属性

    name = fields.Char(string='昵称')
    age = fields.Integer(string='年龄')
    gender = fields.Selection([
        ('man', '男'),
        ('women', '女'),
    ], string='性别')

    # self._fields.keys()
    def read(self, fields=None, load='_classic_read'):
        result_set = []
        # 新建的时候没有id
        if not self.id:
            latest_id = client.get_new_id(self._tb_name)
            data = client.get_data_by_id(self._tb_name, latest_id)
        else:
            data = client.get_data_by_id(self._tb_name, self.id)
        result = {
            'id': data[0][0],
            'name': data[0][1],
            'age': data[0][2],
            'gender': data[0][3],
        }
        result_set.append(result)
        return result_set

    def search(self, args, offset=0, limit=None, order=None, count=False):
        data_set = client.get_all_by_page(self._tb_name, offset, limit)
        result_set = []
        for i in range(len(data_set)):
            data = {
                'id': data_set[i][0],
                'name': data_set[i][1],
                'age': data_set[i][2],
                'gender': data_set[i][3],
            }
            result_set.append(data)
        return result_set

然后是配置数据库的:

# -*- coding: utf-8 -*-

from odoo import fields, models


class ResConfigSettings(models.TransientModel):
    _inherit = 'res.config.settings'

    host = fields.Char(string='主机', config_parameter='host')
    user = fields.Char(string='用户', config_parameter='user')
    password = fields.Char(string='密码', config_parameter='password')

这里继承了基础的设置,因为不使用官方指定的库,要在页面上设置只能通过这个方法实现。
然后是设置页面的视图:

<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <record id="res_config_settings_view_form" model="ir.ui.view">
        <field name="name">res.config.settings.view.form.inherit.db_config</field>
        <field name="model">res.config.settings</field>
        <field name="priority" eval="103"/>
        <field name="inherit_id" ref="base.res_config_settings_view_form"/>
        <field name="arch" type="xml">
            <xpath expr="//div[@data-key='general_settings']" position="inside">
                <h2>MySQL数据库设置</h2>
                <div class="row mt16 o_settings_container">
                    <div class="col-12 col-lg-6 o_setting_box">
                        <div class="o_setting_left_pane" style="width:250px;">
                            主机:<field name="host"/>
                            用户:<field name="user"/>
                            密码:<field name="password"/>
                        </div>
                        <div class="o_setting_right_pane">
                            <div class="text-muted">
                                请填写相关信息
                            </div>
                        </div>
                    </div>
                </div>
            </xpath>
        </field>
    </record>
</odoo>

别忘了,在__manifest__.py里面 : ‘depends’: [‘base’, ‘base_setup’], 不然会报错哦,设置依赖 base_setup
接着是 connection.py , 获取连接对象以及操作数据库的对象的:

import pymysql
from odoo.http import request
from odoo.exceptions import ValidationError


class Connection(object):
    def __init__(self, config):
        self.connection = pymysql.connect(**config)
        self.cursor = self.connection.cursor()

    def __del__(self):
        self.connection.close()

    def get_all_by_page(self, tb_name, offset=0, limit=None):
        '''
        分页查询
        :param tb_name:
        :param offset:
        :param limit:
        :return:
        '''
        self.connection.ping(reconnect=True)
        sql = '''
                SELECT * FROM {} LIMIT {}, {}
            '''.format(tb_name, offset, limit)
        self.cursor.execute(sql)
        result = self.cursor.fetchall()
        return result

    def get_data_by_id(self, tb_name, id):
        """
        根据id查询记录
        :param tb_name:
        :param id:
        :return:
        """
        self.connection.ping(reconnect=True)
        sql = '''
            SELECT * FROM {} WHERE id = {}
        '''.format(tb_name, id)
        self.cursor.execute(sql)
        return self.cursor.fetchall()

    def insert(self, sql):
        '''
        插入数据
        :param sql:
        :return:
        '''
        try:
            self.connection.ping(reconnect=True)
            self.cursor.execute(sql)
            self.connection.commit()
        except ValidationError as e:
            self.connection.rollback()
            raise e

    def delete_by_id(self, tb_name, id):
        '''
        根据id删除
        :param tb_name:
        :param id:
        :return:
        '''
        try:
            self.connection.ping(reconnect=True)
            sql = '''
                DELETE FROM {} WHERE id = {}
            '''.format(tb_name, id)
            self.cursor.execute(sql)
            self.connection.commit()
        except ValidationError as e:
            self.connection.rollback()
            raise e

    def eitd_by_id(self, sql):
        '''
        根据ID修改
        :param sql:
        :return:
        '''
        try:
            self.connection.ping(reconnect=True)
            self.cursor.execute(sql)
            self.connection.commit()
        except ValidationError as e:
            self.connection.rollback()
            raise e

    def get_new_id(self, tb_name):
        '''
        返回最新的id
        :param tb_name:
        :return:
        '''
        self.connection.ping(reconnect=True)
        sql = """
                SELECT MAX(id) FROM {}
            """.format(tb_name)
        self.cursor.execute(sql)
        data = self.cursor.fetchall()
        return data[0][0]


# 获取配置信息
host = request.env['ir.config_parameter'].sudo().get_param('host')
user = request.env['ir.config_parameter'].sudo().get_param('user')
password = request.env['ir.config_parameter'].sudo().get_param('password')

# 默认连接信息
DEFAULT_CONFIG = {
    "host": host if host else 'localhost',
    "user": user if user else 'root',
    "password": password if password else 'admin',
    "db": 'demo',
    "charset": "utf8",
    "ssl": {'ssl': {}}
}

mysql_client = Connection(DEFAULT_CONFIG)


def flush_connection():
    global mysql_client
    mysql_client = Connection(DEFAULT_CONFIG)


model视图:

<odoo>
    <data>
        <record model="ir.ui.view" id="mysql_users_view_tree">
            <field name="name">mysql.user.tree</field>
            <field name="model">mysql.user</field>
            <field name="arch" type="xml">
                <tree>
                    <field name="name"/>
                    <field name="age"/>
                    <field name="gender"/>
                </tree>
            </field>
        </record>

        <record model="ir.ui.view" id="mysql_users_view_form">
            <field name="name">mysql.user.form</field>
            <field name="model">mysql.user</field>
            <field name="arch" type="xml">
                <form>
                    <sheet>
                        <group>
                            <field name="name"/>
                            <field name="age"/>
                            <field name="gender"/>
                        </group>
                    </sheet>
                </form>
            </field>
        </record>

        <record model="ir.actions.act_window" id="mysql_users_action_window">
            <field name="name">员工</field>
            <field name="res_model">mysql.user</field>
            <field name="view_mode">tree,form,kanban</field>
        </record>

        <!-- Top menu item -->
        <menuitem name="Mysql" id="mysql_l1_menu_root" web_icon="pdc_mysql,static/description/icon.png"/>
        <menuitem name="用户" id="mysql_l2_menu_users" parent="mysql_l1_menu_root"
                  action="mysql_users_action_window"/>
    </data>
</odoo>

安全配置:

id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink

access_mysql_user,mysql.user,model_mysql_user,,1,1,1,1

然后在我们熟悉的MySQL数据库建个简单的表:

create database demo;
use demo;
create table users(id int(5) primary key not null auto_increment,name varchar(20), age int, gender varchar(10));

完事,可以去安装了。注意的是,前面我在models.py里面写了默认的数据库配置信息,请先按照默认的配置好数据库再去安装。安装好之后在浏览器上web之后加上参数 ?debug=1打开开发者模式,点击设置,可以看到这里,在这里设置数据库信息后点击保存:
在这里插入图片描述

至此,已经实现跨库CURD啦。
在这里插入图片描述
代码没有做过多、更优雅的封装,主要是实现的过程。另外,这么做有个很明显的缺点,不够通用。当然如果各位有时间的话可以尝试去做个更加通用的。
完整代码:
网盘链接
提取码:ttka
有不对的地方或者疑问小伙伴们及时提出哈。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值