5.2. System ORM Peewee
Używanie systemów ORM, takich jak Peewee, w prostych projektach sprowadza się do schematu, który poglądowo można opisać w trzech krokach:
deklaracja modelu opisującego bazę
utworzenie na podstawie modelu tabel w bazie,
wykonywanie operacji CRUD.
Przez model (zob. też: model bazy danych) rozumiemy tutaj deklaracje klas i ich właściwości (atrybutów) opisujące obiekty, które będą przechowywane w bazie. Systemy ORM na podstawie klas tworzą odpowiednie tabele i pola, uwzględniając ich typy i powiązania. Odwzorowanie klas i ich właściwości na tabele, kolumny i relacje w bazie stanowi istotę mapowania relacyjno-obiektowego.
Poniżej spróbujemy pokazać, jak wykonywać typowe operacje na bazie z wykorzystaniem biblioteki Peewee.
Informacja
Wyjaśnienia podanego niżej kodu są uproszczone ze względu na przejrzystość i poglądowość instrukcji. Do używania systemów ORM wystarczające jest poznanie ich interfejsu API.
5.2.1. Środowisko pracy
Informacja
Do kodowania i uruchamiania skryptu możesz użyć dowolnych narzędzi, np. ulubionego edytora kodu i terminala. Sugerujemy jednak wykorzystanie środowiska typu PyCharm lub innego, ponieważ ułatwiają przygotowania i pracę nad projektami w języku Python.
Przed rozpoczęciem pracy przygotuj w wybranym katalogu, np. baza_orm` wirtualne środowisko Pythona
i w aktywnym środowisku zainstaluj pakiet Peewee:
(.venv) ~/baza_orm$ pip install peewee
5.2.2. Klasa bazowa
W ulubionym edytorze utwórz plik o nazwie orm_pw.py z następującym kodem:
1import os
2from peewee import SqliteDatabase, Model
3from peewee import CharField, ForeignKeyField
4
5plik_bazy = 'baza_pw.db'
6if os.path.exists(plik_bazy):
7 os.remove(plik_bazy)
8
9# tworzymy instancję klasy Database do obsługi bazy
10baza = SqliteDatabase(plik_bazy) # ':memory:'
11
12# klasa bazowa dla modeli
13class Base(Model):
14 class Meta:
15 database = baza
16
Na początku importujemy potrzebne klasy. Dalej tworzymy zmienną plik_bazy,
która będzie przechowywała nazwę pliku z bazą danych.
Jeżeli plik znajduje się na dysku (if os.path.exists()), usuwamy go (os.remove()),
aby zapewnić bezproblemowe działanie skryptu podczas wielokrotnego uruchamiania.
Następnie tworzymy obiekt baza do obsługi bazy SQlite3 przechowywanej w pliku baza_pw.db.
Wskazówka
Jeżeli zamiast nazwy pliku, podamy argument :memory:, baza utworzona zostanie w pamięci RAM,
co może być przydatne podczas testowania.
Do utworzenia modeli danych potrzebna będzie klasa bazowa, którą tworzymy w oparciu o klasę Model,
w podklasie Meta dodatkowo przypisujemy obiekt służący do komunikacji z bazą do atrybutu database.
5.2.3. Model danych
Dodajemy definicje klas opisujących dwa obiekty reprezentujące klasę i ucznia. Każda klasa ma swoją nazwę i profil, każdy uczeń ma imię, nazwisko oraz przynależy do jakiejś klasy.
18# klasy Klasa i Uczen opisują rekordy tabel "klasa" i "uczen" oraz relacje między nimi
19class Klasa(Base):
20 nazwa = CharField(null=False)
21 profil = CharField(default='')
22
23
24class Uczen(Base):
25 imie = CharField(null=False)
26 nazwisko = CharField(null=False)
27 klasa = ForeignKeyField(Klasa, related_name='uczniowie')
28
29
30baza.connect() # nawiązujemy połączenie z bazą
31baza.create_tables([Klasa, Uczen]) # tworzymy tabele
32
Deklarowanie modelu opiera się na dziedziczonej klasie podstawowej Base.
Klasy o nazwach Klasa i Uczen reprezentują tabele w bazie. Właściwości tych klas odpowiadają polom.
Każde pole jest instancją klasy określającej typ danych i ma ograniczenia podawane jako dodatkowe argumenty
konstruktora:
CharFiled()– klasa definiująca pole zawierające ciąg znaków,null=False– ograniczenie, pole nie może zawierać wartościNULL,default=''– ograniczenie, wartość domyślna przechowywana w polu,ForeignKeyField()– klasa definiująca relację, konstruktor otrzymuje nazwę klasy powiązanej, z którą tworzymy relację, oraz nazwę atrybutu określającego relację zwrotną w powiązanej klasie; dzięki temu wywołanie w postaciKlasa.uczniowieda nam dostęp do obiektów reprezentujących uczniów przypisanych do danej klasy.
Po zdefiniowaniu modelu, co jest relatywnie najtrudniejsze, trzeba go przetestować,
czyli utworzyć tabele i kolumny w bazie. W Peewee łączymy się z bazą (baza.connect())
i wywołujemy metodę create_tables(), której podajemy w liście nazwy klas reprezentujących tabele.
Omówiony kod można już uruchomić, w katalogu, z którego uruchamiamy skrypt, powinien zostać utworzony
plik bazy baza_pw.db.
5.2.3.1. Ćwiczenie
Wykorzystaj interpreter sqlite3 i sprawdź, czy zostały utworzone tabele, czyli jak wygląda kod SQL wygenerowany przez ORM. Przykładowy zrzut poniżej.
Informacja
Nazwy utworzonych tabel to nazwy klas, które je opisują, podobnie nazwy pól odpowiadają nazwom atrybutów.
Warto zauważyć, że Peewee nie wymaga definiowania kluczy głównych, są tworzone automatycznie
jako pola o nazwie id zawierające liczby całkowite.
5.2.4. Dodawanie danych
Dodawanie (ang. create) danych w Peewee wykonywane jest za pomocą obiektów reprezentujących rekordy zdefiniowanych tabel oraz ich metod.
33# dodajemy dwie klasy
34klasa1 = Klasa.create(nazwa='1A', profil='matematyczny')
35klasa2 = Klasa(nazwa='1B', profil='humanistyczny')
36klasa2.save()
37
38# lista uczniów, których dane zapisane są w słownikach
39uczniowie = [
40 {'imie': 'Tomasz', 'nazwisko': 'Nowak', 'klasa': klasa1},
41 {'imie': 'Jan', 'nazwisko': 'Kos', 'klasa': klasa2},
42 {'imie': 'Piotr', 'nazwisko': 'Kowalski', 'klasa': klasa2}
43]
44# dodajemy dane wielu uczniów
45Uczen.insert_many(uczniowie).execute()
46
Metoda create() modelu służy do utworzenia jego instancji (obiektu) i zapisania odpowiedniego rekordu w bazie,
czyli wykonania klauzuli INSERT języka SQL. Nazwane argumenty metody odpowiadają atrybutom modelu:
klasa = '1A', profil = 'matematyczny').
Innym sposobem jest utworzenie instancji modelu i zapisanie obiektu w bazie jako rekordu za pomocą metody .save().
Można również dodawać wiele rekordów na raz. Tworzymy listę słowników uczniowie. Każdy słownik zawieraja dane
w formacie „klucz”:”wartość”, przy czym klucze są nazwami atrybutów klasy. Wartością klucza 'klasa' jest
instancja modelu Klasa.
Następnie za pomocą metody insert_many(), której jako argument podajemy przygotowaną listę słowników,
dodajemy rekordy z danymi wielu uczniów do bazy.
5.2.5. Odczyt danych
Odczyt danych może być realizowany na wiele sposobów. Zacznijmy od uzupełnienia kodu skryptu:
47# odczytujemy wiele rekordów
48print('Klasy:')
49klasy = Klasa.select()
50for klasa in klasy:
51 print(klasa.id, klasa.nazwa, klasa.profil)
52print()
53
54# odczytujemy jeden rekord
55klasa = Klasa.select().where(Klasa.nazwa == '1A').get()
56# klasa = Klasa.get(Klasa.nazwa == '1A')
57print('Klasa:', klasa.nazwa)
58print()
59
60def wypisz_listę_uczniow():
61 """ Odczytujemy i wypisujemy dane uczniów, w tym klasę"""
62 if Uczen.select().count():
63 print('Uczniowie:')
64 uczniowie = Uczen.select().join(Klasa)
65 for uczen in uczniowie:
66 print(uczen.id, uczen.imie, uczen.nazwisko, uczen.klasa.nazwa)
67 print()
68 else:
69 print('Brak uczniów w bazie!')
70
71wypisz_listę_uczniow()
72
Do odczytywaniu wielu rekordów służy metoda select() modelu, która zwraca listę obiektów
zapisanych w bazie. Listę możemy odczytać za pomocą pętli, np.: for klasa in klasy:.
Mamy również dostęp do atrybutów odczytywanych obiektów, możemy je wypisać dzięki notacji z kropką:
print(klasa.id, klasa.nazwa, klasa.profil).
Do odczytania jednego rekordu (obiektu) z bazy danych na podstawie wartości któregoś z jego atrybutów,
możemy użyć metody where(), która odpowiada klauzuli warunkowej WHERE języka SQL, np.:
Klasa.select().where(Klasa.nazwa == '1A').get(). Dopóki interesuje nas jeden rekord z jednej tabeli,
możemy też użyć skróconego zapytania: klasa = Klasa.get(Klasa.nazwa == '1A').
W funkcji wypisz_liste_uczniow() do sprawdzenia liczby obiektów zapisanych w bazie używamy metody count().
Jeżeli w bazie zapisano jakichś uczniów (if Uczen().select().count():),
pobieramy ich dane używając złączenia z modelem Klasa.
Używamy metody join(), która odpowiada klauzuli INNER JOIN języka SQL:
Uczen.select().join(Klasa). Zwróconą listę rekordów odczytujemy za pomocą pętli for.
Jeżeli w bazie nie ma żadnych uczniów, wypisujemy odpowiedni komunikat.
5.2.6. Modyfikowanie danych
Systemy ORM ułatwiają modyfikowanie danych w bazie, ponieważ operacja ta polega na zmianie wartości pól wybranego obiektu. W naszym skrypcie dopisujemy kod:
73# zmiana klasy ucznia o identyfikatorze 2
74uczen = Uczen.select().join(Klasa).where(Uczen.id == 2).get()
75nowa_klasa = Klasa.select().where(Klasa.nazwa == '1A').get()
76print('Zmieniam klasę ucznia:', uczen.imie, uczen.nazwisko, uczen.klasa.nazwa)
77uczen.klasa = nowa_klasa
78uczen.save() # zapisanie zmian w bazie
79wypisz_listę_uczniow()
80
Na początku odczytujemy obiekt klasy Uczen o podanym identyfikatorze oraz obiekt klasy Klasa o podanej nazwie.
Następnie atrybutowi klasa obiektu reprezentującego ucznia przypisujemy obiekt klasy:
uczen.klasa = nowa_klasa. Na koniec zapisujemy zmiany w bazie za pomocą metody save().
5.2.7. Usuwanie danych
Do skryptu dodajemy poniższy kod:
87print('Usuwam uczniów z klasy 1A')
88zapytanie = Uczen.delete().where(Uczen.klasa == Klasa.get(Klasa.nazwa == '1A'))
89zapytanie.execute()
90wypisz_listę_uczniow()
91
92baza.close()
Za pomocą kwerendy warunkowej odczytujemy obiekt ucznia o identyfikatorze „3”,
a następnie wywołujemy metodę delete_instance(), która usuwa obiekt z bazy.
Do usuwania wielu rekordów służy metoda delete() modelu połączona z metodą where(),
która pozwala wskazać rekordy do usunięcia. Należy pamiętać, żeby po utworzeniu
zapytania wykonać go za pomocą metody execute().
Po zakończeniu operacji wykonywanych na danych powinniśmy pamiętać o zamknięciu połączenia.
Robimy to używając metody obiektu bazy baza.close().
5.2.8. Zadania
Spróbuj dodać do bazy korzystając z systemu Peewee wiele rekordów na raz pobranych z pliku
uczniowie.csv. Wykorzystaj i zmodyfikuj funkcjępobierz_dane()opisaną w materiale Dane z pliku.Dodaj do aplikacji konsolowy interfejs, który umożliwi operacje odczytu, zapisu, modyfikowania i usuwania rekordów. Dane powinny być pobierane z klawiatury od użytkownika.
Przedstawione rozwiązania warto użyć w aplikacjach internetowych jako relatywnie szybki i łatwy sposób obsługi danych. Zobacz, jak to zrobić na przykładzie scenariusza aplikacji Quiz ORM.
Przejrzyj scenariusz aplikacji internetowej Czat, zbudowanej z użyciem frameworku Django, korzystającego z własnego modelu ORM.
Materiały Python 101
udostępniane przez
Centrum Edukacji Obywatelskiej na licencji
Creative Commons Uznanie autorstwa-Na tych samych warunkach 4.0 Międzynarodowa.
- Utworzony:
2026-05-30 o 19:12 w Sphinx 7.3.7
- Autorzy: