├── LICENSE ├── README.md ├── odoo_dev_essential.sublime-workspace ├── 第一章-Odoo开发入门.md ├── 第七章-ORM应用逻辑——业务过程支持.md ├── 第三章-继承-扩展已有应用.md ├── 第二章-构建自定义的Odoo应用.md ├── 第五章-模型-结构化应用数据.md ├── 第八章-创建看板视图和报告.md ├── 第六章-视图-用户界面.md └── 第四章-数据序列化和模块数据.md /LICENSE: -------------------------------------------------------------------------------- 1 | CC0 1.0 Universal 2 | 3 | Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an "owner") of an original work of 8 | authorship and/or a database (each, a "Work"). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific 12 | works ("Commons") that the public can reliably and without fear of later 13 | claims of infringement build upon, modify, incorporate in other works, reuse 14 | and redistribute as freely as possible in any form whatsoever and for any 15 | purposes, including without limitation commercial purposes. These owners may 16 | contribute to the Commons to promote the ideal of a free culture and the 17 | further production of creative, cultural and scientific works, or to gain 18 | reputation or greater distribution for their Work in part through the use and 19 | efforts of others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation 22 | of additional consideration or compensation, the person associating CC0 with a 23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work 25 | and publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights ("Copyright and 31 | Related Rights"). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 34 | i. the right to reproduce, adapt, distribute, perform, display, communicate, 35 | and translate a Work; 36 | 37 | ii. moral rights retained by the original author(s) and/or performer(s); 38 | 39 | iii. publicity and privacy rights pertaining to a person's image or likeness 40 | depicted in a Work; 41 | 42 | iv. rights protecting against unfair competition in regards to a Work, 43 | subject to the limitations in paragraph 4(a), below; 44 | 45 | v. rights protecting the extraction, dissemination, use and reuse of data in 46 | a Work; 47 | 48 | vi. database rights (such as those arising under Directive 96/9/EC of the 49 | European Parliament and of the Council of 11 March 1996 on the legal 50 | protection of databases, and under any national implementation thereof, 51 | including any amended or successor version of such directive); and 52 | 53 | vii. other similar, equivalent or corresponding rights throughout the world 54 | based on applicable law or treaty, and any national implementations thereof. 55 | 56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright 59 | and Related Rights and associated claims and causes of action, whether now 60 | known or unknown (including existing as well as future claims and causes of 61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 62 | duration provided by applicable law or treaty (including future time 63 | extensions), (iii) in any current or future medium and for any number of 64 | copies, and (iv) for any purpose whatsoever, including without limitation 65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes 66 | the Waiver for the benefit of each member of the public at large and to the 67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver 68 | shall not be subject to revocation, rescission, cancellation, termination, or 69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work 70 | by the public as contemplated by Affirmer's express Statement of Purpose. 71 | 72 | 3. Public License Fallback. Should any part of the Waiver for any reason be 73 | judged legally invalid or ineffective under applicable law, then the Waiver 74 | shall be preserved to the maximum extent permitted taking into account 75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver 76 | is so judged Affirmer hereby grants to each affected person a royalty-free, 77 | non transferable, non sublicensable, non exclusive, irrevocable and 78 | unconditional license to exercise Affirmer's Copyright and Related Rights in 79 | the Work (i) in all territories worldwide, (ii) for the maximum duration 80 | provided by applicable law or treaty (including future time extensions), (iii) 81 | in any current or future medium and for any number of copies, and (iv) for any 82 | purpose whatsoever, including without limitation commercial, advertising or 83 | promotional purposes (the "License"). The License shall be deemed effective as 84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the 85 | License for any reason be judged legally invalid or ineffective under 86 | applicable law, such partial invalidity or ineffectiveness shall not 87 | invalidate the remainder of the License, and in such case Affirmer hereby 88 | affirms that he or she will not (i) exercise any of his or her remaining 89 | Copyright and Related Rights in the Work or (ii) assert any associated claims 90 | and causes of action with respect to the Work, in either case contrary to 91 | Affirmer's express Statement of Purpose. 92 | 93 | 4. Limitations and Disclaimers. 94 | 95 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 96 | surrendered, licensed or otherwise affected by this document. 97 | 98 | b. Affirmer offers the Work as-is and makes no representations or warranties 99 | of any kind concerning the Work, express, implied, statutory or otherwise, 100 | including without limitation warranties of title, merchantability, fitness 101 | for a particular purpose, non infringement, or the absence of latent or 102 | other defects, accuracy, or the present or absence of errors, whether or not 103 | discoverable, all to the greatest extent permissible under applicable law. 104 | 105 | c. Affirmer disclaims responsibility for clearing rights of other persons 106 | that may apply to the Work or any use thereof, including without limitation 107 | any person's Copyright and Related Rights in the Work. Further, Affirmer 108 | disclaims responsibility for obtaining any necessary consents, permissions 109 | or other rights required for any use of the Work. 110 | 111 | d. Affirmer understands and acknowledges that Creative Commons is not a 112 | party to this document and has no duty or obligation with respect to this 113 | CC0 or use of the Work. 114 | 115 | For more information, please see 116 | 117 | -------------------------------------------------------------------------------- /odoo_dev_essential.sublime-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "auto_complete": 3 | { 4 | "selected_items": 5 | [ 6 | [ 7 | "cu", 8 | "custom-addons module" 9 | ] 10 | ] 11 | }, 12 | "buffers": 13 | [ 14 | { 15 | "file": "第二章-构建自定义的Odoo应用.md", 16 | "settings": 17 | { 18 | "buffer_size": 44978, 19 | "line_ending": "Unix" 20 | } 21 | } 22 | ], 23 | "build_system": "", 24 | "build_system_choices": 25 | [ 26 | ], 27 | "build_varint": "", 28 | "command_palette": 29 | { 30 | "height": 374.0, 31 | "last_filter": "mark", 32 | "selected_items": 33 | [ 34 | [ 35 | "mark", 36 | "Markdown Preview: Save to HTML" 37 | ], 38 | [ 39 | "ip", 40 | "Package Control: Install Package" 41 | ], 42 | [ 43 | "auto", 44 | "Anaconda: Autoformat PEP8 Errors" 45 | ], 46 | [ 47 | "pac", 48 | "Package Control: List Packages" 49 | ], 50 | [ 51 | "pack", 52 | "Package Control: Install Package" 53 | ], 54 | [ 55 | "packge", 56 | "Package Control: List Packages" 57 | ], 58 | [ 59 | "i", 60 | "Package Control: Install Package" 61 | ], 62 | [ 63 | "packag", 64 | "Package Control: List Packages" 65 | ] 66 | ], 67 | "width": 435.0 68 | }, 69 | "console": 70 | { 71 | "height": 126.0, 72 | "history": 73 | [ 74 | "import urllib.request,os,hashlib; h = '2915d1851351e5ee549c20394736b442' + '8bc59f460fa1548d1514676163dafc88'; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); by = urllib.request.urlopen( 'http://packagecontrol.io/' + pf.replace(' ', '%20')).read(); dh = hashlib.sha256(by).hexdigest(); print('Error validating download (got %s instead of %s), please try manual install' % (dh, h)) if dh != h else open(os.path.join( ipp, pf), 'wb' ).write(by)" 75 | ] 76 | }, 77 | "distraction_free": 78 | { 79 | "menu_visible": true, 80 | "show_minimap": false, 81 | "show_open_files": false, 82 | "show_tabs": false, 83 | "side_bar_visible": false, 84 | "status_bar_visible": false 85 | }, 86 | "expanded_folders": 87 | [ 88 | "/Users/m-l/Documents/Github/Odoo-Development-Essentials" 89 | ], 90 | "file_history": 91 | [ 92 | "/Users/m-l/Documents/Github/Odoo-Development-Essentials/第六章-视图-用户接口.md", 93 | "/Users/m-l/Documents/Github/Odoo-Development-Essentials/第五章-模型-结构化应用数据.md", 94 | "/Users/m-l/Documents/Github/Odoo-Development-Essentials/第四章-数据序列化和模块数据.md", 95 | "/Users/m-l/Documents/Github/Odoo-Development-Essentials/第三章-继承-扩展已存在的应用.md", 96 | "/Users/m-l/Documents/BitBuket/fluent_python/第二章-序列数组.md", 97 | "/Users/m-l/Documents/docs/a_resume.md", 98 | "/Users/m-l/Documents/docs/a_sume.md", 99 | "/Users/m-l/Documents/Github/Odoo-Development-Essentials/第二章-构建自定义的Odoo应用.md", 100 | "/Users/m-l/web-dev/flask_blog/app.py", 101 | "/Users/m-l/Documents/docs/tor_briges.md" 102 | ], 103 | "find": 104 | { 105 | "height": 25.0 106 | }, 107 | "find_in_files": 108 | { 109 | "height": 0.0, 110 | "where_history": 111 | [ 112 | ] 113 | }, 114 | "find_state": 115 | { 116 | "case_sensitive": false, 117 | "find_history": 118 | [ 119 | "modifications", 120 | "file", 121 | "`.", 122 | "Features(技术特性)复选框来完成这个操作。", 123 | "永远不要忘记,在你添加模型字段时,必须升级模块。当改版Python代码,包括清单文件时,都需要对服务器重启。", 124 | "技术菜单", 125 | "技术", 126 | " \n", 127 | "shipped", 128 | " \n", 129 | "django.utils", 130 | "0", 131 | "There", 132 | "```\n", 133 | " \n", 134 | "` ", 135 | "```\n", 136 | ")` ", 137 | " \n", 138 | "` ", 139 | "pagination", 140 | "Running", 141 | "Creating" 142 | ], 143 | "highlight": true, 144 | "in_selection": false, 145 | "preserve_case": false, 146 | "regex": false, 147 | "replace_history": 148 | [ 149 | ], 150 | "reverse": false, 151 | "show_context": true, 152 | "use_buffer2": true, 153 | "whole_word": false, 154 | "wrap": true 155 | }, 156 | "groups": 157 | [ 158 | { 159 | "selected": 0, 160 | "sheets": 161 | [ 162 | { 163 | "buffer": 0, 164 | "file": "第二章-构建自定义的Odoo应用.md", 165 | "semi_transient": true, 166 | "settings": 167 | { 168 | "buffer_size": 44978, 169 | "regions": 170 | { 171 | }, 172 | "selection": 173 | [ 174 | [ 175 | 0, 176 | 0 177 | ] 178 | ], 179 | "settings": 180 | { 181 | "syntax": "Packages/MarkdownLight/MarkdownLight.tmLanguage" 182 | }, 183 | "translation.x": 0.0, 184 | "translation.y": 0.0, 185 | "zoom_level": 1.0 186 | }, 187 | "stack_index": 0, 188 | "type": "text" 189 | } 190 | ] 191 | } 192 | ], 193 | "incremental_find": 194 | { 195 | "height": 25.0 196 | }, 197 | "input": 198 | { 199 | "height": 34.0 200 | }, 201 | "layout": 202 | { 203 | "cells": 204 | [ 205 | [ 206 | 0, 207 | 0, 208 | 1, 209 | 1 210 | ] 211 | ], 212 | "cols": 213 | [ 214 | 0.0, 215 | 1.0 216 | ], 217 | "rows": 218 | [ 219 | 0.0, 220 | 1.0 221 | ] 222 | }, 223 | "menu_visible": true, 224 | "output.find_results": 225 | { 226 | "height": 0.0 227 | }, 228 | "output.markdown": 229 | { 230 | "height": 25.0 231 | }, 232 | "pinned_build_system": "", 233 | "project": "odoo_dev_essential.sublime-project", 234 | "replace": 235 | { 236 | "height": 46.0 237 | }, 238 | "save_all_on_build": true, 239 | "select_file": 240 | { 241 | "height": 0.0, 242 | "last_filter": "", 243 | "selected_items": 244 | [ 245 | ], 246 | "width": 0.0 247 | }, 248 | "select_project": 249 | { 250 | "height": 0.0, 251 | "last_filter": "", 252 | "selected_items": 253 | [ 254 | ], 255 | "width": 0.0 256 | }, 257 | "select_symbol": 258 | { 259 | "height": 0.0, 260 | "last_filter": "", 261 | "selected_items": 262 | [ 263 | ], 264 | "width": 0.0 265 | }, 266 | "selected_group": 0, 267 | "settings": 268 | { 269 | }, 270 | "show_minimap": true, 271 | "show_open_files": true, 272 | "show_tabs": true, 273 | "side_bar_visible": true, 274 | "side_bar_width": 242.0, 275 | "status_bar_visible": true, 276 | "template_settings": 277 | { 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /第一章-Odoo开发入门.md: -------------------------------------------------------------------------------- 1 | # Chapter 1 Getting Started with Odoo Development 2 | ******************* 3 | 4 | Before we dive into Odoo development, we need to set up our development environment, and you need to learn the basic administration tasks for it. 5 | 6 | In this chapter, you will learn how to set up the work environment, where we will later build our Odoo applications. 7 | 8 | You will also learn how to set up a Debian or Ubuntu system to host our development server instances, and how to install Odoo from the GitHub source code. Then you will learn how to set up le sharing with Samba, allowing you to work on Odoo les from a workstation running Windows or any other operating system. 9 | 10 | Odoo is built using the Python programming language and uses the PostgreSQL database for its data storage, so these are the main requirements we should have in our Odoo host. 11 | 12 | To run Odoo from source, we will need to install rst the Python libraries it depends on. The Odoo source code can then be downloaded from GitHub and executed from source. While we can download a zip or tarball, it's best to get the sources using GitHub, so we'll also have it installed on our Odoo host. 13 | 14 | ## Setting up a host for the Odoo server 15 | We will prefer using Debian/Ubuntu for our Odoo server, even though you will still be able to work from your favorite desktop system, be it Windows, Macintosh, or Linux. 16 | 17 | Odoo can run on a variety of operating systems, so why pick Debian at the expense of other operating systems? Because Odoo is developed primarily with the Debian/ Ubuntu platform in mind, it supports Odoo better. It will be easier to nd help and additional resources if working with Debian/Ubuntu. 18 | 19 | It's also the platform the majority of developers work on, and where most deployments are rolled out. So, inevitably, Odoo developers will be expected to be comfortable with that platform. Even if you're from a Windows background it will be important to have some knowledge about it. 20 | 21 | In this chapter, you will learn how to set up and work with Odoo hosted in a Debian system, using only the command line. For those more at home with a Windows system, we will cover how to set up a virtual machine to host the Odoo server. As a bonus, the techniques you will learn will also allow you to manage Odoo in cloud servers where your only access will be through **Secure Shell (SSH)**. 22 | 23 | >### Note 24 | >Keep in mind that these instructions are intended to set up a new system for development. If you want to try some of them in an existing system, always take a backup ahead of time to be able to restore it in case something goes wrong. 25 | 26 | ### Provisions for a Debian host 27 | As explained earlier, we will need a Debian host for our Odoo version 8.0 server. If these are your rst steps with Linux, you may like to know that Ubuntu is a Debian-based Linux distribution, so they are very similar. 28 | 29 | >### Note 30 | >Odoo is guaranteed to work with the current stable version of Debian or Ubuntu. At the time of writing this book, these are Debian 7 "Wheezy" and Ubuntu 14.04 "Trusty Tahr". Both ship with Python 2.7, necessary to run Odoo. 31 | 32 | If you are already running Ubuntu or another Debian-based distribution, you're set; this machine can also be used as a host for Odoo. 33 | 34 | For the Windows and Macintosh operating systems, it is possible to have Python, PostgreSQL, and all the dependencies installed, and then run Odoo from source natively. 35 | 36 | 37 | However, that could prove to be a challenge, so our advice is to use a virtual machine running Debian or Ubuntu Server. You're welcome to choose your preferred virtualization software to get a working Debian system in a VM. If you need some guidance, here is some advice: regarding the virtualization software, 38 | you have several options, such as Microsoft Hyper-V (available in some versions 39 | of Windows), Oracle VirtualBox, or VMWare Player (or VMWare Fusion for Macintosh). VMWare Player is probably easier to use, and free-to-use downloads can be found at https://my.vmware.com/web/vmware/downloads. 40 | 41 | Regarding the Linux image to use, Ubuntu Server is more user friendly to install than Debian. If you're beginning with Linux, I would recommend trying a ready- to-use image. TurnKey Linux provides easy-to-use, preinstalled images in several formats, including ISO. The ISO format will work with any virtualization software you choose, or even on a bare-metal machine you might have. A good option might be the LAPP image, found at http://www.turnkeylinux.org/lapp. 42 | 43 | Once installed and booted, you should be able to log in to a command-line shell. 44 | 45 | If you are logging in using root, your rst task should be to create a user to use for your work, since it's considered bad practice to work as root. In particular, the Odoo server will refuse to run if you are using `root`. 46 | 47 | If you are using Ubuntu, you probably won't need this since the installation process has already guided you in the creation of a user. 48 | 49 | ### Creating a user account for Odoo 为Odoo创建一个账户 50 | First, make sure sudo is installed. Our work user will need it. If logged in as `root`: 51 | 52 | 首先,已经安装了sudo。我们要创建的这个用户需要用到它。如果您已经以`root`身份登录shell,请执行以下命令: 53 | 54 | ```shell 55 | # apt-get update && apt-get upgrade # Install system updates 56 | # apt-get install sudo # Make sure 'sudo' is installed 57 | ``` 58 | 59 | The following commands will create an odoo user: 60 | 61 | 下面的命令会创建一个odoo用户: 62 | 63 | ```shell 64 | # useradd -m -g sudo -s /bin/bash odoo # Create an 'Odoo' user with sudo powers 创建一个拥有sudo权限的用户‘Odoo’ 65 | # passwd odoo # Ask and set a password for the new user 为新用户设置一个密码 66 | ``` 67 | 68 | You can change odoo to whatever username you want. The `-m` option has its home directory created. The `-g sudo` adds it to the sudoers list, so it can run commands as root, and the `-s /bin/bash` sets the default shell to bash, which is nicer to use than the default `sh`. 69 | 70 | 你可以将odoo改为任意一个自己喜欢的名字。 `-m` 选项表示用户创建了自己家目录。`-g sudo`将用户添加到sudoer用户列表,这样用户才可以用root身份运行命令,`-s /bin/bash` 设置默认的shell为bash,这比默认使用的`sh`更为友好。 71 | 72 | Now we can log in as the new user and set up Odoo. 73 | 74 | 现在我们可以以新用户身份进行登录,并设着Odoo了。 75 | 76 | ## Installing Odoo from source 77 | Ready-to-install Odoo packages can be found at nightly.odoo.com, available as Windows (.exe), Debian (.deb), CentOS (.rpm), and source code tarballs (.tar.gz). 78 | 79 | As developers, we will prefer installing directly from the GitHub repository. This will end up giving us more control over versions and updates. 80 | 81 | To keep things tidy, let's work in an /odoo-dev directory inside your home directory. Throughout the book, we will assume this is where your Odoo server is installed. 82 | 83 | First, make sure you are logged in as the user created above, or during the installation process, and not as root. Assuming your user is odoo, you can con rm this with the following command: 84 | 85 | ```shell 86 | $ whoami 87 | odoo 88 | $ echo $HOME 89 | /home/odoo 90 | ``` 91 | 92 | Now we can use this script. It shows us how to install Odoo from source in a Debian system: 93 | 94 | ```shell 95 | $ sudo apt-get update && sudo apt-get upgrade # Install system updates 96 | $ sudo apt-get install git # Install Git 97 | $ mkdir ~/odoo-dev # Create a directory to work in 98 | $ cd ~/odoo-dev # Go into our work directory 99 | $ git clone https://github.com/odoo/odoo.git -b 8.0 # Get Odoo source code 100 | $ ./odoo/odoo.py setup_deps # Installs Odoo system dependencies 101 | $ ./odoo/odoo.py setup_pg # Installs PostgreSQL & db superuser for unix 102 | user 103 | ``` 104 | 105 | At the end, Odoo should be ready to be used. The ~ symbol is a shortcut for 106 | your home directory (for example, /home/odoo). The git -b 8.0 option asks to explicitly download the 8.0 branch of Odoo. At the time of writing this book, this is redundant, since 8.0 is the default branch, but this may change, so it will make the script time resilient. 107 | 108 | To start an Odoo server instance, just run odoo.py: 109 | 110 | ```shell 111 | $ ~/odoo-dev/odoo/odoo.py 112 | ``` 113 | 114 | By default, Odoo instances listen from port 8069, so if we point a browser to http://:8069 we will reach that instance. When we are accessing it for the rst time, it will show us an assistant to create a new database, as shown in the following screenshot: 115 | 116 | img:omit 117 | 118 | But we will learn how to initialize new databases from the command line, now so press Ctrl + C to stop the server and get back to the command prompt. 119 | 120 | ## Initializing a new Odoo database 初始化Odoo数据库 121 | To be able to create a new database, your user must be a PostgreSQL superuser. The ./odoo.py setup_pg does that for you; otherwise use the following command to create a PostgreSQL superuser for the current Unix user with: 122 | 123 | ```shell 124 | $ sudo createuser --superuser $(whoami) 125 | ``` 126 | 127 | To create a new database we use the command createdb. Let's create a v8dev database: 128 | 129 | ```shell 130 | $ createdb v8dev 131 | ``` 132 | 133 | To initialize this database with the Odoo data schema we should run Odoo on the empty database by using the -d option: 134 | 135 | ```shell 136 | $ ~/odoo-dev/odoo/odoo.py -d v8dev 137 | ``` 138 | 139 | This will take a couple of minutes to initialize a v8dev database, and will end with an INFO log message Modules loaded. Then the server will be ready to listen to client requests. 140 | 141 | By default, this method will initialize the database with demonstration data, which often is useful on development databases. To initialize a database without demonstration data, add to the command the option: --without-demo-data=all. 142 | 143 | Open `http://:8069` in your browser to be presented with the login screen. If you don't know your server name, type the hostname command at the terminal to nd it, or the ifconfig command to nd the IP address. 144 | 145 | If you are hosting Odoo in a virtual machine you might need to do some network con guration to be able to use it as a server. The simplest solution is to change the VM network type from NAT to Bridged. With this, instead of sharing the host IP address, the guest VM will have its own IP address. It's also possible to use NAT, but that requires you to con gure port forwarding, so your system knows that some ports, such as 8069, should be handled by the VM. In case you're having trouble, hopefully these details can help you nd help in the documentation for your chosen virtualization software. 146 | 147 | The default administrator account is admin with password admin. Upon login you are presented with the Settings menu, displaying the installed modules. Remove the Installed lter and you will be able to see and install any of the of cial modules. 148 | 149 | Whenever you want to stop the Odoo server instance and return to the command line, press `Ctrl + C`. At the bash prompt, pressing the Up arrow key will bring you the previous shell command, so it's a quick way to start Odoo again with the same options. You will see the `Ctrl + C `followed by Up arrow and Enter is a frequently used combination to restart the Odoo server during development. 150 | 151 | ### Managing your databases 管理数据库 152 | We've seen how to create and initialize new Odoo databases from the command line. There are more commands worth knowing for managing databases. 153 | 154 | You already know how to use the createdb command to create empty databases, but it can also create a new database by copying an existing one, by using a --template option. 155 | 156 | Make sure your Odoo instance is stopped and you have no other connection open on the v8dev database created above, and run: 157 | 158 | ```shell 159 | $ createdb --template=v8dev v8test 160 | ``` 161 | 162 | In fact, every time we create a database, a template is used. If none is speci ed, a prede ned one called template1 is used. 163 | 164 | To list the existing databases in your system use the PostgreSQL utility psql utility with the -l option: 165 | 166 | ```shell 167 | $ psql -l 168 | ``` 169 | 170 | Running it we should see listed the two databases we created so far: v8dev and v8test. The list will also display the encoding used in each database. The default is UTF8, which is the encoding needed for Odoo databases. 171 | 172 | To remove a database you no longer need (or want to recreate), use the dropdb command: 173 | 174 | ```shell 175 | $ dropdb v8test 176 | ``` 177 | 178 | Now you know the basics to work with several databases. To learn more on PostgresSQL, the of cial documentation can be found at http://www.postgresql. org/docs/ 179 | 180 | >### WARNING: 181 | >The drop database will irrevocably destroy your data. Be careful when using it and always keep backups of your important databases before using it. 182 | 183 | ## A word about Odoo product versions 论Odoo的生产版本 184 | At the date of writing, Odoo's latest stable is version 8, marked on GitHub as branch 8.0. This is the version we will work with throughout the book. 185 | 186 | 当编写本书的时候,Odoo的最新稳定本是8,在Github上标记为分支8.0。整本书使用的都是这一个版本。 187 | 188 | It's important to note that Odoo databases are incompatible between Odoo major versions. This means that if you run Odoo 8 server against an Odoo/OpenERP 7 database, it won't work. Non-trivial migration work is needed before a database can be used with a later version of the product. 189 | 190 | The same is true for modules: as a general rule a module developed for an Odoo major version will not work with other versions. When downloading a community module from the Web, make sure it targets the Odoo version you are using. 191 | On the other hand, major releases (7.0, 8.0) are expected to receive frequent updates, but these should be mostly xes. They are assured to be "API stable", meaning 192 | that model data structures and view element identi ers will remain stable. This is important because it means there will be no risk of custom modules breaking due to incompatible changes on the upstream core modules. 193 | 194 | And be warned that the version in the master branch will result in the next major stable version, but until then it's not "API stable" and you should not use it to build custom modules. Doing so is like moving on quicksand: you can't be sure when some changes will be introduced that will make you custom module break. 195 | 196 | ## More server configuration options 另外的服务器配置选项 197 | The Odoo server supports quite a few other options. We can check all available options with the --help option: 198 | 199 | ```shell 200 | $ ./odoo.py --help 201 | ``` 202 | 203 | It's worth while to have an overview on the most important ones. 204 | 205 | ### Odoo server configuration files Odoo的服务端配置文件 206 | Most of the options can be saved in a con guration le. By default, Odoo will use the .openerp-serverrc le in your home directory. Conveniently, there is also the --save option to store the current instance con guration into that file: 207 | 208 | ```shell 209 | $ ~/odoo-dev/odoo/odoo.py --save --stop-after-init # save configuration to file 210 | ``` 211 | 212 | Here we also used the `--stop-after-init` option, to have the server stop after it nishes its actions. This option is often used when running tests or asking to run a module upgrade to check if it installs correctly. 213 | 214 | Now we can inspect what was saved in this default configuration file: 215 | 216 | ```shell 217 | $ more ~/.openerp_serverrc # show the configuration file 218 | ``` 219 | 220 | This will show all con guration options available with the default values for them. Editing them will be effective the next time you start an Odoo instance. Type q to quit and go back to the prompt. 221 | 222 | We can also choose to use a speci c con guration le, using the `--conf=` option. Con guration les don't need to have all those the options you've just seen. Only the ones that actually change a default value need to be there. 223 | 224 | ### Changing the listening port 变更侦听端口 225 | The `--xmlrpc-server=` command allows us to change the default 8069 port where the server instance listens. This can be used to run more than one instances at the same time, on the same server. 226 | 227 | `--xmlrpc-server=`命令允许我们改变服务器实例侦听的默认8069端口。这就可以在相同的服务器上同一时间运行多个实例。 228 | 229 | Let's try that. Open two terminal windows. On the first one run: 230 | 231 | 让我们来试一试。打开终端窗口。在第一个终端中运行下面的命令: 232 | 233 | ```shell 234 | $ ~/odoo-dev/odoo.py --xmlrpc-port=8070 235 | ``` 236 | 237 | and on the other run: 238 | 239 | 接着在另外一个终端运行: 240 | 241 | ```shell 242 | $ ~/odoo-dev/odoo.py --xmlrpc-port=8071 243 | ``` 244 | 245 | And there you go: two Odoo instances on the same server listening on different ports. The two instances can use the same or different databases. And the two could be running the same or different versions of Odoo. 246 | 247 | 这里你可以看到:在同一个服务器上两个Odoo实例分别侦听了不同的端口。 248 | 249 | ### Logging 日志 250 | The `--log-level` option allows us to set the log verbosity. This can be very useful to understand what is going on in the server. For example, to enable the debug log level use: `--log-level=debug` 251 | 252 | `--log-level`选项允许我们设置详细日志。这对于理解服务器上所发生的事情非常有帮助。例如,启用调试日志级别为:`--log-level=debug` 253 | 254 | The following log levels can be particularly interesting: 255 | 256 | 下面的日志级别都有各自特别的用处: 257 | 258 | - debug_sql to inspect SQL generated by the server 259 | - debug_rpc to detail the requests received by the server 260 | - debug_rpc to detail the responses sent by the server 261 | 262 | - debug_sql 检查服务器生成的SQL 263 | - debug_rpc 详细打印服务器接收到的请求 264 | - debug_rpc 详细打印服务端发生的响应 265 | 266 | By default the log output is directed to standard output (your console screen), but it can be directed to a log le with the option `--logfile=`. 267 | 268 | 默认,日志直接地输出标准输出(在你的控制台屏幕),不过日志也可使用选项`--logfile=`直接地记录到日志文件。 269 | 270 | Finally, the `--debug` option will bring up the Python debugger (`pdb`) when an exception is raised. It's useful to do a post-mortem analysis of a server error. Note that it doesn't have any effect on the logger verbosity. More details on the Python debugger commands can be found here: https://docs.python.org/2/library/pdb.html#debugger-commands. 271 | 272 | 最后,当一个异常被抛出时,`--debug`选项将引用Python调试器(pdb)。注意该选项对于记录详细日志没有任何的效果。这里,你可以找到关于Python调试器的更多信息:https://docs.python.org/2/library/pdb.html#debugger-commands。 273 | 274 | ## Developing from your workstation 在自己工作站上进行开发 275 | You may be running Odoo with a Debian/Ubuntu system, either in a local virtual machine or in a server over the network. But you may prefer to do the development work in your personal workstation, using your favorite text editor or IDE. 276 | 277 | This may frequently be the case for developers working from Windows workstations. But it also may be the case for Linux users that need to work on an Odoo server over the local network. 278 | 279 | A solution for this is to enable le sharing in the Odoo host, so that les are easy to edit from our workstation. For Odoo server operations, such as a server restart, we can use an SSH shell (such as PuTTY on Windows) alongside our favorite editor. 280 | 281 | ### Using a Linux text editor 使用Linux下的文本编辑器 282 | Sooner or later, we will need to edit les from the shell command line. In many Debian systems the default text editor is vi. If you're not comfortable with it, then you probably could use a friendlier alternative. In Ubuntu systems the default text editor is nano. You might prefer it since it's easier to use. In case it's not available in your server, it can be installed with: 283 | 284 | ```shell 285 | $ sudo apt-get install nano 286 | ``` 287 | 288 | In the following sections we will assume nano as the preferred editor. If you prefer any other editor, feel free to adapt the commands accordingly. 289 | 290 | ### Installing and configuring Samba Samba的安装和配置 291 | The Samba project provides Linux le sharing services compatible with Microsoft Windows systems. We can install it on our Debian/Ubuntu server with: 292 | 293 | ```shell 294 | $ sudo apt-get install samba samba-common-bin 295 | ``` 296 | 297 | The samba package installs the file sharing services and the samba-common-bin package is needed for the smbpasswd tool. By default users allowed to access shared les need to be registered with it. We need to register our user odoo and set a password for its le share access: 298 | 299 | ```shell 300 | $ sudo smbpasswd -a odoo 301 | ``` 302 | 303 | After this the odoo user will be able to access a leshare for its home directory, but it will be read only. We want to have write access, so we need to edit Sambas, con guration le to change that: 304 | 305 | ```shell 306 | $ sudo nano /etc/samba/smb.conf 307 | ``` 308 | 309 | In the con guration file, look for the [homes] section. Edit its con guration lines so that they match the settings below: 310 | 311 | ```shell 312 | [homes] 313 | comment = Home Directories 314 | browseable = yes 315 | read only = no 316 | create mask = 0640 317 | directory mask = 0750 318 | ``` 319 | 320 | For the con guration changes to take effect, restart the service: 321 | 322 | ```shell 323 | $ sudo /etc/init.d/smbd restart 324 | ``` 325 | 326 | >### Tip 327 | >Downloading the example code 328 | >You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www. packtpub.com/support and register to have the files e-mailed directly to you. 329 | 330 | >### 提示 331 | >下载示例代码 332 | >你可以从Packt图书下载所有的示例代码。如果你从其他地方购买本书,你可以访问http://www. packtpub.com/support ,注册后会有文件直接电邮给你。 333 | 334 | To access the les from Windows, we can map a network drive for the path `\\\odoo` using the speci c user and password de ned with smbpasswd. When trying to log in with the odoo user, you might nd trouble with Windows adding the computer's domain to the user name (for example `MYPC\odoo`). To avoid this, use an empty domain by prepending a \ to the login (for example `\odoo`). 335 | 336 | img:omit 337 | 338 | If we now open the mapped drive with Windows Explorer, we will be able to access and edit the contents of the odoo user home directory. 339 | 340 | img:oimt 341 | 342 | ## Enabling the on-board technical tools 启动自带的技术工具 343 | Odoo includes some tools that are very helpful for developers, and we will make use of them throughout the book. They are the Technical Features and the Developer Mode. 344 | 345 | These are disabled by default, so this is a good moment to learn how to enable them. 346 | 347 | ### Activating the Technical Features 激活技术功能 348 | Technical Features provide advanced server con guration tools. 349 | 350 | They are disabled by default, and to enable them, we need to log in as admin. In the Settings menu, select Users and edit the Administrator user. In the Access Rights tab, you will nd a Technical Features checkbox. Let's check it and save. 351 | 352 | Now we need to reload the page in our web browser. Then we should see in the Settings menu a new Technical menu section giving access to many Odoo server internals. 353 | 354 | The Technical menu option allows us to inspect and edit all Odoo con gurations stored in the database, from user interface to security and other system parameters. You will be learning more about many of these throughout the book. 355 | 356 | ### Activating the Developer mode 激活开发者模式 357 | The Developer mode enables a combobox near the top of Odoo windows, making a few advanced con guration options available throughout the application. It also disables the mini cation of JavaScript and CSS used by the web client, making it easier to debug client-side behavior. 358 | 359 | To enable it, open the drop-down menu from the top-right corner of the browser window, next to the username, and select the About Odoo option. In the About dialog, click on the Activate the developer mode button at the top-right corner. 360 | 361 | After this, we will see a Debug View combo box at the top left of the current form area. 362 | 363 | ## Installing third-party modules 安装第三方模块 364 | Making new modules available in an Odoo instance so they can be installed is something that newcomers to Odoo frequently nd confusing. But it doesn't have to be so, so let's demystify it. 365 | 366 | ### Finding community modules 雪照社区模块 367 | There are many Odoo modules available from the Internet. The apps.odoo.com website is a catalogue of modules that can be downloaded and installed in your system. The Odoo Community Association (OCA) coordinates community contributions and maintains quite a few module repositories on GitHub, at https://github.com/OCA/ 368 | 369 | 因特网上有很多的可用的Odoo模块。 370 | 371 | To add a module to an Odoo installation we could just copy it into the addons directory, alongside the official modules. In our case, the addons directory is at ~/odoo-dev/odoo/addons/. This might not be the best option for us, since our Odoo installation is based on a version controlled code repository, and we will want to keep it synchronized with the GitHub repository. 372 | 373 | 要对Odoo添加一个模块,你可以直接将模块复制到插件目录,和官方模块放倒一起。对我们而言,插件目录位于~/odoo-dev/odoo/addons/。对我们来说这可能不是最好的选择,因为Odoo的安装基于了版本控制的代码仓库,我们 374 | 375 | Fortunately, we can use additional locations for modules, so we can keep our custom modules in a different directory, without having them mixed with the of cial addons. 376 | 377 | As an example, we will download the OCA project department and make its modules available in our Odoo installation. This project is a set of very simple modules adding a Department eld on several forms, such as Projects or CRM Opportunities. 378 | 379 | To get the source code from GitHub: 380 | 381 | ```shell 382 | $ cd ~/odoo-dev 383 | $ git clone https://github.com/OCA/department.git -b 8.0 384 | ``` 385 | 386 | We used the optional -b option to make sure we are downloading the modules for the 8.0 version. Since at the moment of writing 8.0 is the projects default branch we could have omitted it. 387 | 388 | After this, we will have a new `/department` directory alongside the `/odoo` directory, containing the modules. Now we need to let Odoo know about this new module directory. 389 | 390 | ### Configuring the addons path 配置插件目录 391 | The Odoo server has a con guration option called addons-path setting where to look for modules. By default this points at the /addons directory where the Odoo server is running. 392 | 393 | Fortunately, we can provide Odoo not only one, but a list of directories where modules can be found. This allows us to keep our custom modules in a different directory, without having them mixed with the of cial addons. 394 | 395 | Let's start the server with an addons path including our new module directory: 396 | 397 | ```shell 398 | $ cd ~/odoo-dev/odoo 399 | $ ./odoo.py -d v8dev --addons-path="../department,./addons" 400 | ``` 401 | 402 | If you look closer at the server log you will notice a line reporting the addons path in use: **INFO ? openerp: addons paths**: (...). Con rm that it contains our department directory. 403 | 404 | ### Updating the module list 更新模块列表 405 | We still need to ask Odoo to update its module list before these new modules are available to install. 406 | 407 | For this we need the Technical menu enabled, since the Update Modules List menu option is provided by it. It can be found in the Modules section of the Settings menu. 408 | 409 | After running the modules list update we can con rm the new modules are available to install. In the Local Modules list, remove the Apps lter and search for department. You should see the new modules available. 410 | 411 | ## Summary 总结 412 | In this chapter, you learned how to set up a Debian system to host Odoo and to install it from GitHub sources. We also learned how to create Odoo databases and run Odoo instances. To allow developers to use their favorite tools in their personal workstation, we also explained how to con gure le sharing in the Odoo host. 413 | 414 | We should now have a functioning Odoo environment to work with and be comfortable managing databases and instances. 415 | 416 | With this in place, we're ready to go straight into the action. In the next chapter we will create from scratch our rst Odoo module and understand the main elements it involves. 417 | 418 | So let's get started! 419 | 420 | 421 | -------------------------------------------------------------------------------- /第三章-继承-扩展已有应用.md: -------------------------------------------------------------------------------- 1 | #Chapter 3 Inheritance – Extending Existing Applications 2 | ******************* 3 | One of Odoo's most powerful features is the ability to add features without directly modifying the underlying objects. 4 | Odoo最强大的一个特性便是能够不用直接修改底层对象就可以添加功能。 5 | 6 | This is achieved through inheritance mechanisms, functioning as modification layers on top of existing objects. These modifications can happen at all levels: models, views, and business logic. Instead of directly modifying an existing module, we create a new module to add the intended modifications. 7 | 8 | 使用继承机制,在已存在对象的上层运行修改,就可以实现这个目的。这些修改在所有级别都都会发生:模型,视图,以及业务逻辑。与直接修改已存在模块相反,我们应该创建一个新的模块,以便添加所期望的修改。 9 | 10 | Here, you will learn how to write your own extension modules, empowering you to leverage existing core or community applications. As a relevant example, you will learn how to add Odoo's social and messaging features to your own modules. 11 | 12 | 这里,你可以学到如何编写自己的扩展模块,使你能够改变已经存在的内核或者社区应用。举个例子来说,你可以学会如何将Odoo的社交和消息功能添加到自己的模块。 13 | 14 | ## Adding sharing capability to the To-Do app 对To-Do应用添加共享 15 | Our To-Do application now allows users to privately manage their own to-do 16 | tasks. Won't it be great to take the app to another level by adding collaboration and social networking features to it? We will be able to share tasks and discuss them with other people. 17 | 18 | 我们的To-Do应用现状可以让用户独立地管理自己的to-do任务。通过添加协作和社交网络功能就可以将应用提升到另外一个层次岂不是更好?这样,我们能够和其它人分享和讨论任务。 19 | 20 | We will do this with a new module to extend the previously created To-Do app to add these new features. Here is what we expect to achieve by the end of this chapter: 21 | 22 | 我们将通过创建一个新的模块来扩张值钱创建的To-Do应用已添加这些新功能。下图是我们希望在本章结束时可以实现的目标: 23 | 24 | img:omit 25 | 26 | ### Road map for the user sharing features 用户分享的路线图 27 | Here is our work plan for the feature extensions to be implemented: 28 | 29 | 这里是我们将要实现的功能扩展的工作计划: 30 | 31 | - Add fields to the Task model, such as the user who owns the task 32 | - Modify the business logic to operate only on the current user's tasks, instead of all tasks the user is able to see 33 | - Add the necessary fields to the views 34 | - Add social networking features: the message wall and the followers 35 | 36 | - 添加字段到Task模型,比如任务的创建人。 37 | - 仅对当前用户修改业务逻辑已操作,而不是修改用户可以看到的所有用户。 38 | - 对视图添加必须要的视图。 39 | - 添加社交网络功能:消息墙和粉丝。 40 | 41 | We will start creating the basic skeleton for the module alongside the todo_app module. Following the installation example in Chapter 1, Getting Started with Odoo Development we are hosting our modules at `~/odoo-dev/custom-addons/`: 42 | 43 | 我们从在todo_app模块旁边为模块创建基本的框架开始。下面是一个第一章——Odoo开发入门钟的的安装示例,我们将这个模块放到`~/odoo-dev/custom-addons/`: 44 | 45 | ```shell 46 | $ mkdir ~/odoo-dev/custom-addons/todo_user 47 | $ touch ~/odoo-dev/custom-addons/todo_user/__init__.py 48 | ``` 49 | 50 | Now create the `__openerp__.py`, containing this code: 51 | 52 | 现在,创建一个包含以下代码的文件`__openerp__.py`: 53 | 54 | ```json 55 | { 'name': 'Multiuser To-Do', 56 | 'description': 'Extend the To-Do app to multiuser.', 57 | 'author': 'Daniel Reis', 58 | 'depends': ['todo_app'], } 59 | ``` 60 | 61 | We haven't done that, but including the summary and category keys can be important when publishing modules to the Odoo online app store. 62 | 63 | 我们的工作还没有完成,而且对summary和category键的包括在发布模块到Odoo在线应用商店时特别重要。 64 | 65 | Next, we can install it. It should be enough to update the Modules List from the Settings menu, find the new module in the Local Modules list and click on its Install button. For more detailed instructions on discovering and installing a module you can refer back to `Chapter 1, Getting Started with Odoo Development`. 66 | 67 | 接下来,我们可以安装这个模块。现在是时候从设置菜单更新模块列表了,找到本地模块中的新模块,然后点击它的安装按钮。关于发现和安装一个模块更多详细说明你可以回溯到`第一章,Odoo开发入门`。 68 | 69 | Now, let's start adding the new features to it. 70 | 71 | 现在,我们开始对它添加新的功能。 72 | 73 | ## Extending the to-do task model 扩展to-do task模型 74 | New models are defined through Python classes. Extending them is also done through Python classes, but using an Odoo specific mechanism. 75 | 76 | 新模块是通过Python类来定义的。所以扩展模块也可以通过编写Python类来实现,而不是只使用Odoo的专用机制。 77 | 78 | To extend an existing model we use a Python class with a `_inherit` attribute. This identifies the model to be extended. The new class inherits all the features of the parent Odoo model, and we only need to declare the modifications that we wish to introduce. 79 | 80 | 我们使用一个拥有`_inherit`属性的Python类来扩展已有模型。这个属性表明模型可以被扩展。新类继承了父级Odoo模型全部的特性,我们只需要声明希望应用的修改。 81 | 82 | In fact, Odoo models exist outside our particular module, in a central registry. This registry can also be referred to as the pool, and can be accessed from model methods using `self.env[]`. For example, to reference the `res.partner` model we would write `self.env['res.partner']`. 83 | 84 | 实际上,Odoo模块存在于特殊模块之外,在一个注册中心里面。这个注册中心可以被引用为pool,也可以使用`self.env[]`从模块的方法来访问。例如,要引用`res.partner`模型,我们可以这样写`self.env['res.partner']`。 85 | 86 | To modify an Odoo model we get a reference to its registry class and then perform in place changes on it. This means that these modifications will also be available everywhere else where the model is used. 87 | 88 | 如果修改一个引用自模型注册中心的Odoo模型,那么可以模型执行原地修改。这就意味着,这些修改也可以运用在其它的使用到模型的地方。 89 | 90 | In the module loading sequence, during a server start, modifiations will only be visible to the modules loaded afterward. So, the loading sequence is important and we should make sure that the module dependencies are correctly set. 91 | 92 | 按照模块的载入顺序,当服务器启动时,修改仅在模块被载入后可以看到。所以,载入顺序很很重要,我们应该保证模块依赖被正确地设置。 93 | 94 | ### Adding fields to a model 对模型添加字段 95 | We will extend the `todo.task` model to add a couple of fields to it: the user responsible for the task, and a deadline date. 96 | 97 | 我们扩展todo.task模型以添加几个字段:任务的负责人以及截止日期。 98 | 99 | Create a new `todo_task.py` file declaring a class extending the original model: 100 | 101 | 创建一个新的`todo_task.py` 文件以声明扩展原始模型的类: 102 | 103 | ```python 104 | # -*- coding: utf-8 -*- 105 | from openerp import models, fields, api 106 | 107 | 108 | class TodoTask(models.Model): 109 | _inherit = 'todo.task' 110 | 111 | user_id = fields.Many2one('res.users', 'Responsible') 112 | date_deadline = fields.Date('Deadline') 113 | ``` 114 | 115 | The class name is local to this Python file, and in general is irrelevant for other modules. The `_inherit` class attribute is the key here: it tells Odoo that this class is inheriting from the todo.task model. Notice the `_name` attribute absent. It is not needed because it is already inherited from the parent model. 116 | 117 | 类名称在这个Python文件是个本地变量,通常与其它模块没有关联。类属性`_inherit`的重点是:它告诉Odoo这个类是从todo.task模型继承而来的。注意`_name`属性的缺失。我们并不需要`_name`属性,因为这个属性已经从父模型继承了。 118 | 119 | The next two lines are regular field declarations. The `user_id` represents a user from the Users model, `res.users`. It's a Many2one field, the equivalent to a foreign key in database jargon. The date_deadline is a simple date field. In **Chapter 5, Models Structuring the Application Data** we will be explaining in more detail the types of fields available in Odoo. 120 | 121 | 接下来的两行识常规字段的声明。`user_id`表示用户来自用户模型`res.users`。`res.users`是一个多对一字段,在数据库术语中它等同于外键。date_deadline 是一个简单的日期字段。在第五章——模型——结构化应用数据,我们会详细解释Odoo中可用的字段类型。 122 | 123 | We still need to add to the `__init__.py` file the import statement to include it in the module: 124 | 125 | 我们需要在模块中对`__init__.py`文件添加import语句以导入模块。 126 | 127 | ```python 128 | from . import todo_task 129 | ``` 130 | 131 | To have the new fields added to the model's supporting database table we need to perform a module upgrade. If everything goes as expected, you should see the new fields when inspecting the `todo.task` model, in the **Technical** menu, **Database Structure** | **Models** option. 132 | 133 | 为了将新字段添加到模型支持的数据库表中,我们需要执行模块升级。如果没有问题话,你可以在检查Technical菜单的Database Structure| Models选项的`todo.task`模型时看到新字段。 134 | 135 | ### Modifying existing fields 修改已存在的字段 136 | As you can see, adding new fields to an existing model is quite straightforward. Since Odoo 8, modifying attributes on already existing fields is also possible. It's done by adding a field with the same name, and setting values only for the attributes to be changed. 137 | 138 | 如你所见,对现有模型添加新字段相当简单直接。从Odoo 8开始,修改已存在的字段的属性也是可行的。通过添加一个同名字段,然后只对需要改变的属性设置值。 139 | 140 | For example, to add a help tooltip to the name field, we could add this line to the `todo_task.py` described above: 141 | 142 | 例如,对name字段添加一个工具提示,我们对上面提到的`todo_task.py`添加下面这一行内容: 143 | 144 | ```python 145 | name = fields.Char(help="What needs to be done?") 146 | ``` 147 | 148 | If we upgrade the module, go to a to-do task form, and pause the mouse pointer over the **Description** field, the above tooltip text will be displayed. 149 | 150 | 如果需要我们升级模块,那么可以找到to-do task表单,然后把鼠标指针悬停在Description字段,上面的工具提示内容就会显示出来。 151 | 152 | ### Modifying model's methods 修改模型的方法 153 | Inheritance also works at the business logic level. Adding new methods is simple: just declare their functions inside the inheriting class. 154 | 155 | 继承也可以运用在业务逻辑层面。添加新方法很简单:在继承类的内部声明类的函数就好了。 156 | 157 | To extend existing logic, the corresponding method can be overridden by declaring a method with the exact same name, and the new method will replace the previous one. But it can extend the code of the inherited class, by using Python's `super()` keyword to call the parent method. 158 | 159 | 要扩展现存的逻辑,你可以通过声明一个完全相同的名字来重写对应的方法,这样新方法就会替换之前的那个方法。不过,使用Python的`super()`关键字去调用父级方法就可以扩展类的代码。 160 | 161 | It's best to avoid changing the method's function signature (that is, keep the same arguments) to be sure that the existing calls on it will keep working properly. In case you need to add additional parameters, make them optional (with a default value) keyword arguments. 162 | 163 | 最好避免改变方法函数的签名(也就是,使用相同的参数)以保证已经调用这个函数能够正常使用。这种情况,你需要添加额外的参数,对函数应用可选的(携带默认值)关键字参数。 164 | 165 | The original `Clear All Done` action is not appropriate for our task-sharing module anymore, since it clears all tasks regardless of their user. We need to modify it so that it clears only the current user tasks. 166 | 167 | 原始的`Clear All Done`动作对于task-sharing模块来说已经不在适用,因为这个动作无视模块的用户就去清除所有的任务。我们需要修改它,让它只能够修改当前用户的任务。 168 | 169 | For this, we will override the original method with a new version that first finds the list of completed tasks for the current user, and then inactivates them: 170 | 171 | 为此,我们用一个新方法来重写原始的方法,该新方法首先找到当前用户的已完成任务列表,然后激活这些列表: 172 | 173 | ```python 174 | @api.multi 175 | def do_clear_done(self): 176 | domain = [('is_done', '=', True), '|', ('user_id', '=', self.env.uid), ('user_id', '=', False)] 177 | done_recs = self.search(domain) 178 | done_recs.write({'active': False}) 179 | return True 180 | ``` 181 | 182 | We first list the `done` records to act upon using the search method with a filter expression. The filter expression follows an Odoo speci c syntax referred to as a domain. 183 | 184 | 我们首先列出 `done` 记录,以作用于上面的使用过滤器表达式的search方法。过滤器表达式依照被引用为域的Odoo专用语法。 185 | 186 | The filter domain used is defined the first instruction: it is a list of conditions, where each condition is a tuple. 187 | These conditions are implicitly joined with an AND operator ('&' in domain syntax). To add an OR operation a pipe ('|') is used in place of a tuple, and it will affect the next two conditions. We will go into more details about domains in `Chapter 6, Views - Designing the User Interface`. 188 | 189 | 过滤器的域用来定义第一条指令:这是一个条件列表,其中每个条件都是一个元组。这些条件都隐式地使用了AND运算符('&')连接。为了添加OR运算符,管道('|')就被放倒了元组中使用,它会影响到接下来的两个条件。我们会在第六章-视图——设计用户界面中了解关于域的更多细节。 190 | 191 | The domain used here filters all done tasks (`'is_done', '=', True`) that either have the current user as responsible (`'user_id', '=', self.env.uid`) or don't have a current user set (`'user_id', '=', False`). 192 | 193 | 这里用域来过滤所有的已完成任务(`'is_done', '=', True`),其中这些已完成任务可以是当前用户作为负责人(`'user_id', '=', self.env.uid`),也可以是非当前用户。 194 | 195 | What we just did was to completely overwrite the parent method, replacing it with a new implementation. 196 | 197 | 我们刚刚完全地重写了父方法,即,使用新的实现替换了它。 198 | 199 | But this is not what we usually want to do. Instead we should extend the existing logic to add some additional operations to it. Otherwise we could break already existing features. Existing logic is inserted in an overriding method using Python's super() command, to call the parent's version of the method. 200 | 201 | 但是这并不是我们通常想要做的。相反我们应该扩展已存在的逻辑以添加一些额外的操作。否则,我们会破坏已存在的功能。使用Python的super()命令可以把现存的逻辑插入到方法中,以调用父版本的方法。 202 | 203 | Let's see an example of this: we could write a better version of `do_toggle_done()` that only performs its action on the Tasks assigned to our user: 204 | 205 | 然我们来看一看这个例子:我们可以编写一个更好版本的`do_toggle_done()`,这个方法只对分配到用户任务执行动作: 206 | 207 | ```python 208 | @api.one 209 | def do_toggle_done(self): 210 | if self.user_id != self.env.user: 211 | raise Exception('Only the responsible can do this!') 212 | else: 213 | return super(TodoTask, self).do_toggle_done() 214 | ``` 215 | 216 | These are the basic techniques for overriding and extending business logic defined in model classes. Next we will see how to extend the user interface views. 217 | 218 | 这些都是重写或者扩展定义在模型类中业务逻辑的基本技术。接下来,我们可以看到如何扩展用户界面视图。 219 | 220 | ## Extending views 扩展视图 221 | Forms, lists, and search views are defined using the arch XML structures. To extend views we need a way to modify this XML. This means locating XML elements and then introducing modifications on those points. 222 | 223 | 表单、列表、和搜索视图都是用XML的arch结构定义的。要扩展视图我们需要一种能够修改这个XML文件的方法。这意味定位XML元素,然后对这些点运用修改。 224 | 225 | Inherited views allow just that. An inherited view looks like this: 226 | 227 | 继承的视图就可以实现这个目的。继承的视图的样子大概如此: 228 | 229 | ```xml 230 | 231 | Todo Task form – User extension 232 | todo.task 233 | 234 | 235 | 236 | 237 | 238 | ``` 239 | 240 | The `inherit_id` field identifies the view to be extended, by referring to its external identifier using the special ref attribute. External identifiers will be discussed in more detail in `Chapter 4, Data Serialization and Module Data`. 241 | 使用特殊ref属性来引用自身的外部标识符,视图的标识符字段`inherit_id就能够被扩展。外部标识符会在第四章——数据序列化和模块数据中详细谈论。 242 | 243 | The natural way to locate elements in XML is to use XPath expressions. For example, taking the form view defined in the previous chapter, the XPath expression to locate the `` element is: `//field[@name]='is_done'`. This expression finds a `field` element with a name attribute equal to `is_done`. You can find more information on XPath at: `https://docs.python.org/2/library/xml. etree.elementtree.html#xpath-support`. 244 | 245 | 定位在XML中元素的原生方法是使用XPath表示。例如,拿前面一章中定义的表单视图来说,其用来定位``元素的XPath表达式为 `//field[@name]='is_done'`。 该表达式查找一个拥有name属性等于`is_done`的`field`元素。你可以在这里找到更多关于XPath的信息:`https://docs.python.org/2/library/xml. etree.elementtree.html#xpath-support`. 246 | 247 | Having name attributes on elements is important because it makes it a lot easier to select them for extension points. Once the extension point is located, it can be modified or have XML elements added near it. 248 | 249 | 元素拥有name属性非常重要,因为这可以让扩展点在选择这些元素时更容易操作。只要扩展点被定位,元素就可以被修改,或者是在元素周围添加XML元素。 250 | 251 | As a practical example, to add the date_deadline field before the `is_done` field, we would write in the `arch`: 252 | 253 | 例如,在`is_done`字段之前添加date_deadline字段,我们在`arch`中写入内容: 254 | 255 | ```xml 256 | 257 | 258 | 259 | ``` 260 | 261 | Fortunately Odoo provides shortcut notation for this, so most of the time we can avoid the XPath syntax entirely. Instead of the `xpath` element above we can use the element type we want to locate and its distinctive attributes. The above could also be written as: 262 | 263 | 幸运地是Odoo对此提供了快捷标记,所以很多时候我们能够避免编写完整的XPath语法。与在上面使用的`xpath`元素相反,我们使用希望被定位的元素类型,以及该元素的不同属性。上面的内容也可以被写成: 264 | 265 | ```xml 266 | 267 | 268 | 269 | ``` 270 | 271 | Adding new fields next to existing fields is done often, so the `` tag is frequently used as the locator. But any other tag can be used: ``, ``, `
`, and so on. The name attribute is usually the best choice for matching elements, but sometimes, we may need to use `string` (the displayed label text) or the CSS `class` element. 272 | 273 | 在现有字段旁边添加新字段经常执行的操作,所以``标签常常被用做定位符。但是其它标签也可以拿来用:``, ``, `
`,等等。name属性通常是用来匹配元素的最佳选择,但是有时候,我们也需要使用`string`(显示在页面的标签文本)或者CSS`class`元素。 274 | 275 | The `position` attribute used with the locator element is optional, and can have the following values: 276 | 277 | `position`元素和定位符的联合使用是可选的,并可以应用下面的值: 278 | 279 | - after: This is added to the parent element, after the matched node. 280 | - before: This is added to the parent element, before the matched node. 281 | - inside (the default value): This is appended to the content of the matched node. 282 | - replace: This replaces the matched node. If used with empty content, it deletes an element. 283 | - attributes: This modifies the XML attributes of the matched element (there are more details described following this list). 284 | 285 | - after: 添加到匹配节点之后到父元素。 286 | - before: 添加到匹配节点之前到父元素。 287 | - inside(该定位符为默认值):被追加到匹配节点的内容。 288 | - replace: 提花匹配的节点。如果使用了空白内容,它会将元素删除。 289 | - attributes: 修改匹配元素的XML属性(更多详情在下面的列表有描述) 290 | 291 | The `attribute` position allows us to modify the matched element's attributes. This is done using `` elements with the new attribute values. 292 | 293 | `attribute` 位置允许我们修改匹配元素的属性。使用拥有新属性值的``元素可以实现这种操作。 294 | 295 | In the Task form, we have the **Active** field, but having it visible is not that useful. Maybe, we can hide it from the user. This can be done setting its `invisible` attribute: 296 | 297 | 在Task表单中,我们设置了“Active”字段之,但是让这字段不可见不是太好用。或许,我们让它对用户进行隐藏。你可以通过对字段设置`invisible`属性来实现这个目的: 298 | 299 | ```xml 300 | 301 | 1 302 | 303 | ``` 304 | 305 | Setting the invisible attribute to hide an element is a good alternative to using the replace locator to remove nodes. Removing should be avoided, since it can break extension models that may depend on the deleted node. 306 | 307 | 设置不可见属性来隐藏一个元素是除了使用替换定位符来移除节点之外的另一个好选择。你应该避免执行移除操作,因为这样做会破坏扩展模型所依赖的可能被删除掉的节点。 308 | 309 | Finally, we can put all of this together, add the new fields, and get the following complete inheritance view to extend the to-do tasks form: 310 | 311 | 最后,我们可以将这些内容放到一起,添加新字段,然后以下面完整的继承视图来扩展to-do tasks表单: 312 | 313 | ```xml 314 | 315 | Todo Task form – User extension 316 | todo.task 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | I have to... 327 | 328 | 329 | 330 | ``` 331 | 332 | This should be added to a `todo_view.xml` file in our module, inside the `` and `` tags, as shown in the previous chapter. 333 | 334 | 就像前一章所示,这些内容应该被添加到模块中的`todo_view.xml`文件,``和``标签的内部。 335 | 336 | >### Note 337 | >Inherited views can also be inherited, but since this creates more intricate dependencies, it should be avoided. 338 | 339 | >### 注释 340 | >继承到视图还可以被继承,但是这样做会产生更加错综复杂的依赖关系,所以你应该避免这样做。 341 | 342 | Also, we should not forget to add the data attribute to the `__openerp__.py` descriptor file: 343 | 344 | 而且,我们不应该忘记将数据属性添加到描述符文件`__openerp__.py`: 345 | 346 | ```json 347 | 'data': ['todo_view.xml'], 348 | ``` 349 | 350 | ### Extending tree and search views 扩展树形视图和搜索视图 351 | Tree and search view extensions are also defined using the arch XML structure, and they can be extended in the same way as form views. We will follow example of a extending the list and search views. 352 | 353 | 树形视图和搜索视图扩展也可以使用arch XML结构来定义, 354 | 355 | For the list view, we want to add the user field to it: 356 | 357 | 对于列表视图,我们希望对它添加user字段: 358 | 359 | ```xml 360 | 361 | Todo Task tree – User extension 362 | todo.task 363 | 364 | 365 | 366 | 367 | 368 | 370 | ``` 371 | 372 | For the search view, we will add search by user, and predefined filters for the user's own tasks and tasks not assigned to anyone: 373 | 374 | 对于搜索视图,我们让用户来添加搜索,并给用户的所拥有的任务以及还没有分配给其它用户的用户预定义过滤器: 375 | 376 | ```xml 377 | 378 | Todo Task tree – User extension 379 | todo.task 380 | 381 | 382 | 383 | 384 | 386 | 388 | 389 | 390 | 391 | ``` 392 | 393 | Don't worry too much about the views-specific syntax. We'll cover that in more detail in `Chapter 6, Views - Designing the User Interface`. 394 | 395 | 不要太过担心视图专有语法。我们将在`第六章-视图-设计用户界面`中详细说明。 396 | 397 | ## More on using inheritance to extend models 使用继承扩展模型的更多话题 398 | We have seen the basic in place extension of models, which is also the most frequent use of inheritance. But inheritance using the `_inherit` attribute has more powerful capabilities, such as **mixin** classes. 399 | 400 | 我们已经看过了基本的原地模型扩展,这也是最常使用的继承方式。不过使用了`_inherit`属性的继承更加具有强大的兼容性,比如mixin类。 401 | 402 | We also have available the delegation inheritance method, using the `_inherits` attribute. It allows for a model to contain other models in a transparent way for the observer, while behind the scenes each model is handling its own data. 403 | 404 | 我们也可以使用`_inherits`来委托继承方法。这样能够让一个模型对于观察者来说透明地包含了其他模型,而后台中每个模型都可以处理自己的数据。 405 | 406 | Let's explore these possibilities in more detail. 407 | 408 | 我们来详细地浏览这些可能性。 409 | 410 | ### Copying features using prototype inheritance 使用原型继承来复制功能 411 | The method we used before to extend a model used just the `_inherit` attribute. We defined a class inheriting the `todo.task` model, and added some features to it. The class `_name` was not explicitly set; implicitly it was `todo.task` also. 412 | 413 | 我们之前使用的方法只是使用了 `_inherit`属性来扩展一个模型。我们定义了一个继承`todo.task`模型的类,并对类添加了一个功能。类的`_name`并没有明确设置,`todo.task`也是同样。 414 | 415 | But using the `_name` attribute allows us to create mixin classes, by setting it to the model we want to extend. Here is an example: 416 | 417 | 通过将`_name`属性设置为我们希望扩展的模型,它就允许我们创建mixin类。下面是例子: 418 | 419 | ```python 420 | from openerp import models 421 | 422 | 423 | class TodoTask(models.Model): 424 | _name = 'todo.task' 425 | _inherit = 'mail.thread' 426 | ``` 427 | 428 | This extends the `todo.task` model by copying to it the features of the `mail.thread` model. The `mail.thread` model implements the Odoo messages and followers features, and is reusable, so that it's easy to add those features to any model. 429 | 430 | 本次对`todo.task`模型的扩展是通过将这个模型复制`mail.thread`模型的功能而实现的。`mail.thread`模型实现Odoo消息和关注人功能,而且是可以被重复使用的,因此,我们可以很轻松地将这些功能添加任何一个模型。 431 | 432 | Copying means that the inherited methods and fields will also be available in the inheriting model. For fields this means that they will be also created and stored in the target model's database tables. The data records of the original (inherited) and the new (inheriting) models are kept unrelated. Only the definitions are shared. 433 | 434 | 复制意味着继承的方法和字段可以在继承模型中使用。对于字段来说,这就意味着它们可以被创建并存春刀目标模型的数据库表中。陨石的数据集(被继承的)和新(在继承的)模型是存在关联的。只有定义被共享。 435 | 436 | These mixins are mostly used with abstract models, such as the `mail.thread` used in the example. Abstract models are just like regular models except that no database representation is created for them. They act like templates, describing fields and logic to be reused in regular models. The fields they define will only be created on those regular models inheriting from them. In a moment we will discuss in detail how to use this to add `mail.thread` and its social networking features to our module. In practice when using mixins we rarely inherit from regular models, because this causes duplication of the same data structures. 437 | 438 | 这些mixin大多数时间是和抽象模型一起使用的,比如例子中的`mail.thread`。除了没有为抽象模型创建数据库表现之外,抽象模型就和普通的模型一样。它们的行为像是模板,描述了在普通模型可以重复使用的字段和逻辑。抽象模型定义的字段只会在继承自己的普通模型上创建。过一会儿,我们会讨论详细如何使用抽象来对自定义的模块添加`mail.thread`和社交网络功能。实际上,当使用mixin时,我们很少从普通模型继承,因为这样做会造成相同数据结构的重复。 439 | 440 | Odoo provides the delegation inheritance mechanism, which avoids data structure duplication, so it is usually preferred when inheriting from regular models. Let's look at it in more detail. 441 | 442 | Odoo提供了委托继承机制,它可以避免数据结构的重复,所以从普通模型执行继承时通常偏向于选择此种机制。我们来看一看委托继承的更多详情。 443 | 444 | ### Embedding models using delegation inheritance 使用委托继承来嵌入模型 445 | Delegation inheritance is the less frequently used model extension method, but it can provide very convenient solutions. It is used through the `_inherits` attribute (note the additional -s) with a dictionary mapping inherited models with fields linking to them. 446 | 447 | 委托继承是很少用到的的模型扩展方式,但是它可以提供非常便捷的解决办法。委托继承通过使用`_inherits` 448 | 449 | A good example of this is the standard Users model, res.users, that has a Partner model embedded in it: 450 | 451 | 这种情况的一个好例子就是标准的拥有内嵌Partner模型的User模型res.users: 452 | 453 | ```python 454 | from openerp import models, fields 455 | 456 | 457 | class User(models.Model): 458 | _name = 'res.users' 459 | _inherits = {'res.partner': 'partner_id'} 460 | partner_id = fields.Many2one('res.partner') 461 | ``` 462 | 463 | With delegation inheritance the model `res.users` embeds the inherited model `res.partner`, so that when a new `User` is created, a partner is also created and a reference to it is kept in the `partner_id` field of the `User`. It is similar to the polymorphism concept in object oriented programming. 464 | 465 | 模型`res.users`使用委托继承,嵌入了继承模型`res.partner`,所以,当`User`创建时,合作伙伴也被创建,对合作伙伴的引用也被保存在`User`的`partner_id`字段中。这类似于面向对象编程中的多态概念。 466 | 467 | All fields of the inherited model, Partner, are available as if they were User fields, through the delegation mechanism. For example, the partner name and address fields are exposed as User fields, but in fact they are being stored in the linked Partner model, and no data structure duplication occurs. 468 | 469 | 在使用委托机制的情况下,如果继承模型——Partner出现在User字段中,那么Partner模型的所有字段都可以被使用的。例如,合作伙伴的名字和地址字段暴露在了User的字段,但是,实际情况是这些字段存储在链接的Partner模型中,而且也不会有数据结构重复的事情发生。 470 | 471 | The advantage of this, compared to prototype inheritance, is that there is no need 472 | to repeat data structures in many tables, such as addresses. Any new model that needs to include an address can delegate that to an embedded Partner model. And if modifications are introduced in the partner address fields or validations, these are immediately available to all the models embedding it! 473 | 474 | 继承的优势在于,相较于原型继承,委托继承不需要在很多表中重复数据结构,比如地址。任何一个需要使用地址的新模型可以把自己委托到一个内嵌的Partner模型中。如果在partner的地址字段上应用修改或者是验证,这些操作在所有正内嵌Partner模型的模型中都是立即可用的。 475 | 476 | >### Note 477 | >Note that with delegation inheritance, fields are inherited, but methods are not. 478 | 479 | >### 注释 480 | >注意,使用委托继承时,只有字段被继承,方法却不能够被继承。 481 | 482 | ### Using inheritance to add social network features 运用继承添加社交网络功能 483 | The social network module (technical name mail) provides the message board found at the bottom of many forms, also called Open Chatter, the followers are featured along with the logic regarding messages and notifications. This is something we will often want to add to our models, so let's learn how to do it. 484 | 485 | 社交网络模块(技术名为mail)在很多表单的底部提供了消息面板,所以这个模块也称作Open Chatter,关注人关心的是消息和通知。这也是我们经常对自己模型多做的事情,那么我们来学习一下它是如何实现的。 486 | 487 | The social network messaging features are provided by the mail.thread model of the mail module. To add it to a custom model we need to: 488 | 489 | 社交网络消息的功能是由mail模块中的mail.thread模型提供的。要把mail.thread添加到自定义模型我们需要: 490 | 491 | - Have the module depend on mail. 492 | - Have the class inherit from mail.thread. 493 | - Have the Followers and Thread widgets added to the form view. 494 | - Optionally, set up record rules for followers. 495 | 496 | - 模型依赖于mail。 497 | - 类从mail.thread继承。 498 | - 对表单视图添加了关注人和Thread部件。 499 | - 可选择的是,你可以对关注人设置记录规则。 500 | 501 | Let's follow this checklist: 502 | 503 | 我们来检查一下这张清单: 504 | 505 | Regarding `#1`, since our extension module depends on todo_app, which in turn depends on mail, the dependency on mail is already implicit, so no action is needed. 506 | 507 | 关于第一条内容,因为我们的扩展模块依赖于todo_app,按照依赖顺序是mail,对mail的依赖也是隐式包含的,所以也不需要动作。 508 | 509 | Regarding `#2`, the inheritance on mail.thread is done using the `_inherit` attribute we used before. But our to-do task extension class is already using the `_inherit` attribute. Fortunately it can also accept a list of models to inherit from, so we can use that to make it also include the inheritance on `mail.thread`: 510 | 511 | 关于第二条内容,对`mail.thread`的继承是通过我们之前使用过的`_inherit`属性来实现的。而且我们的to-do task扩展类已经使用了`_inherit`属性。幸运的是,它也可以接受需要继承的一个模型列表,所以使用这个方法,可以让我们对`mail.thread`继承包括在内。 512 | 513 | ```python 514 | _name = 'todo.task' 515 | _inherit = ['todo.task', 'mail.thread'] 516 | ``` 517 | 518 | The `mail.thread` model is an abstract model. Abstract models are just like regular models except that they don't have a database representation; no actual tables are created for them. Abstract models are not meant to be used directly. Instead they are expected to be used in mixin classes, as we just did. We can think of them as templates with ready-to-use features. To create an abstract class we just need it to use `models.AbstractModel` instead of `models.Model`. 519 | 520 | `mail.thread`模型是一个抽象模型。抽象模型除了不拥有数据库表现(没有创建实际的表)之外它们就和普通模型一样。抽象模型并不可以直接使用。相反它们被期望用在mixin类中,就我们刚才所作的那样。我们把它们认为是拥有开箱即用特性的模板。创建一个抽象类,我们只要使用`models.AbstractModel`而不是`models.Model`就好了。 521 | 522 | For `#3`, we want to add the social network widgets at the bottom of the form. We can reuse the inherited view we already created, `view_form_todo_task_inherited`, and add this into its arch data: 523 | 524 | 对于第三条内容,我们想要在表单的底部添加社交网络部件。我们可以重复使用我们之前已经创建的继承了的视图,`view_form_todo_task_inherited`,然后对这个视图添加arch数据: 525 | 526 | ```xml 527 | 528 |
529 | 530 | 531 |
532 |
533 | ``` 534 | 535 | The two fields we add here haven't been explicitly declared by us, but they are provided by the `mail.thread` model. 536 | 537 | 我们添加到视图中两个字段并没明确声明,因为它们是由`mail.thread`提供的。 538 | 539 | The final step is to set up record rules for followers. This is only needed if our model has record rules implemented that limit other users' access to its records. In this case, we need to make sure that the followers for each record have at least read access to it. 540 | 541 | 最后一步为关注者设置记录规则。仅在我们实现了限制其他用户访问自定义模型的记录时才需要用到。在这种情况下,我们需要保证关注者对于每一条记录至少都能够访问记录。 542 | 543 | We do have record rules on the to-do task model so we need to address this, and that's what we will do in the next section. 544 | 545 | 我们拥有了to-do task模型的记录规则,所以我们需要记录规则定位,这就是我们在下一节要做的事情。 546 | 547 | ## Modifying data 修改数据 548 | Unlike views, regular data records don't have an XML arch structure and can't be extended using XPath expressions. But they can still be modified to replace values in their fields. 549 | 550 | 和视图不同,普通的数据记录并不包含XML的arch结构,而且也不能够使用XPath表达式来括扩展。但是它们仍旧可以被修改以替换它们的值。 551 | 552 | The `` element is actually performing an insert or update operation on the model: if x does not exist, it is created; otherwise, it is updated/written over. 553 | 554 | `` 元素实际上对模型执行的是一个插入或者更新的操作:如果x不存在,那么就新建;否则,就更新或者重写。 555 | 556 | Since records in other modules can be accessed using a `.` identifier, it's perfectly legal for our module to overwrite something that was written before by another module. 557 | 558 | 因为其他模块中的记录能够使用标识符`.`来访问, 559 | 560 | >### Note 561 | >Note that the dot is reserved to separate the module name from the object identifier, so they shouldn't be used in identifiers. Instead use the underscore. 562 | 563 | >### 注释 564 | >注意点号被保留的,以便从对象标识符中分隔模块名称,所以它们不应该用在标识符中。相反你应该使用的是下划线。 565 | 566 | As an example, let's change the menu option created by the todo_app module to into **My To Do**. For that we could add the following to the todo_user/todo_view.xml file: 567 | 568 | 如例所示,我们把todo_app模块创建的菜单选项更改为My To Do。为此,我们添加以下内容到文件todo_user/todo_view.xml: 569 | 570 | ```xml 571 | 572 | 573 | My To-Do 574 | 575 | 576 | 578 | 579 | {'search_default_filter_my_tasks': True} 580 | 581 | 582 | ``` 583 | 584 | ### Extending the record rules 扩展记录规则 585 | The To-Do application included a record rule to ensure that each task would only be visible to the user that created it. But now, with the addition of the social features, we need the task followers to also have access to them. The social network module does not handle this by itself. 586 | 587 | To-Do应用包含了记录规则以确保每个任务都只对任务的创建人可见。不过,现在,随着社交功能的添加,我们需要任务的关注人也能够访问到它们。社交网络模块自身并没有处理这种情况。 588 | 589 | Also, now tasks can have users assigned to them, so it makes more sense to have the access rules to work on the responsible user instead of the user who created the task. 590 | 591 | 现在,任务也可以用户分配给它们了,所以把访问规则用在负责人上而不是任务的创建人上更有意义。 592 | 593 | The plan would be the same as we did for the menu item: overwrite the `todo_app.todo_task_user_rule` to modify the domain_force field to a new value. 594 | 595 | 计划和我们对菜单选项所做的一样:重写`todo_app.todo_task_user_rule`以修改domain_force 字段为一个新的值。 596 | 597 | Unfortunately this won't work this time. Remember the `` we used in the security rules XML file: it prevents later write operations on it. 598 | 599 | 不幸的是,这一次的操作并不会生效。记住,我们在安全规则XML文件中使用的``:它能够阻止 600 | 601 | Since updates on that record are being prevented, we need a workaround. That will be to delete that record and add a replacement for it in our module. 602 | 603 | 因为,对该条记录的更新被阻止了,所以我们需要作出改变。这会删除记录,然后替换自定义模块只能够的记录。 604 | 605 | To keep things organized, we will create a security/todo_access_rules.xml file and add the following content to it: 606 | 607 | 为了内容清晰有序,我们创建一个文件`security/todo_access_rules.xml`,并把以下内容添加到文件: 608 | 609 | ```xml 610 | 611 | 612 | 613 | 614 | 615 | ToDo Tasks only for owner 616 | 617 | 618 | 619 | ['|',('user_id','in', [user.id,False]), ('message_follower_ids','in',[user.partner_id.id])] 620 | 621 | 622 | 623 | 624 | ``` 625 | 626 | This finds and deletes the `todo_task_user_rule` record rule from the todo_app module, and then creates a new `todo_task_per_user_rule` record rule. The domain filter we will now use makes a task visible to the responsible user user_id, to everyone if the responsible user is not set (equals False), and to all followers. 627 | The rule will run in a context where user is available and represents the current session user. The followers are partners, not User objects, so instead of `user.id`, we need to use `user.partner_id.id`. 628 | 629 | 这会从todo_app模块中发现并删除`todo_task_user_rule`记录规则,然后创建一条新的规则`todo_task_per_user_rule`。我们现在要使用的域过滤器将产生一个负责人的可见的任务,这条规则会在当前用户以及可用用户的上下文中运行。关注者是合作伙伴而不是User对象,所以我们需要使用`user.partner_id.id`,而不是`user.id`。 630 | 631 | >### Tips 632 | >Working on data files with `` is tricky because any later edit won't be updated on Odoo. To avoid that, temporarily use `` during development, and change it back only when you're done with the module. 633 | 634 | >### 提示 635 | >对数据文件使用``是一种技巧,因为在Odoo上的任何编辑都不再被更新。为了避免此种情况,在开发时临时使用``,仅在完成模块时才改回来。 636 | 637 | As usual, we must not forget to add the new file to the `__openerp__.py` descriptor file in the data attribute: 638 | 639 | 和往常一样,我们一定不能忘记把新文件添加到`__openerp__.py`描述文件中数据属性中: 640 | 641 | ```python 642 | 'data': ['todo_view.xml', 'security/todo_access_rules.xml'], 643 | ``` 644 | 645 | Notice that on module upgrade, the `` element will produce an ugly warning message, because the record to delete does not exist anymore. It is not an error and the upgrade will be successful, so we don't need to worry about it. 646 | 647 | 注意在模型升级时,``元素会产生一个很难看的警告信息,因为需要删除的记录根本就不存在了。这并不是一个错误,而且升级也会成功的,所以我们不需要担心这件事。 648 | 649 | ## Summary 650 | You should now be able to create new modules to extend existing modules. We saw how to extend the To-Do module created in the previous chapter. 651 | 652 | 现在,你应该能够创建新模块来括扩展现存的模块。我们在前一章也学习了对需要扩展的To-Do模块的创建。 653 | 654 | New features were added onto the several layers that make up an application. 655 | We extended the Odoo model to add new fields, and extended the methods with its business logic. Next, we modi ed the views to make the new elds available on them. Finally, you learned how to extend a model by inheriting from other models, and we used that to add the social network features to our To-Do app. 656 | 657 | 新特性被添加到了装饰器一个应用的多个层面。我们扩展Odoo模块以添加新字段,并使用它的业务逻辑来扩展方法。接下来,我们修改视图,以便可以在视图上使用新的字段。最后,你学会如何通过继承其它模型来扩展一个模型,同时我们使用这个方法对To-Do应用添加社交网络功能。 658 | 659 | With these three chapters, we had an overview of the common activities involved in Odoo development, from Odoo installation and setup to module creation and extension. The next chapters will focus on specific areas, most of which we visited briefly in these overviews. In the following chapter, we will address data serialization and the usage of XML and CSV les in more detail. 660 | 661 | 通过这三章,从Odoo的安装和设置,到模块创建和扩展,我们对常见的Odoo开发活动就有了一个大概了解。接下的章节我们关注于特殊的领域, 662 | -------------------------------------------------------------------------------- /第五章-模型-结构化应用数据.md: -------------------------------------------------------------------------------- 1 | # Chapter 5 Models – Structuring the Application Data 2 | # 第五章-结构化应用数据 3 | ******** 4 | In the previous chapters, we had an end-to-end overview of creating new modules for Odoo. In Chapter 2, Building Your First Odoo Application, a completely new application was built, and in Chapter 3, Inheritance – Extending Existing Applications, we explored inheritance and how to use it to create an extension module for our application. In Chapter 4, Data Serialization and Module Data, we discussed how to add initial and demonstration data to our modules. 5 | 6 | 在前一章,我们从头到尾地了解了Odoo的新模块创建。在第二章——构建你的第一个Odoo应用,构建了一个全新的应用,在第三章,继承-扩展现有应用,我们浏览了继承,以及如何使用它为应用创建一个扩展模块。在第四章,数据序列化和模块数据,我们讨论了如何对模块添加初始示例数据。 7 | 8 | In these overviews, we touched all the layers involved in building a backend application for Odoo. Now, in the following chapters, it's time to explain in more detail these several layers making up an application: models, views, and business logic. 9 | 10 | 通过这些概要,我们接触到了构建Odoo后端应用所涉及到全部层面。现在,下面的章节,就是用来详细解释构建一个的几个层面:模型,视图,以及业务逻辑。 11 | 12 | In this chapter, you will learn how to design the data structures supporting an application, and how to represent the relations between them. 13 | 14 | 在这一章,你会学习到如何设计支持一个应用的数据结构,以及如何表现两者之间的关系。 15 | 16 | ## Organizing application features into modules 将应用功能组织到模块 17 | As before, we will use an example to help explain the concepts. One of the great things about Odoo is being able to pick any existing application or module and add, on top of it, those extra features you need. So we are going to continue improving our to-do modules, and in no time they will form a fully featured application! 18 | 19 | 就像之前那样,我们举例来说明概念。Odoo的其中一个强大的之处在于它能够选择任意现有的应用或者模块,然后在其之上,添加你需要的额外功能。所以我们要继续改进to-do模块,不需要太久的时间它们就可以形成一个全功能的应用了! 20 | 21 | It is a good practice to split Odoo applications into several smaller modules, each of them responsible for specific features. This reduces overall complexity and makes them easier to maintain and upgrade to later Odoo versions. The problem of having to install all these individual modules can be solved by providing an app module packaging all those features, through its dependencies. To illustrate this approach we will be implementing the additional features using new to-do modules. 22 | 23 | 将Odoo应用分割到多个较小的模块就是一个最佳实践,每个模块各自负责特定的功能。这样可以减少整体上的复杂性,而且让这些模块便于维护,以及升级到新版本Odoo。安装这些独立模块的问题可以通过提供一个打包了全部功能和依赖的应用模块来解决。为了说明这个方法,我们 24 | 25 | ## Introducing the todo_ui module 介绍todo_ui模块 26 | In the previous chapter, we first created an app for personal to-dos, and then extended it so that the to-dos could be shared with other people. 27 | 28 | 在前面以后做那个,我们首先创建了一个人的todo应用,然后扩展它,以便能够与其它人共同使用。 29 | 30 | Now we want to take our app to a new level by adding to it a kanban board and a few other nice user interface improvements. The kanban board will let us organize our tasks in columns, according to their stages, such as Waiting, Ready, Started or Done. 31 | 32 | 现在,我们想要 33 | 34 | We will start by adding the data structures to enable that vision. We need to add stages and it will be nice if we add support for tags as well, allowing the tasks to be categorized by subject. 35 | 36 | 我们从添加数据结构实现愿景开始。我们需要添加 37 | 38 | The first thing to figure out is how our data will be structured so that we can design the supporting Models. We already have the central entity: the to-do task. Each task will be in a stage, and tasks can also have one or more tags on them. This means we will need to add these two additional models, and they will have these relations: 39 | 40 | 第一件要弄明白的事情是我们数据将如何构建,这样我们能够设计支持的模型。我们已经拥有中心模型了:to-do task。每个任务 41 | 42 | - Each task has a stage, and there can be many tasks in each stage. 43 | - Each task can have many tags, and each tag can be in many tasks. 44 | 45 | 每个任务都有一个阶段,而且在每个阶段中也可以拥有许多的任务。 46 | 每个任务拥有很多的标签,每个标签可以存在于很多的任务重。 47 | 48 | This means that tasks have many to one relation with stages, and many to many relations with tags. On the other hand, the inverse relations are: stages have a one to many relationship with tasks and tags have a many to many relation with tasks. 49 | 50 | 这就意味着,任务与阶段存在多对一段关系,和标签存在多对多关系。换句话来说,反向关系就是:阶段与任务存在一对多点关系,而标签和任务之间存在多对多关系。 51 | 52 | We will start by creating the new `todo_ui` module and add the to-do stages and to-do tags models to it. 53 | 54 | 我们从创建新的`todo_ui`模块开始,然后把to-do的stage模型和to-do的标签模型添加到这个模块。 55 | 56 | We've been using the `~/odoo-dev/custom-addons/` directory to host our modules. To create the new module alongside the existing ones, we can use these shell commands: 57 | 58 | 我们已经使用`~/odoo-dev/custom-addons/`目录来托管自定义的模块。为了在现有模块的 59 | 60 | ```shell 61 | $ cd ~/odoo-dev/custom-addons 62 | $ mkdir todo_ui 63 | $ cd todo_ui 64 | $ touch todo_model.py 65 | $ echo "from . import todo_model" > __init__.py 66 | ``` 67 | 68 | Next, we should add the `__openerp__.py` manifest file with this content: 69 | 70 | 接下来,我们应该把下面的内容添加到`__openerp__.py`清单文件: 71 | 72 | ```json 73 | { 'name': 'User interface improvements to the To-Do app', 74 | 'description': 'User friendly features.', 75 | 'author': 'Daniel Reis', 76 | 'depends': ['todo_app'] } 77 | ``` 78 | 79 | Note that we are depending on todo_app and not on todo_user. In general, it is a good idea to keep modules as independent as possible. When an upstream module is changed, it can impact all other modules that directly or indirectly depend on it. It's best if we can keep the number of dependencies low, and also avoid long dependency stacks, such as `todo_ui → todo_user → todo_app` in this case. 80 | 81 | 注意我们依赖于todo_app而不是todo_user。通常, 82 | 83 | Now we can install the module in our Odoo work database and get started with the models. 84 | 85 | 现在我们可以在Odoo工作数据库中安装模块, 86 | 87 | ## Creating models 创建模型 88 | For the to-do tasks to have a kanban board, we need stages. Stages are the board columns, and each task will t into one of these columns. 89 | 90 | 对于to-do tasks拥有一个看板视图来说我们需平台。平台是看板点列, 91 | 92 | Let's add the following code to the `todo_ui/todo_model.py` file: 93 | 94 | 让我们添加下面的代码到`todo_ui/todo_model.py`文件: 95 | 96 | ```python 97 | # -*- coding: utf-8 -*- 98 | from openerp import models, fields, api 99 | 100 | 101 | class Tag(models.Model): 102 | _name = 'todo.task.tag' 103 | name = fields.Char('Name', 40, translate=True) 104 | 105 | 106 | class Stage(models.Model): 107 | _name = 'todo.task.stage' 108 | _order = 'sequence,name' 109 | _rec_name = 'name' # the default 110 | _table = 'todo_task_stage' # the default 111 | name = fields.Char('Name', 40, translate=True) 112 | sequence = fields.Integer('Sequence') 113 | ``` 114 | 115 | Here, we created the two new Models we will be referencing in the to-do tasks. 116 | 117 | 这里,我们创建了两个会在to-do tasks中引用的新模型。 118 | 119 | Focusing on the task stages, we have a Python class, Stage, based on the class `models.Model`, defining a new Odoo model, `todo.task.stage`. We also defined two fields, name and sequence. We can see some model attributes, (prefixed with an underscore) that are new to us. Let's have a closer look at them. 120 | 121 | 我们把注意力集中在task的阶段上,我们拥有一个基于`models.Model`类的Python类Stage,它定义了一个新的Odoo模型,`todo.task.stage`。我们也定义了两个字段,name和sequence。我们可以看到一些模型属性, 122 | 123 | ## Model attributes 模型属性 124 | Model classes can have additional attributes used to control some of their behaviors: 125 | 126 | 模型类可以拥有用来控制自身行为的额外属性: 127 | 128 | - `_name`: This is the internal identifier for the Odoo model we are creating. 129 | - `_order`: This sets the order to use when the model's records are browsed. It is a text string to be used as the SQL order by clause, so it can be anything you could use there. 130 | - `_rec_name`: This indicates the field to use as the record description when referenced from related fields, such as a many to one relation. By default, it uses the name field, which is a commonly found field in models. But this attribute allows us to use any other field for that purpose. 131 | - `_table`: This is the name of the database table supporting the model. Usually, it is left to be calculated automatically, and is the model name with the dots replaced by underscores. But it can be set to indicate a specific table name. 132 | 133 | - `_name`: 134 | 135 | For completeness, we can also have the `_inherit` and `_inherits` attributes, as explained in *Chapter 3, Inheritance - Extending Existing Applications*. 136 | 137 | ### Models and Python classes 模型Python类 138 | Odoo models are represented by Python classes. In the preceding code, we have a Python class Stage, based on the `models.Model` class, used to define a new Odoo model todo.task.stage. 139 | 140 | Odoo模型由Python类表示,。在之前的代码中,我们编写了Python类Stage,它基于被用来定义一个新的Odoo模型的todo.task.stage的models.Model类。 141 | 142 | Odoo models are kept in a central registry, also referred to as pool in the previous versions. It is a dictionary keeping references of all the model classes available in the instance, and can be referenced by model name. Speci cally, the code in a model method can use self.envl['x'] or self.env.get('x') to get a reference to a class representing model x. 143 | 144 | Odoo模型被保存在一个注册中心里,在之前的版本中也被引用为pool。 145 | 146 | You can see that model names are important since they are the key used to access 147 | the registry. The convention for model names is to use a list of lowercase words joined with dots, like todo.task.stage. Other examples from the core modules are project.project, project.task or project.task.type. We should use the singular form: todo.task instead of todo.tasks. For historical reasons it's possible to nd some core models not following this, such as res.users, but that is not the rule. 148 | 149 | Model names must be globally unique. Because of this, the first word should correspond to the main application the module relates to. In our example, it is todo. Other examples from the core modules are `project`, `crm`, or `sale.` 150 | 151 | Python classes, on the other hand, are local to the Python file where they are declared. The identi er used for them is only signi cant for the code in that file. 152 | 153 | Because of this, class identi ers are not required to be pre xed by the main application they relate to. For example, there is no problem to call just Stage to our class for the todo.task.stage model. There is no risk of collision with possible classes with the same name on other modules. 154 | 155 | Two different conventions for class identi ers can be used: snake_case or CamelCase. Historically, Odoo code used snake case, and it is still very frequent to nd classes using that convention. But the recent trend is to use camel case, since it is the Python standard de ned by the PEP8 coding conventions. You may have noticed that we are using the latter form. 156 | 157 | ### Transient and Abstract models 临时模型和抽象模型 158 | In the preceding code, and in the vast majority of Odoo models, classes are based on the models.Model class. This type of models have database persistence: database tables are created for them and their records are stored until explicitly deleted. 159 | 160 | 在前面的代码中,以及非常多的Odoo模型中,类都基于models.Model类。 161 | 162 | But Odoo also provides two other model types to be used: Transient and Abstract models. 163 | 164 | 不过Odoo也提供了其它两种类型的模型来使用:临时模型和抽象模型。 165 | 166 | **Transient models** are based on the models.TransientModel class and are used for wizard-style user interaction. Their data is still stored in the database, but it is expected to be temporary. A vacuum job periodically clears old data from these tables. 167 | 168 | 临时模型基于类models.TransientModel,它被用在wizard风格的用户交互之上。它们的数据仍旧是存储在数据库中的, 169 | 170 | **Abstract models** are based on the models.AbstractModel class and have no data storage attached to them. They act as reusable feature sets to be mixed in with other models. This is done using the Odoo inheritance capabilities. 171 | 172 | ### Inspecting existing models 检查现有模型 173 | The information about models and elds created with Python classes is available through the user interface. In the Settings top menu, select the Technical | Database Structure | Models menu item. Here, you will nd the list of all models available in the database. Clicking on a model in the list will open a form with its details. 174 | 175 | img:omit 176 | 177 | This is a good tool to inspect the structure of a Model, since you have in one place the result of all additions that may come from several different modules. In this case, as you can see at the In Modules eld, on the top right, the todo.task de nitions are coming from the todo_app and todo_user modules. 178 | 179 | In the lower area, we have some information tabs available: a quick reference for the model Fields, the Access Rights granted, and also list the Views available for this model. 180 | 181 | We can nd the model's External Identi er, by activating the Developer Menu and accessing its View Metadata option. These are automatically generated but fairly predictable: for the todo.task model, the External Identi er is model_todo_task. 182 | 183 | >### Tip 184 | >The Models form is editable! It's possible to create and modify models, elds, and views from here. You can use this to build prototypes before carving them into proper modules. 185 | 186 | ## Creating fields 创建字段 187 | After creating a new model, the next step is to add fields to it. Let's explore the several types of fields available in Odoo. 188 | 189 | 在创建一个新模型之后,下一步就是对这个模型添加字段。我们来浏览一下Odoo中可以使用的几种字段类型。 190 | 191 | ### Basic field types 基本字段类型 192 | We now have a Stage model and will expand it to add some additional elds. We should edit the todo_ui/todo_model.py le, by removing some unnecessary attributes included before for the purpose of explanation, making it look like this: 193 | 194 | ```python 195 | class Stage(models.Model): 196 | _name = 'todo.task.stage' 197 | _order = 'sequence,name' 198 | 199 | # String fields: 200 | name = fields.Char('Name', 40) 201 | desc = fields.Text('Description') 202 | state = fields.Selection( 203 | [('draft','New'), ('open','Started'),('done','Closed')], 204 | 'State') 205 | docs = fields.Html('Documentation') 206 | # Numeric fields: 207 | sequence = fields.Integer('Sequence') 208 | perc_complete = fields.Float('% Complete', (3, 2)) 209 | # Date fields: 210 | date_effective = fields.Date('Effective Date') 211 | date_changed = fields.Datetime('Last Changed') 212 | # Other fields: 213 | fold = fields.Boolean('Folded?') 214 | image = fields.Binary('Image') 215 | ``` 216 | 217 | 218 | Here, we have a sample of the non-relational field types available in Odoo, with the basic arguments expected by each function. For most, the first argument is the field title, corresponding to the string keyword attribute. It's an optional argument, but it is recommended to be provided. If not, a title will be automatically generated from the eld name. 219 | 220 | There is a convention for date fields to use date as a prefix in their name. For example, we should use date_effective instead of effective_date. This can also apply to other elds, such as amount_, price_ or qty_. 221 | 222 | A few more arguments are available for most field types: 223 | 224 | - Char accepts a second, optional argument, size, corresponding to the maximum text size. It's recommended to use it only if you have a good reason to. 225 | - Text differs from Char in that it can hold multiline text content, but expects the same arguments. 226 | - Selection is a drop-down selection list. The first argument is the list of selectable options and the second is the title string. The selection list items are ('value', 'Title') tuples for the value stored in the database and the corresponding description string. When extending through inheritance, the selection_add argument can be used to append items to an existing selection list. 227 | - Html is stored as a text field, but has specific handling to present HTML content on the user interface. 228 | - Integer just expects a string argument for the field title. 229 | - Float has a second optional argument, an (x,y) tuple with the field's 230 | precision: x is the total number of digits; of those, y are decimal digits. 231 | - Date and Datetime data is stored in UTC time. There are automatic conversions made, based on the user time zone preferences, made available through the user session context. This is discussed in more detail in Chapter 6, Views – Designing the User Interface. 232 | - Boolean only expects the field title to be set, even if it is optional. 233 | - Binary also expects only a title argument. 234 | Other than these, we also have the relational elds, which will be introduced later in this chapter. But now, there is still more to learn about these eld types and their attributes. 235 | 236 | 237 | ### Common field attributes 常见字段属性 238 | Fields also have a set of attributes we can use, and we'll explain these in more detail: 239 | 240 | - string is the field title, used as its label in the UI. Most of the time it is not used as a keyword argument, since it can be set as a positional argument. 241 | - default sets a default value for the field. It can be a static value or a callable, either a function reference or a lambda expression. 242 | - size applies only to Char fields, and can set a maximum size allowed. 243 | - translate applies to text fields, Char, Text and Html, and makes the field 244 | translatable: it can have different values for different languages. 245 | - help provides the text for tooltips displayed to the users. 246 | - readonly=True makes the field not editable on the user interface. 247 | - required=True makes the field mandatory. 248 | - index=True will create a database index on the field. 249 | - copy=False has the field ignored when using the copy function. The non-relational fields are copyable by default. 250 | - groups allows limiting the field's access and visibility to only some groups. It is a comma-separated list of strings for security group XML IDs. 251 | - states expects a dictionary mapping values for UI attributes depending on values of the state field. For example: states={'done':[('readonly',True) ]}. Attributes that can be used are readonly, required, and invisible. 252 | 253 | For completeness, two other attributes are sometimes used when upgrading between Odoo major versions: 254 | 255 | - deprecated=True logs a warning whenever the field is being used. 256 | - oldname='field' is used when a field is renamed in a newer version, 257 | enabling the data in the old field to be automatically copied into the new field. 258 | 259 | ### Reserved field names 保留字段名 260 | A few field names are reserved to be used by the ORM: 261 | 一些字段的名称是被ORM保留使用的: 262 | 263 | - id is an automatic number uniquely identifying each record, and used as the database primary key. It's automatically added to every model. 264 | 265 | The following fields are automatically created on new models, unless the `_log_ access=False` model attribute is set: 266 | 267 | 下列的字段在新模型上能够自动的创建,除非设置了模型属性`_log_ access=False`: 268 | 269 | - create_uid for the user that created the record 270 | - create_date for the date and time when the record is created 271 | - write_uid for the last user to modify the record 272 | - write_date for the last date and time when the record was modified 273 | 274 | This information is available from the web client, using the Developer Mode menu and selecting the View Metadata option. 275 | 276 | 这条信息也可以在web客户端看到,使用开发者模式菜单然后选择“查看元数据”选项。 277 | 278 | There some built-in effects that expect specific field names. We should avoid using them for purposes other than the intended ones. Some of them are even reserved and can't be used for other purposes at all: 279 | 280 | 希望指定的字段名会带来一些内置效应。我们应该避免将这这些字段用于多种目的。这些字段中的一部分是保留的 281 | 282 | - name is used by default as the display name for the record. Usually it is a Char, but other field types are also allowed. It can be overridden by setting the _rec_name model attribute. 283 | 284 | - name 在显示记录名称时它被默认使用。通常是一个Char,不过其他字段类型也是被允许的。 285 | 286 | - active (type Boolean) allows inactivating records. Records with active==False will automatically be excluded from queries. To access them an ('active','=',False) condition must be added to the search domain, or 'active_test': False should be added to the current context. 287 | 288 | - active (布尔类型)能够讲记录失效。含有active==False的记录可以自动的在查询时被排除。要访问 289 | 290 | - sequence (type Integer) if present in a list view, allows to manually define the order of the records. To work properly it should also be in the model's _order. 291 | 292 | - sequence (整数类型)如果它出现在列表视图中, 293 | 294 | - state (type Selection) represents basic states of the record's life cycle, and can be used by the state's field attribute to dynamically modify the view: some form fields can be made read only, required or invisible in specific record states. 295 | 296 | - state (多选类型)表示记录的基本状态 297 | 298 | - parent_id, parent_left, and parent_right have special meaning for parent/child hierarchical relations. We will shortly discuss them in detail. 299 | 300 | So far we've discussed scalar value fields. But a good part of an application data structure is about describing the relationships between entities. Let's look at that now. 301 | 302 | 到目前为止我们讨论了无向量值的字段。 303 | 304 | ## Relations between models 模型之间的关系 305 | Looking again at our module design, we have these relations: 306 | 307 | 再来看下我们的模块设计,我们 308 | 309 | - Each task has a stage – that's a many to one relation, also known as a foreign key. The inverse relation is a one to many, meaning that each stage can have many tasks. 310 | - Each task can have many tags – that's a many to many relation. The inverse relation, of course, is also a many to many, since each tag can also have many tasks. 311 | 312 | Let's add the corresponding relation fields to the to-do tasks in our `todo_ui/todo_model.py` file: 313 | 314 | 让我们在`todo_ui/todo_model.py`文件中对to-do tasks添加对应的关联字段: 315 | 316 | ```python 317 | class TodoTask(models.Model): 318 | _inherit = 'todo.task' 319 | stage_id = fields.Many2one('todo.task.stage', 'Stage') 320 | tag_ids = fields.Many2many('todo.task.tag', string='Tags') 321 | ``` 322 | 323 | The preceding code shows the basic syntax for these fields, setting the related model and the eld's title string. The convention for relational field names is to append _id or _ids to the field names, for to one and to many relations, respectively. 324 | 325 | 上面的代码展示了这些字段的基本语法, 326 | 327 | As an exercise, you may try to also add on the related models, the corresponding inverse relations: 328 | 329 | 作为练习,你可以试着 330 | 331 | - The inverse of the Many2one relation is a One2many field on stages: each stage can have many tasks. We should add this field to the Stage class. 332 | - The inverse of the Many2many relation is also a Many2many field on tags: each tag can also be used on many tasks. 333 | Let's have a closer look at relational field definitions. 334 | 335 | ### Many to one relations 多对一关系 336 | Many2one accepts two positional arguments: the related model (corresponding to the comodel keyword argument) and the title string. It creates a field in the database table with a foreign key to the related table. 337 | 338 | Many2one 接受两个位置参数:关联模型(对应到联合模型的关键字参数)以及title字符串。它使用一个关联到表的外键创建了一数据库表中的字段。 339 | 340 | Some additional named arguments are also available to use with this type of field: 341 | 342 | 一些额外的命名参数也可以 343 | 344 | - ondelete defines what happens when the related record is deleted. Its default is set null, meaning it is set to an empty value if the related record is deleted. Other possible values are restrict, raising an error preventing the deletion, and cascade also deleting this record. 345 | 346 | - ondelete 定义了当关联记录被删除时所执行的动作。 347 | 348 | - context and domain are meaningful for the web client views. They can be set on the model to be used by default on any view where the field is used. They will be better explained in the Chapter 6, Views - Designing the User Interface. 349 | 350 | - 上下文和域对web客户端视图是很有意义的。它们可以设置模型上以便用于 351 | 352 | - auto_join=True allows the ORM to use SQL joins when doing searches using this relation. By default this is False to be able to enforce security rules. If joins are used, the security rules will be bypassed, and the user could have access to related records the security rules wouldn't allow, but the SQL queries will be more efficient and run faster. 353 | 354 | - auto_join=True 允许ORM在使用这个关系进行搜索 355 | 356 | 357 | ### Many to many relations 多对多关系 358 | The Many2many minimal form accepts one argument for the related model, and it is recommended to also provide the string argument with the field title. 359 | 360 | Many2many最小化形式接收一个关联模型的字段, 361 | 362 | At the database level, this does not add any column to the existing tables. Instead, it automatically creates a new relation table with only two ID fields with the foreign keys to the related tables. The relation table name and the eld names are automatically generated. The relation table name is the two table names joined with an underscore with `_rel` appended to it. 363 | 364 | 在数据库层面,这样做不会对现有的模型添加任何列。相反,它自动的创建一个新的 365 | 366 | These defaults can be manually overridden. One way to do it is to use the longer form for the field definition: 367 | 368 | 这些默认值可以被手动地重写。 369 | 370 | ```python 371 | # TodoTask class: Task <-> Tag relation (long form): 372 | tag_ids = fields.Many2many( 373 | 'todo.task.tag', # related model 374 | 'todo_task_tag_rel', # relation table name 375 | 'task_id', # field for "this" record 376 | 'tag_id', # field for "other" record 377 | string='Tasks') 378 | ``` 379 | 380 | Note that the additional arguments are optional. We could just set the name for the relation table and let the field names use the automatic defaults. 381 | 382 | 注意额外地参数是可选的。我们可以只设置关联表的名称, 383 | 384 | If you prefer, you may use the long form using keyword arguments instead: 385 | 386 | 要是你喜欢的话,你可以使用下面的 387 | 388 | ```python 389 | # TodoTask class: Task <-> Tag relation (long form): 390 | tag_ids = fields.Many2many( 391 | comodel_name='todo.task.tag', # related model 392 | relation='todo_task_tag_rel', # relation table name 393 | column1='task_id', # field for "this" record 394 | column2='tag_id', # field for "other" record 395 | string='Tasks') 396 | ``` 397 | 398 | Like many to one fields, many to many fields also support the domain and context keyword attributes. 399 | 400 | 像多对一字段,多对多字段也支持域和上下文关键字属性。 401 | 402 | On some rare occasions we may have to use these long forms to override the automatic defaults, in particular, when the related models have long names or when we need a second many to many relation between the same models. 403 | 404 | 在某些很少的情况下,我们必须使用这些 405 | 406 | >### Tips 407 | >PostgreSQL table names have a limit of 63 characters, and this can be a problem if the automatically generated relation table name exceeds that limit. That is a case where we should manually set the relational table name using the relation attribute. 408 | 409 | >#### 提示 410 | >PostgreSQL表名称存在63个字符的限制,这对于自动生成的关联表名称超过限制时是个问题。这种情况下,我们应该使用关联属性手动地设置关联表的名称。 411 | 412 | The inverse of the Many2many relation is also a Many2many field. If we also add a Many2many field to the tags, Odoo infers that this many to many relation is the inverse of the one in the task model. 413 | 414 | Many2many关系的反向关系也可以是一个Many2many字段。如果我们也可以对tags添加Many2many字段,Odoo引用这个多对多关系 415 | 416 | The inverse relation between tasks and tags can be implemented like this: 417 | 418 | task和tag之间的反向关系可以像下面代码这样来实现: 419 | 420 | ```python 421 | #class Tag(models.Model): 422 | # _name = 'todo.task.tag' 423 | #Tag class relation to Tasks: 424 | task_ids = fields.Many2many('todo.task', # related model 425 | string='Tasks') 426 | ``` 427 | 428 | ### One to many inverse relations 一对多的反向关系 429 | The inverse of a Many2one can be added to the other end of the relation. This has no impact on the actual database structure, but allows us easily browse from the "one" side the "many" side records. A typical use case is the relation between a document header and its lines. 430 | 431 | Many2one字段的反向可以被添加关联的尾部。这对于实际的数据库结构没有影响,而且允许我们更容易的从“一”端到“多”端的记录。典型的用法是文档头部和尾部之前的关系。 432 | 433 | On our example, with a One2many inverse relation on stages, we could easily list all the tasks in that stage. To add this inverse relation to stages, add the code shown here: 434 | 435 | 在我们的例子中,对stage使用One2many反向关系,我们可以轻松地列出这个stage中的所有任务。为了将这个反向关系添加到stage,请添加如下代码: 436 | 437 | ```python 438 | # class Stage(models.Model): 439 | # _name = 'todo.task.stage' 440 | # Stage class relation with Tasks: 441 | tasks = fields.One2many( 442 | 'todo.task', # related model 443 | 'stage_id', # field for "this" on related model 444 | 'Tasks in this stage') 445 | ``` 446 | 447 | The One2many accepts three positional arguments: the related model, the field name in that model referring this record, and the title string. The two first positional arguments correspond to the comodel_name and inverse_name keyword arguments. 448 | 449 | One2many接受三个位置参数:关联的模型,这个模型中引用该条记录的字段名称,以及标题字符串。前面两个位置参数对应的是comodel_name和inverse_name关键字参数。 450 | 451 | The additional keyword parameters available are the same as for many to one: context, domain, ondelete (here acting on the "many" side of the relation), and auto_join. 452 | 453 | 其它的关键字参数同样也可以应用到多对一字段:context, domain, ondelete(这里它扮演的是“多”这一边的关系),以及auto_join。 454 | 455 | ### Hierarchical relations 层次关系 456 | Parent-child relations can be represented using a Many2one relation to the same model, to let each record reference its parent. And the inverse One2many makes it easy for a parent to keep track of its children. 457 | 458 | 父子关系可以使用 Many2one关系在相同的模型中表现出来,以便让每条记录引用自身的父记录。 459 | 460 | Odoo also provides improved support for these hierarchic data structures: faster browsing through tree siblings, and simpler search with the additional child_of operator in domain expressions. 461 | 462 | Odoo还提供了对这些分层数据结构的改进: 463 | 464 | To enable these features we need to set the _parent_store flag attribute and add the helper elds: parent_left and parent_right. Mind that this additional operation comes at storage and execution time penalties, so it's best used when you expect to read more frequently than write, such as a the case of a category tree. 465 | 466 | 为了启用这些功能,我们需要设置旗帜属性_parent_store,并添加辅助字段:parent_left和parent_right。注意这个额外的操作带来的是存储与执行时间上的不利,所以当你希望读取更频繁于写入时最好使用它,比如在目录树的例子中。 467 | 468 | Revisiting the tags model defined in the todo_ui/todo_model.py file, we should now edit it to look like this: 469 | 470 | 重新反问定义在`todo_ui/todo_model.py`文件中的tag模型,我们应该编辑这个文件让它看起来是这个样子: 471 | 472 | ```python 473 | class Tags(models.Model): 474 | _name = 'todo.task.tag' 475 | _parent_store = True 476 | 477 | # _parent_name = 'parent_id' 478 | name = fields.Char('Name') 479 | parent_id = fields.Many2one( 480 | 'todo.task.tag', 'Parent Tag', ondelete='restrict') 481 | parent_left = fields.Integer('Parent Left', index=True) 482 | parent_right = fields.Integer('Parent Right', index=True) 483 | ``` 484 | 485 | Here, we have a basic model, with a `parent_id` field to reference the parent record, and the additional `_parent_store` attribute to add hierarchic search support. When doing this, the `parent_left` and `parent_right` fields also have to be added. 486 | 487 | 这里,我们编写了一个基础模型,它拥有一个引用到父记录的parent_id字段,以及额外的为了添加层次搜索支持的`_parent_store` 属性。在你这样做的时候,`parent_left` 和 `parent_right` 字段也必须添加进去。 488 | 489 | The field referring to the parent is expected to be named `parent_id`. But any other field name can be used by declaring it with the `_parent_name` attribute. 490 | 491 | 应用到父的字段期望使用的名称是`parent_id`。但是其它的任意字段名称也能够和`_parent_name`属性放到一起声明。 492 | 493 | Also, it is often convenient to add a field with the direct children of the record: 494 | 495 | 而且,对一个字段直接的添加记录的子记录也是很方便的: 496 | 497 | ```python 498 | child_ids = fields.One2many('todo.task.tag', 'parent_id', 'Child Tags') 499 | ``` 500 | 501 | ### Referencing fields using dynamic relations 使用动态关系应用字段 502 | So far, the relation fields we've seen can only reference one model. The Reference field type does not have this limitation and supports dynamic relations: the same field is able to refer to more than one model. 503 | 504 | 到目前为止,我们所见到的关系字段只能够引用一个模型。Reference的字段去不存在这个限制,而且还支持动态属性:相同的字段能够引用不止一个模型。 505 | 506 | We can use it to add a To-do Task field, Refers to, that can either refer to a User or a Partner: 507 | 508 | 我们可以使用它来添加一个To-do Task的字段,Refers to,它能够引用User或者Partner中的任何一个: 509 | 510 | ```python 511 | # class TodoTask(models.Model): 512 | refers_to = fields.Reference([('res.user', 'User'), ('res.partner', 'Partner')], 'Refers to') 513 | ``` 514 | 515 | You can see that the field definition is similar to a Selection field, but here the selection list holds the models that can be used. On the user interface, the user will rst pick a model from the list, and then pick a record from that model. 516 | 517 | 你能够给发现字段的定义类似于Selection字段,但是这里的下拉列表拥有可被使用的模型。 518 | 519 | This can be taken to another level of flexibility: a Referencable Models con guration table exists to con gure the models that can be used in Reference elds. It is available in the Settings | Technical | Database Structure menu. When creating such a eld we can set it to use any model registered there, with the help of the referencable_models() function in the openerp.addons.res.res_request module. In Odoo version 8, it is still using the old-style API, so we need to wrap it to use with the new API: 520 | 521 | ```python 522 | from openerp.addons.base.res import res_request 523 | 524 | 525 | def referencable_models(self): 526 | return res_request.referencable_models(self, self.env.cr, self.env.uid, context=self.env.context) 527 | ``` 528 | 529 | Using the preceding code, the revisited version of the Refers to field would look like this: 530 | 531 | ```python 532 | # class TodoTask(models.Model): 533 | refers_to = fields.Reference(referencable_models, 'Refers to') 534 | ``` 535 | 536 | ## Computed fields 计算字段 537 | Fields can have values calculated by a function, instead of simply reading a database stored value. A computed eld is declared just like a regular eld, but has an additional argument compute with the name of the function used to calculate it. 538 | 539 | In most cases computed elds involve writing some business logic, so we will develop this topic more in Chapter 7, ORM Application Logic - Supporting Business Processes. We can still explain them here, but keeping the business logic side as simple as possible. 540 | 541 | Let's work on an example: stages have a fold eld. We will add to tasks a computed eld with the Folded? ag for the corresponding stage. 542 | 543 | We should edit the TodoTask model in the todo_ui/todo_model.py le to add the following: 544 | 545 | ```python 546 | class TodoTask(models.Model): 547 | stage_fold = fields.Boolean('Stage Folded?', compute='_compute_stage_fold') 548 | 549 | @api.one 550 | @api.depends('stage_id.fold') 551 | def _compute_stage_fold(self): 552 | self.stage_fold = self.stage_id.fold 553 | ``` 554 | 555 | The preceding code adds a new `stage_fold` field and the `_compute_stage_fold` method used to compute it. The function name was passed as a string, but it's also allowed to pass it as a callable reference (the function identi er with no quotes). 556 | 557 | 之前的代码添加了一个新的`stage_fold`字段, 558 | 559 | Since we are using the `@api.one` decorator, self will represent a single record. If we used `@api.multi` instead, it would represent a recordset and our code would need to handle the iteration over each record. 560 | 561 | 因为我们使用的是`@api.one`装饰器, 562 | 563 | The `@api.depends` is necessary if the computation uses other fields: it tells the server when to recompute stored or cached values. It accepts one or more field names as arguments and dot-notation can be used to follow eld relations. 564 | 565 | `@api.depends` 566 | 567 | The computation function is expected to assign a value to the field or fields to compute. If it doesn't, it will error. Since self is a record object, our computation is simply to get the Folded? field using `self.stage_id.fold`. The result is achieved by assigning that value (writing it) to the computed field, `self.stage_fold`. 568 | 569 | We won't be working yet on the views for this module, but you can make a quick edit on the task form to confirm if the computed field is working as expected: using the Developer Menu pick the Edit View option and add the eld directly in the form XML. Don't worry: it will be replaced by the clean module view on the next upgrade. 570 | 571 | 我们不会 572 | 573 | ### Search and write on computed fields 对计算字段进行查找和更新 574 | The computed field we just created can be read, but it can't be searched or written. This can be enabled by providing specialized functions for that. Along with the compute function, we can also set a search function, implementing the search logic, and the inverse function, implementing the write logic. 575 | 576 | In order to do this, our computed field declaration becomes like this: 577 | 578 | 我们实现这个目标,我们想下面这样计算字段声明: 579 | 580 | ```python 581 | # class TodoTask(models.Model): 582 | stage_fold = fields.Boolean(string='Stage Folded?', 583 | compute='_compute_stage_fold', 584 | # store=False) # the default 585 | search='_search_stage_fold', 586 | inverse='_write_stage_fold') 587 | ``` 588 | 589 | The supporting functions are: 590 | 591 | 支持的函数如下: 592 | 593 | ```python 594 | def _search_stage_fold(self, operator, value): 595 | return [('stage_id.fold', operator, value)] 596 | 597 | def _write_stage_fold(self): 598 | self.stage_id.fold = self.stage_fold 599 | ``` 600 | 601 | The search function is called whenever a (field, operator, value) condition on this eld is found in a search domain expression. It receives the operator and value for the search and is expected to translate the original search element into an alternative domain search expression. 602 | 603 | 搜索函数被调用 604 | 605 | The inverse function performs the reverse logic of the calculation, to nd the value to write on the source elds. In our example, it's just writing on stage_id.fold. 606 | 607 | ### Storing computed fields 存储计算字段 608 | Computed field's values can also be stored on the database, by setting store to True on their definition. They will be recomputed when any of their dependencies change. Since the values are now stored, they can be searched just like regular elds, so a search function is not needed. 609 | 610 | 计算字段的值也可以被存储在数据库上,通过设置store为True 611 | 612 | ### Related fields 关联字段 613 | The computed field we implemented in the previous section is a special case that can be automatically handled by Odoo. The same effect can be achieved using Related fields. They make available, directly on a model, elds that belong to a related model, accessible using a dot-notation chain. This makes them usable in situations where dot-notation can't be used, such as UI forms. 614 | 615 | 计算我们在前面小节中实现的字段是个特殊情形,它可以由Odoo自动的来处理。 616 | 617 | To create a related eld, we declare a eld of the needed type, just like with regular computed elds, and instead of compute, use the related attribute indicating the dot-notation eld chain to reach the desired eld. 618 | 619 | To-do tasks are organized in customizable stages and these is turn map into basic states. We will make them available on tasks, and will use this for some client-side logic in the next chapter. 620 | 621 | Similarly to stage_fold, we will add a computed eld on the task model, but now using the simpler Related eld: 622 | 623 | ```python 624 | # class TodoTask(models.Model): 625 | stage_state = fields.Selection( 626 | related='stage_id.state', 627 | string='Stage State') 628 | ``` 629 | 630 | Behind the scenes, Related elds are just computed elds that conveniently implement search and inverse. This means that we can search and write on them out of the box, without having to write any additional code. 631 | 632 | ## Model constraints 模型限制 633 | To enforce data integrity, models also support two types of constraints: SQL and Python. 634 | 635 | 要强制启用数据的完整性,模型也可以支持两个类型的约束:SQL和Python。 636 | 637 | SQL constraints are added to the table definition in the database and implemented by PostgreSQL. They are defined using the class attribute _sql_constraints. It is a list of tuples with the constraint identifier name, the SQL for the constraint, and the error message to use. 638 | 639 | SQL约束被添加到了数据库中定义的,由PostgreSQL实现的表。它们使用类属性_sql_constraints来定义。 640 | 641 | A common use case is to add unique constraints to models. Suppose we didn't want to allow the same user to have two active tasks with the same title: 642 | 643 | ```python 644 | # class TodoTask(models.Model): 645 | _sql_constraints = [ 646 | ('todo_task_name_uniq', 647 | 'UNIQUE (name, user_id, active)', 648 | 'Task title must be unique!')] 649 | ``` 650 | 651 | Since we are using the `user_id` field added by the todo_user module, this dependency should be added to the depends key of the `__openerp__.py` manifest file. 652 | 653 | 因为我们 654 | 655 | Python constraints can use a piece of arbitrary code to check conditions. The checking function needs to be decorated with @api.constrains indicating the list of elds involved in the check. The validation is triggered when any of them is modi ed, and will raise an exception if the condition fails: 656 | 657 | Python约束 658 | 659 | ```python 660 | from openerp.exceptions import ValidationError 661 | # class TodoTask(models.Model): 662 | 663 | @api.one 664 | @api.constrains('name') 665 | def _check_name_size(self): 666 | if len(self.name) < 5: 667 | raise ValidationError('Must have 5 chars!') 668 | ``` 669 | 670 | The preceding example prevents saving task titles with less than 5 characters. 671 | 672 | 上面的例子阻止了少于5个字符的任务名称的保存。 673 | 674 | ## Summary 总结 675 | We went through a thorough explanation of models and elds, using them to extend the To-do app with tags and stages on tasks. You learned how to de ne relations between models, including hierarchical parent/child relations. Finally, we saw simple examples of computed elds and constraints using Python code. 676 | 677 | 我们通过 678 | 679 | In the next chapter, we will work on the user interface for these back-end model features, making them available in the views used to interact with the application. 680 | 681 | 在下一章,我们 -------------------------------------------------------------------------------- /第八章-创建看板视图和报告.md: -------------------------------------------------------------------------------- 1 | # 第八章-QWeb创建公告栏视图和报告 2 | *************** 3 | 4 | QWeb is a template engine used by Odoo. It is XML based and is used to generate HTML fragments and pages. QWeb was rst introduced in version 7.0 to enable richer kanban views, and with version 8.0, is also used for report generation and CMS website pages. 5 | 6 | Qweb是一个用于Odoo的模板引擎。它基础XML用来生成HTML片段和页面。Qweb首先在7.0版本中引入,用来启用富公告栏视图,在8.0版本中用左生成报告以及CMS网站的页面。 7 | 8 | Here you will learn about the QWeb syntax and how to use it to create your own kanban views and custom reports. 9 | 10 | 这里和你会学到QWeb语法,以及如何使用它来创建自己的公告栏视图和自定义报告。 11 | 12 | To understand kanban boards, kanban is a word of Japanese origin that is used to represent a work queue management method. It takes inspiration from the Toyota Production System and Lean Manufacturing, and has become popular in the software industry with its adoption in Agile methodologies. 13 | 14 | 你需要理解kanban board,kanban是一个源自日本的用来表现工作队列管理方法的单词。它从Toyota的生产-高效制造管理系统中启发而来,而且它日益在软件工业中流行开来,以便适应敏捷开发。 15 | 16 | The **kanban board** is a tool to visualize the work queue. Work items are represented by cards that are organized in columns representing the stages of the work process. New work items start on the left-most column and travel through the board until they reach the right-most column, representing completed work. 17 | 18 | 公告板是一个用来形象化工作队列的工具。 19 | 20 | ## Getting started with kanban board 21 | The simplicity and visual impact of kanban board make them excellent to support simple business processes. A basic example of a kanban board can have three columns, as shown in the following image: "To Do," "Doing," and "Done," but it can of course be extended to whatever speci c process steps we may need: 22 | 23 | img:omit 24 | *Photo credits: A Scrum board suggesting using kanban by Jeff.lasovski. Courtesy of Wikipedia.* 25 | 26 | Kanban views are a distinctive Odoo feature, making it easy to implement these boards. Let's learn how to use them. 27 | 28 | ### Kanban views 29 | In form views, we use mostly speci c XML elements, such as and , and few HTML elements, such as

