Skip to content

Commit 5119daa

Browse files
committed
Getting very very close to postgres working! Just have to fix the nocase statement.
Out of time for a bit, just pushing these up as is for now
1 parent a557e06 commit 5119daa

File tree

2 files changed

+121
-3
lines changed

2 files changed

+121
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import PySimpleGUI as sg
2+
import pysimplesql as ss # <=== PySimpleSQL lines will be marked like this. There's only a few!
3+
import logging
4+
logger=logging.getLogger(__name__)
5+
logging.basicConfig(level=logging.INFO) # <=== You can set the logging level here (NOTSET,DEBUG,INFO,WARNING,ERROR,CRITICAL)
6+
7+
8+
# -------------------------
9+
# CREATE PYSIMPLEGUI LAYOUT
10+
# -------------------------
11+
# Define the columns for the table selector
12+
headings=['id','Date: ','Mood: ','Title: ']
13+
visible=[0,1,1,1] # Hide the id column
14+
layout=[
15+
[ss.selector('sel_journal','Journal',sg.Table,num_rows=10,headings=headings,visible_column_map=visible)],
16+
[ss.actions('act_journal','Journal')],
17+
[ss.record('Journal.entry_date')],
18+
[ss.record('Journal.mood_id', sg.Combo, label='My mood:', size=(30,10), auto_size_text=False)],
19+
[ss.record('Journal.title')],
20+
[ss.record('Journal.entry', sg.MLine, size=(71,20))]
21+
]
22+
win=sg.Window('Journal (external) example', layout, finalize=True)
23+
24+
elephant_postgres = {
25+
'host':'queenie.db.elephantsql.com',
26+
'user':'yunaahtj',
27+
'password':'OMX8u8CDKNVTrldLbnBFsUjxkArTg4Wj',
28+
'database':'yunaahtj'
29+
}
30+
31+
driver=ss.Postgres(**elephant_postgres)
32+
frm=ss.Form(driver, bind=win) #<=== Here is the magic!
33+
# Note: sql_script is only run if journal.frm does not exist! This has the effect of creating a new blank
34+
# database as defined by the sql_script file if the database does not yet exist, otherwise it will use the database!
35+
36+
# Reverse the default sort order so new journal entries appear at the top
37+
frm['Journal'].set_order_clause('ORDER BY entry_date DESC')
38+
# Set the column order for search operations. By default, only the column designated as the description column is searched
39+
frm['Journal'].set_search_order(['entry_date','title','entry'])
40+
41+
# ---------
42+
# MAIN LOOP
43+
# ---------
44+
while True:
45+
event, values = win.read()
46+
47+
if ss.process_events(event, values): # <=== let PySimpleSQL process its own events! Simple!
48+
logger.info(f'PySimpleDB event handler handled the event {event}!')
49+
elif event == sg.WIN_CLOSED or event == 'Exit':
50+
frm.close() # <= ensures proper closing of the sqlite database and runs a database optimization
51+
break
52+
else:
53+
logger.info(f'This event ({event}) is not yet handled.')
54+
win.close()
55+
56+
"""
57+
I hope that you enjoyed this simple demo of a Journal database.
58+
Without comments, this could have been done in about30 lines of code! Seriously - a full database-backed
59+
usable program! The combination of PySimpleSQL and PySimpleGUI is very fun, fast and powerful!
60+
61+
Learnings from this example:
62+
- Using Query.set_search_order() to set the search order of the query for search operations.
63+
- creating a default/empty database with an external sql script with the sql_script keyword argument to ss.Form()
64+
- using Form.record() and Form.selector() functions for easy GUI element creation
65+
- using the label keyword argument to Form.record() to define a custom label
66+
- using Tables as Form.selector() element type
67+
- changing the sort order of Queries
68+
"""

pysimplesql/pysimplesql.py

