在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. }

OpenERP|odoo Web开发的更多相关文章

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

    转自:http://www.chinamaker.net/ OpenERP(odoo)开发实例之搜索过滤:检索过去3个月的数据 解决这个问题的重点在于 relativedelta 的应用 示例代码如下 ...

  2. Odoo Web Service API

    来自 Odoo Web服务暴露出相关的服务,路由分别是 /xmlrpc/ /xmlrpc/2/ /jsonrpc 根据 services 调用 后端对应服务的 方法method [定义 openerp ...

  3. 免费下载获取Odoo中文开发 指南 手册

    引言 Odoo是一个强大的商业应用开源平台.在此基础上,构建了一套紧密集成的应用程序,涵盖了从CRM到销售到股票和会计的所有业务领域.Odoo有一个动态和不断增长的社区,不断增加功能.连接器和其他商业 ...

  4. 第十二章 Odoo 12开发之报表和服务端 QWeb

    报表是业务应用非常有价值的功能,内置的 QWeb 引擎是报表的默认引擎.使用 QWeb 模板设计的报表可生成 HTML 文件并被转化成 PDF.也就是说我们可以很便捷地利用已学习的 QWeb 知识,应 ...

  5. 第十一章 Odoo 12开发之看板视图和用户端 QWeb

    QWeb 是 Odoo 使用的模板引擎,它基于 XML 来生成 HTML 片断和页面.通过 QWeb可生成内容丰富的看板(Kankan)视图.报表和 CMS 网页.本文中我们将学习QWeb 语法以及如 ...

  6. 第十章 Odoo 12开发之后台视图 - 设计用户界面

    本文将学习如何为用户创建图形化界面来与图书应用交互.我们将了解不同视图类型和小组件(widgets)之间的差别,以及如何使用它们来提供更优的用户体验. 本文主要内容有: 菜单项 窗口操作(Window ...

  7. 第五章 Odoo 12开发之导入、导出以及模块数据

    大多数Odoo 模块的定义,如用户界面和安全规则,实际是存储在对应数据表中的数据记录.模块中的 XML 和 CSV 文件不是 Odoo 应用运行时使用,而是载入数据表的手段.正是因为这个原因,Odoo ...

  8. 第四章 Odoo 12 开发之模块继承

    Odoo 的一个强大功能是无需直接修改底层对象就可以添加功能.这是通过其继承机制来实现的,采取在已有对象之上修改层来完成.这种修改可以在不同层上进行-模型层.视图层和业务逻辑层.我们创建新的模块来做出 ...

  9. 第三章 Odoo 12 开发之创建第一个 Odoo 应用

    Odoo 开发通常都需要创建自己的插件模块.本文中我们将通过创建第一个应用来一步步学习如何在 Odoo 中开启和安装这个插件.我们将从基础的开发流学起,即创建和安装新插件,然后在开发迭代中更新代码来进 ...

随机推荐

  1. LeetCode 137. Single Number II(只出现一次的数字 II)

    LeetCode 137. Single Number II(只出现一次的数字 II)

  2. keil中的memory model

    这两天仿真遇到的怪事真的是一大堆. 还是读写Flash的代码.keil编译OK,但是仿真就是莫名其妙地挂掉出现一些乱七八糟的事情. 后面发现是keil 中的memory model勾选错了,勾选的是l ...

  3. 由字符串反转(使用递归)引申出来一道Java面试题

    如何面试一个从事编程工作的开发人员既困难又乏味,幸好还有很多值得参考的指南,比如:<Joel Guerilla Guide to interviewing>,但最后雇佣与否,还得由你自己决 ...

  4. CentOS下Supervisor的安装与使用入门

    [转载]http://www.51bbo.com/archives/2120 Supervisor是一个进程管理工具,官方的说法 用途就是有一个进程需要每时每刻不断的跑,但是这个进程又有可能由于各种原 ...

  5. 剖析ironic

    关键技术 在安装操作系统时需要存储介质来存储系统镜像.需要控制物理机开关机,在网络部署环境中还需要预启动环境. PXE (预启动环境) IPMI(电源管理) iSCSI(存储) 什么是PXE PXE( ...

  6. Vue 2.0学习(一)简介

    简介 Vue是一套用于构建用户界面的渐进式框架.简单小巧( 压缩后仅17KB),Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件.它不仅易于上手,还便于与第三方库或既 ...

  7. bound和unbound方法,类的绑定和非绑定是什么

    作者:灵剑链接:https://www.zhihu.com/question/41006598/answer/148994582来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明 ...

  8. Redis学习篇(四)之List类型及其操作

    Redis的List是一个双向链表 LPUSH 作用:向列表左端添加元素 语法:LPUSH key value value... 从左到右逐个添加到左端,前面的先添加, 可以一次添加多个元素 RPUS ...

  9. 看雪论坛 破解exe 看雪CTF2017第一题分析-『CrackMe』-看雪安全论坛

    韩梦飞沙  韩亚飞  313134555@qq.com  yue31313  han_meng_fei_sha 逆向 黑客 破解 学习 论坛 『CrackMe』 http://bbs.pediy.co ...

  10. [BZOJ4517][SDOI2016]排列计数(错位排列)

    4517: [Sdoi2016]排列计数 Time Limit: 60 Sec  Memory Limit: 128 MBSubmit: 1616  Solved: 985[Submit][Statu ...