P-programowanie P-programowanie
  • Języki programowania
  • Nauka i praca
  • Porady
  • Więcej niż programowanie
ARTYKUŁ: Jak pracować z bazami danych w Pythonie – ORM i surowe zapytania
Udostępnij
P-programowanieP-programowanie
Font ResizerAa
Wyszukiwarka
  • Języki programowania
  • Nauka i praca
  • Porady
  • Więcej niż programowanie
Social media
Copyright © P-programowanie.
Języki programowaniaPorady

Jak pracować z bazami danych w Pythonie – ORM i surowe zapytania

Miłosz Kenig
przez Miłosz Kenig
Aktualizacja: 2026-08-03
15 min. czytania
Słowo platforma i uśmiechnięta bizneswoman myśli przeciw futurystycznemu czarnemu i błękitnemu tłu
Udostępnij

Ten obszerny artykuł omawia podstawy i zaawansowane techniki pracy z bazami danych w Pythonie, obejmując zarówno frameworki ORM, jak i surowe zapytania SQL.

Spis treści
  • Wprowadzenie do pracy z bazami danych w Pythonie
  • Zrozumienie podstaw ORM
    • Wzorzec Active Record kontra Data Mapper
  • Popularne frameworki ORM w Pythonie
    • Django ORM
    • SQLAlchemy
    • Asynchroniczne alternatywy – TortoiseORM
  • Surowe zapytania SQL w Pythonie
    • Bezpośrednie wykonywanie SQL
    • Zapytania parametryzowane i zapobieganie SQL injection
  • Szczegółowe porównanie – ORM kontra surowe SQL
    • Szybkość tworzenia i czytelność kodu
    • Elastyczność i kontrola
    • Kwestie wydajności
    • Przenośność między bazami danych
  • Kwestie bezpieczeństwa w pracy z bazą danych
    • Zapobieganie SQL injection
    • Bezpieczne zarządzanie poświadczeniami
    • Zarządzanie uprawnieniami w bazie danych
  • Techniki optymalizacji wydajności
    • Pula połączeń
    • Strategia indeksowania
    • Rozwiązania problemu N+1 zapytań
    • Buforowanie zapytań i optymalizacja wyników
  • Migracje bazy danych i zarządzanie schematem
    • Zarządzanie ewolucją schematu
    • Bezpieczne praktyki migracji
  • Praktyczne wzorce implementacji
    • Podstawowe operacje CRUD z użyciem surowego SQL
    • Definiowanie modeli ORM i relacji
    • Strategie zarządzania transakcjami
  • Kiedy używać ORM, budowniców zapytań i surowego SQL
  • Najlepsze praktyki i rekomendacje

Django ORM wyróżnia się szybkością tworzenia i czytelnością dzięki wbudowanemu panelowi administracyjnemu, podczas gdy SQLAlchemy oferuje wyjątkową elastyczność i kontrolę, odpowiednią do złożonych aplikacji.

Surowe zapytania SQL zapewniają bezpośredni dostęp do bazy i możliwości optymalizacji, lecz wymagają szczególnej dbałości o bezpieczeństwo i ręczne zarządzanie połączeniami. Problem N+1 zapytań to kluczowe wyzwanie wydajnościowe, które można eliminować przez eager loading, batch fetching i właściwą konfigurację ORM.

Zrozumienie, kiedy i jak stosować każde podejście — ORM (Django, SQLAlchemy), budowniców zapytań lub surowy SQL — pozwala tworzyć skalowalne, łatwe w utrzymaniu aplikacje, łączące szybkość developmentu z efektywnością bazodanową.

Wprowadzenie do pracy z bazami danych w Pythonie

Praca z relacyjnymi bazami danych to podstawowa umiejętność programistów Pythona budujących aplikacje zorientowane na dane. Python oferuje wiele paradygmatów dostępu do danych — od niskopoziomowych sterowników po pełne frameworki ORM.

W rdzeniu mamy trzy główne podejścia do interakcji z bazą danych:

  • surowy SQL – pełna kontrola nad zapytaniami i optymalizacją kosztem większej złożoności;
  • budowniców zapytań – warstwa ekspresji SQL zapewniająca bezpieczne, programistyczne konstruowanie zapytań;
  • ORM – mapowanie obiektowo-relacyjne, które ukrywa szczegóły SQL i przyspiesza development.

