Odoo 网页开发

request.make_response()     #   仅返回包含 HTML 的字符串

request.render()   #  返回一个模板 

# 对于 json请求 。 只需要返回客户端想要的数据结构即可。 
# odoo 会处理序列化。让其动作,限制数据为json可序列化的类型

# request.env 属性,包含了与模型self.env 相同的Environment对象。 
# request.session    是werkzeug对Session对象的轻微封装。 OpenERPSession 对象

  

# route 装饰器 可带有其他的参数来进一步定义其行为。 默认允许HTTP所有方法
	 methods 参数 接收 方法列表
# 允许跨域  设置cors参数

# odoo 对每个请求传递 token 来保护请求免受跨站伪造请求 csrf的攻击

# 多数据库  参考下面文字
https://github.com/OCA/server-tools

  

限制网络可访问路径的访问

# auth='none'  任何用户都可以
# auth='public'   公共用户
# auth='user'      已验证的用户提供内容 ,通过request.env.user 指向有才能在用户


# 验证方法  在base插件的ir.http模型中

from odoo import exceptions, http, models
from odoo.http import request


class IrHttp(models.Model):
    _inherit = 'ir.http'

    def _auth_method_base_group_user(self):
        self._auth_method_user()
        if not request.env.user.has_group('base.group_user'):
            raise exceptions.AccessDenied()

  

传递消耗参数到你的handler

@http.route('/my_library/book_details', type='http', auth='none')
def book_details(self, book_id):  # 传递参数 book_id
    record = request.env['library.book'].sudo().browse(int(book_id))
    return u'<html><body><h1>%s</h1>Authors: %s' % (
        record.name,
        u', '.join(record.author_ids.mapped('name')) or 'none',
    )

  

操作response.headers来添加或删除HTTP头部
渲染整个不同的模板,可以覆盖response.template
response首先是否是基于QWeb,使用response.is_qweb进行查询
response.render()可获取结果HTML代码

  

管理静态资源

web.assets_common   包含所有通用的基本工具:JQ ,Fa等资源。 odoo所有地方都加载

web.assets_backend 包含所有与web客户端,视图,字段,组件,动作管理器相关代码

web.assets_frontent  用于前台,所有与网站端相关的代码。 电商,博客,线上活动,论坛和在线聊天等。 不包含网站编辑和网站构造器

web_editor.assets_editor和web_editor.summernote: 包含网站编辑组件以及拖拽功能。 批量邮件设计器

web.report_assets_commo : QWeb 仅通过html 生成PDF文件



### odoo 通过  AssetsBundle 管理其静态资源
		位于: /odoo/addons/base/models/assetsbundle.py
		1. 多个 JavaScript 和css文件
		2. 通过从文件内容中删除注释,多余空格以及回车换行来 最小化 JavaScript和Css文件、删除这些额外资源会减小静态资源的大小并提升页面速度
		3. 对css 预处理的内置支持,如SASS和LESS。 自动会编译并添加到资源包中
		4. 在达到4095的规则上限时它自动拆分样式表资源
		
		
### 页面引入
	1. link 标签添加
	2. QWeb中 使用 t-call-assets  引入   <t t-call-assets="my_module.my_custom_assets" tcss="false"/>

     3.t-css和 t-js属性公用于加载样式表或脚本
        
# 开发阶段来说:  odoo资源仅会生成一次
	命令中使用 dev=xml   这样会直接加载资源,无需重启服务
    

  

拓展css和js

# 1. 在已有页面 加载 样式和js
	<link href="/my_library/static/src/css/my_library.css" rel="stylesheet" type="text/css"/>
	<link href="/my_library/static/src/scss/my_library.scss" rel="stylesheet" type="text/scss"/>
	<script src="/my_library/static/src/js/my_library.js" type="text/JavaScript" />
# 2.  编写内容
	参考地址:
		https://alanhou.org/cms-website-development/
		
		
			
