Author: |
My Company |
License: |
no license |
Branch: |
14.0 |
Repository: |
twtrubiks/odoo-demo-addons-tutorial |
Dependencies: |
base |
Languages: |
Markdown (175, 50.3%),
Python (62, 17.8%),
and
XML (111, 31.9%) |
Other branches: |
master |
<h1>odoo 觀念 - TransientModel - Wizard</h1>
<p>建議觀看影片, 會更清楚:smile:</p>
<p><a href="https://youtu.be/Gc-wRnAhbKs">Youtube Tutorial - odoo 觀念 - TransientModel - Wizard</a></p>
<p>建議在閱讀這篇文章之前, 請先確保了解看過以下的文章 (因為都有連貫的關係)</p>
<p><a href="https://github.com/twtrubiks/odoo-demo-addons-tutorial/tree/master/demo_odoo_tutorial">odoo 手把手建立第一個 addons</a></p>
<p>本篇文章主要介紹 odoo 中的 wizard 這部份</p>
<h2>說明</h2>
<p>在 odoo 中, wizard 是一個很特別的 model, 之前除了介紹過最基本的 BaseModel (可參考 <a href="https://github.com/twtrubiks/odoo-demo-addons-tutorial/tree/master/demo_odoo_tutorial">demo<em>odoo</em>tutorial</a>)
之外,</p>
<p>今天要來介紹另一個 model, 也就是 TransientModel,</p>
<p><code>TransientModel</code> 繼承自 Model, <code>_transient = True</code>,</p>
<p>先簡單看一下它的定義,</p>
<p>```python
class TransientModel(Model):
""" Model super-class for transient records, meant to be temporarily
persisted, and regularly vacuum-cleaned.</p>
<pre><code>A TransientModel has a simplified access rights management, all users can
create new records, and may only access the records they created. The super-
user has unrestricted access to all TransientModel records.
"""
_auto = True # automatically create database backend
_register = False # not visible in ORM registry, meant to be python-inherited only
_abstract = False # not abstract
_transient = True # transient
</code></pre>
<p>```</p>
<p>TransientModel 是一種特殊的 model, TransientModel 所產生的 model 會在一個時間定期被刪除,</p>
<p>所以 TransientModel 只適合建立暫時的數據, 也就是接著要介紹的 wizard.</p>
<p>先來看 <a href="wizard/model_wizard.py">wizard/model_wizard.py</a></p>
<p>```python
class DemoWizard(models.TransientModel):
_name = "demo.wizard"
_description = "Demo Wizard"</p>
<pre><code>wizard_partner_id = fields.Many2one('res.partner', string='Partner')
wizard_test_context = fields.Char('wizard_test_context')
@api.model
def default_get(self, fields):
res = super(DemoWizard, self).default_get(fields)
default_partner_id = self.env.context.get('default_partner_id', [])
res.update({
'wizard_partner_id': default_partner_id,
})
# or
# res['wizard_partner_id'] = default_partner_id
return res
def btn_validate(self):
self.ensure_one()
context = dict(self._context or {})
default_test_pass_data = context.get('default_test_pass_data', [])
_logger.warning('============= btn_validate ==================')
_logger.warning('default_test_pass_data: %s', default_test_pass_data)
_logger.warning('wizard_test_context: %s', self.wizard_test_context)
return {'type': 'ir.actions.act_window_close'}
</code></pre>
<p>```</p>
<p>注意:exclamation: 這邊是使用 <code>models.TransientModel</code>.</p>
<p>注意:exclamation: 從 odoo14 開始, Transient models <strong>需要</strong> access rules.</p>
<p>可參考 <a href="https://github.com/twtrubiks/odoo-demo-addons-tutorial/blob/14.0/demo_odoo_tutorial_wizard/security/ir.model.access.csv">security/ir.model.access.csv</a> 中的設定.</p>
<p><a href="views/view.xml">views/view.xml</a></p>
<p>```xml
......</p>
<p><record id="view_form_demo_odoo_tutorial" model="ir.ui.view">
<field name="name">Demo Odoo Tutorial Form</field>
<field name="model">demo.odoo.wizard.tutorial</field>
<field name="arch" type="xml">
<form string="Demo Odoo Tutorial">
<header>
<button name="%(demo_odoo_tutorial_wizard.demo_wizard_action)d"
type="action"
string="Call Wizard"
class="oe_highlight"
context="{'default_partner_id': partner_id}"/>
</header>
<sheet>
<group>
<field name="name"/>
<field name="partner_id"/>
</group>
</sheet>
</form>
</field>
</record>
......
```</p>
<p>重點在 button 這段, 這段是去呼叫 <code>demo_wizard_action</code>,</p>
<p>寫法是 <code>name="%(demo_odoo_tutorial_wizard.demo_wizard_action)d"</code>,</p>
<p>路徑在 <a href="wizard/model_wizard.xml">wizard/model_wizard.xml</a></p>
<p>```xml
......
<record id="demo_wizard_view_form" model="ir.ui.view">
<field name="name">demo.wizard.form</field>
<field name="model">demo.wizard</field>
<field name="arch" type="xml">
<form string="Wizard Form">
<sheet>
<div class="oe_title">
<h1>Wizard Title</h1>
</div>
<group>
<field name="wizard_partner_id"/>
<field name="wizard_test_context"/>
</group>
</sheet>
<footer>
<button string='Validate' name="btn_validate" type="object" class="btn-primary"/>
<button string="Cancel" class="btn-secondary" special="cancel"/>
</footer>
</form>
</field>
</record></p>
<pre><code><!-- demo_wizard_action -->
<record id="demo_wizard_action" model="ir.actions.act_window">
<field name="name">Demo Wizard Action</field>
<field name="res_model">demo.wizard</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="view_id" ref="demo_wizard_view_form"/>
<field name="target">new</field>
<field name="context">{'default_test_pass_data': 'hello 123'}</field>
</record>
</code></pre>
<p>......</p>
<p>```</p>
<p>點選 Call Wizard,</p>
<p><img src="https://i.imgur.com/CXK9ePn.png" alt="alt tag"></p>
<p>會跳出 Wizard,</p>
<p><img src="https://i.imgur.com/aIOT2mI.png" alt="alt tag"></p>
<p>Partner 會幫你自動帶入 partner_id,</p>
<p>原因是因為使用了 context <code>{'default_partner_id': partner_id</code>,</p>
<p>以及 <code>def default_get(self, fields)</code> 的方法實現.</p>
<p>當你在 <code>wizard_test_context</code> 輸入任何內容 (twtrubiks), 然後點選 Validate</p>
<p><img src="https://i.imgur.com/2WWQQCj.png" alt="alt tag"></p>
<p>它會 call <code>def btn_validate</code>,</p>
<p>然後從 CLI 你會看到兩條 log,</p>
<p><img src="https://i.imgur.com/nZDDTmp.png" alt="alt tag"></p>
<p>log 1, <code>default_test_pass_data: hello 123</code></p>
<p>會出現這條訊息的原因是因為設定了預設的 context,</p>
<p><code><field name="context">{'default_test_pass_data': 'hello 123'}</field></code></p>
<p>log 2, <code>wizard_test_context: twtrubiks</code></p>
<p>這條訊息則是顯示剛剛輸入的內容.</p>
<p>另一種傳值的方式, 也可以全部透過 python 來完成,</p>
<p>可參考 <a href="models/models.py">models/models.py</a></p>
<p>```python
......
@api.multi
def action<em>context</em>demo(self):
# if self.<em>context.get('context</em>data', False):
if self.env.context.get('context_data'):
raise ValidationError('have context data')
raise ValidationError('hello')</p>
<p>@api.multi
def action<em>button(self):
for record in self:
record.with</em>context(context<em>data=True).action</em>context_demo()
......
```</p>
<p><a href="views/view.xml">views/view.xml</a> 的部份,</p>
<p>```xml
......
<button name="action_context_demo"
type="object"
string="action context demo"
class="oe_highlight"/></p>
<p><button name="action_button"
type="object"
string="action button"
class="oe_highlight"/>
......
```</p>
<p><img src="https://i.imgur.com/oqnr1Ox.png" alt="alt tag"></p>
<p>當點下 <code>action context demo</code> button 時,</p>
<p>會跳出 hello, 因為 <code>context_data</code> 為 <code>False</code>.</p>
<p><img src="https://i.imgur.com/6rMlJHK.png" alt="alt tag"></p>
<p>當點下 <code>action button</code> button 時,</p>
<p>會跳出 have context data, 因為 <code>context_data</code> 為 <code>True</code>,</p>
<p>主要透過 <code>record.with_context(context_data=True).action_context_demo()</code> 這段,</p>
<p>將 <code>context_data</code> 送進去.</p>
<p><img src="https://i.imgur.com/YIoy0yL.png" alt="alt tag"></p>