Managing Module Data

Trong phần này chúng ta sẽ tìm hiểu thêm về cách thức làm sao để Mô đun có thể cung cấp dữ liệu tại thời điểm cài đặt. Điều này rất hữu dụng khi chúng ta muốn cung cấp dữ liệu mặc định, thêm siêu dữ liệu (như đặc tả giao diện, trình đơn, hoặc hành động). Một cách sử dụng quan trọng khác nữa, chính là cung cấp dữ liệu demo, những dữ liệu demo này sẽ được nạp vào khi Cơ sở dữ liệu được khởi tạo với tùy chọn Nạp dữ liệu demo.

Trong phần này, chúng ta sẽ đề cập đến những nội dung chính sau:

  • Sử dụng định danh ngoài (external ID) và không gian tên (namespace)

  • Nạp dữ liệu sử dụng tệp tin XML

  • Sử dụng cờ noupdateforcecreate

  • Nạp dữ liệu sử dụng tệp tin CSV

  • Cập nhật Mô đun và di trú dữ liệu

  • Xóa bản ghi từ tệp tin XML

  • Gọi hàm từ tệp tin XML

Yêu cầu kỹ thuật

Yêu cầu kỹ thuật cho phần này bao gồm nền tảng Odoo.

Tất cả mã nguồn sử dụng trong phần này được lấy từ ứng dụng Quản lý giáo dục mà chúng tôi phát triển.

Sử dụng định danh ngoài và không gian tên

Định danh ngoài hay XML ID được Odoo sử dụng để định danh bản ghi. Từ đầu cho đến phần này, chúng ta đã sử dụng XML ID trong nhiều chỗ như đặc tả giao diện, trình đơn, hành động. Tuy nhiên chúng ta chưa đề cập đến XML ID thực sự là gì. Nội dung trong phần này sẽ giúp bạn hiểu sâu hơn về nó.

Làm sao để thực hiện nó...

Chúng ta sẽ cập nhập một bản ghi đang có để mô phỏng cách sử dụng:

  1. Cập nhật tệp tin manifest của mô đun quản lý giáo dục để đăng ký tệp tin dữ liệu:

    'data': [
        'data/data.xml',
    ]
    
  2. Trong tệp tin dữ liệu đó, chúng ta sẽ tạo một bản ghi mới cho model education.school:

    <record id="main_school" model="education.school">
        <field name="name">My School</field>
        <field name="name">SCHOOL001</field>
        <field name="company_id" ref="base.main_company"/>
    </record>
    
  3. Cập nhật tên cho bản ghi main_company của mô đun base để đổi tên công ty:

    <record id="base.main_company" model="res.company">
        <field name="name">Education Company</field>
    </record>
    

Cài đặt mô đun quản lý giáo dục để áp dụng thay đổi trên. Sau khi cài đặt, một bản ghi mới cho model education.school (My School) sẽ được tạo và tên của công ty sẽ được đổi tên thành Education Company.

Nó thực hiện như thế nào ...

