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()方法哦,这里就不再做详细分析了,坑我给你们填,直接上代码吧。
先看看我的模块结构:
在这里插入图片描述
models.py , 已经重写完需要重写的方法,如下:

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

from odoo import models, fields, api
from .connection import Connection as connection
from ..tools import mysql_tools

CLIENT, CONN, CURSOR = mysql_tools.MySQLClient(), None, None


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

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

    def init(self):
        global CLIENT, CONN, CURSOR
        CLIENT, CONN, CURSOR = connection.get_client_and_cursor(self._db_name)

    # form视图加载主要是这个方法
    def read(self, fields=None, load='_classic_read'):
        result_record = []
        # 新建的时候没有id
        if not self.id:
            latest_id = CLIENT.get_new_id(CONN, CURSOR, self._tb_name)
            data = CLIENT.get_data_by_id(CURSOR, self._tb_name, latest_id)
        else:
            data = CLIENT.get_data_by_id(CURSOR, self._tb_name, self.id)
        result = {
            'id': data[0][0],
            'name': data[0][1],
            'age': data[0][2],
            'gender': data[0][3],
        }
        result_record.append(result)
        return result_record

    def search(self, args, offset=0, limit=None, order=None, count=False):
        datas = CLIENT.get_all_record(CURSOR, self._tb_name)
        mysql_objs = []
        for i in range(len(datas)):
            data = {
                'id': datas[i][0],
                'name': datas[i][1],
                'age': datas[i][2],
                'gender': datas[i][3],
            }
            mysql_objs.append(data)
        print(mysql_objs)
        return mysql_objs

    # tree视图加载时主要是这个,form也会用到
    @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 create(self, values):
        sql = """
            INSERT INTO {} (name,age,gender) VALUES('{}',{},'{}')
        """.format(self._tb_name, values['name'], values['age'], values['gender'])
        CLIENT.insert(CONN, CURSOR, sql)
        return self

    def unlink(self):
        CLIENT.delete_by_id(CONN, CURSOR, 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)  # 删除第一个逗号,拼成合法的SQL语句
        print(sql)
        CLIENT.eitd_by_id(CONN, CURSOR, sql)
        return True

然后是配置数据库的:

# -*- 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 , 获取连接的:

from odoo import exceptions
from ..tools.mysql_tools import MySQLClient
from odoo.http import request


class Connection(object):
    @staticmethod
    def get_db_msg(db_name):
        '''
        返回连接信息
        :param db_name:
        :return:
        '''
        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')
        if not host or not user or not password:
            raise exceptions.UserError('请配置正确的数据库连接信息!')
        db_name = db_name
        return host, user, password, db_name

    @staticmethod
    def get_client_and_cursor(db_name):
        '''
        获取数据库客户端对象、光标对象
        :return:
        '''
        host, user, password, db_name = Connection().get_db_msg(db_name)
        client = MySQLClient()
        conn, cursor = client.connect_and_cursor(host, user, password, db_name)
        return client, conn, cursor

tools包下的 mysql_tools.py

import pymysql


class MySQLClient(object):
    def connect_and_cursor(self, host, user, password, database, charset='utf8'):
        '''
        获取本类对象以及光标对象(可执行一系列的SQL操作对象)
        :param host:
        :param user:
        :param password:
        :param database:
        :param charset:
        :return:
        '''
        conn = pymysql.connect(
            host=host,
            user=user,
            password=password,
            database=database,
            charset=charset
        )
        cursor = conn.cursor()
        return conn, cursor

    def get_all_record(self, cursor, tb_name):
        """
            获取全部记录
            :param db_name: 查询的数据库名
            :return: 对应数据库的全部记录
            """
        sql = '''
                SELECT * FROM {}
            '''.format(tb_name)
        if type(cursor) == 'NoneType':
            return
        cursor.execute(sql)
        result = cursor.fetchall()
        return result

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

    def insert(self, conn, cursor, sql):
        '''
        插入数据
        :param client:  连接对象
        :param cursor:  光标对象
        :param sql: SQL语句
        :return:
        '''
        cursor.execute(sql)
        conn.commit()

    def delete_by_id(self, conn, cursor, tb_name, id):
        '''
        根据id删除
        :param client:
        :param cursor:
        :param tb_name:
        :param id:
        :return:
        '''
        sql = '''
            DELETE FROM {} WHERE id = {}
        '''.format(tb_name, id)
        cursor.execute(sql)
        conn.commit()

    def eitd_by_id(self, conn, cursor, sql):
        '''
        根据ID修改
        :param client:
        :param cursor:
        :param tb_name:
        :param id:
        :return:
        '''
        cursor.execute(sql)
        conn.commit()

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

model视图:

<odoo>
    <data>
        <record model="ir.ui.view" id="mysql_employees_view_tree">
            <field name="name">mysql.operation.tree</field>
            <field name="model">mysql.operation</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_employees_view_form">
            <field name="name">mysql.operation.form</field>
            <field name="model">mysql.operation</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_employees_action_window">
            <field name="name">员工</field>
            <field name="res_model">mysql.operation</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_employees" parent="mysql_l1_menu_root"
                  action="mysql_employees_action_window"/>
    </data>
</odoo>

安全配置:

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

access_mysql_operation,mysql.operation,model_mysql_operation,,1,1,1,1

安装好之后在浏览器上web之后加上参数 ?debug=1打开开发者模式,点击设置,可以看到这里,在这里设置数据库信息后点击保存:
在这里插入图片描述

扫描二维码关注公众号,回复: 11462571 查看本文章

至此,已经实现跨库CURD啦。
在这里插入图片描述
代码没有做过多、更优雅的封装,主要是实现的过程。
完整代码:
网盘链接
提取码:68e1
有不对的地方或者疑问小伙伴们及时提出哈。

猜你喜欢

转载自blog.csdn.net/weixin_43274097/article/details/107639089