or
. With kanban views, it's quite the opposite; they are HTML-based templates and support only two Odoo-speci c elements, and 390 |
391 | ``` 392 | 393 | The container for the buttons is a div with the oe_button_box class and also oe_right, to have it aligned to the right hand side of the form. 394 | 395 | In the example the button displays the total number of to-do tasks the document responsible has. Clicking on it will browse them, and if creating new tasks the original responsible will be used as default. 396 | 397 | The button attributes used are: 398 | 399 | - class="oe_stat_button" is to use a rectangle style instead of a button. 400 | - icon is the icon to use, chosen from the Font Awesome icon set. 401 | - type will be usually action, for a window action, and name will be the ID of the action to execute. It can be inserted using the formula %(action- external-id)d, to translate the external ID into the actual ID number. This action is expected to open a view with related records. 402 | - string can be used to add text to the button. It is not used here because the contained field already provides the text for it. 403 | - context will set defaults on the target view, when clicking through the button, to filter data and set default values for new records created. 404 | - help is the tooltip to display. 405 | 406 | The button itself is a container and can have inside it's elds to display statistics. These are regular elds using the widget statinfo. The eld should be a computed eld, de ned in the underlying module. We can also use static text instead or alongside the `statinfo` fields, such as: `
User's To-dos
` 407 | 408 | #### Organizing content in a form 组织表单中的内容 409 | The main content of the form should be organized using tags. A group is a grid with two columns. A eld and its label take two columns, so adding elds inside a group will have them stacked vertically. 410 | 411 | If we nest two elements inside a top group, we will be able to get two columns of elds with labels, side by side. 412 | 413 | ```xml 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | ``` 425 | 426 | Groups can have a string attribute, used as a title for the section. Inside a group section, titles can also be added using a separator element. 427 | 428 | >### Tips 429 | >Try the Toggle Form Layout Outline option of the Developer menu: it draws lines around each form section, allowing for a better understanding of how the current view is organized. 430 | 431 | #### Tabbed notebooks 432 | Another way to organize content is the notebook, containing multiple tabbed sections called pages. These can be used to keep less used data out of sight until needed or to organize a large number of elds by topic. 433 | 434 | We won't need this on our to-do task form, but here is an example that could be added in the task stages form: 435 | 436 | ```xml 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | ``` 446 | 447 | It is good practice to have names on pages, to make it more reliable for other modules to extend them. 448 | 449 | ## View elements 视图元素 450 | We have seen how to organize the content in a form, using elements such as header, group, and notebook. Now, we can take a closer look at the eld and button elements, and what we can do with them. 451 | 452 | ### Buttons 按钮 453 | Buttons support these attributes: 454 | 455 | - icon to display. Unlike smart buttons, icons available for regular buttons are 456 | those found in addons/web/static/src/img/icons. 457 | - string is the button text description. 458 | - type can be workflow, object or action, to either trigger a workflow signal, call a Python method, or run a window action. 459 | - name is the workflow trigger, model method, or window action to run, depending on the button type. 460 | - args can be used to pass additional parameters to the method, if the type is object. 461 | - context sets values on the session context, which can have an effect after the windows action is run, or when a Python method is called. In the latter case, it can sometimes be used as an alternative to args. 462 | - confirm adds a dialog with this message text asking for a confirmation. 463 | - special="cancel" is used on wizards, to cancel and close the form. It 464 | should not be used with type. 465 | 466 | ### Fields 字段 467 | Fields have these attributes available for them. Most are taken from what was de ned in the model, but can be overridden in the view. 468 | 469 | General attributes: 470 | 471 | - name: identifies the field technical name. 472 | - string: provides label text description to override the one provided by the model. 473 | - help: tooltip text to use replace the one provided by the model. 474 | - placeholder: provides suggestion text to display inside the field. 475 | - widget: overrides the default widget used for the field's type. We will explore the available widgets a bit later in the chapter. 476 | - options: holds additional options to be used by the widget. 477 | - class: provides CSS classes to use for the field's HTML. 478 | - invisible="1": makes the field invisible. 479 | - nolabel="1": does not display the field's label, it is only meaningful for fields inside a element. 480 | - readonly="1": makes the field non editable. 481 | - required="1": makes the field mandatory. 482 | 483 | Attributes specific for some field types: 484 | 485 | 部分字段类型的特定属性: 486 | 487 | - sum, avg: for numeric fields, and in list/tree views, add a summary at the end with the total or the average of the values. 488 | - password="True": for text fields, displays the field as a password field. 489 | - filename: for binary fields, is the field for the name of the file. 490 | - mode="tree": for One2many fields, is the view type to use to display the 491 | records. By default it is tree, but can also be form, kanban or graph. 492 | 493 | For the Boolean attributes in general, we can use True or 1 to enable and False or 0 494 | to disable them. For example, readonly="1" and readonly="True" are equivalent. 495 | 496 | #### Relational fields 关联字段 497 | On relational fields, we can have some additional control on what the user is allowed to do. By default, the user can create new records from these fields (also known as quick create) and open the related record form. This can be disabled using the options field attribute: 498 | 499 | 在关联字段中,我们能够对用户被允许做的事情之上做出额外的控制。默认能够用户从这些字段(也称之为快速创建)创建新的记录,打开关联的记录表单。可以通过options字段属性来禁用它。 500 | 501 | ```python 502 | options={'no_open': True, 'no_create': True} 503 | ``` 504 | 505 | The context and domain are also particularly useful on relational elds. The context can de ne default values for the related records, and the domain can limit the selectable records, for example, based on another eld of the current record. Both context and domain can be de ned in the model, but they are only used on the view. 506 | 507 | #### Field widgets 字段部件 508 | Each field type is displayed in the form with the appropriate default widget. But other additional widgets are available and can be used as well: 509 | 510 | Widgets for text fields: 511 | 文本字段的部件: 512 | 513 | - email: makes the e-mail text an actionable mail-to address. 514 | - url: formats the text as a clickable URL. 515 | - html: expects HTML content and renders it; in edit mode it uses a WYSIWYG editor to format the content without the need to know HTML. 516 | Widgets for numeric elds: 517 | - handle: specifically designed for sequence fields, this displays a handle to drag lines in a list view and manually reorder them. 518 | - float_time: formats a float value as time in hours and minutes. 519 | - monetary: displays a float field as a currency amount. The currency to use can be taken from a field, such as options="{'currency_field': 'currency_id'}". 520 | - progressbar: presents a float as a progress percentage, usually it is used on a computed field calculating a completion rate. 521 | 522 | - email: 523 | 524 | Some widgets for relational and selection fields: 525 | selection字段和relational字段所使用的部分字段: 526 | 527 | - many2many_tags: displays a many to many field as a list of tags. 528 | - selection: uses the Selection field widget for a many to one field. 529 | - radio: allows picking a value for a selection field option using radio buttons. 530 | - kanban_state_selection: shows a semaphore light for the kanban state selection list. 531 | - priority: represents a selection as a list of clickable stars. 532 | 533 | - many2many_tags:把多对多字段显示为一个标签列表。 534 | - selection: 对多对一字段时候用Selection字段的部件。 535 | - radio: 536 | 537 | #### On-change events 事件On-change 538 | Sometimes we need the value for a eld to be automatically calculated when another eld is changed. The mechanism for this is called on-change. 539 | 540 | 有时候我们需要字段的值另外字段改变时能够自动地进行计算。这种机制成为on-change。 541 | 542 | Since version 8, the on-change events are defined on the model layer, without the need for any specific markup on the views. This is done by creating the methods to perform the calculations and binding them to the triggering field(s) using a decorator @api.onchange('field1', 'field2'). 543 | 544 | 从版本8开始,on-change时间定义在了模型层,而无需对视图指定任何对装饰。 545 | 546 | In previous versions, this binding was done in the view layer, using the onchange eld attribute to set the class method called when that eld was changed. This is still supported, but is deprecated. Be aware that the old-style on-change methods can't be extended using the new API. If you need to do that, you should use the old API. 547 | 548 | ### Dynamic views 549 | The elements visible as a form can also be changed dynamically, depending, for example, on the user's permissions or the process stage the document is in. 550 | 551 | These two attributes allow us to control the visibility of user interface elements: 552 | 553 | • groups: makes the element visible only for members of the specified security groups. It expects a comma separated list of group's XML IDs. 554 | • states: makes the element visible only when the document is in the specified state. It expects a comma-separated 555 | list of State codes, and the document model must have a state field. 556 | 557 | For more exibility, we can instead set an element's visibility using client-side evaluated expressions. This is done using the attrs attribute with a dictionary mapping the invisible attribute to the result of a domain expression. 558 | 559 | For example, to have the refers_to eld visible in all states except draft: 560 | 561 | ```xml 562 | 564 | ``` 565 | 566 | The invisible attribute is available in any element, not only elds. We can use it on notebook pages or groups, for example. 567 | 568 | The attrs can also set values for two other attributes: readonly and required, but these only make sense for data elds, making them not editable or mandatory. With this we can add some client logic such as making a eld mandatory, depending on the value from another eld, or only from a certain state onward. 569 | 570 | ## List views 列表视图 571 | Compared to form views, list views are much simpler. A list view can contain fields and buttons, and most of their attributes for forms are also valid here. 572 | 573 | 和视图表单相比较,列表视图更为简单。列表视图可以包含字段按钮,下面列表视图的大多数的表单属性在这里也是有效的。 574 | 575 | Here is an example of a list view for our To-do Tasks: 576 | 577 | 下面是一个应用To-do Tasks中的列表视图示例: 578 | 579 | ```xml 580 | 581 | To-do Task Tree 582 | todo.task 583 | 584 | 587 | 588 | 589 | 590 | 591 | 592 | ``` 593 | 594 | The attributes for the tree top element are: 595 | 596 | - editable: makes the records editable directly on the list view. The possible 597 | values are top and bottom, the location where new records will be added. 598 | - colors: dynamically sets the text color for the records, based on their content. It is a semicolon-separated list of color:condition values. The color is a CSS valid color (see http://www.w3.org/TR/css3-color/#html4), and 599 | the condition is a Python expression to evaluate on the context of the current record. 600 | - fonts: dynamically modifies the font for the records based on their content. Similar to the colors attribute, but instead sets a font style to bold, italic or underline. 601 | - create, delete, edit: if set to false (in lowercase), these disable the corresponding action on the list view. 602 | 603 | ## Search views 搜索视图 604 | The search options available on views are de ned with a search view. It de nes the elds to be searched when typing in the search box It also provides prede ned lters that can be activated with a click, and data grouping options for the records on list and kanban views. 605 | 606 | Here is a search view for the to-do tasks: 607 | 608 | ```xml 609 | 611 | To-do Task Filter 612 | todo.task 613 | 614 | 615 | 617 | 618 | 620 | 622 | 623 | 625 | 626 | 627 | 628 | ``` 629 | 630 | We can see two elds to be searched for: name and user_id. On name we have a custom lter rule that makes the "if search" both on the description and on the responsible user. Then we have two prede ned lters, ltering the not done and done tasks. These lters can be activated independently, and will be joined with an "OR" operator if both are enabled. Blocks of lters separated with a element will be joined with an "AND" operator. 631 | 632 | The third filter only sets a group-by context. This tells the view to group the records by that eld, user_id in this case. 633 | 634 | The field elements can use these attributes: 635 | 636 | - name: identifies the field to use. 637 | - string: provides a label text to use instead of the default. 638 | - operator: allows us to use a different operator other than the default – "=" for numeric fields and ilike for the other field types. 639 | - filter_domain: can be used to set a specific domain expression to use for the search, providing much more flexibility than the operator attribute. The text being searched for is referenced in the expression using self. 640 | - groups: makes the search on the field available only for a list of security groups (identified by XML IDs). 641 | 642 | For the filter elements these are the attributes available: 643 | 644 | - name: is an identifier to use for inheritance or for enabling it through 645 | search_default_ keys in the context of window actions. 646 | - string: provides label text to display for the filter (required). 647 | - domain: provides the filter domain expression to be added to the active domain. 648 | - context: is a context dictionary to add to the current context. Usually this sets a group_by key with the name of the field to group the records. 649 | - groups: makes the search filter available only for a list of groups. 650 | 651 | ## Other types of views 其他类型的视图 652 | The most frequent view types used are the form and list views, discussed until now. Other than these, a few other view types are available, and we will give a brief overview on each of them. Kanban views won't be addressed now, since we will cover them in Chapter 8, QWeb – Creating Kanban Views and Reports. 653 | 654 | Remember that the view types available are defined in the view_mode attribute of the corresponding window action. 655 | 656 | ### Calendar views 日历视图 657 | As the name suggests, this view presents the records in a calendar. A calendar view for the to-do tasks could look like this: 658 | 659 | 就像标题名称所声称的那样,这种视图用日历的方式来表现记录。 660 | 661 | ```xml 662 | 663 | view_calendar_todo_task 664 | todo.task 665 | 666 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | ``` 675 | 676 | The calendar attributes are these: 677 | 日历属性如下: 678 | 679 | - date_start: This is the field for the start date (mandatory). 680 | - date_end: This is the field for the end date (optional). 681 | - date_delay: This is the field with the duration in days. This is to be used instead of date_end. 682 | - color: This is the field used to color the calendar entries. Each value in the field will be assigned a color, and all its entries will have the same color. 683 | - display: This is the text to be displayed in the calendar entries. Fields can be inserted using []. These fields must be declared inside the calendar element. 684 | 685 | ### Gantt views 甘特视图 686 | This view presents the data in a Gantt chart, which is useful for scheduling. The to-do tasks only have a date eld for the deadline, but we can use it to have a basic Gantt view working: 687 | 688 | ```xml 689 | 690 | view_gantt_todo_task 691 | todo.task 692 | 693 | 695 | 696 | 697 | ``` 698 | 699 | Attributes that can be used for Gantt views are as follows: 700 | 701 | - date_start: This is the field for the start date (mandatory). 702 | - date_stop: This is the field for the end date. It can be replaced by the date_delay. 703 | - date_delay: This is the field with the duration in days. It can be used instead of date_stop. 704 | - progress: This is the field that provides completion percentage (between 0 and 100). 705 | - default_group_by: This is the field used to group the Gantt tasks. 706 | 707 | ### Graph views 图形视图 708 | The graph view type provides a data analysis of the data, in the form of a chart or an interactive pivot table. 709 | We will add a pivot table to the to-do tasks. First, we need a eld to be aggregated. In the TodoTask class, in the todo_ui/todo_model.py le, add this line: 710 | 711 | ```python 712 | effort_estimate = fields.Integer('Effort Estimate') 713 | ``` 714 | 715 | This should also be added to the to-do task form so that we can set some data on it. Now, let's add the graph view with a pivot table: 716 | 717 | ```xml 718 | 719 | view_graph_todo_task 720 | todo.task 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | ` 730 | ``` 731 | 732 | The graph element has a type attribute set to pivot. It can also be bar (default), pie, or line. In the case of bar, an additional stacked="True" can be used to make it a stacked bar chart. 733 | 734 | The graph should contain elds that have these possible attributes: 735 | 736 | - name: This identifies the field to use in the graph, as in other views. 737 | - type: This describes how the field will be used, as a row group (default), as a col group (column), or as a measure. 738 | - interval: only meaningful for date fields, this is the time interval used to group time data by day, week, month, quarter or year. 739 | 740 | ## Summary 总结 741 | You learned more about Odoo views used to build the user interface. We started 742 | by adding menu options and the window actions used by them to open views. The concepts of context and domain were explained in more detail in following sections. 743 | 744 | You also learned about designing list views and con guring search options using search views. Next, we had an overview of the other view types available: calendar, Gantt, and graph. Kanban views will be explored later, when you learn how to use QWeb. 745 | 746 | We have already seen models and views. In the next chapter, you will learn how to implement server-side business logic. 747 | -------------------------------------------------------------------------------- /第四章-数据序列化和模块数据.md: -------------------------------------------------------------------------------- 1 | # Chapter 4 Data Serialization and Module Data 数据序列化以及模块数据 2 | **************** 3 | 4 | Most Odoo configurations, from user interfaces to security rules, are actually 5 | data records stored in internal Odoo tables. The XML and CSV files found in modules are not used to run Odoo applications. They are just a means to load those configurations into the database tables. 6 | 7 | 从用户界面到安全规则,大多数的Odoo配置,实际数据记录都是存储在Odoo表的内部。模块中发现的XML和CSV并不用来运行Odoo应用。它们仅表示将文件中的配置载入到数据库表中。 8 | 9 | Because of this, an important part of Odoo modules is about representing (serializing) that data into files so that it can be later loaded into a database. 10 | 11 | 因此,Odoo模块中很重要的一部分是关于数据到文件的表现(序列化),所以这些数据文件可以稍晚时候再载入到数据库。 12 | 13 | Modules can also have initial and demonstration (fixture) data. Data serialization allows adding that to our modules. Additionally, understanding Odoo data serialization formats is important in order to export and import data in the context of a project implementation. 14 | 15 | 模块也可以拥有初始和说明(固定的)数据。数据序列化允许对模块添加这些数据。此外,理解Odoo的数据序列化格式在一个项目实现的上下文中导入和导出数据显得特别重要。 16 | 17 | Before we go into practical cases, we will first explore the external identifier concept, which is the key to Odoo data serialization. 18 | 19 | 在我们开始实际演练之前,我们首先来浏览外部标识符的概念,这也是Odoo数据序列化的关键所在。 20 | 21 | ## Understanding external identifiers 理解外部标识符 22 | All records in the Odoo database have a unique identifier, the `id` field. 23 | 24 | 在Odoo数据中的所有记录都拥有一个唯一的标识符,`id`字段。 25 | 26 | It is a sequential number automatically assigned by the database. However, this automatic identifier can be a challenge when loading interrelated data: how can we reference a related record if we can't know beforehand what database ID will be assigned to it? 27 | 28 | 这是一个由数据库自动赋值的序列编号。不过,在载入相互关联的数据时这个自动生成的标识符就是个挑战:在我们不能预先知道什么数据库ID被分配的情况下,我们如何在我们不能预先知道分配到关联数据的数据库ID的情况下引用关联记录? 29 | 30 | Odoo's answer to this is the external identifier. External identifiers solve this problem by assigning named identiers to the data records to be loaded. A named identifier can be used by any other piece of record data to reference it later on. Odoo will take care of translating these identifier names into the actual database IDs assigned to them. 31 | 32 | Odoo对于这种情况的解决方法是使用外部标识符。外部标识符依靠对需要载入的数据记录赋予命名标识符来解决这个问题。命名的标识符可以被任意其他记录数据在稍后使用。Odoo会将这些标识符名称翻译到实际的数据ID 33 | 34 | The mechanism behind this is quite simple: Odoo keeps a table with the mapping between the named External IDs and their corresponding numeric database IDs. That is the `ir.model.data` model. 35 | 36 | 这背后的机制也相当简单:Odoo使用被命名的外部ID及其对应的数据库数字ID之间映射保存表。这就是`ir.model.data`模型。 37 | 38 | To inspect the existing mappings, go to the **Technical** section of the **Settings** menu, and select the **Sequences & Identifiers** | **External Identifiers** menu item. 39 | 40 | 检查现存的映射,可以找到“设置”菜单的“技术”部分,然后选择“序列与标识符|外部标识符”菜单选项。 41 | 42 | For example, if we visit the **External Identifiers** list and filter it by the `todo_app` module, we will see the external identifiers generated by the module created previously. 43 | 44 | 例如,如果我们访问“外部标识符”列表,然后输入`todo_app`进行过滤,我们可以看见模块在之前创建的外部标识符。 45 | 46 | img:omit 47 | 48 | You can see that the external identi ers have a **Complete ID** label. This is composed of the module name and the identifier name joined by a dot, for example, `todo_app.action_todo_task`. 49 | 50 | 你可以看到外部标识符拥有一个“完整ID”标签。 51 | 52 | Since only the **Complete ID** is required to be unique, the module name ends up acting as a namespace for identi ers. This means that the same named identi er can be repeated in different modules, and we don't need to worry about identi ers in our module colliding with identi ers in other modules. 53 | 54 | At the top of the list, you can see the `todo_app.action_todo_task` ID. This is the menu action we created for the module, which is also referenced in the corresponding menu item. By clicking on it, you can open a form with its details: the `action_todo_task` in the `todo_app` module maps to a specific record ID in the `ir.actions.act_window` model. 55 | 56 | 在列表的顶部,你可以看到 `todo_app.action_todo_task`ID。这是一个我们为模块创建的菜单动作,它也被应用到了对应的菜单项目中。通过点击这个菜单,你可以打开一个拥有自身细节的表单:`todo_app`模块中的`action_todo_task`被映射到了`ir.actions.act_window`模型中的指定记录ID。 57 | 58 | img:omit 59 | 60 | Besides providing a way for records to easily reference other records, External IDs also allow avoiding data duplication on repeated imports. If the External ID is already present, the existing record will be updated, instead of creating a new record. This is why, on subsequent module upgrades, previously loaded records are updated instead of being duplicated. 61 | 62 | 除了为记录提供了简单的引用其它记录值一种办法之外,外部ID也能够在重复导入时避免数据的重复。如果外部ID已经存在,那么存在的记录将被更新,而不是另外创建一个新纪录。 63 | 64 | ### Finding External IDs 查找外部ID 65 | When preparing configuration and demonstration data files for modules, we frequently need to look up existing External IDs that are needed for references. 66 | 67 | 在准备模块的配置和演示文件时,我们经常需要查找需要引用到的现有外部ID。 68 | 69 | We can use the External Identifiers menu shown earlier, but the **Developer Menu** can provide a more convenient method for that. As you may recall from `Chapter 1, Getting Started with Odoo Development`, the **Developer Menu** is activated in the About Odoo option, and then, it is available at the top-left corner of the web client view. 70 | 71 | 我们可以使用前面出现过的外部标识符菜单,但是Developer Menu提供了一种更为方便的方式。 72 | 73 | To find the External ID for a data record, on the corresponding Form view, select the **View Metadata** option from the **Developer Menu**. This will display a dialog with the record's database ID and External ID (also known as XML ID). 74 | 75 | 要查找数据记录的外部ID, 76 | 77 | As an example, to look up the Demo user ID, we can navigate to its Form view (Settings | Users) and select the View Metadata option, after which we will be shown this: 78 | 79 | 例如,查询Demo用户的ID,我们可以 80 | 81 | img:omit 82 | 83 | To find the External ID for view elements, such as form, tree, search, and action, the **Developer Menu** is also a good help. For that, use its **Manage Views** option or open the information for the desired view using the **Edit ** options, and then select their **View Metadata** option. 84 | 85 | 要查找视图元素的外部ID,比如表单、树形、搜索和动作,Developer Menu也可以用到。为此,我们使用Manage Views的Manage Views选项,或者 86 | 87 | ## Exporting and importing data 导出和导入数据 88 | We will start exploring how data export and import work in Odoo, and from there, we will move on to the more technical details. 89 | 90 | ### Exporting data 导出数据 91 | Data export is a standard feature available in any List view. To use it, we must first select the rows to export by selecting the corresponding checkboxes on the far left, and then select the **Export** option from the **More** button. 92 | 93 | 数据导出是在任何一个列表视图中有存在的标准功能。要使用它,首先,我们必须选择 94 | 95 | Here is an example, using the recently created to-do tasks: 96 | 97 | 下面是一个例子,使用最近创建的to-do tasks: 98 | 99 | img:omit 100 | 101 | The **Export** option takes us to a dialog form, where we can choose what to export. The **Import Compatible Export** option makes sure that the exported file can be imported back to Odoo. We will need to use this. 102 | 103 | “导出”选项把我们带到一个对话表单,这里我们可以选择希望导入的内容。 104 | 105 | The export format can be CSV or Excel. We will prefer CSV file to get a better understanding of the export format. Next, we should pick the columns we want to export and click on the **Export To File** button. This will start the download of a file with the exported data. 106 | 107 | 导出格式可以是CSV或者EXcel。我们倾向于选择CSV文件以更好理解导出的格式。接下来,我们应该选择我们希望导入的列,然后点击“导出到文件”按钮。这样做会下载一个含有导出数据的文件。 108 | 109 | img:omit 110 | 111 | If we follow these instructions and select the fields shown in the preceding screenshot, we should end up with a CSV text file similar to this: 112 | 113 | 如果我们依照这些命令,然后选择之前截图中出现的字段,我们应该向下面这样结束一个CSV文本文件: 114 | 115 | ```csv 116 | "id","name","user_id/id","date_deadline","is_done" 117 | "__export__.todo_task_1","Install Odoo","base.user_root","2015-01- 118 | 30","True" 119 | "__export__.todo_task_2","Create dev database","base.user_ 120 | root","","False" 121 | ``` 122 | 123 | Notice that Odoo automatically exported an additional id column. This is an External ID that is automatically generated for each record. These generated External IDs use `__export__` in place of an actual module name. New identi ers are only assigned to records that don't already have one, and from there on, they are kept bound to the same record. This means that subsequent exports will preserve the same External IDs. 124 | 125 | 注意Odoo可以自动地导出外部id列。这是一个为每个记录自动生成的ID。这些外部ID 126 | 127 | ## Importing data 导入数据 128 | First we have to make sure the import feature is enabled. This is done in the Settings menu, **Configuration** | **General Settings** option. Under the Import / Export topic, make sure the **Allow users to import data from CSV files** checkbox is enabled. 129 | 130 | 首先,我们必须确保导入功能启用。你可以运用设置菜单中的“配置|通用设置”选项来完成这项操作。 131 | 132 | With this option enabled, the List views show an **Import** option next to the **Create** button at the top of the list. 133 | 134 | 当启用这个选项时,列表视图显示一个“导入”选项,旁边是一个在列表顶部的“创建”按钮。 135 | 136 | Let's perform a mass edit on our to-do data: open in a spreadsheet or a text editor the CSV file we just downloaded, then change a few values and add some new rows. 137 | 138 | 让我们来对to-do数据执行批量编辑:在电子表格或者文本编辑器中打开我们刚才下载的CSV文件,然后改版几个值,再添加几个新行。 139 | 140 | As mentioned before, the first `id` column provides a unique identifier for each row allowing already existing records to be updated instead of duplicated when we import the data back to Odoo. For new rows we may add to the CSV le, the id should be left blank, and a new record will be created for them. 141 | 142 | 就像之前说的那样,第一个`id`列为每一个行都提供了唯一标识符,以允许已经存在的记录可以被更新,而不是被复制, 143 | 144 | After saving the changes on the CSV file, click on the **Import** option (next to the **Create** button) and we will be presented with the import assistant. There we should select the CSV file location on disk and click on **Validate** to check its format for correctness. Since the file to import is based on an Odoo export, there is a good chance it will be valid. 145 | 146 | 当保存对CSV文件变更之后,点击“导入”选项(靠近“创建”按钮),我们 147 | 148 | img:omit 149 | 150 | Now we can click on Import and there you go: our modifications and new records should have been loaded into Odoo. 151 | 152 | 现在,我们可以点击“导入”就可以得到你想要的结果:我们的修改以及新纪录都应该被载入到了Odoo中。 153 | 154 | ### Related records in CSV data files CSV数据文件中的关联记录 155 | In the example seen above, the user responsible for each task is a related record in the users model, with a many to one (or foreign key) relation. The column name used for it was `user_id/id` and the eld values were External IDs for the related records, such as `base.user_root` for the administrator user. 156 | 157 | 在上面的例子中,用户负责的每个一个任务都是用户模型的关联记录, 158 | 159 | Relation columns should have `/id` appended to their name, if using External IDs, or `/.id`, if using database (numeric) IDs. Alternatively, a colon (`:`) can be used in place of the slash for the same effect. 160 | 161 | 如果使用外部ID的话,关联列应该将`/id`追加到自己的名字上,如果使用数据库(数字形式的)ID则使用`/.id`。 162 | 163 | Similarly, **many to many** relations are also supported. An example of a many to many relations is the one between Users and Groups: each User can be in many Groups, and each Group can have many Users. The column name for this type offi eld should have appended a `/id`. The field values accept a comma-separated list of External IDs, surrounded by double quotes. 164 | 165 | 类似的,“多对多”关系也是被支持的。一个多对多关系的例子就是用户和组之间的关系:每个用户都可能属于多个组,而每个组也可以拥有多个用户。 166 | 167 | For example, the to-do task follower is a many-to-many relation between To-do Tasks and Partners. It's column name could be `follower_ids/id` and a field value with two followers could be: 168 | 169 | 例如,在 To-do Tasks和Partners之间to-do task的关注者是一个多对多关系。它的列名称是`follower_ids/id`,而且一个字段值拥有两个关注者: 170 | 171 | ```csv 172 | "__export__.res_partner_1,__export__.res_partner_2" 173 | ``` 174 | 175 | Finally, **one to many** relations can also be imported through a CSV. The typical example of this type of relations is a document "head" with several "lines". 176 | 177 | 最后,“一对多”关系也可以通过CSV来导入。这种关系类型的典型例子是一个文档“头部”含有多个“行”。 178 | 179 | We can see an example for such a relation in the company model (form view available in the **Settings** menu): a company can have several bank accounts, each with its own details, and each bank account belongs to (has a many-to-one relation with) only one company. 180 | 181 | 我们看到company模型中就有这类关系的一个例子(“设置”菜单中可用的表单视图):一家公司拥有多个银行帐户,每个账户都拥有自己的具体内容,而且每个银行帐户都只属于(属于多对一的关系)一家公司。 182 | 183 | It's possible to import companies along with their bank accounts in a single file. For this, some columns will correspond to the company, and other columns will correspond to the bank account details. The bank details column names should be pre xed with the one-to-many elds linking the company to the banks; `bank_ids` in this case. 184 | 185 | 在单个文件中可以使用银行帐户导入公司。为此, 186 | 187 | The first bank account details goes in the same row as its related company data. The next bank account's details go in the next rows, but only the bank details related columns should have values; the company data columns should be empty in those lines. 188 | 189 | 第一个银行帐户的细节放到了相同的行。 190 | 191 | Here is an example loading a company with three banks: 192 | 193 | 下面是一个使用三个银行载入一个公司的例子: 194 | 195 | ```csv 196 | id,name,bank_ids/id,bank_ids/acc_number,bank_ids/state 197 | base.main_company,YourCompany,__export__.res_partner_ 198 | bank_4,123456789,bank 199 | ,,__export__.res_partner_bank_5,135792468,bank 200 | ,,__export__.res_partner_bank_6,1122334455,bank 201 | ``` 202 | 203 | 204 | Notice that the two last lines begin with two commas: this corresponds to empty values in the first two columns, id and name, regarding the head company data. But the remaining columns, regarding bank accounts, have the values for the second and third bank records. 205 | 206 | 注意,最后两行以两个逗号开始:这对应了头两行中的空值,id和name——公司数据的头部。但是剩下的列,像银行帐户,拥有 207 | 208 | These are the essentials on working with export and import from the GUI. It's useful to set up data in new Odoo instances, or to prepare data files to be included in Odoo modules. Next we will learn more about using data les in modules. 209 | 210 | 从GUI导出和导入这些工作都是相当基础的。在新的Odoo实例中设置数据相当实用,或者准备需要包含在Odoo模块中的数据文件。接下来,我们会学习更多在模块中使用数据文件的内容。 211 | 212 | ## Module data 模块数据 213 | Modules use data files to load their configurations into the database, initial data and demonstration data. This can be done using both CSV and XML files. For completeness, the YAML file format can also be used, but this is rarely used for data loading, so we won't be discussing it. 214 | 215 | 模块使用数据文件将配置载入到数据库,初始化数据和说明数据。使用CSV文件和XML文件可以实现这个目的。为了实现的完整性,YAML文件格式也可以使用,在这种格式很少用来到数据载入上,所以,我们不会讨论他。 216 | 217 | CSV files used by modules are exactly the same as those we have seen and used for the import feature. When using them in modules, the only additional restriction is that the file name must match the name of the model to which the data will be loaded. 218 | 219 | 模块使用的CSV文件和我们之前看过的用来导入功能的CSV完全一样。当在模块中应用这些文件时,唯一一个额外限制就是文件名称必须匹配会载入数据的模型的名称。 220 | 221 | A common example is security access, to load into the `ir.model.acess` model. This is usually done using CSV files, and they should be named `ir.model.acess.csv`. 222 | 223 | 常见的一个例子是安全访问,载入到`ir.model.acess`模型。我们通常使用CSV文件来完成这项操作,而且CSV文件应该使用名称`ir.model.acess.csv`。 224 | 225 | ### Demonstration data 演示数据 226 | Odoo modules may install demo data. This is useful to provide usage examples for a module and data sets to be used in tests. It's considered good practice for modules to provide demonstration data. Demonstration data for a module is declared using the demo attribute of the `__openerp__.py` manifest file. Just like the data attribute, it is a list of file names with the corresponding relative paths inside the module. 227 | 228 | Odoo模块也可以安装演示数据。在测试的时候这个办法为模块和数据集提供了很有用的例子。模块的最佳实践被认为是提供演示数据。模块的演示数据使用清单文件`__openerp__.py`的demo属性来声明。和data属性一样,这是一个拥有模块内部中相对路径的文件名称列表。 229 | 230 | We will be adding demonstration data to our `todo_user` module. We can start by exporting some data from the to-do tasks, as explained in the previous section. Next we should save that data in the `todo_user` directory with file name todo.task.csv. Since this data will be owned by our module, we should edit the id values to replace the `__export__` prefix in the identifiers with the module technical name. 231 | 232 | 我们对`todo_user`模块添加示范数据。如前一节所说,我们从to-do tasks导入一些数据开始。接下来我们应该在`todo_user`使用文件名todo.task.csv来保存这份数据。因为这份数据时自定义模块所有,所以在标识符中,我们应该编辑id的值,使用模块技术名来替换`__export__`前缀。 233 | 234 | As an example our `todo.task.csv` data file might look like this: 235 | 236 | 我们的例子中的`todo.task.csv`数据文件看起来是这个样子的: 237 | 238 | ```csv 239 | id,name,user_id/id,date_deadline 240 | todo_task_a,"Install Odoo","base.user_root","2015-01-30" 241 | todo_task_b","Create dev database","base.user_root","" 242 | ``` 243 | 244 | 245 | We must not forget to add this data file to the `__openerp__.py` manifest demo attribute: 246 | 247 | 我们一定不能忘记将这个数据文件添加到清单文件`__openerp__.py`的demo属性中: 248 | 249 | ``` 250 | 'demo': ['todo.task.csv'], 251 | ``` 252 | 253 | Next time we update the module, as long as it was installed with demo data enabled, the content of the file will be imported. Note that this data will be rewritten whenever a module upgrade is performed. 254 | 255 | 下一次,我们更新模块时,只要模块安装了demo数据,文件的内容就可以被导入了。注意,不论何时,该数据在模块执行升级时便会被重写。 256 | 257 | XML files can also be used for demonstration data. Their file names are not required to match the model to load, because the XML format is much richer and that information is provided by the XML elements inside the file. 258 | 259 | XML文件也可以被用做示范数据。它们的文件名并不需要匹配被载入的模型,因为XML格式更为丰富,需要的信息由 260 | 261 | Let's learn more about what XML data files allow us to do that CSV files don't. 262 | 263 | 让我们来学习更多的关系XML数据文件可以做到而CSV文件不能做的事情。 264 | 265 | ## XML data files XML数据文件 266 | While CSV files provide a simple and compact format to serialize data, XML files are more powerful and give more control over the loading process. 267 | 268 | XML文件更为强大、可以更为牢固地控制载入过程, 而CSV文件对序列化数据提供了一个简单而小巧的格式。 269 | 270 | We have already used XML data files in the previous chapters. The user interface components, such as views and menu items, are in fact records stored in system models. The XML files in the modules are a means used to load those records into the server. 271 | 272 | 在之前的章节我们已经使用XML数据。用户接口组件,比如视图和菜单选项中的实际记录是存储在系统模型中的。模块中的XML文件被用来载入这些记录到服务器。 273 | 274 | To showcase this, we will add a second data file to the todo_user module, named todo_data.xml, with the following content: 275 | 276 | 为了说明这种情况,我们添加第二个称作todo_data.xml的包含下列内容的数据文件到todo_user模块: 277 | 278 | ```xml 279 | 280 | 281 | 282 | 283 | 284 | Reinstall Odoo 285 | 286 | 2015-01-30 287 | 288 | 289 | 290 | ``` 291 | 292 | This XML is equivalent to the CSV data file we have just seen in the previous section. 293 | 294 | 这个XML等同于我们在前一节见到的CSV数据文件。 295 | 296 | XML data files have a `` element containing `` elements, inside of which we can have have several `` elements, corresponding to the CSV data rows. 297 | 298 | XML数据文件拥有一个包含``元素的``元素,这其中我们可以拥有多个对应到CSV数据列的``元素。 299 | 300 | A `` element has two mandatory attributes, `model` and `id` (the external identifier for the record), and contains a `` tag for each field to write on. 301 | 302 | ``元素拥有两个必写属性,`model`和`id`(记录的外部标识符),并为每个需要编写的字段的附加了``标签。 303 | 304 | Note that the slash notation in field names is not available here: we can't use ``. Instead the `ref` special attribute is used to reference External IDs. We'll discuss the values for the relational "to many" fields in a moment. 305 | 306 | 注意字段名称中的反斜杠记号在这里是不可用的:我们不能使用``。相反,`ref` 特殊属性被用来引用外部ID。一会儿我们将讨论到关联“对多”字段的值。 307 | 308 | ### The data noupdate attribute 数据的非更新属性 309 | When the data loading is repeated, existing records from the previous run are rewritten. 310 | 311 | 当数据载入发生重复时,之前运行的已存在记录会被重写。 312 | 313 | This is important to keep in mind: it means that upgrading a module will overwrite any manual changes that might have been made to the data. Notably, if views were modified with customizations, those changes will be lost with the next module upgrade. The correct procedure is to instead create inherited views for the changes we need, as discussed in the `Chapter 3, Inheritance – Extending Existing Applications`. 314 | 315 | 牢记这条规则在心是非常重要的:这就是说升级一个模块将会重写任何应用到数据的手动变更。特别的,如果视图应用了自定义修改,那么这些变更在下一次模块升级时被丢弃。正确的流程是为我们需要应用的变更创建继承视图,一如在`第三章,继承——扩展现有模型`所讨论的那样。 316 | 317 | This overwrite behavior is the default, but it can be changed, so that when an already created record is loaded again no change is made to it. This is done by adding to the `` element a `noupdate="1"` attribute. With this, its records will be created the first time they are loaded, and in subsequent module upgrades nothing will be done to them. 318 | 重写行为是默认的,但是也可以改变的,所以一个已经创建过的记录被再次载入时变更被应用到记录。通过对``元素添加一个`noupdate="1"`属性就可以实现。这样,记录在模型第一次载入时被创建,而且之后的模块升级也不会影响到这些数据。 319 | 320 | This allows for manually made customizations to be safe from module upgrades. It is often used with record access rules, allowing them to be adapted to implementation specific needs. 321 | 322 | 这样做就允许手动。这经常和记录访问规则一起使用, 323 | 324 | It is also possible to have more than one `` section in the same XML file. We can take advantage of this to have a data set with noupdate="1" and another with noupdate="0". 325 | 326 | 在相同的XML文件中可以拥有不止一个``区域。我们 327 | 328 | The `noupdate` flag is stored in the External Identifier information for each record. It's possible to edit it directly using the External Identifier form available in the Technical menu, with the **Non Updatable** checkbox. 329 | 330 | 每个记录的odoWizard旗标都存储在了外部标识符中。在技术菜单中,可以直接使用外部标识符表单来对Non Updatable复选框进行编辑。 331 | 332 | >### Tips 333 | >The `noupdate` attribute is tricky when developing modules, because changes made to the data later will be ignored, and Odoo won't pick up later modifications. A solution is to keep `noupdate="0"` during development and only set it to 1 once finished. 334 | 335 | >### 提示 336 | >在开发时应用`noupdate`属性是个技巧,因为对数据的变更在之后会被忽略掉,而且Odoo也不会接受之后的修改。解决方案时在开发时保证`noupdate="0"`而且尽在开发完成后才设置为1. 337 | 338 | ### Defining Records in XML 在XML中定义记录 339 | Each `` element has two basic attributes, id and model, and contains `` elements assigning values to each column. As mentioned before, the id attribute corresponds to the record's External ID and the model to the target model where the record will be written. The elements have available a few different ways to assign values. Let's look at them in detail. 340 | 341 | ``元素都拥有两个基本属性,id和model,并包含赋值到每个列的``元素。就像之前所说的那样,id属性对应到记录的外部ID,model对应到需要写入记录的模型中。元素有好几种不同的方法来赋值。让我们来仔细看看。 342 | 343 | ### Setting field values 设置字段的值 344 | The `` element defines a data record, and contains elements to set values on each field. 345 | The name attribute of the eld element identi es the eld to be written. 346 | The value to write is the element content: the text between the eld's opening and closing tag. In general this is also suitable to set non-text values: for Booleans, "0"/ "1" or "False"/"True" values will be correctly converted; for dates and datetimes, stringswith"YYYY-MM-DD"and"YYYY-MM-DD HH:MI:SS"willbeconvertedproperly. 347 | 348 | ``元素定义了数据记录,包含 349 | 350 | ### Setting values using expressions 使用表达式设置值 351 | A more advanced alternative to define a field value is using the eval attribute instead. This evaluates a Python expression and assigns the resulting value to the field. 352 | 353 | 更为高级的可以选择的是使用evle属性定义一个字段的值。该方法通过对Python表达式求值,然后对字段赋予结果值。 354 | 355 | The expression is evaluated in a context that, besides Python built-ins, also has some additional identifiers available. Let's have a look at them. 356 | 357 | 在上下文中表达式的求值除了可以使用Python内建的方法之外,还可以使用一些额外的标识符。我们就来看看这些它们。 358 | 359 | To handle dates, the following modules are available: time, datetime, timedelta and relativedelta. They allow calculating date values, something that is frequently used in demonstration (and test) data. For example, to set a value to yesterday we would use: 360 | 361 | 为了处理日期,下列模块是使用的:time, datetime, timedelta 以及 relativedelta。它们能够计算日期的值, 362 | 363 | ```xml 364 | 365 | ``` 366 | 367 | Also available in the evaluation context is the ref() function, used to translate an External ID into the corresponding database ID. This can be used to set values for relational fields. As an example, we have used it before to set the value for the user_id: 368 | 369 | 在求值上下文中还可以使用的是ref()函数,该函数用来翻译外部ID到对象的数据库ID。它可以用来设置关联字段的值。例如,我们之前曾使用它为user_id设置值。 370 | 371 | ```xml 372 | 373 | ``` 374 | 375 | The evaluation context also has a reference available to the current Model being written through `obj`. It can be used together with ref() to access values from other records. Here is an example from the Sales module: 376 | 377 | 计算上下文也可以 378 | 379 | ```xml 380 | 381 | ``` 382 | 383 | ### Setting values for relation fields 为关联字段设置值 384 | We have just seen how to set a value on a many-to-one relation field, such as user_id, using the eval attribute with a ref() function. But there is a simpler way. 385 | 386 | 我们刚刚看到了如何在一个多对一关联字段设置值,比如user_id,就可以使用含有ref()函数的eval属性。不过这里有一个简单的办法。 387 | 388 | The element also has a ref attribute to set the value for a many-to-one field using an External ID. Using it, we can set the value for user_id using just: 389 | 390 | 元素也包含ref属性来使用外部ID为多对一字段设置值。使用这个属性我们可以设置 391 | 392 | ```xml 393 | 394 | ``` 395 | 396 | For one-to-many and many-to-many fields, a list of related IDs is expected, so a different syntax is needed, and Odoo provides a special syntax to write on this type of fields. 397 | 398 | 对于一对多和多对多字典,需要用到关联ID的列表,所以也就需要不同的语法来实现,而且Odoo也为提供了特殊的语法来写这种类型的字段。 399 | 400 | The following example, taken from the Fleet app, replaces the list of related records of a tag_ids field: 401 | 402 | 下面的例子,取自Fleet应用,它: 403 | 404 | ```xml 405 | 410 | ``` 411 | 412 | To write on a to many-field we use a list of triples. Each triple is a write command that does different things according to the code used: 413 | 414 | 我们使用一个三元组列表来重写多个字段。对于所用的代码,每个三元组都是一个实现不同事情的写命令: 415 | 416 | - (0,_ ,{'field': value}): This creates a new record and links it to this one 417 | - (1,id,{'field': value}): This updates values on an already linked record 418 | - (2,id,_): This unlinks and deletes a related record 419 | - (3,id,_): This unlinks but does not delete a related record 420 | - (4,id,_): This links an already existing record 421 | - (5,_,_): This unlinks but does not delete all linked records 422 | - (6,_,[ids]): This replaces the list of linked records with the provided list 423 | 424 | - (0,_ ,{'field': value}): 创建一的新的记录,并把新纪录链接过来。 425 | - (1,id,{'field': value}): 对一个已有的链接记录更新值。 426 | - (2,id,_): unlink 和 delete 一条关联记录。 427 | - (3,id,_): 对关联的记录仅执行unlink而不是delete 428 | - (4,id,_): 链接到一条现有的记录。 429 | - (5,_,_):对全部链接的记录执行unlink而不是delete。 430 | - (6,_,[ids]):使用用户提供的链接来替换链接记录的列表。 431 | 432 | The underscore symbol used above represents irrelevant values, usually filled with 0 or False. 433 | 434 | 上面用到的下划线符号表示无关联的值,通常使用0或者False来填充。 435 | 436 | ### Shortcuts for frequently used Models 常用模型的快捷键 437 | If we go back to Chapter 2, Building Your First Odoo Application, we can find in the XML files elements other than ``, such as `` and ``. 438 | 439 | 如果我们回到第二章, 440 | 441 | These are convenient shortcuts for frequently used Models that can also be loaded using regular `` elements. They load data into base Models supporting the user interface and will be explored in more detail later, in Chapter 6, Views - Designing the User Interface. 442 | 443 | 这些方便的捷径常常使用的是可以使用普通``元素 444 | 445 | For reference, so that we can better understand XML files we may encounter in existing modules, the following shortcut elements are available with the corresponding Models they load data into: 446 | 447 | 所以,我们可以更好的理解可能在现有模块中遇到的XML文件,下面的快捷元素 448 | 449 | - ``: This is the Window Actions model ir.actions.act_window 450 | - ``: This is the Menu Items model ir.ui.menu 451 | - ``: This is the Report Actions model ir.actions.report.xml 452 | - `