Ekosystem Pythona dostarcza natywne połączenia przez różne sterowniki i konektory zgodne z Python DB-API 2.0. Najczęściej używane sterowniki to:

  • sqlite3 – moduł standardowej biblioteki do pracy z SQLite bez zewnętrznych zależności;
  • psycopg2 – wydajny sterownik do PostgreSQL z obsługą zaawansowanych funkcji;
  • mysql-connector-python – oficjalny sterownik do MySQL wspierający pooling i przygotowane zapytania.

Rozpoczynając pracę z bazami w Pythonie, programiści zwykle startują od operacji CRUD — Create, Read, Update, Delete. W SQLite tworzy się połączenie przez sqlite3.connect(), następnie kursor do wykonywania instrukcji SQL. Bezpośrednie podejście jest proste, ale bywa rozwlekłe i podatne na błędy przy złożonych zapytaniach.

Złożoność ręcznego zarządzania połączeniami i SQL doprowadziła do rozwoju wyższych abstrakcji. Frameworki ORM odpowiadają na niedopasowanie między światem obiektowym a relacyjnym modelem danych, znacząco przyspieszając development przy typowych modelach i standardowym CRUD.

Zrozumienie podstaw ORM

Object-Relational Mapping (ORM) łączy świat obiektowy Pythona z relacyjnym światem baz danych. ORM pozwala pisać kod w Pythonie, a następnie tłumaczy operacje na instrukcje SQL, synchronizując stan obiektów z tabelami.

Kluczowe wzorce, na których opiera się ORM, to:

  • Identity Map – zapewnia unikalność obiektów odpowiadających tym samym wierszom, ograniczając duplikację;
  • Unit of Work – kolekcjonuje zmiany i stosuje je do bazy w jednej transakcji, poprawiając spójność;
  • Lazy loading – opóźnia pobieranie powiązanych danych do momentu faktycznej potrzeby, redukując liczbę zapytań.

Wzorzec Active Record kontra Data Mapper

Dwa podstawowe podejścia architektoniczne stoją za głównymi frameworkami ORM w Pythonie.

Django ORM implementuje wzorzec Active Record, ściśle łącząc definicje modeli z operacjami na bazie. Klasa dziedzicząca po Model reprezentuje tabelę i udostępnia metody (np. save()) do zapisu zmian.

SQLAlchemy stosuje wzorzec Data Mapper, wyraźnie rozdzielając schemat bazy od struktury obiektów. Mapery zarządzają translacją między klasami a tabelami; daje to większą kontrolę kosztem dodatkowej konfiguracji i zarządzania sesjami.

Active Record w Django świetnie sprawdza się przy szybkim tworzeniu aplikacji webowych, a Data Mapper w SQLAlchemy błyszczy przy złożonych schematach i skomplikowanym składaniu zapytań.

Popularne frameworki ORM w Pythonie

Django ORM

Django ORM to najpowszechniej używany ORM w społeczności Pythona dzięki ścisłej integracji z frameworkiem Django. Zapewnia migracje, tłumaczy modele na tabele i relacje, a QuerySet oferuje łańcuchowe API do budowy zapytań bez SQL.

Wbudowany panel administracyjny automatycznie tworzy interfejs do zarządzania rekordami, skracając czas developmentu. Framework wspiera PostgreSQL, MySQL i SQLite, ułatwiając start i późniejszą migrację.

SQLAlchemy

SQLAlchemy jest standardem dla wymagających projektów w ekosystemach FastAPI i Flask. Rozdziela SQL Expression Language od warstwy ORM, zapewniając wyjątkową elastyczność.

Strategie ładowania relacji (lazy, eager), precyzyjna kontrola złączeń, dostęp do warstwy ekspresji SQL i możliwość zejścia do surowego SQL sprawiają, że sprawdza się w złożonych zastosowaniach. Krzywa uczenia jest wyższa, ale elastyczność procentuje w długim okresie.