XML ID là một chuỗi ký tự tham chiếu đến một bản ghi trong Cơ sở dữ liệu. Những ID đó bản chất là những bản ghi trong model ir.model.data. Model này chứa những dữ liệu như tên mô đun định nghĩa ra XML ID (ví dụ base, chuỗi ký tự ID (ví dụ main_company), tham chiếu model (ví dụ res.company), tham chiếu ID bản ghi (ví dụ 1).

Mỗi khi chúng ta sử dụng XML ID trên thẻ <record>, Odoo sẽ kiểm tra xem chuỗi ký tự đó có phải là không gian tên hay không (nghĩa là nó có chứa chính xác một dấu chấm hay không), nếu không, nó sẽ thêm tên mô đun hiện tại vào để tạo không gian tên. Sau đó nó sẽ tìm kiếm xem có hay không bản ghi trong model ir.model.data ứng với XML ID này. Nếu có tồn tại, lệnh UPDATE sẽ được thực thi cho những trường được liệt kê. Nếu không, lệnh CREATE sẽ được thực thi để tạo bản ghi mới. Đây là cách bạn cung cấp dữ liệu thành phần cho bản ghi đang có trong Cơ sở dữ liệu.

Trong ví dụ đầu tiên của mục này, bản ghi có ID main_school. Do không có không gian tên được cung cấp, nên Odoo sẽ lấy tên của mô đun hiện tại để tạo lên XML ID, giá trị ID cuối cùng sẽ có dạng <tên đun>.main_school. Sau đó Odoo sẽ cố gắng tìm xem có bản ghi nào đang tồn tại trong Cơ sở dữ liệu ứng với XML ID này hay không. Nếu không có bản ghi nào, nó sẽ thực hiện tạo ra bản ghi mới.

Trong ví dụ thứ hai, chúng ta đã sử dụng bản ghi với XML ID là base.main_company. Theo như định danh này, bản ghi này sẽ thuộc mô đun base. Do bản ghi này đang tồn tại trong Cơ sở dữ liệu, nên Odoo sẽ thực hiện cập nhật bản ghi này theo những trường được liệt kê. Trong ví dụ này là trường name. Lúc này tên công ty sẽ được cập nhật thành Viindoo.

Quan trọng

Một ứng dụng phổ biến của dữ liệu thành phần, ngoài việc thay đổi những bản ghi được định nghĩa bởi những mô đun khác, là việc sử dụng phần tử phím tắt để tạo mới bản ghi mới một cách thuận tiện và cập nhật một số trường của bản ghi đó trong trường hợp phần tử phím tắt không hỗ trợ lúc tạo bản ghi.

Ví dụ dùng phần tử phím tắt để tạo bản ghi:

<act_window id="education_student_action" name="Education Student Action" model="education.student" />

Ví dụ dùng thẻ record để cập nhật một trường mà phần tử phím tắt không hỗ trợ:

<record id="education_student_action" model="ir.actions.act_window">
    <field name="auto_search" eval="False" />
</record>

Hàm ref được sử dụng trong mục Nạp dữ liệu sử dụng tệp tin XML cũng thêm tên mô đun hiện tại vào không gian tên nếu cần thiết, tuy nhiên sẽ có lỗi được trả về nếu XML ID đó không tồn tại. Nó cũng áp dụng vào thuộc tính id nếu không gian tên chưa được cung cấp.

Mẹo

Nếu bạn muốn xem danh sách tất cả XML ID, chọn chế độ Nhà phát triển và vào trình đơn Settings ‣ Technical ‣ Sequence & Identifiers ‣ External Identifiers.

Còn nữa ...

Sớm hay muộn, bạn chắc chắn sẽ cần truy cập vào bản ghi thông quan XML ID từ mã nguồn Python. Sử dụng self.env.ref() trong tình huống đó. Nó sẽ trả về một recordset của bản ghi được tham chiếu. Lưu ý là bạn luôn luôn phải truyền XML ID theo định dạng đầy đủ <tên đun>.<chuỗi tự ID>.

Bạn có thể xem được XML ID của những bản ghi thông qua giao diện người dùng. Để làm điều đó, bạn cần kích hoạt chế độ Nhà phát triển. Sau khi kích hoạt, bạn vào giao diện Form của một bản ghi, nhấp chọn biểu tượng con bọ trên thanh điều khỉển phí trên bên phải màn hình. Từ trình đơn liệt kê xuống, bạn chọn trình đơn View Metadata để xem thông tin chi tiết của bản ghi đang mở, lúc đó bạn sẽ thấy thông tin của XML ID.

Xem thêm

Tham khảo mục Sử dụng cờ noupdate và forcecreate để hiểu tại sao tên công ty chỉ được cập nhật trong lúc cài đặt mô đun.

Nạp dữ liệu sử dụng tệp tin XML

Trong mục trước, chúng ta đã tạo một trường học mới với định danh ngoài main_school. Trong mục này chúng ta sẽ thêm nhiều loại dữ liệu khác từ tệp tin XML. Chúng ta sẽ thêm một vài khối học (model education.class.group) và một vài lớp học (model education.class).

Làm sao để thực hiện nó...

Để diễn giải đầy đủ nội dung cho phần này, chúng ta sẽ cập nhật mô đun quản lý giao dục để bổ xung một số quan hệ cần thiết. Chúng ta sẽ thêm thông tin của giáo viên vào cho lớp học, giả sử một lớp học có nhiều giáo viên và một giáo viên có thể dạy nhiều lớp. Như vậy ta sẽ thêm vào model education.class một trường teacher_ids để tham chiếu đến model res.partner.

teacher_ids = fields.Many2many('res.partner', string='Teachers')

Làm theo những bước sau đây để tạo dữ liệu demo từ tệp tin XML:

  1. Thêm những tệp tin dữ liệu demo vào phần demo trong tệp tin manifest:

    'demo': [
        ...
        'demo/res_partner_demo.xml',
        'demo/student_level_demo.xml',
        'demo/education_class_group_demo.xml',
        'demo/education_class_demo.xml',
        'demo/education_student_demo.xml',
        ...
    ]
    
  2. Thêm dữ liệu demo vào tệp tin demo res_partner_demo.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <odoo>
        ...
        <record id="res_partner_teacher_tim" model="res.partner">
            <field name="name">Tim Smith</field>
            <field name="city">New York</field>
            <field name="country_id" ref="base.us" />
        </record>
        <record id="res_partner_teacher_john" model="res.partner">
            <field name="name">John Wilson</field>
            <field name="city">New York</field>
            <field name="country_id" ref="base.us" />
        </record>
        ...
    </odoo>
    
  3. Thêm dữ liệu demo vào tệp tin demo student_level_demo.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <odoo>
        ...
        <record id="demo_student_level_high" model="student.level">
            <field name="name">High School</field>
        </record>
        ...
    </odoo>
    
  4. Thêm dữ liệu demo vào tệp tin demo education_class_group_demo.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <odoo>
        ...
        <record id="demo_education_class_group_12" model="education.class.group">
            <field name="name">Group 12</field>
        </record>
        ...
    </odoo>
    
  5. Thêm dữ liệu demo vào tệp tin demo education_class_demo.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <odoo>
        ...
        <record id="demo_education_class_12a1" model="education.class">
            <field name="name">Class 12A1</field>
            <field name="school_id" ref="main_school"/>
            <field name="class_group_id" ref="demo_education_class_group_12"/>
            <field name="teacher_ids" eval="[(6, 0, [ref('res_partner_teacher_tim'),
                                                     ref('res_partner_teacher_john')])]">
        </record>
        ...
    </odoo>
    
  6. Thêm dữ liệu demo vào tệp tin demo education_student_demo.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <odoo>
        ...
        <record id="demo_education_student_groening" model="education.student">
            <field name="name">Matt Groening</field>
            <field name="gender">male</field>
            <field name="student_code">MT001</field>
            <field name="mobile">0912345612</field>
            <field name="email">mattgroening@example.com</field>
            <field name="dob">2008-08-22</field>
            <field name="country_id" ref="base.us"/>
            <field name="state_id" ref="base.state_us_5"/>
            <field name="nationality_id" ref="base.us"/>
            <field name="class_group_id" ref="demo_education_class_group_12"/>
            <field name="class_id" ref="demo_education_class_12a1"/>
            <field name="student_level_id" ref="demo_student_level_high"/>
            <field name="image_1920" type="base64" file="viin_education/static/img/groening.jpeg"/>
        </record>
        ...
    </odoo>
    

