ODOO18菜鸟二次开发系列(3)-简单的本地脚本备份数据库及源码


前言

二次开发容易出现问题,作为菜鸟不会用git版本管理,但又怕文件及数据系统出错。做好备份非常重要。


一、odoo目录结构

如果要备份,最好先搞清楚哪些需要备份。
在这里插入图片描述NSSM 是一个用于将任何可执行文件作为 Windows 服务运行的工具。其安装目录包含了 NSSM 的可执行文件nssm.exe以及相关的依赖文件。
POSTGRES是数据库。
python不用管
server是主要的服务器文件夹,所有服务器文件在这里面,是后续修改重点需要备份。
sessions 用于存储用户会话信息。当用户登录 Odoo 系统后,系统会生成一个会话 ID,并将相关的会话数据存储在这个文件夹中。这些会话数据可以用于跟踪用户的登录状态、权限信息等,确保用户在一定时间内可以保持登录状态并正常使用系统。
thirdparty第三方应用。

二、如何备份文件

1.数据库的备份

数据库相关备份方案比较成熟,可以直接用内置的或安装自动备份模块。
方案一,用原生的内置数据备份http://localhost/web/database/manager,备份后自动下载到本地。注意restore恢复必须是建新数据库名称,不能覆盖原数据库。恢复完成后可以改odoo.conf内的设置,映射到新的数据库上。备份和还原建议用默认的数据库密码AAAA-BBBB-CCCC
方案二,到github下载https://github.com/Yenthe666/auto_backup或者csdn https://download.youkuaiyun.com/download/cgplayer/90487727
注意如果安装模块出现 pysftp错误,说明未安装pysftp,需在venv下pip3 install pysftp==0.2.9,提前安装好pysftp
在这里插入图片描述
如果执行备份时右边出现一堆错误,结尾写 Command pg_dump not found. ,可到odoo.conf增加:
pg_path = C:\Program Files\PostgreSQL\16\bin 路径根据实际postgresql安装路径修改。

2.源程序文件任务计划自动备份

由于比较菜,不会用提高性能的Linux,因此选了windows server 服务器
开启任务计划程序,新建任务计划。
在这里插入图片描述
可以使用以下脚本定期自动备份7天内所有修改的文件(20250421更新:此方案比较保险。如果熟练后,可分开备份数据库及session文件夹即可,源程序git版本管理)