Asynchroniczne alternatywy – TortoiseORM

TortoiseORM to nowoczesna, asynchroniczna alternatywa inspirowana składnią Django ORM, oferująca „pythoniczny” interfejs dla async/await.

Sprawdza się w aplikacjach o wysokiej współbieżności, zapobiegając blokowaniu i spadkom wydajności. Wadą jest mniejszy ekosystem i brak przewagi w aplikacjach stricte synchronicznych.

Surowe zapytania SQL w Pythonie

Bezpośrednie wykonywanie SQL

Mimo popularności ORM-ów, umiejętność pisania surowego SQL pozostaje niezbędna. Surowy SQL daje pełny dostęp do możliwości silnika bazy i precyzyjnej optymalizacji.

Typowy przepływ obejmuje nawiązanie połączenia, utworzenie kursora, wykonanie instrukcji i pobranie wyników. Oto przykładowy kod z sqlite3 z bezpiecznym bindowaniem parametrów:

import sqlite3

with sqlite3.connect('database.db') as conn:
cur = conn.cursor()
cur.execute('SELECT * FROM users WHERE id = ?', (1,))
row = cur.fetchone()
if row:
print(row)

Zapytania parametryzowane i zapobieganie SQL injection

SQL injection to najpoważniejsze zagrożenie dla aplikacji bazodanowych korzystających z surowego SQL.

Podstawową obroną są zapytania parametryzowane z bindowaniem parametrów — zamiast sklejania łańcuchów używaj placeholderów i osobnego przekazania wartości do execute():

# BEZPIECZNIE — zapytanie parametryzowane
cursor.execute("SELECT * FROM users WHERE username = ?", (username,))

# NIEBEZPIECZNIE — konkatenacja stringów (podatne na SQL injection)
cursor.execute(f"SELECT * FROM users WHERE username = '{username}'")

Szczegółowe porównanie – ORM kontra surowe SQL

Szybkość tworzenia i czytelność kodu

ORM znacząco przyspiesza development i poprawia czytelność dzięki deklaratywnym modelom, migracjom i łańcuchowym API zapytań. Surowy SQL wymaga pełnych instrukcji w stringach, co zwiększa ryzyko błędów i rozprasza uwagę.

Wadą abstrakcji bywa ukrywanie faktycznych operacji SQL, co może utrudniać diagnostykę problemów wydajnościowych.

Elastyczność i kontrola

Surowy SQL daje pełną swobodę — od funkcji okienkowych, przez złożone CTE, po specyficzne rozszerzenia. SQLAlchemy oferuje złoty środek: ORM z możliwością zejścia do ekspresji SQL i surowych zapytań tam, gdzie to potrzebne.

Kwestie wydajności

W wielu przypadkach dobrze skonfigurowane ORM-y są zbliżone wydajnością do ręcznie pisanego SQL, jednak automatycznie generowane zapytania mogą być nieoptymalne w złożonych relacjach. Problem N+1 zapytań wymaga świadomego sterowania strategią ładowania.

Przenośność między bazami danych

ORM-y zwiększają przenośność między PostgreSQL, MySQL i SQLite poprzez warstwę dialektów. Surowy SQL często wiąże aplikację z konkretnym silnikiem i jego dialektem.

Aby szybko porównać oba podejścia, zestaw kluczowych różnic prezentuje tabela:

Kryterium ORM (Django, SQLAlchemy) Surowe SQL
Szybkość developmentu Wysoka – modele, migracje, admin, łańcuchowe API Niższa – ręczne zapytania i obsługa wyników
Kontrola i elastyczność Średnia/Wysoka – z możliwością zejścia do SQL w SQLAlchemy Maksymalna – pełen dostęp do dialektu i rozszerzeń
Wydajność Dobra, ale podatna na N+1; wymaga świadomej konfiguracji Potencjalnie najwyższa, kosztem większej złożoności
Przenośność Wysoka – dialekty i abstrakcja Niska/Średnia – zapytania często specyficzne dla silnika

Kwestie bezpieczeństwa w pracy z bazą danych

Zapobieganie SQL injection