Khi bạn cập nhật mô đun, bạn sẽ thấy xuất hiện thêm những dữ liệu demo trên, nếu Cơ sở dữ liệu được kích hoạt chế độ dữ liệu demo.

Nó thực hiện như thế nào ...

Tệp tin dữ liệu XML dùng thẻ <record> để tạo bản ghi mới trong Cơ sở dữ liệu. Thẻ <record> có hai thuộc tính bắt buộc là idmodel. Sau đó chúng ta sử dụng thẻ <field> để điền nội dung cho các trường của model trong Cơ sở dữ liệu. Model bản thân nó cũng đã có định nghĩa cho những trường bắt buộc và giá trị mặc định cho các trường. Với những trường mặc định, bạn không cần phải cung cấp dữ liệu nếu không cần thiết.

Có hai cách để đăng ký tệp tin dữ liệu XML trong tệp tin manifest của mô đun, đó là dùng từ khóa data hoặc demo. Những tệp tin dữ liệu XML được liệt kê trong phần data sẽ được nạp mỗi khi bạn cài hoặc cập nhật mô đun. Trong khi những tệp tin dữ liệu XML được liệt kê trong phần demo sẽ chỉ được nạp khi Cơ sở dữ liệu được kích hoạt chế độ dữ liệu demo.

Ở bước 1, chúng ta đăng ký tệp tin dữ liệu XML trong phần demo, nên những tệp dữ liệu này chỉ được nạp vào Cơ sở dữ liệu nếu chế độ dữ liệu demo được kích hoạt.

Ở bước 2, thẻ <field> có thể chứa giá trị dưới dạng ký tự văn bản đơn giản trong trường hợp giá trị vô hướng. Nếu bạn muốn truyền nội dung của một tệp tin (ví dụ ảnh), sử dụng thuộc tính file trong thẻ <field>, sau đó truyền tên tệp tin tương đối với đường dẫn mô đun.

Để thiết lập tham chiếu, có hai cách khả thi. Cách đơn giản nhất là dùng thuộc tính ref, thuộc tính này hoạt động cho trường many2one và chỉ chứa XML ID của bản ghi được tham chiếu. Với trường one2many hoặc many2many, chúng ta cần dùng thuộc tính eval. Đây là thuộc tính có mục đích để kiểm tra đoạn mã Python được dùng làm giá trị cho trường. Trường X2many mong đợi được điền bởi một danh sách bộ ba (three tuples), trong đó giá trị đầu tiên của bộ ba này quy định hành động sẽ thực hiện. Bên trong thuộc tính eval này, chúng ta có thể sử dụng hàm ref, hàm này trả về ID của bản ghi được lưu trong Cơ sở dữ liệu của XML ID được truyền vào. Nó cho phép chúng ta tham chiếu đến một bản ghi khi không biết chính xác ID của bản ghi đó trong Cơ sở dữ liệu. Danh sách một số bộ ba được sử dụng được liệt kê như dưới đây:

  • (2, id, False) : Thao tác này sẽ xóa bản ghi liên kết với ID khỏi Cơ sở dữ liệu. Phần tử thứ ba trong bộ ba này bị bỏ qua.

  • (3, id, False) : Thao tác này sẽ tách bản ghi liên kết với ID khỏi trường one2many, Lưu ý rằng, thao tác này không xóa bản ghi, nó giữ nguyên bản ghi hiện có. Phần tử thứ ba trong bộ ba này bị bỏ qua.

  • (4, id, False) : Thao tác này sẽ thêm liên kết với bản ghi đang có. Phần tử thứ ba trong bộ ba này bị bỏ qua. Đây có lẽ là thao tác bạn sẽ sử dụng nhiều nhất, thường đi kèm với hàm ref để lấy ID của bản ghi trong Cơ sở dữ liệu theo XML ID.

  • (5, False, False) : Thao tác này sẽ cắt bỏ tất cả liên kết, nhưng sẽ giữ nguyên những bản ghi được liên kết đó.

  • (6, False, [id, ...]) : Thao tác này sẽ xóa tất cả các bản ghi đang được liên kết và thay thế bằng những bản ghi mới được liệt kê trong danh sách ID kèm theo. Phần tử thứ hai trong bộ ba bị bỏ qua.

