Psycopg: Cursor & Row Factory
Postingan terakhir mengenai cursor factory di sqlite bikin saya penasaran dengan fitur serupa tapi untuk database postgre kebetulan kerjaan saya menggunakan postgre, jadi, munculah tulisan ini.
Saat mencari referensi saya menemukan di tutorial di internet banyak yang masih mengunakan psycopg2 padahal psycopg3 sudah rilis, jadi daripada pusing coba saja dulu dua-duanya.
Kode Awal
Psycopg2
kode awal saya buat seperti ini
import psycopg2
# Connect to PostgreSQL database
conn = psycopg2.connect(
dbname="dbnames",
user="users",
password="passwords",
host="localhost",
port="5432",
)
cursor = conn.cursor()
query = "SELECT id, isbn, title FROM books"
cursor.execute(query)
rows = cursor.fetchall()
for r in rows:
print(f"{r[0]} - {r[1]} - {r[2]}")
# Result
1 - 4449623391 - Book 5210 by Author 5
2 - 1717473490 - Book 1800 by Author 5
3 - 1117078207 - Book 4719 by Author 2
4 - 4022514182 - Book 4644 by Author 2
5 - 1063894849 - Book 517 by Author 4
Dictionary
Seperti argumen di tulisan sebelumnya, menggunakan nomor untuk menampilkan data bukanlah hal yang menyenangkan jadi mari ubah agar setidaknya bisa mengakses nilai menggunakan nama field.
Ubah kode menjadi seperti berikut
import psycopg2
from psycopg2 import extras # Tambahkan ini
# Connect to PostgreSQL database
conn = psycopg2.connect(
dbname="dbnames",
user="users",
password="passwords",
host="localhost",
port="5432",
)
cursor = conn.cursor(cursor_factory=extras.DictCursor) #ubah jadi seperti init
query = "SELECT id, isbn, title FROM books"
cursor.execute(query)
rows = cursor.fetchall()
for r in rows:
print(f"{r['id']} - {r['isbn']} - {r['title']}")
Terdapat dua perbedaan:
- Saya melakukan import extras
- berbeda dengan sqlite di psycopg2 diberi nama cursor_factory
Jika di sqlite saya bisa menemukan untuk mengubah hasil query menjadi objek dataclass di sini saya tidak menemukannya tapi ada hal yang bisa menghasilkan bagaimana hasil dari query bisa diakses seperti book.title
yaitu dengan cara NamedTuplleCursor
.
NamedTuppleCursor
Untuk mengubah cara di atas menjadi namedTuplle
cukup bagian ini yang diubah
cursor = conn.cursor(cursor_factory=extras.NamedTupleCursor)
.
.
.
for r in rows:
print(f"{r.id} - {r.isbn} - {r.title}")
Kembali dengan 3 cara di atas saya bandingkan mendapatkan hasil
namedtuple
64437 function calls (63610 primitive calls) in 0.097 seconds
dictionary
194371 function calls (193544 primitive calls) in 0.218 seconds
default
34365 function calls (33538 primitive calls) in 0.085 seconds
Psycopg3
Ubah kode awal hanya saja saya menggunakan Psycopg3 dibanding 2.
import psycopg
# Connect to PostgreSQL database
conn = psycopg.connect(
dbname="dbanames",
user="users",
password="passwords",
host="localhost",
port="5432",
)
cursor = conn.cursor()
query = "SELECT id, isbn, title FROM books"
cursor.execute(query)
rows = cursor.fetchall()
for r in rows:
print(f"{r[0]} - {r[1]} - {r[2]}")
Bisa dilihat penggunaan default hampir tidak ada bedanya kecuali yang sebelumnya bernama psycopg2 yang ini pscopg
Serupa dengan versi sqlite, di sini namanya adalah row_factory untuk memanipulasi hasil dari sql.
dict_row
from psycopg.rows import dict_row # tambahkan ini
.
.
.
.
cursor = conn.cursor(row_factory=dict_row) # ubah jadi seperti ini
.
.
.
.
#panggil seperti ini
print(f"{r['id']} - {r['isbn']} - {r['title']}")
Dataclass
Penggunaan dataclass di psycopg lebih simple dibanding sqlite, jika di sqlite perlu membuat converter-nya terlebih dahulu di psycopg tidak perlu, cukup kirimkan objek dataclass ke dalam classrow
from psycopg.rows import class_row
from dataclasses import dataclass
@dataclass
class Books:
id: int
isbn: str
title: str
cursor = conn.cursor(row_factory=class_row(Books))
print(f"{r.id} - {r.isbn} - {r.title}")
Perbandingan
Default
78624 function calls (275568 primitive calls) in 0.521 seconds
Dict_row
288642 function calls (285586 primitive calls) in 0.578 seconds
class_row/Dataclass
288983 function calls (285923 primitive calls) in 0.569 seconds
Referensi