Работа с данными
Оглавление
Основные сведения
В документации все уже описано, но я бы хотел сфокусироваться на некоторых неочевидных моментах.
Все xml файлы - это файлы с данными. Т.е. представления, пункты меню, экшены, правила безопасности и много другое является данными, которые при установке либо при обновлении модуля записываются в базу.
Информация о каждой записи, созданной с помощью xml файла имеет уникальный идентификатор в системе, который назвается xml_id
или external_id
. Он состоит из двух частей - техническое_имя_модуля
и уникально_имя_записи_внутри_модуля
, которые разделяются точкой. И может выглядеть примерно вот так: first_module.record_001_first_model
. И хранится информация о записях в модели ir.model.data
.
Все это является частью платформы и используется повсеместно в системе. Вы можете ссылаться на уже созданные записи как внутри самих xml
файлов так и с помощью кода:
record = self.env.ref("first_module.record_001_first_model")
в переменной record
будет рекордсет из одной записи, которая была создана с помощью xml
файла:
Как вы можете видеть, данные о записях, которые имы видели:
Если мы внимательно изучим данные о записи в xml
файле то увидим, что в теге <record>
есть атрибут model
который указывает на имя модели, в которой будет создана запись. Атрибут id
и есть тот самый xml_id
, который есть у каждой записи созданной с помощью xml
файла.
Внутри тега <record>
вы можете видеть теги field
со значениями атрибута name
. Это имена полей. Значением поля может быть:
- текст внутри тега
field
- если поле имеет тип
many2one
, то значение можно указать как ссылку на другую запись с помощью атрибутаref
. Разумеется ссылаться можно не любую запись любой модели а только на запись, которая создается для модели, указанной в параметреcomodel_name
текущего поля. Пример - с помощью атрибута
eval
. В этом случае текст внутри этого атрибута будет исполнен как код python. Пример
Особенность обновления данных
Когда вы разрабатываете модуль, вы будете часть менять xml
файлы, а так же обновлять модуль(и) чтобы измененные данные попали в систему. В связи с этим есть один неочевидный нюанс, который может отнять у вас достаточно много нервов и я бы хотел сфокусироваться на нем.
К примеру, вы создали вот такую запись:
<record id="record_001_first_model" model="first.model">
<field name="name">record 001</field>
<field name="field_one">10</field>
<field name="field_two">0.1</field>
</record>
В этом случае система, если не найдет такого xml_id
создаст новый и создаст запись, в модели first.model
с прописанными значениями полей.
Если же мы поменяем какое либо значение одного из полей, то система обновит это поле новым значением.
А теперь вопрос, что произойдет с данными если вы удалите определение одного из полей, например удалите определение поля field_two
?
<record id="record_001_first_model" model="first.model">
<field name="name">record 001</field>
<field name="field_one">10</field>
</record>
Чисто интуитивно, нам будет казаться, что система удалит данные из поля field_two
. Так вот это не так. В поле останется последнее добавленное значение. Т.е. удалив определение поля из xml
файла, вы не удаляете значение из него, а просто перестает записывать туда новое значение. Удалить данные из этого поля можно либо вручную, либо создав новую базу, где ваши файлы не будут создавать значение для этого поля, либо создавать запись с помощью функции, которая будет вызывать метод create
если записи не существует, либо вызывать метод write
если запись существует.
Вызов функций
В документации он описан, но сделано это, на мой взгляд, не очевидным образом. Я приведу несколько примеров того, как можно вызывать различные функции и как туда передавать параметры.
<function
model="first.model"
name="start_function"
eval="([ref('first_module.record_001_first_model')])"
/>
Это самый простой вариант - просто вызывает метод для рекордсета, который
<function model="product.template" name="_create_variant_ids">
<function
model="product.template"
name="search"
eval="[
[
('id', '=', ref('имя_модуля.искомый_id_записи')),
]
]"
/>
</function>
В этом примере вы можете видеть, что функция _create_variant_ids
будет вызвана для рекордсета, который получится в результате вызова во вложенной функции метода search
. В этом и предыдущем случае можно увидеть, что позиционный параметр функции можно передать с помощью списка внутри атрибута eval
. При этом вы можете использовать ссылки на другие записи.
<function model="ir.model.data" name="_update_xmlids">
<value
model="base"
eval="[{
'xml_id': 'имя_вашего_модуля.id_записи_который_вы_хотите_присвоить',
'record': obj().env['first.model'].search([('id', '=', 100)]),
'noupdate': True,
}]"
/>
</function>
Весьма комплексный пример, который показывает как можно создать xml_id
для автоматически создаваемой записи.
Использование атрибута noupdate
Иногда у вас может возникать ситуация, чтобы данные из xml
файлов загружались только один раз при установке модуля, а при дальнейших обновлениях игнорировались. Для этого вы можете к тегу <odoo>
либо к тегу <data>
добавить атрибут noupdate="1"
. Этот атрибут укажет системе, что записи с xml_id
которые входят внутрь тега этим атрибутом, не будут обновляться при обновлении модуля. При его переустановке - будут.
Лично я при разработке стараюсь заполнять данные так, чтобы они правильно загружались даже при повторном обновлении, это очень сильно помогает избежать проблем при передаче модуля клиенту.
Тем не менее такой функционал есть, и на него завязана некоторая часть других функций системы
Обсуждение
Обсудить можно здесь