### odoo  cms的底层名为QWeb的XML模板引擎。 
		1. 通过 web.assets_frontend 中列出了样式表和JavaScript文件
		2. scss 语法  : odoo12版本之前使用的是less预处理器,12版本后使用的bs4和SCSS预处理器
		3.RequireJS 语法:
				odoo.define('模块名',function(require){  //require 必要参数 
					
					 //代码
					
				})
				

  

创建或更改QWeb

# contenteditable 属性 不可编辑 

# t-call 调用模板  <t t-call="website.layout">
# website.layout 包含所有需要的工具:如 bootstrap JQ fontAw 资源
	# website.layout 包含默认的头部,底部 ,代码片段和页面编辑功能
    
# t-foreach 属性,重复迭代成员
	语法:    t-as  相当于 每个成员的别名,  t-esc输出别名
    		<t  t-foreach='[1,2,3]' t-as='num'>
    			<p><t t-esc='num'></p>
            </t>
            
            
 ## 查看 t-call 元素的内部。  末班通过上下文渲染
	book_index  返回遍历中的当前索引值 ,从0开始
    book_first  和 book_last  分别在遍历第一个和最后一个时为True
    book_value 遍历的是一个字典会包含各项值。通过字典的键进行遍历
    book_size 集合的大小
    book_even   和  book_add  根据遍历的索引获取true的值
    book_parity 在遍历的索引为偶数时包含even的值,奇数时包含odd值
    
    
# QWeb 动态设置属性值
		1。t-att-$attr_name   创建一个$attr_name属性,它的值是任意有效的python表达式
    				<div  t-att-total="10+5+5"></div>
        						↓
            		<div total='20'></div>
                
        2。 t-attf-$attr_name   与上一个区别是{{...}}和#{...} 之间的字符串
        		<t t-foreach="['info', 'danger', 'warning']" t-as="color">
                      <div t-attf-class="alert alert-#{color}">   
                        		Simple bootstrap alert
                      </div>
				</t>
                
           3。 t-att=mapping  选项,末班渲染自定后转换为属性和值接受这个字典
        		<div t-att= "{'id':'my_el_id', 'class': 'alert alert-danger'}"/>
        						↓
                <div id='my_el_id'  class='alert alert-danger'/>
                
                
 # 字段  t-field 和 t-esc
	t-field='字段名'
    t-options='{}'  # 传递一个字典,给字段设置渲染器   {'widget':'image'}
    t-esc  是 t-field的替代属性。 t-esc属性并不只局限与记录集,它也可以是任意数据类型。但在网站内不可编辑
    
    # t-field 和 t-esc 区别
    	t-field  基于用户的语言值
        t-esc 显示数据库中的原始值
        
# 条件语句 t-if 
	t-if="state == 'new'"
    t-elif="state == 'progress'"
    t-else=""
    
#  设置变量
	<t t-set='my_var' t-value='5+1'/>
    <t-esc='my_var'/>
    
# 子模板
    <template id="first_template">
            <div> Test Template </div>
    </template>

    <template id="second_template">
            <t t-call="first_template"/>
    </template>
    
# 行内编辑
	  t-field 节点加载的数据 默认是可编辑的
	  禁用行内编辑   contenteditable=False

 # 启用 页面拖拽 功能  oe_structure
	 添加样式将开启 页面启用组件 拖拽功能  oe_structure
    
        
# 网站编辑器编辑视图会在视图中设置noupdate标记。这表示后续的代码修改不会在客户的数据库中体现。
# 继承  inherit_id 字段     

  

动态路由

@http.route('/books/<model("library.book"):book>', type='http', auth="user", website=True)
def function(self,book):  # book 必须传递,否则报错
    pass


# 视图   t-attf-href=设置动态属性, #{}  取值
 <a t-attf-href="/books/#{book.id}" class="btn btnprimary btn-sm">
      <i class="fa fa-book"/> Book Detail
</a>