Các bước tiếp theo sẽ giống như cách thực hiện ở bước 2.

Quan trọng

Lưu ý rằng thứ tự nạp dữ liệu rất quan trọng, những bản ghi trong tệp dữ liệu chỉ có thể tham chiếu đến bản ghi được định nghĩa trước đó.

Dữ liệu demo trong mục demo luôn được nạp sau những dữ liệu được định nghĩa trong mục data.

Còn nữa ...

Mặc dù về cơ bản bạn có thể làm bất cứ điều gì với phần tử record, nhưng có những phần tử phím tắt giúp chúng ta thuận tiện hơn trong việc tạo một số loại bản ghi. Chúng bao gồm menu item, templateact windows. Tham chiếu đến Backend ViewsCMS Website Development để tìm hiểu thêm.

Phẩn tử field cũng có thể chứa phần tử function, phần tử này sẽ gọi một hàm được định nghĩa trong model để cung cấp giá trị cho trường. Tham khảo thêm mục Gọi hàm từ tệp tin XML để thấy cách chúng ta sẽ gọi hàm để cập nhật dữ liệu trực tiếp vào Cơ sở dữ liệu.

Ở danh sách bộ ba trước, chúng ta không đề cập đến các bộ ba có phần tử đầu là 01, bởi vì chúng không hữu dụng trong việc nạp dữ liệu. Chúng sẽ được dùng như sau:

  • (0, False, {'key': value}) : Thao tác này sẽ tạo mới một bản ghi cho model được tham chiếu với dữ liệu được cung cấp trong phần tử thứ ba của bộ ba.Phần tử thứ hai bị bỏ qua. Do những thao tác này không có XML ID, và nó sẽ được nạp mỗi khi cập nhật mô đun, nên sẽ dẫn đến việc bị trùng lặp dữ liệu. Chúng ta nên tránh những điều đó.

  • (1, id, {'key': value}) : Thao tác này sẽ cập nhật bản ghi được liên kết với ID của Cơ sở dữ liệu được cung cấp. Vì lý do tương tự nêu trên, chúng ta nên tránh sử dụng nó khi nạp dữ liệu.

Những cú pháp này giống với những cú pháp chúng ta đã đề cập đến trong Basic Server-Side Development.

Sử dụng cờ noupdate và forcecreate

Hầu hết những mô đun có những kiểu dữ liệu khác nhau. Một số dữ liệu đơn giản chỉ cần tồn tại để mô đun hoạt động bình thường, một số khác thì người dùng không nên thay đổi, và phần lớn còn lại có thể thay đổi theo nhu cầu của người dùng. Mục này sẽ hướng dẫn chi tiết cách giải quyết cho những kiểu khác nhau đó. Đầu tiên, chúng ta sẽ cập nhật một trường của một bản ghi đang có và sau đó chúng ta sẽ tạo một bản ghi mà được cho là sẽ được tạo lại khi mô đun cập nhật.

Làm sao để thực hiện nó...

Chúng ta có thể thực thi những hàng động khác nhau từ Odoo khi nạp dữ liệu bằng cách thiết lập một số thuộc tính nhất định trên thẻ <odoo> hoặc thẻ record.

  1. Thêm mới một trường học lúc cài đặt mô đun, nhưng không cập nhật khi cập nhật mô đun. Nhưng nếu người dùng xóa nó đi, nó sẽ được tạo lại khi cập nhật mô đun:

    <odoo noupdate="1">
        <record id="main_school" model="education.school">
            <field name="name">My School</field>
            <field name="name">SCHOOL001</field>
            <field name="company_id" ref="base.main_company"/>
        </record>
    </odoo>
    
  2. Thêm một khối lớp, mà sẽ không bị cập nhật lại khi cập nhật mô đun, nhưng sẽ không tạo mới khi người dùng xóa nó đi:

    <odoo noupdate="1">
        ...
        <record id="demo_education_class_group_12" model="education.class.group" forcecreate="false">
            <field name="name">Group 12</field>
        </record>
        ...
    </odoo>
    

Nó thực hiện như thế nào ...

Thẻ <odoo> có thể chứa thuộc tính noupdate, được truyền đến những bản ghi trong model ir.model.data được tạo ra khi nạp dữ liệu lần đầu tiên, và được lưu trong một cột dữ liệu của bảng này.

Khi Odoo cài đặt mô đun (trong chế độ init), tất cả bản ghi sẽ được ghi bất kể noupdate có được thiết lập là có hay không. Khi bạn cập nhật mô đun (trong chế độ update), những XML ID đang tồn tại sẽ được kiểm tra xem có thuộc tính noupdate được thiết lập là gì, dựa vào đó để quyết định xem có cập nhật bản ghi đó hay không. Nó sẽ không áp dụng cho trường hợp bản ghi được duyệt bị xóa bởi người dùng. Đó là lý do vì sao bạn có thể thiết lập forcecreate để quyết định có tạo lại bản ghi bị xóa bởi người dùng hay không.

Quan trọng

