Python adalah bahasa yang paling sering dipadukan dengan PostgreSQL untuk analisis data, backend API, sampai skrip operasional — dan psycopg adalah driver yang menjadi jembatannya. Berbeda dengan dua chapter sebelumnya yang berjalan dokumentatif, lingkungan latihan kursus ini memasang psycopg 3 sungguhan, sehingga seluruh contoh chapter ini benar-benar dieksekusi melawan PostgreSQL 17 dan output-nya direkam apa adanya. Kita akan mulai dari koneksi pertama dan pembuatan tabel produk Toko Maju Jaya dari dalam Python, lanjut ke query berparameter %s, baris berbentuk dictionary, transaksi dengan context manager, sisip massal executemany, dan ditutup dengan penanganan error berbasis SQLSTATE.
Instalasi psycopg #
psycopg 3 dipasang dari PyPI; paket psycopg[binary] membawa wheel yang sudah memuat pustaka binary sehingga tidak perlu compiler:
pip install "psycopg[binary]"Paket inti bernama psycopg (tanpa angka) dan bisa diimpor sebagai import psycopg. Versi 3 menggantikan psycopg 2 yang legendaris dengan arsitektur baru, tetapi sebagian besar pola dasarnya — connect, cursor, execute — tetap dikenali. Seluruh contoh di bawah diuji dengan psycopg 3.3.
Koneksi Pertama: Kata Kunci dan Variabel Environment #
psycopg.connect() menerima parameter koneksi sebagai kata kunci, sebagai satu string conninfo ala libpq, atau keduanya sekaligus. Contoh-contoh chapter ini memakai bentuk kata kunci dan membaca nilai dari variabel environment PG* — konvensi libpq yang sama dengan psql, sehingga kode berjalan tanpa perubahan di lingkungan latihan maupun di server Anda. File pertama membuat tabel produk dan mengisinya lewat executemany:
import os
import psycopg
# Parameter koneksi mengikuti variabel environment PG* -- konvensi
# libpq yang sama dengan psql. Sesuaikan untuk server Anda; kata
# sandi cukup diberikan lewat variabel environment PGPASSWORD.
KONEKSI = dict(
host=os.environ.get("PGHOST", "localhost"),
port=os.environ.get("PGPORT", "5432"),
user=os.environ.get("PGUSER", "postgres"),
dbname=os.environ.get("PGDATABASE", "toko_db"),
)
# Data awal produk Toko Maju Jaya untuk seluruh contoh chapter ini.
PRODUK_AWAL = [
(1, "Mouse Kantor Ergonomis", 85000, 25),
(2, "Papan Ketik Mekanik", 750000, 12),
(3, "Monitor 24 inci IPS", 1850000, 8),
(4, "Kabel HDMI 2 meter", 65000, 40),
]
# Blok `with` menutup koneksi di akhir; keluar tanpa error
# berarti seluruh pernyataan di dalamnya di-commit.
with psycopg.connect(**KONEKSI) as conn:
conn.execute("DROP TABLE IF EXISTS produk")
conn.execute(
"""
CREATE TABLE produk (
id integer PRIMARY KEY,
nama text NOT NULL,
harga bigint NOT NULL CHECK (harga >= 0),
stok integer NOT NULL DEFAULT 0
)
"""
)
cur = conn.cursor()
cur.executemany(
"INSERT INTO produk (id, nama, harga, stok) VALUES (%s, %s, %s, %s)",
PRODUK_AWAL,
)
print("Tabel produk dibuat,", len(PRODUK_AWAL), "baris terisi")
# Verifikasi: baca kembali isi tabel dari koneksi kedua.
with psycopg.connect(**KONEKSI) as conn:
cur = conn.execute("SELECT id, nama, harga FROM produk ORDER BY id")
for id_, nama, harga in cur.fetchall():
print(f" {id_} {nama} Rp{harga:,}".replace(",", "."))Output yang Dihasilkan:
Tabel produk dibuat, 4 baris terisi
1 Mouse Kantor Ergonomis Rp85.000
2 Papan Ketik Mekanik Rp750.000
3 Monitor 24 inci IPS Rp1.850.000
4 Kabel HDMI 2 meter Rp65.000Mari kita bedah bagian-bagian pentingnya:
KONEKSI = dict(...)— kata sandi sengaja tidak ditulis di kode; psycopg membacanya dariPGPASSWORDbila variabel itu ada, sama sepertipsql.with psycopg.connect(...) as conn:— psycopg membuka transaksi otomatis pada pernyataan pertama; keluar dari blok tanpa exception menjalankancommit(), keluar karena exception menjalankanrollback()lalu menutup koneksi. DDL sepertiCREATE TABLEikut menikmati jaminan itu.conn.execute(...)— pintasan untuk kursor sekali pakai; psycopg 3 membuat cursor implisit dan mengeksekusinya langsung.cur.executemany(...)— satu template SQL dengan%s, dijalankan berulang untuk setiap elemenPRODUK_AWAL; seluruh baris masuk dalam satu transaksi.- Blok kedua (
withkedua) — koneksi baru terpisah; baris yang terbaca membuktikan transaksi blok pertama benar-benar ter-commit.
Query dengan Parameter %s #
Placeolder psycopg adalah %s, bukan ? ala SQLite atau $1 ala PostgreSQL murni — psycopg menerjemahkan %s menjadi parameter bernomor $n saat berbicara dengan server, sehingga mekanisme pemisahan struktur-dan-datanya tetap yang sama. Nilai parameter dikirim sebagai tuple di argumen kedua execute:
import os
import psycopg
# Parameter koneksi mengikuti variabel environment PG* --
# konvensi libpq yang sama dengan psql.
KONEKSI = dict(
host=os.environ.get("PGHOST", "localhost"),
port=os.environ.get("PGPORT", "5432"),
user=os.environ.get("PGUSER", "postgres"),
dbname=os.environ.get("PGDATABASE", "toko_db"),
)
with psycopg.connect(**KONEKSI) as conn:
# 1) Parameter %s: nilai dikirim terpisah dari teks SQL.
cur = conn.execute(
"SELECT id, nama, harga FROM produk WHERE harga <= %s ORDER BY id",
(200000,),
)
print("Produk dengan harga di bawah Rp200.000:")
for id_, nama, harga in cur.fetchall():
print(f" {id_}. {nama} Rp{harga:,}".replace(",", "."))
# 2) fetchone: mengambil satu baris hasil, cocok untuk count.
cur = conn.execute("SELECT count(*) FROM produk")
print("Jumlah produk:", cur.fetchone()[0])
# 3) Iterasi langsung atas cursor: baris dibaca satu per satu.
cur = conn.execute(
"SELECT nama FROM produk WHERE stok >= %s ORDER BY id", (20,)
)
print("Produk dengan stok >= 20:")
for (nama,) in cur:
print(" ", nama)Output yang Dihasilkan:
Produk dengan harga di bawah Rp200.000:
1. Mouse Kantor Ergonomis Rp85.000
4. Kabel HDMI 2 meter Rp65.000
Jumlah produk: 4
Produk dengan stok >= 20:
Mouse Kantor Ergonomis
Kabel HDMI 2 meterTiga cara membaca hasil tampil di atas: fetchall() mengambil seluruh baris ke memori sekaligus, fetchone() mengambil satu baris (kolom tunggal count(*) dibaca lewat indeks [0]), dan iterasi langsung atas cursor membaca baris satu per satu — pilihan tepat untuk hasil besar karena baris tidak menumpuk di memori aplikasi. Perhatikan juga tuple satu elemen ditulis (200000,) dengan koma; tanpa koma, Python membacanya sebagai ekspresi biasa, bukan tuple.
Baris sebagai Dictionary: dict_row #
Secara default setiap baris berupa tuple posisional. Kolom diakses lewat indeks bisa salah baca saat query diubah; untuk itu psycopg menyediakan row factory, dan dict_row mengubah hasil menjadi dictionary yang kuncinya nama kolom:
import os
import psycopg
from psycopg.rows import dict_row
# Parameter koneksi mengikuti variabel environment PG* --
# konvensi libpq yang sama dengan psql.
KONEKSI = dict(
host=os.environ.get("PGHOST", "localhost"),
port=os.environ.get("PGPORT", "5432"),
user=os.environ.get("PGUSER", "postgres"),
dbname=os.environ.get("PGDATABASE", "toko_db"),
)
# row_factory=dict_row: setiap baris berupa dictionary {kolom: nilai},
# bukan tuple posisional. Kolom dipanggil lewat namanya.
with psycopg.connect(**KONEKSI, row_factory=dict_row) as conn:
cur = conn.execute(
"SELECT nama, harga, stok FROM produk WHERE stok < %s ORDER BY id",
(20,),
)
print("Produk dengan stok di bawah 20 (dictionary):")
for baris in cur.fetchall():
teks = f" {baris['nama']}: Rp{baris['harga']:,} (stok {baris['stok']})"
print(teks.replace(",", "."))Output yang Dihasilkan:
Produk dengan stok di bawah 20 (dictionary):
Papan Ketik Mekanik: Rp750.000 (stok 12)
Monitor 24 inci IPS: Rp1.850.000 (stok 8)Parameter row_factory dipasang saat koneksi dibuat sehingga seluruh cursor mewarisinya; psycopg juga menyediakan namedtuple_row untuk akses baris.nama yang lebih hemat ketikan. Ketika query hanya memilih beberapa kolom dari join kompleks, akses bernama seperti baris['nama'] jauh lebih tahan salah urutan daripada baris[0].
Transaksi dan Context Manager #
psycopg tidak punya perintah “mulai transaksi” yang harus dipanggil manual: transaksi dimulai otomatis pada pernyataan pertama, dan conn.commit() atau conn.rollback() menutupnya. Context manager with membungkus siklus itu — inilah perbedaan paling terasa dibanding psycopg 2. File berikut menjalankan dua transaksi: yang pertama sukses dan ter-commit, yang kedua gagal di tengah jalan dan dibatalkan seluruhnya:
import os
import psycopg
from psycopg import errors
# Parameter koneksi mengikuti variabel environment PG* --
# konvensi libpq yang sama dengan psql.
KONEKSI = dict(
host=os.environ.get("PGHOST", "localhost"),
port=os.environ.get("PGPORT", "5432"),
user=os.environ.get("PGUSER", "postgres"),
dbname=os.environ.get("PGDATABASE", "toko_db"),
)
# Transaksi 1: dua perubahan stok yang harus terjadi bersama-sama
# (pindahan barang dari produk 2 ke produk 1).
with psycopg.connect(**KONEKSI) as conn:
conn.execute("UPDATE produk SET stok = stok - 2 WHERE id = 2")
conn.execute("UPDATE produk SET stok = stok + 2 WHERE id = 1")
# Keluar dari blok `with` tanpa error -> commit otomatis.
print("Transaksi 1 di-commit: stok produk 1 dan 2 berubah bersamaan")
# Transaksi 2: error di tengah jalan. Exception lolos dari blok
# `with` -> psycopg menjalankan rollback sebelum menutup koneksi,
# sehingga UPDATE pertama ikut dibatalkan.
try:
with psycopg.connect(**KONEKSI) as conn:
conn.execute("UPDATE produk SET stok = stok - 5 WHERE id = 3")
conn.execute(
"INSERT INTO produk (id, nama, harga, stok)"
" VALUES (%s, %s, %s, %s)",
(1, "Produk Duplikat", 100000, 1),
)
except errors.UniqueViolation:
print("Transaksi 2 dibatalkan: INSERT duplikat, UPDATE ikut batal")
# Bukti akhir: hanya perubahan transaksi 1 yang tersimpan.
with psycopg.connect(**KONEKSI) as conn:
cur = conn.execute(
"SELECT id, stok FROM produk WHERE id IN (1, 2, 3) ORDER BY id"
)
print("Stok akhir (id, stok):", cur.fetchall())Output yang Dihasilkan:
Transaksi 1 di-commit: stok produk 1 dan 2 berubah bersamaan
Transaksi 2 dibatalkan: INSERT duplikat, UPDATE ikut batal
Stok akhir (id, stok): [(1, 27), (2, 10), (3, 8)]Baca outputnya baris demi baris. Transaksi 1 memindahkan 2 unit stok: produk 1 naik dari 25 ke 27, produk 2 turun dari 12 ke 10 — keduanya terjadi bersama-sama. Transaksi 2 gagal karena INSERT memakai id = 1 yang sudah ada; UPDATE yang menurunkan stok produk 3 sebelumnya ikut dibatalkan, terbukti dari stok produk 3 yang tetap 8. Pola try/except di sekeliling blok with bukan hiasan: exception harus tertangkap agar program bisa melanjutkan, sementara rollback-nya sudah ditangani context manager.
Sisip Massal dengan executemany #
Memasukkan banyak baris dengan loop execute satu-satu berarti bolak-balik pesan ke server. executemany mengirim satu template untuk seluruh daftar baris dalam satu transaksi:
import os
import psycopg
# Parameter koneksi mengikuti variabel environment PG* --
# konvensi libpq yang sama dengan psql.
KONEKSI = dict(
host=os.environ.get("PGHOST", "localhost"),
port=os.environ.get("PGPORT", "5432"),
user=os.environ.get("PGUSER", "postgres"),
dbname=os.environ.get("PGDATABASE", "toko_db"),
)
# Pengiriman baru Toko Maju Jaya: satu daftar baris, satu panggilan
# executemany -- semuanya berjalan dalam satu transaksi.
PRODUK_BARU = [
(81, "Mouse Wireless Senyap", 185000, 15),
(82, "Webcam 1080p", 420000, 6),
(83, "Headset dengan Mikrofon", 350000, 9),
(84, "Lampu Meja LED", 120000, 20),
]
with psycopg.connect(**KONEKSI) as conn:
cur = conn.cursor()
cur.executemany(
"INSERT INTO produk (id, nama, harga, stok) VALUES (%s, %s, %s, %s)",
PRODUK_BARU,
)
print("executemany memasukkan", len(PRODUK_BARU), "baris dalam satu transaksi")
cur.execute("SELECT id, nama FROM produk WHERE id >= 80 ORDER BY id")
print("Isi tabel sekarang (id >= 80):")
for id_, nama in cur.fetchall():
print(f" {id_} {nama}")Output yang Dihasilkan:
executemany memasukkan 4 baris dalam satu transaksi
Isi tabel sekarang (id >= 80):
81 Mouse Wireless Senyap
82 Webcam 1080p
83 Headset dengan Mikrofon
84 Lampu Meja LEDMenangani Error: errors dan SQLSTATE #
Setiap galat dari server tiba sebagai exception di modul psycopg.errors, satu kelas per SQLSTATE — UniqueViolation untuk 23505, ForeignKeyViolation untuk 23503, dan seterusnya. Objek error membawa sqlstate serta diag (pesan detail, nama constraint) untuk keputusan yang presisi:
import os
import psycopg
from psycopg import errors
# Parameter koneksi mengikuti variabel environment PG* --
# konvensi libpq yang sama dengan psql.
KONEKSI = dict(
host=os.environ.get("PGHOST", "localhost"),
port=os.environ.get("PGPORT", "5432"),
user=os.environ.get("PGUSER", "postgres"),
dbname=os.environ.get("PGDATABASE", "toko_db"),
)
with psycopg.connect(**KONEKSI) as conn:
cur = conn.cursor()
# INSERT pertama: sah, masih dalam transaksi yang berjalan.
cur.execute(
"INSERT INTO produk (id, nama, harga, stok) VALUES (%s, %s, %s, %s)",
(90, "Docking Station USB-C", 950000, 5),
)
print("INSERT id=90: sukses")
# INSERT kedua memakai id yang sama: server menolak, psycopg
# menerjemahkan SQLSTATE 23505 menjadi errors.UniqueViolation.
try:
cur.execute(
"INSERT INTO produk (id, nama, harga, stok) VALUES (%s, %s, %s, %s)",
(90, "Docking Station USB-C", 950000, 5),
)
except errors.UniqueViolation as e:
print("INSERT ulang id=90: ditolak server")
print(" sqlstate :", e.sqlstate)
print(" pesan :", e.diag.message_primary)
print(" constraint:", e.diag.constraint_name)
# rollback membatalkan transaksi (termasuk INSERT id=90
# yang tadinya sukses) dan mengembalikan koneksi ke kondisi
# sehat sehingga bisa dipakai untuk transaksi baru.
conn.rollback()
# Transaksi baru pada koneksi yang sama.
cur.execute(
"INSERT INTO produk (id, nama, harga, stok) VALUES (%s, %s, %s, %s)",
(91, "Mouse Pad Besar", 95000, 30),
)
print("Setelah rollback, transaksi baru berjalan normal")
# Verifikasi: hanya baris dari transaksi kedua yang tersimpan.
with psycopg.connect(**KONEKSI) as conn:
cur = conn.execute("SELECT id, nama FROM produk WHERE id >= 90 ORDER BY id")
print("Baris dengan id >= 90 yang tersimpan:")
for id_, nama in cur.fetchall():
print(f" {id_} {nama}")
# lookup: mencari kelas error dari kode SQLSTATE, berguna saat
# kode error baru saja diketahui dari log server.
for kode in ("23505", "23503"):
print(f"errors.lookup({kode!r}) ->", errors.lookup(kode).__name__)Output yang Dihasilkan:
INSERT id=90: sukses
INSERT ulang id=90: ditolak server
sqlstate : 23505
pesan : duplicate key value violates unique constraint "produk_pkey"
constraint: produk_pkey
Setelah rollback, transaksi baru berjalan normal
Baris dengan id >= 90 yang tersimpan:
91 Mouse Pad Besar
errors.lookup('23505') -> UniqueViolation
errors.lookup('23503') -> ForeignKeyViolationAda dua pelajaran penting di output itu. Pertama, INSERT id=90 yang tadinya sukses ikut lenyap — rollback membatalkan seluruh transaksi, bukan hanya pernyataan yang gagal; itulah arti atomicty yang dibahas di chapter transaksi. Kedua, setelah rollback() koneksi kembali sehat dan transaksi baru berjalan normal pada koneksi yang sama — tanpa rollback, setiap pernyataan berikutnya akan ditolak dengan pesan current transaction is aborted sampai transaksi itu ditutup. Fungsi errors.lookup() bekerja ke arah sebaliknya: dari kode SQLSTATE yang terbaca di log, temukan kelasnya untuk ditangkap.
Python dengan psycopg kini tersambung penuh ke PostgreSQL. Berikutnya bahasa JavaScript di sisi server: Node.js dengan paket node-postgres — parameter $1 yang itu, transaksi manual BEGIN/COMMIT, dan object error yang membawa SQLSTATE yang sama.
Referensi #
- psycopg 3 — dokumentasi
- Basic module usage — koneksi dan transaksi
- Passing parameters to SQL queries
- psycopg.errors — exception classes
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
buat-tabel.py
Unduh berkasimport os
import psycopg
# Parameter koneksi mengikuti variabel environment PG* -- konvensi
# libpq yang sama dengan psql. Sesuaikan untuk server Anda; kata
# sandi cukup diberikan lewat variabel environment PGPASSWORD.
KONEKSI = dict(
host=os.environ.get("PGHOST", "localhost"),
port=os.environ.get("PGPORT", "5432"),
user=os.environ.get("PGUSER", "postgres"),
dbname=os.environ.get("PGDATABASE", "toko_db"),
)
# Data awal produk Toko Maju Jaya untuk seluruh contoh chapter ini.
PRODUK_AWAL = [
(1, "Mouse Kantor Ergonomis", 85000, 25),
(2, "Papan Ketik Mekanik", 750000, 12),
(3, "Monitor 24 inci IPS", 1850000, 8),
(4, "Kabel HDMI 2 meter", 65000, 40),
]
# Blok `with` menutup koneksi di akhir; keluar tanpa error
# berarti seluruh pernyataan di dalamnya di-commit.
with psycopg.connect(**KONEKSI) as conn:
conn.execute("DROP TABLE IF EXISTS produk")
conn.execute(
"""
CREATE TABLE produk (
id integer PRIMARY KEY,
nama text NOT NULL,
harga bigint NOT NULL CHECK (harga >= 0),
stok integer NOT NULL DEFAULT 0
)
"""
)
cur = conn.cursor()
cur.executemany(
"INSERT INTO produk (id, nama, harga, stok) VALUES (%s, %s, %s, %s)",
PRODUK_AWAL,
)
print("Tabel produk dibuat,", len(PRODUK_AWAL), "baris terisi")
# Verifikasi: baca kembali isi tabel dari koneksi kedua.
with psycopg.connect(**KONEKSI) as conn:
cur = conn.execute("SELECT id, nama, harga FROM produk ORDER BY id")
for id_, nama, harga in cur.fetchall():
print(f" {id_} {nama} Rp{harga:,}".replace(",", "."))
dict-row.py
Unduh berkasimport os
import psycopg
from psycopg.rows import dict_row
# Parameter koneksi mengikuti variabel environment PG* --
# konvensi libpq yang sama dengan psql.
KONEKSI = dict(
host=os.environ.get("PGHOST", "localhost"),
port=os.environ.get("PGPORT", "5432"),
user=os.environ.get("PGUSER", "postgres"),
dbname=os.environ.get("PGDATABASE", "toko_db"),
)
# row_factory=dict_row: setiap baris berupa dictionary {kolom: nilai},
# bukan tuple posisional. Kolom dipanggil lewat namanya.
with psycopg.connect(**KONEKSI, row_factory=dict_row) as conn:
cur = conn.execute(
"SELECT nama, harga, stok FROM produk WHERE stok < %s ORDER BY id",
(20,),
)
print("Produk dengan stok di bawah 20 (dictionary):")
for baris in cur.fetchall():
teks = f" {baris['nama']}: Rp{baris['harga']:,} (stok {baris['stok']})"
print(teks.replace(",", "."))
error-handling.py
Unduh berkasimport os
import psycopg
from psycopg import errors
# Parameter koneksi mengikuti variabel environment PG* --
# konvensi libpq yang sama dengan psql.
KONEKSI = dict(
host=os.environ.get("PGHOST", "localhost"),
port=os.environ.get("PGPORT", "5432"),
user=os.environ.get("PGUSER", "postgres"),
dbname=os.environ.get("PGDATABASE", "toko_db"),
)
with psycopg.connect(**KONEKSI) as conn:
cur = conn.cursor()
# INSERT pertama: sah, masih dalam transaksi yang berjalan.
cur.execute(
"INSERT INTO produk (id, nama, harga, stok) VALUES (%s, %s, %s, %s)",
(90, "Docking Station USB-C", 950000, 5),
)
print("INSERT id=90: sukses")
# INSERT kedua memakai id yang sama: server menolak, psycopg
# menerjemahkan SQLSTATE 23505 menjadi errors.UniqueViolation.
try:
cur.execute(
"INSERT INTO produk (id, nama, harga, stok) VALUES (%s, %s, %s, %s)",
(90, "Docking Station USB-C", 950000, 5),
)
except errors.UniqueViolation as e:
print("INSERT ulang id=90: ditolak server")
print(" sqlstate :", e.sqlstate)
print(" pesan :", e.diag.message_primary)
print(" constraint:", e.diag.constraint_name)
# rollback membatalkan transaksi (termasuk INSERT id=90
# yang tadinya sukses) dan mengembalikan koneksi ke kondisi
# sehat sehingga bisa dipakai untuk transaksi baru.
conn.rollback()
# Transaksi baru pada koneksi yang sama.
cur.execute(
"INSERT INTO produk (id, nama, harga, stok) VALUES (%s, %s, %s, %s)",
(91, "Mouse Pad Besar", 95000, 30),
)
print("Setelah rollback, transaksi baru berjalan normal")
# Verifikasi: hanya baris dari transaksi kedua yang tersimpan.
with psycopg.connect(**KONEKSI) as conn:
cur = conn.execute("SELECT id, nama FROM produk WHERE id >= 90 ORDER BY id")
print("Baris dengan id >= 90 yang tersimpan:")
for id_, nama in cur.fetchall():
print(f" {id_} {nama}")
# lookup: mencari kelas error dari kode SQLSTATE, berguna saat
# kode error baru saja diketahui dari log server.
for kode in ("23505", "23503"):
print(f"errors.lookup({kode!r}) ->", errors.lookup(kode).__name__)
executemany.py
Unduh berkasimport os
import psycopg
# Parameter koneksi mengikuti variabel environment PG* --
# konvensi libpq yang sama dengan psql.
KONEKSI = dict(
host=os.environ.get("PGHOST", "localhost"),
port=os.environ.get("PGPORT", "5432"),
user=os.environ.get("PGUSER", "postgres"),
dbname=os.environ.get("PGDATABASE", "toko_db"),
)
# Pengiriman baru Toko Maju Jaya: satu daftar baris, satu panggilan
# executemany -- semuanya berjalan dalam satu transaksi.
PRODUK_BARU = [
(81, "Mouse Wireless Senyap", 185000, 15),
(82, "Webcam 1080p", 420000, 6),
(83, "Headset dengan Mikrofon", 350000, 9),
(84, "Lampu Meja LED", 120000, 20),
]
with psycopg.connect(**KONEKSI) as conn:
cur = conn.cursor()
cur.executemany(
"INSERT INTO produk (id, nama, harga, stok) VALUES (%s, %s, %s, %s)",
PRODUK_BARU,
)
print("executemany memasukkan", len(PRODUK_BARU), "baris dalam satu transaksi")
cur.execute("SELECT id, nama FROM produk WHERE id >= 80 ORDER BY id")
print("Isi tabel sekarang (id >= 80):")
for id_, nama in cur.fetchall():
print(f" {id_} {nama}")
query-dasar.py
Unduh berkasimport os
import psycopg
# Parameter koneksi mengikuti variabel environment PG* --
# konvensi libpq yang sama dengan psql.
KONEKSI = dict(
host=os.environ.get("PGHOST", "localhost"),
port=os.environ.get("PGPORT", "5432"),
user=os.environ.get("PGUSER", "postgres"),
dbname=os.environ.get("PGDATABASE", "toko_db"),
)
with psycopg.connect(**KONEKSI) as conn:
# 1) Parameter %s: nilai dikirim terpisah dari teks SQL.
cur = conn.execute(
"SELECT id, nama, harga FROM produk WHERE harga <= %s ORDER BY id",
(200000,),
)
print("Produk dengan harga di bawah Rp200.000:")
for id_, nama, harga in cur.fetchall():
print(f" {id_}. {nama} Rp{harga:,}".replace(",", "."))
# 2) fetchone: mengambil satu baris hasil, cocok untuk count.
cur = conn.execute("SELECT count(*) FROM produk")
print("Jumlah produk:", cur.fetchone()[0])
# 3) Iterasi langsung atas cursor: baris dibaca satu per satu.
cur = conn.execute(
"SELECT nama FROM produk WHERE stok >= %s ORDER BY id", (20,)
)
print("Produk dengan stok >= 20:")
for (nama,) in cur:
print(" ", nama)
transaksi.py
Unduh berkasimport os
import psycopg
from psycopg import errors
# Parameter koneksi mengikuti variabel environment PG* --
# konvensi libpq yang sama dengan psql.
KONEKSI = dict(
host=os.environ.get("PGHOST", "localhost"),
port=os.environ.get("PGPORT", "5432"),
user=os.environ.get("PGUSER", "postgres"),
dbname=os.environ.get("PGDATABASE", "toko_db"),
)
# Transaksi 1: dua perubahan stok yang harus terjadi bersama-sama
# (pindahan barang dari produk 2 ke produk 1).
with psycopg.connect(**KONEKSI) as conn:
conn.execute("UPDATE produk SET stok = stok - 2 WHERE id = 2")
conn.execute("UPDATE produk SET stok = stok + 2 WHERE id = 1")
# Keluar dari blok `with` tanpa error -> commit otomatis.
print("Transaksi 1 di-commit: stok produk 1 dan 2 berubah bersamaan")
# Transaksi 2: error di tengah jalan. Exception lolos dari blok
# `with` -> psycopg menjalankan rollback sebelum menutup koneksi,
# sehingga UPDATE pertama ikut dibatalkan.
try:
with psycopg.connect(**KONEKSI) as conn:
conn.execute("UPDATE produk SET stok = stok - 5 WHERE id = 3")
conn.execute(
"INSERT INTO produk (id, nama, harga, stok)"
" VALUES (%s, %s, %s, %s)",
(1, "Produk Duplikat", 100000, 1),
)
except errors.UniqueViolation:
print("Transaksi 2 dibatalkan: INSERT duplikat, UPDATE ikut batal")
# Bukti akhir: hanya perubahan transaksi 1 yang tersimpan.
with psycopg.connect(**KONEKSI) as conn:
cur = conn.execute(
"SELECT id, stok FROM produk WHERE id IN (1, 2, 3) ORDER BY id"
)
print("Stok akhir (id, stok):", cur.fetchall())