Parametryzowane zapytania w praktyce eliminują ryzyko SQL injection. ORMy stosują je domyślnie, a w surowym SQL należy bezwzględnie parametryzować każde zapytanie z danymi od użytkownika.

Bezpieczne zarządzanie poświadczeniami

Poświadczeń do bazy nie wolno trzymać w kodzie ani w repozytorium. Zalecane sposoby przechowywania to:

  • zmienne środowiskowe – proste i wspierane przez platformy wdrożeniowe;
  • pliki konfiguracyjne poza VCS – skonfigurowane per środowisko z ograniczonym dostępem;
  • menedżery sekretów – np. HashiCorp Vault, AWS Secrets Manager, Azure Key Vault.

Zarządzanie uprawnieniami w bazie danych

Stosuj zasadę najmniejszych uprawnień – konto aplikacji ma mieć wyłącznie niezbędne role, a operacje administracyjne wykonuj osobnymi, uprzywilejowanymi kontami.

Techniki optymalizacji wydajności

Pula połączeń

Połączenia z bazą są kosztowne. Tworzenie nowego połączenia dla każdej operacji jest nieefektywne — używaj puli połączeń.

Wsparcie poolingu w popularnych bibliotekach wygląda następująco:

  • SQLAlchemy – wbudowana pula w create_engine() z konfigurowalnymi parametrami;
  • psycopg2 – SimpleConnectionPool oraz ThreadedConnectionPool do PostgreSQL;
  • mysql-connector-python – wbudowany pooling, konfigurowany w connection stringu.

Rozmiar puli dobieraj empirycznie; często sprawdza się zakres 5–20, ale optymalną wartość wyznaczają testy obciążeniowe.

Strategia indeksowania

Indeksy to najskuteczniejsza pojedyncza metoda przyspieszenia zapytań. Indeksuj kolumny w WHERE, klucze obce i rozważ indeksy złożone. Pamiętaj o kompromisie: indeksy przyspieszają odczyt, ale spowalniają zapis.

Rozwiązania problemu N+1 zapytań

W Django problem N+1 można ograniczać przez select_related() (JOIN) i prefetch_related() (batch prefetch). Przykład zastosowania wygląda tak:

# Bez optymalizacji — potencjalny problem N+1
posts = Post.objects.all()
for post in posts:
print(post.author.name) # dodatkowe zapytanie dla każdego posta

# Z select_related() — jedno zoptymalizowane zapytanie z JOIN
posts = Post.objects.select_related('author')
for post in posts:
print(post.author.name) # brak dodatkowych zapytań

# Z prefetch_related() — dwa zapytania, ale zoptymalizowane dla relacji wielowartościowych
authors = Author.objects.prefetch_related('posts')
for author in authors:
for post in author.posts.all(): # brak dodatkowych zapytań
print(post.title)

W SQLAlchemy podobne efekty dają joinedload() (eager z JOIN) i selectinload() (batch loading). Świadomie steruj strategią ładowania relacji, aby unikać domyślnego lazy loading w krytycznych ścieżkach.

Buforowanie zapytań i optymalizacja wyników

Często odczytywane wyniki warto buforować (np. functools.lru_cache, Redis, Memcached) z właściwą inwalidacją cache. Pobieraj tylko potrzebne kolumny i stosuj stronicowanie dużych zbiorów.

Migracje bazy danych i zarządzanie schematem

Zarządzanie ewolucją schematu

Aplikacje ewoluują, a wraz z nimi schemat bazy. Migracje umożliwiają wersjonowanie zmian, ich śledzenie i odwracanie. W Django migracje są wbudowane (makemigrations, migrate), a w ekosystemie SQLAlchemy używa się Alembic (alembic revision --autogenerate, alembic upgrade head).

Bezpieczne praktyki migracji

Oddziel generowanie migracji od ich stosowania, trzymaj je w VCS i poddawaj code review. W SQLite ograniczone ALTER TABLE komplikuje m.in. dodanie kolumny NOT NULL bez wartości domyślnej — zapewnij dane domyślne lub obejścia. Projektuj migracje odwracalne i ostrożnie planuj długie, potencjalnie blokujące zmiany.

Praktyczne wzorce implementacji

