Wprowadzenie
Pole Many2One to jeden z filarów modelu danych w Odoo. Gdy przypisujesz zamówienie sprzedaży do kontrahenta, kwalifikujesz produkt do kategorii czy przypisujesz zadanie do projektu — korzystasz właśnie z relacji Many2One. To najczęściej spotykany typ relacyjny w Odoo, dlatego jego znajomość jest niezbędna dla każdego, kto poważnie zajmuje się rozwojem lub dostosowywaniem systemu.
Z punktu widzenia biznesu, to właśnie pola Many2One sprawiają, że Odoo działa jako zintegrowane narzędzie. Bez nich moduły funkcjonowałyby jak osobne wyspy danych — z nimi informacje płyną pomiędzy dokumentami, a użytkownicy mogą przeskakiwać między powiązanymi rekordami bez zastanawiania się nad strukturą bazy.
W tym przewodniku wyjaśnimy, co dokładnie przechowuje pole Many2One, jak zachowuje się w ORM i interfejsie, jak je tworzyć w Odoo Studio i w Pythonie oraz pokażemy praktyczne scenariusze z CRM, Sprzedaży, Magazynu i Księgowości.
Czym jest pole Many2One w Odoo
W warstwie ORM Many2One oznacza, że rekord z bieżącego modelu wskazuje dokładnie jeden rekord w modelu docelowym. Nazwa odzwierciedla perspektywę bieżącego modelu: wiele rekordów tutaj może wskazywać na jeden rekord tam. Przykładowo wiele zamówień sprzedaży może wskazywać na jednego klienta, a wiele produktów może należeć do jednej kategorii.
Na poziomie bazy danych pole Many2One to zwykły klucz obcy w tabeli bieżącego modelu. Jeśli zamówienie ma przypisanego klienta o ID 42, kolumna partner_id w tabeli sale_order przechowuje wartość 42. Odoo zajmuje się wykonaniem joinów i pobraniem czytelnej nazwy powiązanego rekordu.
W interfejsie pole Many2One wygląda najczęściej jak rozwijane pole z podpowiedzią (typeahead). Użytkownik zaczyna wpisywać nazwę, a Odoo na żywo filtruje pasujące rekordy. Po wybraniu wartość jest ustawiana, a widoczną informacją jest czytelna nazwa powiązanego rekordu — nie jego numer wewnętrzny — co zdecydowanie poprawia komfort pracy.
Przykład definicji pola w Pythonie:
from odoo import fields, models
class SaleOrder(models.Model):
_inherit = 'sale.order'
x_account_manager_id = fields.Many2one(
comodel_name='res.users',
string='Account Manager',
ondelete='set null',
)
Parametr comodel_name wskazuje techniczną nazwę modelu, do którego linkujesz. ondelete określa, co ma się stać z rekordem wskazującym, gdy rekord docelowy zostanie usunięty. Możliwe wartości to cascade (usuń także rekord wskazujący), set null (wyczyść powiązanie) oraz restrict (zabroń usunięcia, jeśli ktoś do niego wskazuje).
W Odoo Studio pole Many2One znajdziesz w zestawie dostępnych pól pod etykietą Many2One. Po przeciągnięciu na formularz wybierasz model docelowy z listy, a Studio automatycznie tworzy pole. To szybki sposób na utworzenie relacji bez pisania kodu.
Zasada działania pola
Czytając Many2One przez ORM otrzymujesz recordset zawierający powiązany rekord — jeśli pole jest puste, otrzymasz pusty recordset. W Pythonie możesz od razu odwołać się do powiązanego rekordu i jego pól używając notacji kropkowej:
order = self.env['sale.order'].browse(1) customer_name = order.partner_id.name customer_city = order.partner_id.city
Takie łańcuchowe odwołania są jedną z największych wygód Odoo: nie trzeba ręcznie tworzyć joinów ani dopisywać dodatkowych zapytań — ORM robi to za Ciebie w tle.
Przy odczycie przez XML-RPC Many2One zwraca dwuelementową listę: ID powiązanego rekordu i jego nazwę do wyświetlenia, np. [42, "Acme Corp"]. Jeśli pole jest puste, zwracane jest False. Warto o tym pamiętać podczas przetwarzania odpowiedzi w zewnętrznych skryptach.
Kluczowe atrybuty pola
Poniżej najważniejsze właściwości, które możesz skonfigurować w polu Many2One w Odoo:
- comodel_name: techniczna nazwa modelu docelowego. To jedyny obowiązkowy parametr.
- ondelete: zachowanie, gdy rekord docelowy zostanie usunięty. Opcje: 'cascade', 'set null', 'restrict'. Domyślnie 'set null'.
- domain: filtr ograniczający, które rekordy można wybrać z listy. Na przykład domain=[('customer_rank', '>', 0)] zawęzi wybór partnerów tylko do klientów.
- context: dodatkowe wartości kontekstu przekazywane przy otwieraniu powiązanego rekordu lub listy. Użyteczne do wstępnego wypełniania pól przy tworzeniu nowego powiązanego rekordu.
- required: oznacza pole jako obowiązkowe. Rekord nie zostanie zapisany, jeśli pole nie będzie ustawione.
- readonly: blokuje możliwość zmiany powiązania przez użytkownika. Przydatne, gdy pole ustawiane jest automatycznie i nie powinno być edytowane ręcznie.
- delegate: jeśli True, pola modelu powiązanego stają się bezpośrednio dostępne w modelu bieżącym — używane do dziedziczenia modeli, nie jako standardowe relacje.
Wygląd w widokach
W formularzu Many2One renderowany jest jako pole z wyszukiwarką i listą podpowiedzi. Użytkownicy mogą wpisywać fragmenty nazwy, a także kliknąć strzałkę przy polu, by otworzyć formularz powiązanego rekordu — to szybki sposób na poruszanie się między powiązanymi dokumentami.
W widokach list Many2One pokazuje nazwę powiązanego rekordu i można po nim grupować listy: np. zamówienia pogrupowane po kliencie lub zadania pogrupowane po projekcie. Grupowanie to jedna z najczęściej używanych funkcji raportowania w Odoo.
W wyszukiwarkach Many2One służy za filtr i kryterium grupowania. Gdy filtrujesz pipeline CRM po kliencie, w rzeczywistości korzystasz z pola Many2One.
Odwrotność: One2Many
Każde Many2One ma naturalne odbicie w postaci One2Many. Jeśli zamówienia wskazują klienta przez Many2One, rekord klienta może pokazywać listę powiązanych zamówień przez One2Many. Dobrą praktyką jest zdefiniowanie obu stron relacji — dzięki temu z poziomu formularza klienta użytkownik zobaczy wszystkie zamówienia bez dodatkowego wyszukiwania. Pole One2Many nie tworzy dodatkowej kolumny w bazie — jest wyliczane na podstawie klucza obcego po drugiej stronie.
Przykłady zastosowań biznesowych
Pole Many2One występuje niemal w każdym standardowym wdrożeniu Odoo. Poniżej pięć praktycznych przykładów z codziennej pracy.
CRM: przypisanie leadów do handlowców
W module CRM lead ma pole user_id — Many2One do res.users — co oznacza odpowiedzialnego handlowca. Menedżerowie mogą filtrować pipeline po osobie, analizować konwersje na poziomie handlowca i masowo przypisywać leady. Dzięki Many2One segmentacja i raporty działają bez konieczności programowania. Jeśli potrzebujesz dodatkowego opiekuna czy account managera, wystarczy dodać kolejne Many2One do tego samego modelu.
Sprzedaż: powiązanie zamówień z klientami i cennikami
Zamówienie sprzedaży zwykle ma co najmniej dwa pola Many2One: partner_id (klient → res.partner) oraz pricelist_id (cennik → product.pricelist). Po wybraniu klienta Odoo może automatycznie uzupełnić cennik, warunki płatności i adres dostawy na podstawie danych kontrahenta. Takie automatyczne uzupełnienia realizowane są przez onchangey, które czytają Many2One i wypełniają powiązane pola — to jeden z najpraktyczniejszych efektów dobrze zaprojektowanego modelu danych.
Magazyn: produkty i kategorie
Każdy produkt ma przypisaną kategorię przez Many2One (categ_id na product.template). Kategoria wpływa na konta księgowe kosztu i przychodu, sposób wyceny oraz strategie usuwania z magazynu. Poprawne przypisanie kategorii jest kluczowe dla rzetelnych raportów finansowych. Many2One upraszcza ten proces: jedno pole wyboru kategorii dla produktu, jedna karta kategorii współdzielona przez setki produktów.
Księgowość: zapisy i dzienniki
Każdy wpis księgowy jest powiązany z dziennikiem przez Many2One (journal_id na account.move). Dziennik decyduje o numeracji dokumentów, rodzaju zapisu (sprzedaż, zakup, bank itp.) i domyślnych kontach. Błędny wybór dziennika na fakturze czy płatności może umieścić zapis w niewłaściwej części księgi — stąd pole Many2One pełni też funkcję kontroli poprawności danych księgowych.
Zarządzanie projektami: zadania i projekty
W module Project zadanie należy do projektu przez Many2One (project_id na project.task). To pojedyncze powiązanie decyduje o ścieżce etapów, uprawnieniach zespołu oraz rozliczaniu czasu pracy. Dla firm usługowych, które fakturują projektowo, relacja między timesheetami, zadaniami i projektem jest podstawą rozpoznawania przychodów i poprawnego fakturowania.
Tworzenie i dopasowywanie pola Many2One
Sposoby dodawania pola Many2One
Są trzy podstawowe drogi dodania Many2One, w zależności od kontekstu technicznego i sposobu wdrożenia.
Odoo Studio (bez kodu)
- Odoo Studio to narzędzie low-code dostępne w systemie. Aby dodać Many2One bez pisania kodu:
- Uruchom Odoo Studio z menu głównego.
- Przejdź do formularza, na którym chcesz dodać pole.
- Przeciągnij pole Many2One z panelu pól na formularz.
- W panelu właściwości wybierz model docelowy z listy.
- Ustaw etykietę pola i ewentualny filtr domain, aby ograniczyć możliwe wybory.
Zapisz i zamknij Studio.
Studio utworzy pole z prefiksem x_studio_ i doda je do widoku. Pole działa od razu: użytkownicy mogą wyszukiwać i wybierać powiązane rekordy. To najszybszy sposób na rozbudowę formularza o relację bez umiejętności programistycznych.
Definiowanie w Pythonie (moduł niestandardowy)
Dla deweloperów Many2One definiujemy w Pythonie — to rekomendowana metoda przy pracy modułowej, kontroli wersji i wdrożeniach wielośrodowiskowych:
from odoo import fields, models class ProjectTask(models.Model): _inherit = 'project.task' x_client_contact_id = fields.Many2one( comodel_name='res.partner', string='Client Contact', domain=[('type', '=', 'contact')], ondelete='set null', help='The main contact at the client for this task.', )
Po zdefiniowaniu pola w modelu dodaj je do odpowiedniego widoku w XML i zaktualizuj moduł, aby zmiany trafiły do bazy. Ta metoda daje pełną kontrolę nad domain, zachowaniem ondelete oraz integracją z metodami compute i constraint — standardowy sposób pracy z relacjami w produkcyjnych modułach Odoo.
Dobrą praktyką jest też utworzenie odwrotnego pola One2Many na modelu powiązanym, aby nawigacja była dwukierunkowa:
class ResPartner(models.Model): _inherit = 'res.partner' x_task_ids = fields.One2many( comodel_name='project.task', inverse_name='x_client_contact_id', string='Related Tasks', )
Przez XML-RPC (programowo)
Jeśli zarządzasz konfiguracją Odoo zdalnie lub automatyzujesz tworzenie pól, możesz stworzyć Many2One przez XML-RPC API:
field_id = models.execute_kw( ODOO_DB, uid, ODOO_API_KEY, 'ir.model.fields', 'create', [{ 'name': 'x_client_segment_id', 'field_description': 'Client Segment', 'model_id': model_id, 'ttype': 'many2one', 'relation': 'res.partner.category', 'on_delete': 'set null', 'state': 'manual', }] )
Dobre praktyki
Klucz relation określa model docelowy, a on_delete jego zachowanie przy usunięciu. Tworząc pole przez API pamiętaj, aby również dodać odwrotne One2Many — to warunek, by pełna nawigacja działała po obu stronach relacji.
1. Zawsze twórz odwrotne One2Many
Gdy dodajesz Many2One, zadbaj o odpowiadające mu One2Many po drugiej stronie. Nie generuje to dodatkowej kolumny w bazie, ale znacząco ułatwia użytkownikom poruszanie się po danych. Bez odwrotnego pola użytkownik formularza klienta nie zobaczy powiązanych z nim zamówień czy zadań bez ręcznego wyszukiwania.
2. Ograniczaj wybór przez domain
Pole Many2one do res.partner bez filtra pokaże wszystkie typy partnerów — dostawców, klientów, adresy dostaw i kontakty wewnętrzne. Jeśli pole ma sens tylko dla klientów, ustaw domain=[('customer_rank', '>', 0)]. Dzięki temu lista będzie czytelniejsza, a użytkownicy nie wybiorą przypadkowych rekordów, które mogą zepsuć dalsze procesy.
3. Przemyśl ondelete
ondelete ma realne konsekwencje: cascade usunie wszystkie rekordy wskazujące na rekord docelowy, co przy nieostrożnym zastosowaniu może doprowadzić do masowych utrat danych. W większości przypadków bezpieczniejszy jest set null — link zostanie wyczyszczony, ale dane pozostaną. Stosuj restrict, gdy rekord docelowy nie powinien być kasowany, jeśli ktoś na niego wskazuje (np. kategoria produktu z przypisanymi produktami).
4. Nie duplikuj danych zamiast Many2One
Częstym błędem jest dodawanie pola typu char, żeby zapisać nazwę firmy lub kategorię, zamiast wskazać istniejący rekord w innym modelu. Taka duplikacja utrudnia filtrowanie, grupowanie i prowadzi do niespójności przy zmianie nazw. Jeśli dane istnieją w innym modelu — użyj Many2One.
5. Wykorzystuj context do wstępnego wypełniania
Najczęstsze pułapki
Atrybut context pozwala przekazać domyślne wartości przy tworzeniu powiązanego rekordu z poziomu lista/pola. Dzięki temu przy tworzeniu kontaktu z poziomu projektu możesz automatycznie ustawić projekt na nowym rekordzie, co oszczędza czas i poprawia spójność danych.
Zapomnienie o odwrotnym One2Many
Najczęstszy błąd przy dostosowaniach Odoo to dodanie Many2One bez zdefiniowania odwrotnego One2Many. Efekt to jednokierunkowe powiązanie: z poziomu jednego rekordu widzisz link, ale z poziomu rekordu docelowego nie masz wglądu, kto wskazuje na niego. Użytkownicy będą narzekać, że nie mogą znaleźć powiązanych dokumentów, a ktoś w końcu zbuduje raport lub wyszukiwarkę jako obejście.
Używanie cascade bez przemyślenia
Ustawienie ondelete='cascade' dla relacji między rekordami operacyjnymi a rekordem nadrzędnym może prowadzić do poważnej utraty danych. Jeśli kategoria produktu zostanie usunięta i wszystkie produkty mają cascade, wszystkie produkty znikną. Zwykle lepsze są set null lub restrict dla danych biznesowych.
Nie sprawdzanie pustych wartości w Pythonie
Gdy Many2One jest pusty, odczyt w Pythonie zwraca pusty recordset, który jest falsy. Jeśli kod robi order.partner_id.name bez wcześniejszego sprawdzenia partner_id, otrzymasz pusty string zamiast błędu — co często jest akceptowalne, ale przy głębszym łańcuchu (order.partner_id.country_id.name) każda pusta część sprawi, że wynik będzie pusty, co może skrycie zafałszować raporty. Sprawdzaj obecność powiązań, jeśli pole nie jest obowiązkowe.
Wskazywanie na niewłaściwy model
Model res.partner w Odoo zbiera klientów, dostawców, kontakty i firmy. Many2One do res.partner bez filtra pokaże wszystkie typy — jeśli pole miało być tylko dla klientów, brak domain spowoduje, że w dropdownie pojawią się wewnętrzni użytkownicy, adresy dostaw czy kontakty dostawców. Zdefiniuj domain zgodnie z rzeczywistym przeznaczeniem pola.
Nadużywanie Many2One tam, gdzie wystarczy Selection
Podsumowanie
Jeśli zestaw wartości jest stały i niewielki, prostsze i bardziej wydajne będzie pole Selection. Many2One wymaga dodatkowego modelu z rekordami i dodaje join do zapytań. Dla statusów z trzema–czterema opcjami lepszy będzie Selection; Many2One wybieraj, gdy wartości są liczne, zarządzane przez użytkowników lub współdzielone między modelami.
Pole Many2One łączy dane między modułami Odoo. Zrozumienie go to nie tylko sprawa dewelopera — analitycy biznesowi, konsultanci funkcjonalni i zaawansowani użytkownicy korzystający ze Studio również zyskują, wiedząc, kiedy i jak stosować Many2One oraz na co zwracać uwagę.
Najważniejsze zasady: zawsze dodawaj odwrotne One2Many, używaj domain, aby listy były czytelne, świadomie wybieraj ondelete i nie stosuj Many2One tam, gdzie prosty Selection wystarczy.
Niezależnie od tego, czy konfigurujesz pole przez Studio, tworzysz moduł w Pythonie czy zarządzasz modelem przez XML-RPC, poprawne zdefiniowanie relacji od początku sprawia, że wdrożenie będzie stabilniejsze i łatwiejsze w utrzymaniu. W Dasolo wspieramy firmy we wdrażaniu, dostosowywaniu i optymalizacji Odoo w całej organizacji. Jeśli potrzebujesz pomocy w zaprojektowaniu przejrzystego modelu danych, dodaniu relacji do formularzy lub zbudowaniu modułu od podstaw, jesteśmy do dyspozycji. Skontaktuj się z nami