+53-3
Original file line numberDiff line numberDiff line change
@@ -2970,16 +2970,21 @@ def execute_script(self,script):
29702970
# --------------
29712971
try:
29722972
import psycopg2
2973+
import psycopg2.extras
29732974
except ModuleNotFoundError:
29742975
pass
2975-
class Postgre(SQLDriver):
2976+
2977+
class Postgres(SQLDriver):
29762978
def __init__(self,host,user,password,database,sql_script=None, sql_commands=None):
29772979
self.host = host
29782980
self.user = user
29792981
self.password = password
29802982
self.database = database
29812983
self.con = self.connect()
29822984

2985+
query = "CREATE COLLATION nocase (provider = icu, locale = 'und-u-ks-level2');"
2986+
self.execute(query)
2987+
29832988
if sql_commands is not None:
29842989
# run SQL script if the database does not yet exist
29852990
logger.info(f'Executing sql commands passed in')
@@ -3001,14 +3006,16 @@ def connect(self):
30013006
return con
30023007

30033008
def execute(self, query, values=None):
3004-
cursor = self.con.cursor(dictionary=True)
3009+
cursor = self.con.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
30053010
if values:
30063011
cursor.execute(query, values)
30073012
else:
30083013
cursor.execute(query)
3014+
if len(cursor) == 0: return
3015+
results = cursor.fetchall()
30093016

30103017
res=[]
3011-
for row in cursor:
3018+
for row in results:
30123019
res.append(row)
30133020

30143021
lastrowid=cursor.lastrowid if cursor.lastrowid else None
@@ -3025,6 +3032,49 @@ def close(self):
30253032
# Only do cleanup if this is not an imported database
30263033
self.con.close()
30273034

3035+
def table_names(self):
3036+
query = "SELECT table_name FROM information_schema.tables WHERE table_schema='public' AND table_type='BASE TABLE';"
3037+
#query = "SELECT tablename FROM pg_tables WHERE table_schema='public'"
3038+
rows = self.execute(query)
3039+
return [row['table_name'] for row in rows]
3040+
3041+
def column_names(self,table):
3042+
# Return a list of column names
3043+
query = f"SELECT column_name FROM information_schema.columns WHERE table_name = '{table}'"
3044+
rows = self.execute(query)
3045+
return [row['column_name'] for row in rows]
3046+
def pk_column(self,table):
3047+
query = f"SELECT column_name FROM information_schema.table_constraints tc JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name WHERE tc.constraint_type = 'PRIMARY KEY' AND tc.table_name = '{table}' "
3048+
cur = self.execute(query)
3049+
row = cur.fetchone()
3050+
return row['column_name'] if row else None
3051+
def relationships(self):
3052+
# Return a list of dicts {from_table,to_table,from_column,to_column,requery}
3053+
tables=self.table_names()
3054+
relationships = []
3055+
for from_table in tables:
3056+
query = f"SELECT conname, conrelid::regclass, confrelid::regclass, confupdtype, "
3057+
query += f"a1.attname AS column_name, a2.attname AS referenced_column_name "
3058+
query += f"FROM pg_constraint "
3059+
query += f"JOIN pg_attribute AS a1 ON conrelid = a1.attrelid AND a1.attnum = ANY(conkey) "
3060+
query += f"JOIN pg_attribute AS a2 ON confrelid = a2.attrelid AND a2.attnum = ANY(confkey) "
3061+
query += f"WHERE confrelid = '{from_table}'::regclass AND contype = 'f'"
3062+
3063+
rows=self.execute(query, (from_table,))
3064+
for row in rows:
3065+
dic = {}
3066+
# Get the constraint information
3067+
#constraint = self.constraint(row['conname'])
3068+
if row['conname'] == 'c':
3069+
dic['requery'] = True
3070+
else:
3071+
dic['requery'] = False
3072+
dic['from_table'] = row['confrelid']
3073+
dic['to_table'] = row['conrelid']
3074+
dic['from_column'] = row['referenced_column_name']
3075+
dic['to_column'] = row['column_name']
3076+
relationships.append(dic)
3077+
return relationships
30283078

30293079

30303080
# ======================================================================================================================

0 commit comments

Comments
 (0)