Podstawowe operacje CRUD z użyciem surowego SQL

Fundamentalne operacje — tworzenie, odczyt, aktualizacja, usuwanie — realizuje się jawnie w surowym SQL. Przykłady przedstawiono poniżej:

Tworzenie rekordów (INSERT) z parametryzacją:

cursor.execute('INSERT INTO users (name, email) VALUES (?, ?)', ('John Doe', '[email protected]'))
conn.commit()

Odczyt (SELECT) z filtrem i konwersją do słowników (z wykorzystaniem sqlite3.Row):

conn.row_factory = sqlite3.Row
cursor = conn.cursor()
cursor.execute('SELECT id, name, email FROM users WHERE age > ?', (18,))
adult_users = [dict(row) for row in cursor.fetchall()]

Aktualizacja (UPDATE) konkretnych rekordów:

cursor.execute('UPDATE users SET email = ? WHERE id = ?', ('[email protected]', user_id))
conn.commit()

Usuwanie (DELETE) wybranych rekordów:

cursor.execute('DELETE FROM users WHERE id = ?', (user_id,))
conn.commit()

Definiowanie modeli ORM i relacji

Modele w Django ORM dziedziczą po django.db.models.Model, a relacje wyraża się m.in. przez ForeignKey i ManyToManyField:

from django.db import models

class Author(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField()

class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='posts')
published_date = models.DateTimeField(auto_now_add=True)

Modele w SQLAlchemy korzystają z bazy deklaratywnej i definiują kolumny oraz relacje:

from sqlalchemy import Column, Integer, String, DateTime, ForeignKey
from sqlalchemy.orm import relationship, DeclarativeBase
from datetime import datetime

class Base(DeclarativeBase):
pass

class Author(Base):
__tablename__ = "authors"
id = Column(Integer, primary_key=True)
name = Column(String(100))
email = Column(String(120))
posts = relationship("Post", back_populates="author")

class Post(Base):
__tablename__ = "posts"
id = Column(Integer, primary_key=True)
title = Column(String(200))
content = Column(String)
author_id = Column(Integer, ForeignKey("authors.id"))
author = relationship("Author", back_populates="posts")
published_date = Column(DateTime, default=datetime.now)

Strategie zarządzania transakcjami

Transakcje grupują wiele operacji w niepodzielną całość. W Django domyślnie działa autocommit, ale @transaction.atomic pozwala łączyć operacje w jedną transakcję:

from django.db import transaction

@transaction.atomic
def transfer_funds(from_account, to_account, amount):
from_account.balance -= amount
from_account.save()
to_account.balance += amount
to_account.save()
# Obie aktualizacje zatwierdzą się razem lub zostaną wycofane przy wyjątku

W SQLAlchemy transakcje obsługuje Session; operacje w obrębie sesji zatwierdza się jawnie:

from sqlalchemy.orm import Session

with Session(engine) as session:
account1 = session.get(Account, account1_id)
account2 = session.get(Account, account2_id)
account1.balance -= amount
account2.balance += amount
session.commit() # jawny commit

Zrozumienie semantyki transakcji jest kluczowe, gdy wiele operacji musi się udać lub nie udać jako całość.

Kiedy używać ORM, budowniców zapytań i surowego SQL

Decyzję o wyborze warstwy dostępu do danych warto podejmować kontekstowo. Ogólne wskazówki są następujące:

  • ORM – standardowy CRUD na dobrze zdefiniowanych modelach, szybki rozwój, migracje i bezpieczeństwo;
  • budowniców zapytań – złożone zapytania i raporty przy zachowaniu przenośności i bezpieczeństwa parametrów;
  • surowego SQL – krytyczne, wysoko wydajne fragmenty, funkcje okienkowe, rozszerzenia specyficzne dla silnika.

Najlepsze praktyki i rekomendacje

Skuteczna praca z bazą wymaga doboru narzędzi do kontekstu. W praktyce wiele aplikacji stosuje hybrydę: ORM do większości operacji i surowy SQL w krytycznych miejscach.