Trong những mô đun từ phiên bản 8.0 trở về trước, bạn có thể thấy những thẻ openerp bao lấy thẻ <data>. Nó vẫn có thể được sử dụng, tuy nhiên đã được đánh dấu là không dùng nữa. Hiện tại <odoo>, openerp, và <data> có ngữ nghĩa hoàn toàn giống nhau, chúng được hiểu như là dấu ngoặc bao hàm dữ liệu XML.

Còn nữa ...

Nếu bạn muốn nạp dữ liệu, mà dữ liệu này đang được gắn cờ noupdate, bạn có thể chạy lại Odoo với tham số --init=<tên đun> hoặc -i <tên đun>. Nó sẽ buộc Odoo nạp lại dữ liệu cho bạn. Nó cũng khiến những bản ghi bị xóa được tạo lại. Lưu ý rằng nó sẽ dẫn đến dữ liệu bị trùng lặp và lỗi cài đặt nếu mô đun phá vỡ cơ chế XML ID, ví dụ như tạo bản ghi trong mã Python bằng cách gọi thông quan thẻ <function>. Bằng cách này, chúng ta có thể phá vỡ bất cứ cờ noupdate nào, nên trước khi thực hiện, hãy chắc chắn là bạn muốn làm nó. Một lựa chọn khác cho vấn đề nêu trên là sử dụng kịch bản di trú dữ liệu, được đề cập đến trong mục Cập nhật Mô đun và di trú dữ liệu.

Xem thêm

Odoo cũng dùng XML ID để theo dõi xem bản ghi nào bị xóa khi cập nhật mô đun. Nếu bản ghi có XML ID lúc trước khi cập nhật, nhưng XML ID đó không được khôi phục trong quá trình cập nhật, thì cả bản ghi và XML ID đó sẽ bị xóa khỏi Cơ sở dữ liệu vì bị coi là không được dùng nữa. Để thảo luận sâu hơn về vấn đề này, thao khảo mục Cập nhật Mô đun và di trú dữ liệu.

Nạp dữ liệu sử dụng tệp tin CSV

Khi bạn có thể làm mọi thứ với tệp tin XML, định dạng này không phải là thuận tiện nhất khi bạn cần cung cấp một lượng lớn dữ liệu, đặc biệt là đối với những người thích dùng những ứng dụng trang tính để xử lý dữ liệu. Một ưu điểm khác của định dạng CSV là nó chính là những gì bạn nhận được thông qua tính năng trích xuất dữ liệu mặc định. Trong mục này, chúng ta sẽ xem xét cách nạp dữ liệu dạng bảng.

Làm sao để thực hiện nó...

Thông thường, dữ liệu quyền truy cập sẽ được nạp thông qua tệp tin CSV:

  1. Đăng ký tệp tin CSV vào mục data trong tệp tin manifest:

    'data': [
        'security/ir.model.access.csv',
        ...
    ]
    
  2. Bổ xung quyền truy cập cho model:

    id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
    access_education_school_user,education.school user,model_education_school,viin_education_group_user,1,0,0,0
    access_education_school_admin,education.school admin,model_education_school,viin_education_group_admin,1,1,1,1
    ...
    

Giờ chúng ta sẽ đã cho phép người dùng bình thường có quyền đọc bản ghi trường học, nhưng không có quyền thêm, sửa, xóa các bản ghi đó.

Nó thực hiện như thế nào ...

Bạn chỉ cần đăng ký tệp tin dữ liệu trong mục data của tệp tin manifest. Odoo sẽ dựa vào phần tệp tin mở rộng để quyết định xem đó là loại tệp tin gì. Một điểm đặc thù của tệp tin CSV là tên tệp tin phải trùng với tên model được nạp dữ liệu, trong ví dụ này là model ir.model.access. Dòng đầu tiên là dòng tiêu đề, với tên cột khớp chính xác với tên các trường được định nghĩa trong model.

Với giá trị vô hướng, bạn có thể sử dụng dấu nháy kép (nếu cần thiết, vì chuỗi có chứa dấu nháy hoặc dấu phẩy) hoặc không cần dùng dấu nháy kép.

Khi ghi trường many2one với tệp tin CSV, trước tiên, Odoo sẽ cố gắng giải nghĩa giá trị cột như là một XML ID. Nếu không có dấu chấm, Odoo sẽ thêm tên mô đun hiện tại vào không gian thên để tìm kiếm trong model ir.model.data. Nếu không tìm thấy, phương thức name_search của model sẽ được gọi với tham số truyền vào là giá trị của cột. Nếu vẫn không tìm thấy, giá trị của cột được coi như không hợp lệ và Odoo sẽ trả về lỗi.

Quan trọng

Lưu ý rằng, dữ liệu đọc từ tập tin CSV luôn luôn có noupdate=False. Điều đó có nghĩa là các bản cập nhật mô đun tiếp theo sẽ ghi đè dữ liệu cập nhật bởi người dùng. Nếu bạn muốn nạp một lượng lớn dữ liệu, và noupdate là một vấn đề đối với bạn. thì hãy nạp tệp tin CSV từ init hook.

Còn nữa ...