#  其他动态路由
	/page/接收一个整数
    /page/<any(about, help):page_name=””>接收给定的值
    /pages/<page>接收字符串
    /pages/<category>/<int:page>接收多个值

  

为用户提供小组件

https://alanhou.org/cms-website-development/

# 1. 添加插件视图
# 2. 添加 template 模板
# 3. 继承 组件 website.snippets 。 添加组件和选项
# 4. 在已继承的组件模板中添加组件选项
# 5. 添加 css 和js脚本来调试

  

从网站用户获取输入

# 1. 需要一个模型来保存用户提交的问题
	class LibraryBookIssues(models.Model):
              _name = 'book.issue'

              book_id = fields.Many2one('library.book', required=True)
              submitted_by = fields.Many2one('res.users')
              isuue_description = fields.Text()
                
                
#2.   视图中添加 book_issuse_id 字段
<group string="Book Issues">
        <field name="book_issue_id" nolabel="1">
                <tree>
                        <field name="create_date"/>
                        <field name="submitted_by"/>
                        <field name="isuue_description"/>
                </tree>
        </field>
</group>


# 3.  添加模型访问权限 ir.model.access.csv
acl_book_issues,library.book_issue,model_book_issue,group_librarian,1,1,1,1


# 4.  编写处理函数  在 main,py 新增一个路由函数
@http.route('/books/submit_issues', type='http', auth="user", website=True)
def books_issues(self, **post):
    pass   # 

# 5.  添加一个 带有HTML表单的模板
	<template>
    	....
    </template>
# 6. 添加form 表单 , 解决csrf 问题
	<form method='post'>
    	 <input type="hidden" name="csrf_token"    t-att-value="request.csrf_token()"/>
    </form>

  

管理搜索引擎 优化SEO选项

# odoo 模板提供了内置的SEO支持 。 希望为每个URL分离SEO选项


# 继承 website.seo.metadata  mixin类
    _inherit = ['website.seo.metadata']

   
odoo12 添加 对 openGrapht 和Twitter分享meta标签的支持。 如果希望在自己的页面添加自定义meta标签
继承mixin后 重载 _default_website_meta() 

  

# 编写地图函数
from odoo.addons.http_routing.models.ir_http import slug
from odoo.addons.website.models.ir_http import sitemap_qs2dom

class Main(http.Controller):
     ...
  	def sitemap_books(env, rule, qs):
            Books = env['library.book']
            dom = sitemap_qs2dom(qs, '/books', Books._rec_name)
            for f in Books.search(dom):
              loc = '/books/%s' % slug(f)
              if not qs or qs.lower() in loc:
                yield {'loc': loc}

                
 # 编写路由函数
@http.route('/books/<model("library.book"):book>',type='http', auth="user", website=True, sitemap=sitemap_books ) # 添加地图
def library_book_detail(self, book):
        pass

  

获取访客的国家信息

#  下载配置GeoIP内置支持

# 添加字段保存国家
  restrict_country_ids = fields.Many2many('res.country')

# 展示字段
	  <field name="restrict_country_ids"  widget="many2many_tags"/>
    
 # 正确配置nginx  和GeoIP , odoo将会对request.session.geoip添加GeoIP信息
country_code = request.session.geoip and request.session.geoip.get(‘country_code’) or ‘IN’

  

追踪营销活动

# ROI 投资回报率
# UTM 广告的花费进行追踪

# 1. depends  添加 utm模块 
        'depends': ['base', 'website', 'utm'],
 # 2. 继承 utm.mixin
		  _inherit = ['utm.mixin']
# 3.  compaign_id  添加到 视图表单中
	      <field name="campaign_id"/>

    
# # 继承了 utm.mixin  
		campaign_id   :  tm.campaign模型的Many2one字段。它用于追踪不同的活动,如夏季和圣诞特价
        source_id:utm.source model.的Many2one字段。它用于追踪不同的来源,如搜索引擎和其它域名。
        medium_id:utm.medium 模型的Many2one字段。它用于追踪不同的媒介,如贺卡、邮件或横幅广告。
        

  

