OpenERP|odoo Web开发

在OpenERP 7 和 Odoo 8下测试均可。

  1.相关库/框架
  主要:jQuery(使用1.8.3,如果使用新版本,其他jQuery插件也要升级或修改)、Underscore、Qweb
  其他:都在addons\web\static\lib路径下。

  2.示例框架
  下载(需要先安装bzr):bzr branch lp:~niv-openerp/+junk/oepetstore -r 1
  下载后将路径加到OpenERP服务器的addons_path参数中,重启服务器、更新模块列表再安装。
  在__openerp__.py中通过:
[python]

  1. ‘js‘: [‘static/src/js/*.js‘],
  2. ‘css‘: [‘static/src/css/*.css‘],
  3. ‘qweb‘: [‘static/src/xml/*.xml‘],

  将所有js/css/xml(QWeb模板)文件包含进来。
  oepetstore/static/js/petstore.js注释说明:
[javascript]

  1. openerp.oepetstore = function(instance) { // OpenERP模型,必须和模块名称相同。instance参数是OpenERP Web Client自动加载模块时传入的实例。
  2. var _t = instance.web._t,
  3. _lt = instance.web._lt; // 翻译函数
  4. var QWeb = instance.web.qweb; // QWeb实例
  5. instance.oepetstore = {}; // instance实例里面的模块命名空间(namespace),比如和模块名称相同。
  6. instance.oepetstore.HomePage = instance.web.Widget.extend({ // 自定义首页部件
  7. start: function() { // 部件创建时自动调用的方法
  8. console.log("pet store home page loaded");
  9. },
  10. });
  11. instance.web.client_actions.add(‘petstore.homepage‘, ‘instance.oepetstore.HomePage‘);
  12. // 将自定义首页部件与菜单动作绑定
  13. }

  可以在网址后面加“?debug”参数使脚本不压缩以便于调试,例如:
[javascript]

  1. http://localhost:8069/?debug

  3.类的定义
  从instance.web.Class基类扩展:
[javascript]

  1. instance.oepetstore.MyClass = instance.web.Class.extend({
  2. say_hello: function() {
  3. console.log("hello");
  4. },
  5. });
  6. var my_object = new instance.oepetstore.MyClass();
  7. my_object.say_hello();

  构造函数名为init();使用this访问对象实例的属性或方法:
[javascript]

  1. instance.oepetstore.MyClass = instance.web.Class.extend({
  2. init: function(name) {
  3. this.name = name;
  4. },
  5. say_hello: function() {
  6. console.log("hello", this.name);
  7. },
  8. });
  9. var my_object = new instance.oepetstore.MyClass("Nicolas");
  10. my_object.say_hello();

  类可以通过extend()方法继承;使用this._super()调用基类被覆盖的方法。
[javascript]

  1. instance.oepetstore.MySpanishClass = instance.oepetstore.MyClass.extend({
  2. say_hello: function() {
  3. this._super();
  4. console.log("translation in Spanish: hola", this.name);
  5. },
  6. });
  7. var my_object = new instance.oepetstore.MySpanishClass("Nicolas");
  8. my_object.say_hello();

  4.部件(Widget)
  从instance.web.Widget扩展自定义部件。HomePage首页部件见petstore.js。
  在自定义部件中,this.$el表示部件实例的jQuery对象,可以调用jQuery方法,例如:
[javascript]

  1. this.$el.append("

    Hello dear OpenERP user!

    ");

  往部件中添加一个

块及内容。
  部件中可以插入其他部件进行组合:
[javascript]

  1. instance.oepetstore.GreetingsWidget = instance.web.Widget.extend({
  2. start: function() {
  3. this.$el.addClass("oe_petstore_greetings");
  4. this.$el.append("

    We are so happy to see you again in this menu!

    ");

  5. },
  6. });
  7. instance.oepetstore.HomePage = instance.web.Widget.extend({
  8. start: function() {
  9. this.$el.addClass("oe_petstore_homepage");
  10. this.$el.append("

    Hello dear OpenERP user!

    ");

  11. var greeting = new instance.oepetstore.GreetingsWidget(this); // 创建部件的时候传入父部件的实例作为构造参数。
  12. greeting.appendTo(this.$el);
  13. },
  14. });

  父子部件可以通过getChildren()、getParent()进行互相访问。如果重载部件的构造函数,第一个参数必须是父部件,并且必须传递给基类。
[javascript]

  1. instance.oepetstore.GreetingsWidget = instance.web.Widget.extend({
  2. init: function(parent, name) {
  3. this._super(parent);
  4. this.name = name;
  5. },
  6. });

  如果作为顶层部件创建,parent参数应该是null。
  部件实例可以调用destroy()方法销毁。

  5.QWeb模板引擎
  QWeb模板在XML属性上加前缀“t-”表示:
    t-name:模板名称;
    t-esc:引用实例参数,可以使用任意JavaScript表达式;
    t-raw:引用原始实例参数,如果有html标记则保留。
  QWeb模板下面的根元素最好只有一个。
  oepetstore/static/src/xml/petstore.xml:
[html]

  1. Hello 
  2.  
  3.  
    •   定义一个名为“HomePageTemplate”的模板。
        使用方法1:
      [javascript]

      1. instance.oepetstore.HomePage = instance.web.Widget.extend({
      2. start: function() {
      3. this.$el.append(QWeb.render("HomePageTemplate"));
      4. },
      5. });

        使用方法2:
      [javascript]

      1. instance.oepetstore.HomePage = instance.web.Widget.extend({
      2. template: "HomePageTemplate",
      3. start: function() {
      4. ...
      5. },
      6. });

        模板里面的条件控制t-if:
      [html]

      1. true is true
      2. true is not true

        枚举t-foreach和t-as:
      [html]

      1.   
      2. Hello

        属性赋值,在属性名前加前缀“t-att-”:
      [html]

        将input控件的value属性赋值为“defaultName”。
        部件开发示例,显示产品列表。
        JavaScript脚本:
      [javascript]

      1. openerp.oepetstore = function(instance) {
      2. var _t = instance.web._t,
      3. _lt = instance.web._lt;
      4. var QWeb = instance.web.qweb;
      5. instance.oepetstore = {};
      6. instance.oepetstore.HomePage = instance.web.Widget.extend({
      7. start: function() {
      8. var products = new instance.oepetstore.ProductsWidget(this, ["cpu", "mouse", "keyboard", "graphic card", "screen"], "#00FF00");
      9. products.appendTo(this.$el);
      10. },
      11. });
      12. instance.oepetstore.ProductsWidget = instance.web.Widget.extend({
      13. template: "ProductsWidget",
      14. init: function(parent, products, color) {
      15. this._super(parent);
      16. this.products = products;
      17. this.color = color;
      18. },
      19. });
      20. instance.web.client_actions.add(‘petstore.homepage‘, ‘instance.oepetstore.HomePage‘);
      21. }

        QWeb模板:
      [html]

      1.   

        CSS样式:
      [css]

      1. .oe_products_item {
      2. display: inline-block;
      3. padding: 3px;
      4. margin: 5px;
      5. border: 1px solid black;
      6. border-radius: 3px;
      7. }

        6.部件事件与特性
      [javascript]

      1. instance.oepetstore.ConfirmWidget = instance.web.Widget.extend({
      2. start: function() {
      3. var self = this;
      4. this.$el.append("

        Are you sure you want to perform this action?

        " +

      5. "<button class=‘ok_button‘>Ok" +
      6. "<button class=‘cancel_button‘>Cancel");
      7. this.$el.find("button.ok_button").click(function() { // 在按钮上绑定click事件
      8. self.trigger("user_choose", true); // 触发自定义user_choose事件,传递事件参数true/false
      9. });
      10. this.$el.find("button.cancel_button").click(function() {
      11. self.trigger("user_choose", false);
      12. });
      13. },
      14. });
      15. instance.oepetstore.HomePage = instance.web.Widget.extend({
      16. start: function() {
      17. var widget = new instance.oepetstore.ConfirmWidget(this);
      18. widget.on("user_choose", this, this.user_choose); // 在部件上绑定user_choose事件到响应函数user_choose
      19. widget.appendTo(this.$el);
      20. },
      21. user_choose: function(confirm) {
      22. if (confirm) {
      23. console.log("The user agreed to continue");
      24. } else {
      25. console.log("The user refused to continue");
      26. }
      27. },
      28. });

        部件特性(Properties)的使用:
      [javascript]

      1. this.widget.on("change:name", this, this.name_changed); //绑定name特性的change事件
      2. this.widget.set("name", "Nicolas"); // 设置特性值
      3. var getedname = this.widget.get("name"); // 读取特性值

        7.部件访问  简化jQuery选择器:
      [javascript]

      1. this.$el.find("input.my_input")

        等于
      [javascript]

      1. this.$("input.my_input")

        因此事件的绑定:
      [javascript]

      1. this.$el.find("input").change(function() {
      2. self.input_changed();
      3. });

        可以简化为:
      [javascript]

      1. this.$(".my_button").click(function() {
      2. self.button_clicked();
      3. });

        进一步,可以通过部件提供的events字典属性简化为:
      [javascript]

      1. instance.oepetstore.MyWidget = instance.web.Widget.extend({
      2. events: {
      3. "click .my_button": "button_clicked",
      4. },
      5. button_clicked: function() {
      6. ..
      7. }
      8. });

        注意:这种方法只是绑定jQuery提供的DOM事件机制,不能用于部件的on语法绑定部件自身的事件。  event属性的键名由两部分组成:事件名称和jQuery选择器,用空格分开。属性值是响应事件的函数(方法)。
        事件使用示例:
        JavaScript脚本:
      [javascript]

      1. openerp.oepetstore = function(instance) {
      2. var _t = instance.web._t,
      3. _lt = instance.web._lt;
      4. var QWeb = instance.web.qweb;
      5. instance.oepetstore = {};
      6. instance.oepetstore.ColorInputWidget = instance.web.Widget.extend({
      7. template: "ColorInputWidget",
      8. start: function() {
      9. var self = this;
      10. this.$el.find("input").change(function() {
      11. self.input_changed();
      12. });
      13. self.input_changed();
      14. },
      15. input_changed: function() {
      16. var color = "#";
      17. color += this.$el.find(".oe_color_red").val();
      18. color += this.$el.find(".oe_color_green").val();
      19. color += this.$el.find(".oe_color_blue").val();
      20. this.set("color", color);
      21. },
      22. });
      23. instance.oepetstore.HomePage = instance.web.Widget.extend({
      24. template: "HomePage",
      25. start: function() {
      26. this.colorInput = new instance.oepetstore.ColorInputWidget(this);
      27. this.colorInput.on("change:color", this, this.color_changed);
      28. this.colorInput.appendTo(this.$el);
      29. },
      30. color_changed: function() {
      31. this.$el.find(".oe_color_div").css("background-color", this.colorInput.get("color"));
      32. },
      33. });
      34. instance.web.client_actions.add(‘petstore.homepage‘, ‘instance.oepetstore.HomePage‘);
      35. }

        QWeb模板:
      [html]

      1.   
      2. Red: <br >
      3. Green: <br >
      4. Blue: <br >
      5.   

        CSS样式:
      [css]

      1. .oe_color_div {
      2. width: 100px;
      3. height: 100px;
      4. margin: 10px;
      5. }

        8.修改已有的部件和类
        可以用include()方法重载已有的部件和类,这个和继承机制类似,是一种插入的方法:
      [javascript]

      1. var TestClass = instance.web.Class.extend({
      2. testMethod: function() {
      3. return "hello";
      4. },
      5. });
      6. TestClass.include({
      7. testMethod: function() {
      8. return this._super() + " world";
      9. },
      10. });
      11. console.log(new TestClass().testMethod());
      12. // will print "hello world"

        应尽量避免使用这种机制导致的复杂性。

        9.与服务器的交互-读取数据模型
        客户端使用Ajax与服务器交互,不过OpenERP框架提供了简化的方法,通过数据模型进行访问。
        OpenERP自动将服务端的数据模型转化为客户端端模型,直接调用即可。服务器上petstore.py里面的模型:
      [python]

      1. class message_of_the_day(osv.osv):
      2. _name = "message_of_the_day"
      3. def my_method(self, cr, uid, context=None):
      4. return {"hello": "world"}
      5. _columns = {
      6. ‘message‘: fields.text(string="Message"),
      7. ‘color‘: fields.char(string="Color", size=20),
      8. }

        客户端调用例子:
      [javascript]

      1. instance.oepetstore.HomePage = instance.web.Widget.extend({
      2. start: function() {
      3. var self = this;
      4. var model = new instance.web.Model("message_of_the_day");
      5. model.call("my_method", [], {context: new instance.web.CompoundContext()}).then(function(result) {
      6. self.$el.append("

        Hello " + result["hello"] + "

        ");

      7. // will show "Hello world" to the user
      8. });
      9. },
      10. });

        模型的call()方法参数:
          第一个参数name是方法的名称;
          第二个参数args是按照顺序排列的参数数组。OpenERP定义的模型方法前三个参数(self, cr, uid)是固定的,由框架产生,也就是说传递的参数数组从第四个开始插入。而context又是特殊的。例子:
          方法定义:
      [python]

      1. def my_method2(self, cr, uid, a, b, c, context=None):

        调用:
      [javascript]

      1. model.call("my_method", [1, 2, 3], ...// with this a=1, b=2 and c=3

          第三个参数kwargs为命名参数,按照名称传递给Python的方法参数。例如:
      [javascript]

      1. model.call("my_method", [], {a: 1, b: 2, c: 3}, ...// with this a=1, b=2 and c=3

        OpenERP模型中的context是一个特殊参数,表示调用者的上下文,一般就使用客户端Web Client实例提供的instance.web.CompoundContext()类新建一个对象实例即可。
        CompoundContext类提供用户的语言和时区信息。也可以在构造函数中添加另外的数据:
      [javascript]

      1. model.call("my_method", [], {context: new instance.web.CompoundContext({‘new_key‘: ‘key_value‘})})def display_context(self, cr, uid, context=None):    print context    // will print: {‘lang‘: ‘en_US‘, ‘new_key‘: ‘key_value‘, ‘tz‘: ‘Europe/Brussels‘, ‘uid‘: 1}

        (OpenERP服务器端数据模型的方法必须提供4个参数:self, cr, uid, context=None,分别表示模型实例、数据库指针(Cursor)、用户id和用户上下文)

        10.与服务器的交互-查询
        客户端数据模型提供了search()、read()等方法,组合为query()方法,使用例子:
      [javascript]

      1. model.query([‘name‘, ‘login‘, ‘user_email‘, ‘signature‘])     .filter([[‘active‘, ‘=‘, true], [‘company_id‘, ‘=‘, main_company]])     .limit(15)     .all().then(function (users) {    // do work with users records});

        数据模型的query()方法的参数是需要读取的模型字段名称列表;该方法返回的是一个instance.web.Query()类型的查询对象实例,包括一些进一步定义查询结果的方法,这些方法返回的是同一个查询对象自身,因此可以链接:
          filter():指定OpenERP 域(domain),也即过滤查询结果;
          limit():限制返回的记录数量。
        最后调用查询对象的all()方法执行查询。
        查询异步执行,all()返回的是一个deferred,因此要用then()提供回调函数来处理结果。
        数据模型的查询是通过rpc调用实现的。
        示例1:显示每日提示
        JavaScript脚本:
      [javascript]

      1. openerp.oepetstore = function(instance) {
      2. var _t = instance.web._t,
      3. _lt = instance.web._lt;
      4. var QWeb = instance.web.qweb;
      5. instance.oepetstore = {};
      6. instance.oepetstore.HomePage = instance.web.Widget.extend({
      7. template: "HomePage",
      8. start: function() {
      9. var motd = new instance.oepetstore.MessageOfTheDay(this);
      10. motd.appendTo(this.$el);
      11. },
      12. });
      13. instance.web.client_actions.add(‘petstore.homepage‘, ‘instance.oepetstore.HomePage‘);
      14. instance.oepetstore.MessageOfTheDay = instance.web.Widget.extend({
      15. template: "MessageofTheDay",
      16. init: function() {
      17. this._super.apply(this, arguments);
      18. },
      19. start: function() {
      20. var self = this;
      21. new instance.web.Model("message_of_the_day").query(["message"]).first().then(function(result) {
      22. self.$(".oe_mywidget_message_of_the_day").text(result.message);
      23. });
      24. },
      25. });
      26. }

        QWeb模板:
      [html]

          CSS样式:
        [css]

        1. .oe_petstore_motd {
        2. margin: 5px;
        3. padding: 5px;
        4. border-radius: 3px;
        5. background-color: #F0EEEE;
        6. }

          示例2:组合显示每日提示和产品列表
          服务器端从OpenERP的产品表继承一个类(模型):
        [python]

        1. class product(osv.osv):
        2.        _inherit = "product.product"
        3.   
        4.        _columns = {
        5.            ‘max_quantity‘: fields.float(string="Max Quantity"),
        6.        }

          因此数据是保存在product.product表中的,只是扩充了一个“max_quantity”字段;这个例子结合前面的每日提示信息,显示二列,左面一列显示产品列表,右面显示提示信息。
          JavaScript脚本:
        [javascript]

        1. openerp.oepetstore = function(instance) {
        2. var _t = instance.web._t,
        3. _lt = instance.web._lt;
        4. var QWeb = instance.web.qweb;
        5. instance.oepetstore = {};
        6. instance.oepetstore.HomePage = instance.web.Widget.extend({
        7. template: "HomePage",
        8. start: function() {
        9. var pettoys = new instance.oepetstore.PetToysList(this);
        10. pettoys.appendTo(this.$(".oe_petstore_homepage_left"));
        11. var motd = new instance.oepetstore.MessageOfTheDay(this);
        12. motd.appendTo(this.$(".oe_petstore_homepage_right"));
        13. },
        14. });
        15. instance.web.client_actions.add(‘petstore.homepage‘, ‘instance.oepetstore.HomePage‘);
        16. instance.oepetstore.MessageOfTheDay = instance.web.Widget.extend({
        17. template: "MessageofTheDay",
        18. init: function() {
        19. this._super.apply(this, arguments);
        20. },
        21. start: function() {
        22. var self = this;
        23. new instance.web.Model("message_of_the_day").query(["message"]).first().then(function(result) {
        24. self.$(".oe_mywidget_message_of_the_day").text(result.message);
        25. });
        26. },
        27. });
        28. instance.oepetstore.PetToysList = instance.web.Widget.extend({
        29. template: "PetToysList",
        30. start: function() {
        31. var self = this;
        32. new instance.web.Model("product.product").query(["name", "image"])
        33. .filter([["categ_id.name", "=", "Pet Toys"]]).limit(5).all().then(function(result) {
        34. _.each(result, function(item) {
        35. var $item = $(QWeb.render("PetToy", {item: item}));
        36. self.$el.append($item);
        37. });
        38. });
        39. },
        40. });
        41. }

          QWeb模板:
        [html]

        1. <img t-att- src="‘data:image/jpg;base64,‘+item.image">

          CSS样式:
        [css]

        1. .oe_petstore_homepage {
        2. display: table;
        3. }
        4. .oe_petstore_homepage_left {
        5. display: table-cell;
        6. width : 300px;
        7. }
        8. .oe_petstore_homepage_right {
        9. display: table-cell;
        10. width : 300px;
        11. }
        12. .oe_petstore_motd {
        13. margin: 5px;
        14. padding: 5px;
        15. border-radius: 3px;
        16. background-color: #F0EEEE;
        17. }
        18. .oe_petstore_pettoyslist {
        19. padding: 5px;
        20. }
        21. .oe_petstore_pettoy {
        22. margin: 5px;
        23. padding: 5px;
        24. border-radius: 3px;
        25. background-color: #F0EEEE;
        26. }
    时间: 2024-10-21 04:23:34

    OpenERP|odoo Web开发的相关文章

    OpenERP(odoo)开发实例之搜索检索过去3个月的数据

    转自:http://www.chinamaker.net/ OpenERP(odoo)开发实例之搜索过滤:检索过去3个月的数据 解决这个问题的重点在于 relativedelta 的应用 示例代码如下: 1: <!-- filter: last three months --> 2: <filter icon="terp-personal" name="last_three_month" 3: string="Last 3 Months&

    如何OE开发中实现一个在web开发中的小效果

    近期公司里面有一个需求,就是当业务人员点击订单列表中的某一条时希望打开一个新的窗口动作,然后把这一条记录中的某些值带过去,这样一个效果在web开发中很简单,或许就是分分钟的事情.但是在客户端这种开发中确实不易,尤其是OpenERP这种有诸多限制的框架里面. 那么如何实现呢? 这个地方我的想法主要分两步:1.打开一个新的窗口(Form视图的)2.把值带入到新的窗口里面. 第一步里面我按日常的操作在view里面创建一个向导(wizard),然后给name和id赋值,然后在创建一个wizard文件(名

    Odoo Web Service API

    来自 Odoo Web服务暴露出相关的服务,路由分别是 /xmlrpc/ /xmlrpc/2/ /jsonrpc 根据 services 调用 后端对应服务的 方法method [定义 openerp\http.py 之 dispatch_rpc()],然后再将结果从python dict 转换为 xml-rpc 格式 或者 json-rpc 返回    service 对应的后端服务分别是 common, openerp.service.common db,openerp.service.db

    odoo web controller

    Routing openerp.http.route(route=None, **kw) Decorator marking the decorated method as being a handler for requests. The method must be part of a subclass of Controller. Parameters route -- string or array. The route part that will determine which ht

    高性能WEB开发:重排与重绘

    DOM编程可能最耗时的地方,重排和重绘. 1.什么是重排和重绘 浏览器下载完页面中的所有组件--HTML标记.JavaScript.CSS.图片之后会解析生成两个内部数据结构--DOM树和渲染树. DOM树表示页面结构,渲染树表示DOM节点如何显示.DOM树中的每一个需要显示的节点在渲染树种至少存在一个对应的节点(隐藏的DOM元素disply值为none 在渲染树中没有对应的节点).渲染树中的节点被称为"帧"或"盒",符合CSS模型的定义,理解页面元素为一个具有填充

    [Java Web]2\Web开发中的一些架构

    1.企业开发架构: 企业平台开发大量采用B/S开发模式,不管采用何种动态Web实现手段,其操作形式都是一样的,其核心操作的大部分都是围绕着数据库进行的.但是如果使用编程语言进行数据库开发,要涉及很多诸如事务.安全等操作问题,所以现在开发往往要通过中间件进行过渡,即,程序运行在中间件上,并通过中间件进行操作系统的操作,而具体一些相关的处理,如事务.安全等完全由中间件来负责,这样程序员只要完成具体的功能开发即可. 2.Java EE架构: Java EE 是在 Java SE 的基础上构建的,.NE

    七日Python之路--第十二天(Django Web 开发指南)

    <Django Web 开发指南>.貌似使用Django1.0版本,基本内容差不多,细读无妨.地址:http://www.jb51.net/books/76079.html (一)第一部分 入门 (1)内置数字工厂函数 int(12.34)会创建一个新的值为12的整数对象,而float(12)则会返回12.0. (2)其他序列操作符 连接(+),复制(*),以及检查是否是成员(in, not in) '**'.join('**')   或  '***%s***%d' % (str, int)

    Java Web开发基础(3)-JSTL

    在DRP项目中接触到了JSTL标签库,对我这样的比較懒的人来说,第一感觉就是"惊艳". JSTL标签库的使用.能够消除大量复杂.繁复的工作.工作量降低的不是一点半点.是降低了一大半.不论什么工具的引入,都会使我们的工作变的简单.可是问题是我们工作能够变的简单,可是我们不能让自己的大脑变的简单.所以,我们不是简单的会用就能够.我们还须要了解这个工具是怎样工作的.怎样为我们提供便利的.ok.以下进入正题,我们从问题開始-- JSP脚本带来的问题 不知道看到这几个子,各位有什么想法?反正我认

    给大家分享web开发新手修改hosts文件实现本地域名访问的正确方法

    1.如何正确修改hosts文件: 一般打开hosts文件里面都会有个示例,按照其格式修改即可 比如以下内容: # For example: # # 102.54.94.97 rhino.acme.com # source server # 38.25.63.10 x.acme.com # x client host 即代表打开rhino.acme.com这个网址将解析到102.54.94.97,ip地址与网址间至少有一空格,当然建议通过按Table键来编辑,即美观又不容易编写失误;这也就是通过解