Bạn có thể nhập giá trị cho các trường one2manymany2many thông qua tệp tin CSV, nhưng hơi phức tạp. Nói chung bạn nên tạo các bản ghi riêng biệt, sau đó thiết lập quan hệ thông qua tệp tin XML, hoặc làm việc với tệp tin CSV thứ hai để thiết lập mối quan hệ.

Nếu bạn thực sự cần tạo các bản ghi liên kết trong cùng một tệp tin, hãy sắp xếp tất cả các cột sao cho, các trường dữ liệu vô hướng ở bên trái và các trường của model được liên kết đến ở bên phải, với tiêu đề cột chứa tên trường liên kết và tên trường của model được liên kết, phân tách nhau bằng dấu hai chấm.

id,name,model_id:id,perm_read,group_id:name
access_education_school_user,education.school user,model_education_school,1,my group

Nó sẽ tạo một nhóm mới với tên "my group", bạn có thể ghi nhiều trường hơn nhóm trên bằng cách thêm những trường cần ghi vào bên phải. Nếu bạn muốn liên kết nhiều bản ghi, lặp lại bản ghi trên và thay đổi các cột bên phải cho phù hợp. Odoo sẽ điền những cột trống bằng giá trị của dòng trước đó, nên bạn không cần phải sao chép tất cả các cột, bạn có thể chỉ cần thêm một dòng với những giá trị trống được lưu cho những trường của model được liên kết mà bạn muốn điền.

Với những trường x2many chỉ cần liệt kê những XML ID của những bản ghi được liên kết đến.

Cập nhật Mô đun và di trú dữ liệu

Model dữ liệu mà bạn chọn khi viết mô đun có thể có một số điểm yếu, vì vậy bạn có thể cần phải điều chỉnh nó trong vòng đời mô đun của bạn. Để cho phép điều đó, Odoo hỗ trợ lập phiên bản trong mô đun và chạy kịch bản di trú dữ liệu khi cần thiết.

Làm sao để thực hiện nó...

Giả sử, trong model education.school.year, ở phiên bản trước của mô đun, chúng ta để trường start_dateend_date là trường kiểu Char, cho phép người dùng nhập tùy ý dữ liệu ngày tháng. Tuy nhiên bây giờ chúng ta nhận ra rằng, chúng ta cần trường này để tổng hợp dữ liệu và so sánh, nên chúng ta quyết định chuyển chúng thành kiểu Date.

Odoo làm rất tốt trong việc chuyển đổi dữ liệu, tuy nhiên trong vấn đề nêu trên, chúng ta cần phải tự thực hiện, đó là lý do vì sao chúng ta cần phải cung cấp hướng dẫn chuyển đổi dữ liệu của phiên bản trước của mô đun chúng ta đã cài đặt sao cho phù hợp với phiên bản mới. Chúng ta sẽ thực hiện như sau:

  1. Nâng phiên bản của mô đun trong tệp tin manifest:

    'version': '0.2.0',
    
  2. Cung cấp mã nguồn tiền di trú dữ liệu trong tệp tin migrations/14.0.0.2.0/pre-migrate.py:

    def migrate(cr, version):
        cr.execute('ALTER TABLE education_school_year RENAME COLUMN start_date TO start_date_char')
        cr.execute('ALTER TABLE education_school_year RENAME COLUMN end_date TO end_date_char')
    
  3. Cung cấp mã nguồn hậu di trí dữ liệu trong tệp tin migrations/14.0.0.2.0/post-migrate.py:

    from odoo import fields
    from datetime import date
    
    def migrate(cr, version):
        cr.execute('SELECT id, start_date_char, end_date_char FROM education_school_year')
        for record_id, old_start_date, old_end_date in cr.fetchall():
            new_start_date = None
            new_end_date = None
            try:
                new_start_date = fields.Date.to_date(old_start_date)
            except ValueError:
                if len(old_start_date) == 4 and old_start_date.isdigit():
                    new_start_date = date(int(old_start_date), 1, 1)
    
            try:
                new_end_date = fields.Date.to_date(old_end_date)
            except ValueError:
                if len(old_end_date) == 4 and old_end_date.isdigit():
                    new_end_date = date(int(old_end_date), 1, 1)
    
            if new_start_date and new_end_date:
                cr.execute('UPDATE education_school_year SET start_date=%s, end_date=%s', (new_start_date, new_end_date))
    

Với những đoạn mã trên, Odoo sẽ thực hiện đổi tên cột trong bảng Cơ sở dữ liệu của những trường cũ, và thực hiện tạo các cột mới (việc tạo cột mới được thực hiện bởi Odoo), do không có một cách chuyển đổi tự động nào hỗ trợ chuyển đổi trường kiểu Char sang trường kiểu Date.

Nó thực hiện như thế nào ...

Việc đầu tiên bạn cần làm là nâng phiên bản của mô đun, vì việc di trú dữ liệu chỉ thực hiện khi có sự thay đổi phiên bản của mô đun khi chúng ta cập nhật mô đun. Với mỗi lần cập nhật, Odoo ghi phiên bản của mô đun được cung cấp trong tệp tin manifest vào bảng ir_module_module. Trong ví dụ trên, số phiên bản sẽ được Odoo thêm vào với số phiên bản chính và phụ của phiên bản Odoo đang chạy, ví dụ như 14.0, từ đó số phiên bản của mô đun sẽ là 14.0.0.2.0. Chúng ta cũng có thể dùng số phiên bản đầy đủ nếu muốn (14.0.0.2.0).