Utrzymuj ciągły monitoring wydajności (profilery, slow query logi, APM), eliminuj typowe pułapki ORM (np. N+1) przez eager loading i batch fetching. Bezpieczeństwo traktuj priorytetowo: parametryzuj każde zapytanie, nie trzymaj sekretów w kodzie i łącz się na najmniejszych uprawnieniach.

Powiązane wpisy:

  1. Język programowania Transact-SQL – zarządzanie bazami danych i optymalizacja zapytań
  2. Język programowania SQL – historia, standardy i zastosowania w różnych branżach
  3. Język programowania PL/SQL – rozszerza możliwości SQL i wspiera tworzenie aplikacji bazodanowych
  4. Co to jest MySQL? Jak działa i jakie funkcje oferuje system zarządzania relacyjnymi bazami danych?
Podziel się artykułem
Facebook Kopiuj link Drukuj
przezMiłosz Kenig
Follow:
Miłosz Kenig to absolwent informatyki na Politechnice Warszawskiej, który po ukończeniu studiów zdobył ponad 6 lat doświadczenia zawodowego jako programista full-stack w kilku firmach technologicznych. W swojej karierze pracował z szerokim spektrum technologii, sprawnie poruszając się między 5 różnymi językami programowania, w tym Java, Python i JavaScript. Jako autor tekstów na blogu P-programowanie.pl, Miłosz wykorzystuje swoje praktyczne doświadczenie zdobyte przy realizacji ponad 15 komercyjnych projektów technologicznych.
Poprzedni Ręka człowieka pracuje na tablecie z komputerem w tle. Technologia. WebSockets – komunikacja w czasie rzeczywistym w aplikacjach webowych
Brak komentarzy

Dodaj komentarz Anuluj pisanie odpowiedzi

Twój adres email nie zostanie opublikowany. Wymagane pola są oznaczone *


- Reklama -
Opanuj dowolny język programowaniaOpanuj dowolny język programowania
Najnowsze
Słowo platforma i uśmiechnięta bizneswoman myśli przeciw futurystycznemu czarnemu i błękitnemu tłu
Jak pracować z bazami danych w Pythonie – ORM i surowe zapytania
2026-08-03
Ręka człowieka pracuje na tablecie z komputerem w tle. Technologia.
WebSockets – komunikacja w czasie rzeczywistym w aplikacjach webowych
2026-07-23
Mężczyzna używający klawiatury komputera do wyszukiwania punktu lokalizacji informacji
Programowanie funkcyjne w praktyce – map, filter i reduce
2026-07-11
Podwójna ekspozycja wykresu forex i przestrzeni roboczej z komputerem Koncepcja międzynarodowego handlu online
Kolejki komunikatów – RabbitMQ i Kafka w architekturze rozproszonej
2026-07-10
Kaukaskie męskie ramiona na biurku
Jak stworzyć portfolio programisty – praktyczne wskazówki
2026-07-06

P-programowanie

Darmowa wiedza o programowaniu dla każdego.

Przeczytaj też

Język programowania
Języki programowania

Język programowania Bash w automatyzacji zadań i zarządzaniu systemami

17 min. czytania
MacBook Pro with images of computer language codes
Porady

Co to jest MySQL? Jak działa i jakie funkcje oferuje system zarządzania relacyjnymi bazami danych?

19 min. czytania
Język programowania
Języki programowania

Język programowania LabVIEW – jak wspiera tworzenie aplikacji kontrolno-pomiarowych?

26 min. czytania
Język programowania
Języki programowania

Język programowania Java – wszechstronność, popularne frameworki i perspektywy na rynku pracy

19 min. czytania

Twoja wiedza o programowaniu

Szczerze o programowaniu dla każdego.
P-programowanie P-programowanie

O programowaniu bez tajemnic. Blog informacjami, poradnikami, przeglądami dla obecnych i przyszłych programistów.

Strony

  • Strona główna
  • O P-programowanie
  • Polityka prywatności
  • Kontakt

Kategorie

  • Języki programowania
  • Nauka i praca
  • Porady
  • Więcej niż programowanie

100+ języków programowania

Poznaj ponad setkę najpopularniejszych języków programowania w na świecie.
Języki programowania
Welcome Back!

Sign in to your account

Username or Email Address
Password

Lost your password?