管理多站点

#  继承  website.multi.mixin
_inherit = ['website.seo.metadata', 'website.multi.mixin']


# 1. 控制访问 
        domain += request.website.website_domain()  # 将返回域名并过滤出不是来自该网站的图书。

# 2.  can_access_from_current_website()    图书记录针对当前活跃网站的话方法can_access_from_current_website会返回值True,而针对另一个网站时返回False
    if not book.can_access_from_current_website():
        raise werkzeug.exceptions.NotFound()

  

网页客户端开发

自定义组件

// # events  捕获js事件
            events: {
              'click .o_color_pill': 'clickPill',
            },
// # init 初始化  组件构造函数。用于进行初始化。在初始化组件时,会先调用该方法
        init: function () {
                      this.totalColors = 10;
                      this._super.apply(this, arguments);
        },
            
 // willStart():
            这个方法组件初始化以及在DOM中添加的过程中调用。它用于异步将数据初始化到组件中。它还会返回一个延迟对象,只需要通过super()调用即可获取。我们在后面的小节中将会使用到它
            
 // start()  
            该方法在完成组件渲染且未添加到DOM中时调用。这非常有助于后渲染任务,将返回一个延迟对象。可以在this.$el中访问已渲染的对象
                
// destroy()  
            消灭组件时调用  , 如 取消事件绑定
            
// # 重载_renderEdit和_renderReadonly来设置DOM元素:


// # 定义 点击 handler 

    clickPill: function (ev) {
            var $target = $(ev.currentTarget);
            var data = $target.data();
            if (mobile.methods.showToast) {
                mobile.methods.showToast({ 'message': 'Color changed' });
            }
            this._setValue(data.val.toString());
    }
                
                
// 注册组件 
                fieldRegistry.add('int_color', colorField);
 //  插入组件
                  return {
                          colorField: colorField,
                          };  }); // closing 'my_field_widget' namespace

  

客户端QWeb模板

# QWeb的原因是其可扩展性, 客户端与服务端QWeb的很大区别在于。客户端无法使用Xpath表达式,需要使用jQuery选择器和操作

<t t-extend=“FieldColorPills”>
      <t t-jquery=“span” t-operation=“prepend”>
        		<i class=“fa fa-user” />
      </t>
</t>

# t-name 属性 对应 字段
# t-operation 属性  值: append  ,before ,after ,inner  , replace 。 attributes属性

  

服务端做RPC调用

# this.model 存储了当前模型的名称
# this.field 是模型调用 fields_get函数的结果

# 对于x2x字段,fields_get()函数会给出co-model或域的信息。也可以使用它来查询字段的string、size或其可在模型定义时为字段所设置的属性

  

新建一个视图

# 1. 新建视图  ir.ui.view 添加新视图模型
\

  

导览提升客户引导

// 新增一个js
 

odoo.define('my_library.tour', function (require) {
        "use strict";
        var core = require('web.core');
        var tour = require('web_tour.tour');  // 导入了 网站向导
        var _t = core._t;
        tour.register('library_tour', {   //注册向导
            url: "/web",   // 注册运行时需要的URL
        }, [
            tour.STEPS.SHOW_APPS_MENU_ITEM, {
            trigger: '.o_app[data-menuxmlid="my_library.library_base_menu"]',
            content: _t('Manage books and authors in<b>Library app</b>.'),
            position: 'right'    // 指定上下左右的位置
            },
            {
            trigger: '.o_list_button_add',
            content: _t("Let's create new book."),
            position: 'bottom'
        }, {
            trigger: 'input[name="name"]',
            extra_trigger: '.o_form_editable',
            content: _t('Set the book title'),
            position: 'right',
        }, {
            trigger: '.o_form_button_save',
            content: _t('Save this book record'),
            position: 'bottom',
        }
        ]);
});

  

 

posted @ 2023-09-21 13:21  CrossPython  阅读(170)  评论(0编辑  收藏  举报