Chúng ta không cần đăng ký hai tệp tin mã nguồn di trí dữ liệu ở bất cứ đâu, vì Odoo có thể hiểu và tìm được chúng một cách tự động. Khi nâng cấp mô đun, Odoo sẽ kiểm tra xem số phiên bản trong tệp tin manifest có lớn hơn số phiên bản của mô đun được lưu trong bảng ir_module_module hay không. Nếu nó lớn hơn, Odoo sẽ tìm kiếm thư mục migrations của mô đun có chứa những phiên bản ở giữa, lên đến và bao gồm cả phiên bản đang nâng cấp hay không. Sau đó, trong thư mục phiên bản được tìm thấy, Odoo sẽ tìm kiếm tệp tin mã nguồn Python mà có tiền tố là pre-, thực hiện tải tệp tin mã nguồn đó, và mong muốn sẽ có phương thức migrate được cung cấp trong tệp tin này. Phương thức này sẽ có hai tham số, tham số thứ nhất là con trỏ Cơ sở dữ liệu dùng để thao tác với Cơ sở dữ liệu, tham số thứ hai là số phiên bản hiện tại. Việc này được thực hiện trước cả khi Odoo đọc những tệp tin mã nguồn còn lại trong mô đun của bạn, nên nó đảm bảo rằng, sẽ không có gì thay đổi đối với Cơ sở dữ liệu so với phiên bản trước đó của mô đun.

Sau khi tất cả phương thức pre-migrate được thực hiện thành công, Odoo sẽ nạp model và những dữ liệu được định nghĩa trong mô đun của bạn. Lúc này Cơ sở dữ liệu sẽ bị ảnh hưởng. Như ví dụ trên, Odoo sẽ tạo ra những cột mới với tên start_dateend_date với đúng kiểu dữ liệu được định nghĩa.

Sau đó, với phương thức tìm kiếm tương tự, Odoo sẽ tìm kiếm những tệp tin post-migrate và thực thi chúng nếu tìm thấy. Trong trường hợp của chúng ta, chúng ta cần xem xét mọi giá trị để xem liệu chúng ta có thể tạo ra thứ gì đó có thể sử dụng được từ nó hay không; nếu không, chúng ta giữ dữ liệu là NULL. Lưu ý, không nên viết kịch bản duyệt hết cả bảng dữ liệu nếu không cần thiết, vì có thể chúng ta sẽ phải đọc ghi một lượng dữ liệu rất lớn và làm lỗi kịch bản di trú dữ liệu lúc chạy.

Còn nữa ...

Trong cả pre-migrationpost-migration, chúng ta chỉ có thể thao tác với con trỏ Cơ sở dữ liệu, điều này khá bất tiện với những ai muốn sử dụng biến môi trường của Odoo. Tuy nhiên sử dụng biến môi trường của Odoo để truy cập model khi di trú dữ liệu có thể dẫn đến một số lỗi không mong muốn, bởi vì tại thời điểm pre-migrate, model của mô đun hiện tại chưa được tải, còn tại thời điểm post-migrate, những model được định nghĩa trong những mô đun mà mô đun hiện tại phụ thuộc vào cũng chưa được tải. Nếu vấn đề này không phải vấn đề với bạn, khi bạn biết được rằng model bạn sử dụng không bị ảnh hưởng bởi mô đun hiện tại hoặc những mô đun mà mô đun hiện tại phụ thuộc, hoặc bạn chắc rằng vấn đề này không gây lỗi không mong muốn, bạn có thể sử dụng model thông qua biến môi trường như sau:

from odoo import api, SUPERUSER_ID

def migrate(cr, version):
    env = api.Environment(cr, SUPERUSER_ID, {})

Xem thêm

Khi viết những kịch bản di trú dữ liệu, bạn sẽ hay phải làm những công việc lặp đi lặp lại như: kiểm tra cột hay bảng có tồn tại hay không, thay đổi tên, hay ánh xạ những dữ liệu cũ với dữ liệu mới. Điều đó thật sự khó chịu và dễ xảy ra lỗi. Hãy xem xét sử dụng https://github.com/OCA/openupgradelib nếu có thể.

Xóa bản ghi từ tệp tin XML

Trong các mục trước, chúng ta đã tìm hiểu cách để tạo và cập nhật bản ghi thông quan tệp tin XML. Đôi khi, chúng ta cũng muốn xóa một bản ghi thông qua tệp tin XML, lúc đó bạn có thể sử dụng thẻ <delete>.

Sãn sàng

Trong mục này, chúng ta sẽ tạo một số bản ghi thông qua tệp tin XML, sau đó xóa chúng đi. Trong thực tế thường thì chúng ta sẽ tạo bản ghi mới ở một mô đun và xóa chúng nếu cần ở một mô đun khác. Tuy nhiên để cho đơn giản, chúng ta sẽ sử dụng cùng tệp tin XML. Ví dụ trong tệp tin XML chứa dữ liệu demo, chúng ta thêm một số khối học mới:

