SQLAlchemy ORM - budowanie relacji

Ta sesja opisuje tworzenie kolejnej tabeli, która jest powiązana z już istniejącą w naszej bazie danych. Tabela klientów zawiera dane podstawowe klientów. Teraz musimy utworzyć tabelę faktur, która może zawierać dowolną liczbę faktur należących do klienta. To jest przypadek relacji jeden do wielu.

Używając deklaratywnego, definiujemy tę tabelę wraz z jej mapowaną klasą, Faktury, jak podano poniżej -

from sqlalchemy import create_engine, ForeignKey, Column, Integer, String
engine = create_engine('sqlite:///sales.db', echo = True)
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
from sqlalchemy.orm import relationship

class Customer(Base):
   __tablename__ = 'customers'

   id = Column(Integer, primary_key = True)
   name = Column(String)
   address = Column(String)
   email = Column(String)

class Invoice(Base):
   __tablename__ = 'invoices'
   
   id = Column(Integer, primary_key = True)
   custid = Column(Integer, ForeignKey('customers.id'))
   invno = Column(Integer)
   amount = Column(Integer)
   customer = relationship("Customer", back_populates = "invoices")

Customer.invoices = relationship("Invoice", order_by = Invoice.id, back_populates = "customer")
Base.metadata.create_all(engine)

Spowoduje to wysłanie zapytania CREATE TABLE do silnika SQLite, jak poniżej -

CREATE TABLE invoices (
   id INTEGER NOT NULL,
   custid INTEGER,
   invno INTEGER,
   amount INTEGER,
   PRIMARY KEY (id),
   FOREIGN KEY(custid) REFERENCES customers (id)
)

Możemy sprawdzić, czy nowa tabela została utworzona w sales.db za pomocą narzędzia SQLiteStudio.

Klasa faktur stosuje konstrukcję ForeignKey dla atrybutu custid. Ta dyrektywa wskazuje, że wartości w tej kolumnie powinny być ograniczone tak, aby były wartościami obecnymi w kolumnie id w tabeli klientów. Jest to podstawowa cecha relacyjnych baz danych i jest „klejem”, który przekształca niepołączony zbiór tabel w celu uzyskania bogatych, nakładających się relacji.

Druga dyrektywa, znana jako relacja (), mówi ORM, że klasa faktury powinna być połączona z klasą Customer przy użyciu atrybutu Invoice.customer. Relacja () używa relacji klucza obcego między dwiema tabelami, aby określić charakter tego powiązania, określając, że jest to wiele do jednego.

Dodatkowa dyrektywa relations () jest umieszczana w odwzorowanej klasie Customer w atrybucie Customer.invoices. Parametr relations.back_populate jest przypisany do odwoływania się do uzupełniających się nazw atrybutów, dzięki czemu każda relacja () może podejmować inteligentne decyzje dotyczące tej samej relacji, co wyrażono odwrotnie. Z jednej strony Invoices.customer odwołuje się do instancji Invoices, a z drugiej strony Customer.invoices do listy instancji Klientów.

Funkcja relacji jest częścią Relationship API pakietu SQLAlchemy ORM. Zapewnia relację między dwiema mapowanymi klasami. Odpowiada to relacji nadrzędny-podrzędny lub tabeli asocjacyjnej.

Poniżej przedstawiono podstawowe znalezione wzorce relacji -

Jeden za dużo

Relacja jeden do wielu odnosi się do rodzica za pomocą klucza obcego na stole podrzędnym. Relacja () jest następnie określana na rodzicu, jako odwołanie do kolekcji elementów reprezentowanych przez dziecko. Parametr relacji.back_populate służy do ustanowienia relacji dwukierunkowej w trybie jeden-do-wielu, gdzie strona „odwrotna” to wiele do jednego.

Wiele do jednego

Z drugiej strony, relacja Wiele do jednego umieszcza klucz obcy w tabeli nadrzędnej w celu odniesienia się do dziecka. relacja () jest zadeklarowana w rodzicu, gdzie zostanie utworzony nowy atrybut trzymający wartość skalarną. Tutaj ponownie parametr relations.back_populate jest używany dla zachowania dwukierunkowego.

Jeden na jednego

Relacja jeden do jednego jest z natury relacją dwukierunkową. Flaga uselist wskazuje na umieszczenie atrybutu skalarnego zamiast zbioru po stronie „wiele” relacji. Aby przekonwertować relację jeden-do-wielu na typ relacji jeden-do-jednego, ustaw parametr uselist na false.

Wiele do wielu

Relacja wiele do wielu jest ustanawiana przez dodanie tabeli asocjacji związanej z dwiema klasami poprzez zdefiniowanie atrybutów za pomocą ich kluczy obcych. Wskazuje na to drugi argument relacji (). Zwykle tabela używa obiektu MetaData skojarzonego z deklaratywną klasą bazową, dzięki czemu dyrektywy ForeignKey mogą zlokalizować zdalne tabele, z którymi mają zostać połączone. Parametr relacji.back_populate dla każdej relacji () ustanawia relację dwukierunkową. Obie strony relacji zawierają kolekcję.