Wprowadzenie
Jeśli otworzyłeś kiedyś Zamówienie Sprzedaży w Odoo i pod danymi klienta zobaczyłeś listę pozycji, właśnie zobaczyłeś działające pole One2many. To jeden z podstawowych elementów modelu danych Odoo — zrozumienie go jest niezbędne zarówno dla użytkowników konfiguracyjnych, jak i programistów.
Ten przewodnik wyjaśnia istotę pola One2many, jego działanie w ORM Odoo, typowe scenariusze użycia oraz jak je tworzyć — za pomocą Odoo Studio albo bezpośrednio w kodzie Python.
Niezależnie od tego, czy chcesz lepiej pojąć strukturę danych w swoim systemie, czy potrzebujesz praktycznego „how‑to” jako deweloper, znajdziesz tu najważniejsze informacje.
Czym jest pole One2many w Odoo
Pole One2many to typ relacyjny w Odoo, który pozwala jednemu rekordowi rodzicielskiemu powiązać się z wieloma rekordami potomnymi z innego modelu.
Prosto mówiąc: jedna firma może mieć wiele faktur; jedno zamówienie sprzedaży — wiele pozycji; jeden projekt — wiele zadań. Pole One2many służy do reprezentowania takiej relacji „jeden do wielu”.
W interfejsie Odoo One2many zwykle wyświetla się jako wbudowana tabela w formularzu — dzięki temu użytkownik może dodawać, edytować i usuwać rekordy podrzędne bez opuszczania formularza rodzica.
Czym różni się od innych pól relacyjnych
W ORM Odoo występują trzy podstawowe pola relacyjne:
- Many2one: rekord wskazuje na pojedynczy rekord innego modelu (np. zamówienie → klient).
- One2many: rekord rodzica połączony z wieloma rekordami innego modelu (np. zamówienie → pozycje).
- Many2many: relacja wiele‑do‑wielu, gdzie rekordy po obu stronach mogą łączyć się wzajemnie (np. produkt ↔️ tagi).
One2many stosujemy, gdy każdy rekord potomny ma dokładnie jednego rodzica. Gdy rekordy mają być współdzielone przez wielu rodziców, właściwe będzie Many2many.
Istotne: One2many jest „wirtualne” — samo nie przechowuje wartości w tabeli rodzica. Odczytuje dane z tabeli potomnej przez pole Many2one zdefiniowane po stronie dziecka.
Jak działa to pole
W praktyce oznacza to, że w bazie danych nie powstaje osobna kolumna w tabeli rodzica. Wszystkie wskazania przechowywane są jako klucz obcy w tabeli potomnej.
Zasada jest prosta: każdemu One2many musi odpowiadać pola Many2one w modelu dziecka. One2many to w istocie odwrotne zapytanie — Odoo wyszukuje wszystkie rekordy potomne, których Many2one wskazuje na bieżący rekord rodzica.
Relacja One2many ↔️ Many2one
Definiując One2many w Pythonie, podajesz dwie kluczowe właściwości:
- comodel_name: nazwa modelu przechowującego rekordy potomne.
- inverse_name: nazwa pola Many2one w modelu potomnym, które wskazuje na rodzica.
Przykład: w module serwisowym możesz mieć kontrakt usługowy powiązany z wieloma pozycjami deliverable.
deliverable_ids = fields.One2many(
comodel_name='service.deliverable',
inverse_name='contract_id',
string='Deliverables'
)
W tym przykładzie contract_id to pole Many2one zdefiniowane po stronie service.deliverable. Bez niego One2many nie zadziała.
Co dzieje się w bazie danych
W tabeli rodzica nie ma kolumny przechowującej One2many — wszystkie powiązania znajdują się w tabeli potomnej jako kolumna klucza obcego wskazująca ID rodzica.
Kiedy Odoo ładuje formularz rodzica i ma pokazać jego One2many, ORM wykonuje zapytanie SQL: znajdź wszystkie wiersze w tabeli potomnej, których klucz obcy równa się ID bieżącego rodzica. To standardowe zachowanie relacyjnej bazy danych.
Dlatego One2many traktowane jest jako pole „bez kolumny” — widok relacji, a nie przechowywana wartość w tabeli rodzica.
Zastosowania biznesowe
One2many występuje w Odoo tam, gdzie występują naturalne relacje rodzic‑potomek. Poniżej pięć praktycznych przykładów z codziennych procesów biznesowych.
1. Zamówienia sprzedaży i pozycje
W module Sprzedaży model sale.order ma pole order_line_ids, które łączy zamówienie z wieloma rekordami sale.order.line. Każda pozycja zawiera produkt, ilość, cenę jednostkową i rabat — i wszystkie modyfikacje wykonasz bezpośrednio w formularzu zamówienia.
2. Faktury i pozycje faktury
W Księgowości model account.move korzysta z One2many do zarządzania liniami faktury (account.move.line). Każda linia to osobna pozycja rozliczeniowa, a suma faktury jest agregacją tych linii.
3. Projekty i zadania
W module Projektów project.project wykorzystuje One2many do wyświetlania wszystkich zadań należących do projektu — lista może przyjmować formę tabeli, kanbanu czy wykresu Gantta, korzystając z tej samej relacji.
4. Kontrahenci i kontakty powiązane
Model res.partner zawiera pole child_ids, które pokazuje osoby powiązane z firmą. Każdy kontakt ma z kolei pole Many2one parent_id wskazujące na firmę.
5. Modele niestandardowe w serwisie lub produkcji
W modułach szytych na miarę One2many to naturalny wybór wszędzie tam, gdzie jeden rekord „posiada” listę podrzędnych: zlecenie serwisowe z listą części, kurs z wieloma terminami, kontrakt z listą deliverable itd.
Ta elastyczność sprawia, że One2many jest kluczowym narzędziem przy dostosowywaniu Odoo do różnych branż i procesów.
Tworzenie i dostosowywanie pola
Są dwie główne metody dodawania One2many w Odoo: bezkodowo przez Odoo Studio albo przez kod Python w module.
Tworzenie przez Odoo Studio
W Studio nie dodasz One2many bezpośrednio na rodzicu — najpierw musi istnieć pole Many2one po stronie dziecka, bo One2many po prostu odwołuje się do niego.
Polecany przebieg prac w Studio wygląda tak:
- Edytuj model potomny w Studio i dodaj pole Many2one wskazujące na model rodzica.
- Zapisz zmiany, a następnie wróć do modelu rodzica w Studio.
- Dodaj nowe pole typu "One2many" — Studio poprosi o wskazanie modelu powiązanego i nazwy pola inverse (Many2one).
- Po dodaniu pole pojawi się jako wbudowana lista w formularzu rodzica.
Dla większości użytkowników biznesowych to najprostszy sposób na utworzenie relacji One2many bez pisania kodu — zachowuje takie same właściwości jak pole stworzone programistycznie.
Tworzenie przez kod Python w module
Deweloperzy, którzy chcą pełnej kontroli nad zachowaniem i nazwami pól, definiują One2many w plikach Pythona. Przykład poniżej pokazuje kompletną definicję kontraktu i elementów deliverable.
from odoo import fields, models
class ServiceContract(models.Model):
_name = 'service.contract'
_description = 'Service Contract'
name = fields.Char(string='Contract Name', required=True)
deliverable_ids = fields.One2many(
comodel_name='service.deliverable',
inverse_name='contract_id',
string='Deliverables'
)
class ServiceDeliverable(models.Model):
_name = 'service.deliverable'
_description = 'Service Deliverable'
contract_id = fields.Many2one(
comodel_name='service.contract',
string='Contract',
ondelete='cascade'
)
name = fields.Char(string='Description', required=True)
Zwróć uwagę, że pole Many2one (contract_id) musi istnieć po stronie dziecka — inverse_name w definicji One2many musi dokładnie odpowiadać tej nazwie.
Tworzenie pola przez XML‑RPC
Jeżeli zarządzasz polami programowo, możesz tworzyć One2many przez API XML‑RPC, korzystając z modelu ir.model.fields. Reguła jest ta sama: najpierw Many2one, potem One2many z parametrem relation_field wskazującym na nazwę pola Many2one.
Taki sposób sprawdza się przy automatyzacji zmian w wielu środowiskach lub w scenariuszach, gdzie pola tworzone są skryptami.
Dobre praktyki
Na podstawie licznych wdrożeń u klientów warto stosować kilka praktyk, które upraszczają pracę z One2many.
- Zawsze twórz najpierw Many2one. One2many nie zadziała bez odpowiedniego pola inverse po stronie dziecka — dotyczy to Studio, kodu i API.
- Używaj sufiksu
_idsdla nazw One2many. Konwencja nazewnictwa (np.line_ids,task_ids) od razu mówi, że pole zwraca zestaw rekordów, co ułatwia czytelność kodu. - Ustaw
ondelete='cascade'gdy to uzasadnione. Jeśli usunięcie rodzica ma usuwać także dzieci, skonfiguruj to na Many2one, by nie zostawiać po sobie osieroconych rekordów. - Skup listę w formularzu na najważniejszych kolumnach. Pokazywanie zbyt wielu kolumn spowalnia formularz i utrudnia pracę — używaj atrybutu
optionaldla mniej istotnych pól. - Stosuj filtry (domain) dla ograniczenia widocznych rekordów. Przy współdzielonych modelach domain pomoże pokazać tylko te rekordy potomne, które są relewantne dla danego rodzica.
- Ostrożnie przy dużych zestawach danych. Gdy rodzic może mieć tysiące dzieci, nie ładuj ich wszystkich na raz. Rozważ osobny widok listy lub paginację w osadzonej tabeli.
Typowe pułapki
Najczęstsze błędy w pracy z One2many
Brak pola inverse (Many2one)
Najczęstszy błąd to próba zdefiniowania One2many bez istnienia pola Many2one po stronie dziecka — Odoo zgłosi błąd. Zawsze upewnij się, że inverse_name naprawdę istnieje.
Błędna składnia zapisu (write)
Aktualizując One2many przez kod lub API, trzeba stosować specjalne komendy Odoo (tuplowe) — nie można przypisać zwykłej listy Pythona. Prawidłowe polecenia to m.in.:
(0, 0, values_dict)— utworzenie nowego rekordu potomnego.(1, child_id, values_dict)— aktualizacja istniejącego rekordu potomnego.(2, child_id, 0)— usunięcie istniejącego rekordu potomnego.(4, child_id, 0)— dodanie istniejącego rekordu do relacji.(5, 0, 0)— odłączenie wszystkich rekordów potomnych od rodzica (bez kasowania ich).
Próba zapisania czegoś w rodzaju record.line_ids = [1, 2, 3] nie zadziała — trzeba użyć komend opisanych powyżej.
Mylące stosowanie One2many zamiast Many2many
Jeżeli potrzebujesz, by rekordy potomne były współdzielone między wieloma rodzicami, One2many nie jest właściwym wyborem — to pole wymusi jednoznaczne przypisanie i zmusi Cię do duplikowania danych. W takich przypadkach użyj Many2many.
Problemy wydajnościowe przy dużych listach podrzędnych
Wyświetlanie setek czy tysięcy wierszy w osadzonej tabeli znacząco spowalnia ładowanie formularza — typowy problem np. w fakturach czy ruchach magazynowych. Użyj ograniczeń (limit) w widoku listy lub przenieś listę do oddzielnego widoku.
Osierocone rekordy po usunięciu rodzica
Jeżeli usuniesz rodzica bez ustawienia ondelete='cascade' na polu Many2one dziecka, rekordy potomne mogą pozostać w bazie z pustym lub niepoprawnym wskaźnikiem. Z czasem to zaśmieca dane i prowadzi do błędów w raportach — zaplanuj politykę usuwania już na etapie projektowania modelu.
Podsumowanie
Pole One2many to fundament modelu danych Odoo — zasila kluczowe funkcje platformy: pozycje zamówień, linie faktur, zadania projektowe i wiele innych. Zrozumienie relacji One2many ↔ Many2one znacznie ułatwia czytanie i rozbudowę systemu.
Niezależnie czy konfigurujesz Odoo dla firmy, używasz Studio, czy tworzysz moduł od podstaw, One2many będzie jednym z częściej używanych narzędzi. Wiedza, kiedy go stosować i jak unikać błędów, oszczędzi czas i zabezpieczy integralność danych.
Jeżeli chcesz pogłębić wiedzę techniczną, przejrzyj pozostałe poradniki z kolekcji Odoo Data & API.
Potrzebujesz wsparcia przy wdrożeniu Odoo?
Dasolo pomaga firmom wdrażać, dostosowywać i optymalizować Odoo pod konkretne potrzeby biznesowe. Pomagamy projektować model danych, tworzyć moduły, konfigurować pola i zwiększać wartość już działających rozwiązań.
Masz pytania związane z projektem Odoo lub chcesz porozmawiać o optymalnej strukturze danych, skontaktuj się z nami— chętnie pomożemy.