@echo off
echo [%date% %time%] 正在停止 odoo-server 服务... >> odoobak.log
net stop "odoo-server-18.0" >>
<think>我们正在处理一个关于Odoo14二次开发的问题:如何实现模型附件的一键打包批量下载功能。根据用户提供的引用信息,我们可以整合出以下步骤:1.在模型中添加计算附件数量的字段和查看附件的动作(引用[3])。2.在前端添加一个按钮,用于触发批量下载(引用[2])。3.编写一个控制器(Controller)来处理批量下载请求,将多个附件打包成ZIP文件并提供下载。具体步骤:###1.模型层(Model)在目标模型中,我们需要有一个字段来记录附件数量(如引用[3]所示),以及一个方法用于打开附件视图。但为了实现批量下载,我们还需要一个方法用于获取指定记录的所有附件。在模型(例如`sale.order`)中添加:```pythonclassSaleOrder(models.Model):_inherit=&#39;sale.order&#39;#计算附件数量的方法(引用[3])def_compute_attachment_number(self):#...实现计算附件数量,同引用[3]#新增:获取当前记录集的所有附件def_get_attachments(self):self.ensure_one()returnself.env[&#39;ir.attachment&#39;].search([(&#39;res_model&#39;,&#39;=&#39;,self._name),(&#39;res_id&#39;,&#39;=&#39;,self.id)])```###2.前端按钮(ViewandJS)在列表视图(或其他视图)中添加一个按钮,并绑定点击事件(如引用[2]所示)。在自定义模块的XML文件中,为列表视图添加按钮:```xml<recordid="view_order_tree"model="ir.ui.view"><fieldname="name">sale.order.tree</field><fieldname="model">sale.order</field><fieldname="inherit_id"ref="sale.view_order_tree"/><fieldname="arch"type="xml"><xpathexpr="//tree"position="inside"><buttonname="download_attachments"string="下载附件"type="object"class="oe_highlight"/></xpath></field></record>```注意:这里我们使用了`type="object"`,这样点击按钮会调用模型中的`download_attachments`方法。但通常,我们更倾向于使用客户端动作(如引用[2]中通过JS调用)以避免服务器端长时间处理导致超时。因此,我们可以采用引用[2]的方法,在JS中调用一个控制器。###3.创建控制器(Controller)我们需要一个控制器来接收记录ID,打包附件并返回ZIP文件。首先,在模块中创建控制器文件(如`controllers/main.py`):```python#-*-coding:utf-8-*-importosimportzipfilefromodooimporthttpfromodoo.httpimportrequest,content_dispositionclassDownloadAttachmentsController(http.Controller):@http.route(&#39;/web/binary/download_attachments&#39;,type=&#39;http&#39;,auth="user")defdownload_attachments(self,attachment_ids,model_name,**kwargs):#将attachment_ids转换为整数列表attachment_ids=[int(id)foridinattachment_ids.split(&#39;,&#39;)]#获取附件对象attachments=request.env[&#39;ir.attachment&#39;].sudo().search([(&#39;id&#39;,&#39;in&#39;,attachment_ids),(&#39;res_model&#39;,&#39;=&#39;,model_name)])#创建临时ZIP文件zip_filename=&#39;attachments.zip&#39;zip_path=&#39;/tmp/&#39;+zip_filenamewithzipfile.ZipFile(zip_path,&#39;w&#39;)aszipf:forattachmentinattachments:#获取附件内容file_content=attachment.raw#写入ZIP文件zipf.writestr(attachment.name,file_content)#读取ZIP文件内容withopen(zip_path,&#39;rb&#39;)asf:zip_data=f.read()#删除临时文件os.remove(zip_path)#返回响应headers=[(&#39;Content-Type&#39;,&#39;application/zip&#39;),(&#39;Content-Disposition&#39;,content_disposition(zip_filename)),]returnrequest.make_response(zip_data,headers)```###4.前端JS调用控制器修改之前的JS代码(如引用[2]),在点击按钮时调用这个控制器。在自定义JS中(如`static/src/js/download_button.js`):```javascriptodoo.define(&#39;your_module.download_button&#39;,function(require){"usestrict";varListController=require(&#39;web.ListController&#39;);varrpc=require(&#39;web.rpc&#39;);ListController.include({renderButtons:function($node){this._super.apply(this,arguments);if(this.$buttons){this.$buttons.on(&#39;click&#39;,&#39;.download_attachments_button&#39;,this.downloadAttachments.bind(this));}},downloadAttachments:function(){varself=this;varattachment_ids=this.getSelectedIds();varmodel_name=this.modelName;//调用控制器rpc.query({route:&#39;/web/binary/download_attachments&#39;,params:{attachment_ids:attachment_ids.join(&#39;,&#39;),model_name:model_name,},}).then(function(result){//创建一个隐藏的<a>标签用于触发下载vara=document.createElement(&#39;a&#39;);a.href=window.URL.createObjectURL(newBlob([result],{type:&#39;application/zip&#39;}));a.download=&#39;attachments.zip&#39;;document.body.appendChild(a);a.click();document.body.removeChild(a);});}});});```注意:上面的JS代码中,我们通过rpc调用控制器,控制器返回ZIP文件的二进制数据,然后我们创建一个临时的a标签触发下载。###5.安全考虑-在控制器中,我们使用`sudo()`来确保用户有权访问附件(因为附件可能属于不同用户)。但注意,我们已经在控制器中检查了当前用户的权限(通过`auth="user"`),并且只下载与指定模型和记录相关的附件。-注意:在搜索附件时,我们添加了`(&#39;res_model&#39;,&#39;=&#39;,model_name)`条件,确保只下载属于当前模型的附件。###6.注册JS文件在模块的模板XML中注册JS文件:```xml<templateid="assets_backend"name="yourmoduleassets"inherit_id="web.assets_backend"><xpathexpr="."position="inside"><scripttype="text/javascript"src="/your_module/static/src/js/download_button.js"></script></xpath></template>```###总结通过以上步骤,我们实现了:1.在模型层获取附件。2.在视图层添加下载按钮。3.通过控制器将多个附件打包成ZIP文件并提供下载。4.通过前端JS调用控制器并触发下载。这样,用户就可以在列表视图中选择多条记录,然后点击按钮下载所有附件(打包为一个ZIP文件)。###相关问题1.如何优化控制器的打包过程,避免大文件导致的内存问题?2.在Odoo中如何实现后台任务(如使用队列)来处理耗时的打包操作?3.如何限制用户只能下载自己有权限访问的附件?</think>### Odoo 14 实现模型附件一键打包批量下载方案 #### 核心实现步骤: ##### 1. **模型层添加方法** ```python from odoo import models, api, http import zipfile import io class YourModel(models.Model): _inherit = &#39;your.target.model&#39; # 替换为实际模型名 def action_download_attachments(self): """ 生成ZIP包的核心逻辑 """ zip_buffer = io.BytesIO() with zipfile.ZipFile(zip_buffer, &#39;w&#39;) as zip_file: for record in self: attachments = self.env[&#39;ir.attachment&#39;].search([ (&#39;res_model&#39;, &#39;=&#39;, self._name), (&#39;res_id&#39;, &#39;=&#39;, record.id) ]) for attachment in attachments: zip_file.writestr( f"{record.name}/{attachment.name}", # 按记录名建立文件夹 attachment.raw ) zip_buffer.seek(0) return { &#39;type&#39;: &#39;ir.actions.act_url&#39;, &#39;url&#39;: f&#39;/web/binary/download_zip?model={self._name}&ids={self.ids}&#39;, &#39;target&#39;: &#39;self&#39;, } ``` ##### 2. **创建控制器处理下载** ```python class AttachmentController(http.Controller): @http.route(&#39;/web/binary/download_zip&#39;, type=&#39;http&#39;, auth="user") def download_zip(self, model, ids, **kwargs): ids = [int(i) for i in ids.strip(&#39;[]&#39;).split(&#39;,&#39;)] records = http.request.env[model].browse(ids) zip_buffer = io.BytesIO() with zipfile.ZipFile(zip_buffer, &#39;w&#39;) as zip_file: for record in records: attachments = http.request.env[&#39;ir.attachment&#39;].search([ (&#39;res_model&#39;, &#39;=&#39;, model), (&#39;res_id&#39;, &#39;=&#39;, record.id) ]) for att in attachments: zip_file.writestr( f"{record.name}/{att.name}", att.raw ) zip_buffer.seek(0) return http.request.make_response( zip_buffer.getvalue(), headers=[ (&#39;Content-Type&#39;, &#39;application/zip&#39;), (&#39;Content-Disposition&#39;, f&#39;attachment; filename="{model}_attachments.zip"&#39;) ] ) ``` ##### 3. **前端添加下载按钮** ```xml <!-- 列表视图添加按钮 --> <record id="view_your_model_tree" model="ir.ui.view"> <field name="name">your.model.tree</field> <field name="model">your.target.model</field> <field name="inherit_id" ref="original_view_id"/> <field name="arch" type="xml"> <xpath expr="//tree" position="inside"> <button name="action_download_attachments" string="下载附件包" type="object" class="oe_highlight"/> </xpath> </field> </record> <!-- 表单视图添加按钮 --> <record id="view_your_model_form" model="ir.ui.view"> <field name="name">your.model.form</field> <field name="model">your.target.model</field> <field name="inherit_id" ref="original_form_view_id"/> <field name="arch" type="xml"> <xpath expr="//sheet" position="inside"> <button name="action_download_attachments" string="下载所有附件" type="object" class="oe_highlight"/> </xpath> </field> </record> ``` #### 关键技术点: 1. **内存优化**:使用 `io.BytesIO` 避免临时文件写入磁盘 2. **安全控制**: - 通过 `auth="user"` 确保用户认证 - 自动应用当前用户的权限规则 3. **批量处理**: - 支持多记录同时打包(列表视图勾选多条记录) - 按记录名称自动创建文件夹归类 4. **文件命名**: - ZIP文件名包含模型名和时间戳:`${model}_attachments_${date}.zip` - 保持原始附件文件名不变 #### 性能优化建议: 1. 大文件处理:添加分片压缩逻辑 2. 后台任务:超过50个附件时启用队列任务 ```python # 在方法中添加 if len(attachments) > 50: return self.enqueue_zip_creation() ``` #### 使用效果: 1. 列表视图:勾选多条记录 → 点击"下载附件包" 2. 表单视图:打开单条记录 → 点击"下载所有附件" 3. 自动下载ZIP包,包含所有附件并按记录名分类存储 [^1]: 基于Odoo开发者模式自定义功能实现 [^2]: 前端批量操作实现参考 [^3]: 附件处理基础方法
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值