<record id="demo_education_class_group_test1" model="education.class.group">
    <field name="name">Test 1</field>
</record>
<record id="demo_education_class_group_test2" model="education.class.group">
    <field name="name">Test 2</field>
</record>

Làm sao để thực hiện nó...

Có hai cách để xóa bản ghi thông qua tệp tin XML:

  • Dùng XML ID được khai báo ở trên:

    <delete model="education.class.group" id="demo_education_class_group_test1"/>
    
  • Dùng domain tìm kiếm:

    <delete model="education.class.group" search="[('name', 'ilike', 'Test')]"/>
    

Nó thực hiện như thế nào ...

Bạn cần phải sử dụng thẻ <delete>. Để xóa bản ghi của một model nào đó, bạn cần cung cấp tên model thông qua thuộc tính model của thẻ. Thuộc tính này là thuộc tính bắt buộc.

Trong ví dụ thứ nhất, chúng ta cung cấp XML ID của bản ghi mà chúng ta tạo trước đó. Trong quá trình cài đặt, Odoo sẽ tìm xem bản ghi đó có tồn tại hay không. Nếu tìm thấý, Odoo sẽ thực hiện xóa nó, nếu không, Odoo sẽ trả về lỗi. Bạn chỉ có thể xóa các bản ghi mà có XML ID.

Trong ví dụ thứ hai, chúng ta cung cấp domain tìm kiếm trong thuộc tính search. Trong quá trình cài đặt, Odoo sẽ tìm kiếm những bản ghi thông qua domain tìm kiếm được cung cấp. Nếu tìm thấy những bản ghi tương ứng, Odoo sẽ xóa chúng đi. Trong trường hợp này, Odoo sẽ không trả về lỗi nếu không tìm thấy bản ghi. Lưu ý sử dụng lựa chọn này một cách hết sức thận trọng, vì nó có thể xóa những dữ liệu được nhập bởi người dùng vì nó đang tìm kiếm theo domain được cung cấp.

Cảnh báo

Thẻ <delete> hiếm khi được sử dụng trong Odoo vì nó khá nguy hiểm. Nếu bạn không cẩn trọng, bạn có thể làm hỏng hệ thống. Hãy tránh dùng nó nếu có thể

Gọi hàm từ tệp tin XML

Bạn có thể tạo những bản ghi với mọi kiểu dữ liệu thông qua tệp tin XML. Nhưng đôi khi, nó rất khó để tạo bản ghi có liên quan đến những luồng nghiệp vụ. Ví dụ như thay đổi dữ liệu theo một yêu cầu đặc thù và ràng buộc nào đó, cần xử lý dữ liệu trước khi cập nhật dữ liệu. Trong những trường hợp như này, bạn có thể dùng thẻ <function> để gọi hàm của model.

Làm sao để thực hiện nó...

Giả sử, chúng ta muốn bổ sung mã trường nối thêm vào tiền tố của mã học sinh, phục vụ cho việc phân loại và thống kê sau này. Chúng ta sẽ làm như sau:

  1. Thêm một hàm mới _update_student_code() trong model education.student:

    @api.model
    def _update_student_code(self):
         all_students = self.search([])
         for student in all_students:
             student.student_code = '%s_%s' % (student.school_id.code, student.student_code)
    
  2. Thêm thẻ <function> trong tệp tin XML:

    <function model="education.student" name="_update_student_code"/>
    

Nó thực hiện như thế nào ...

Ở bước thứ nhất, chúng ta thêm hàm _update_student_code(), hàm này sẽ tìm kiếm tất cả học sinh và thay đổi mã học sinh bằng cách thêm vào trước nó mã trường học. Chúng ta sử dụng ký tự gạch dưới ở đầu tên hàm vì nó được xem như hàm riêng tư bởi ORM và không thể được gọi thông qua RPC.

Ở bước thứ hai, chúng ta sử dụng thẻ <function> với hai thuộc tính:

  • model: model mà hàm được định nghĩa

  • name: tên của hàm mà chúng ta định gọi

Khi bạn cài đặt mô đun, hàm _update_student_code() sẽ được gọi và bạn sẽ có mã học sinh có đính kèm theo mã trường học.

Quan trọng

Luôn sử dụng cách gọi hàm này với lựa chọn noupdate để tránh việc hàm này luôn được gọi khi cập nhật mô đun.

Còn nữa ...

Với thẻ <function>, chúng ta cũng có thể truyền tham số vào cho hàm. Giả sử bạn muốn thêm một tiền tố vào mã học sinh, mà tiền tố này đứng trước cả mã trường học. Và chúng ta muốn truyền tiền tố này vào trong hàm. Chúng ta sẽ làm như sau:

  • Cập nhật lại hàm _update_student_code() để chấp nhận tham số:

@api.model
def _update_student_code(self, pre_fix):
    all_students = self.search([])
    for student in all_students:
        student.student_code = '%s_%s_%s' % (pre_fix, student.school_id.code, student.student_code)
  • Thay đổi lại cách gọi hàm _update_student_code() trong tệp tin XML bằng cách sử dụng thuộc tính eval để truyền tham số:

<function model="education.student" name="_update_student_code" eval="('HS')"/>