Skip to content
This repository was archived by the owner on Apr 24, 2025. It is now read-only.

Commit f3b51a0

Browse files
🐘 Add Postgres CRUD
1 parent c245957 commit f3b51a0

File tree

5 files changed

+319
-0
lines changed

5 files changed

+319
-0
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# CRUD CLI with 🐘 PostgreSQL
2+
3+
Well, this is a simple CRUD CLI with PostgreSQL. I made this project to help others learn more about PostgreSQL driver for Python and how to create beautiful and interactive CLI's with Python.
4+
5+
Well it is basically a book mark manager, you can add, edit, delete and list your bookmarks.
6+
7+
It is called the Query DB btw.
8+
9+
## What you can learn
10+
11+
I highly recommend you try and read through the code.
12+
As a beginner, it is far easier to read code that is more like what you would write yourself, and this is a good example of that.
13+
It doesn't use any advanced Python features, and it doesn't use any advanced SQL features either.
14+
15+
So, no classes, confusing context managers, or anything like that.
16+
Just plain old functions and SQL.
17+
18+
So, if you are a beginner, you can learn:
19+
20+
- Asking better input from the user
21+
- Using PostgreSQL with Python
22+
- Creating beautiful and interactive CLI's with Python
23+
- Create, Read, Update and Delete (CRUD) operations with PostgreSQL
24+
25+
If you find this project useful, please give it's parent a star.
26+
27+
## Screenshot
28+
29+
![Screenshot](assets/screenshot.png)
30+
31+
## Features
32+
33+
1. Easy to use
34+
2. Interactive
35+
3. Beautiful
36+
4. No need to remember SQL commands
37+
5. Fast and lightweight
38+
39+
## Prerequisites
40+
41+
- Python 3.6 or higher
42+
- PostgreSQL 9.5 or higher
43+
44+
### Python dependencies
45+
46+
- psycopg2
47+
- rich
48+
49+
These dependencies are already in the `requirements.txt` file.
50+
51+
### Create a database
52+
53+
We use postgresql as our database, so you need to create a database with any name and enter the credentials in the `creds.json` file.
54+
55+
```bash
56+
createdb db_name
57+
```
58+
59+
## How to use
60+
61+
First, you need to install the dependencies:
62+
63+
```bash
64+
pip install -r requirements.txt
65+
```
66+
67+
Then, you need to create a `creds.json` file with the following info:
68+
69+
```json
70+
{
71+
"username": "postgres",
72+
"password": "password",
73+
"host": "127.0.0.1",
74+
"port": "5432",
75+
"database": "test"
76+
}
77+
```
78+
79+
After that, you can run the CLI with:
80+
81+
```bash
82+
python main.py
83+
```
84+
85+
It's done! Now you can use the CLI.
Loading
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"username": "joshi",
3+
"password": "NoobScience",
4+
"host": "127.0.0.1",
5+
"port": "5432",
6+
"database": "learn_sql"
7+
}

projects/CRUD-with-postgresql/main.py

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
# Author: NoobScience
2+
# filename: db.py
3+
# License: MIT
4+
# 3rd party Modules Used:
5+
# Rich
6+
# psycopg2
7+
8+
# Import the necessary modules
9+
# Rich for formatting
10+
from rich.console import Console
11+
from rich.table import Table
12+
from rich import print
13+
from rich.text import Text
14+
from rich.panel import Panel
15+
from rich.prompt import Prompt, Confirm
16+
# For working with postgres
17+
import psycopg2
18+
from psycopg2 import Error
19+
import json
20+
import string
21+
import random
22+
console = Console()
23+
24+
try:
25+
# Connect to an existing database
26+
try:
27+
# Get the secrets
28+
file = open("creds.json", "r")
29+
stuff = file.read()
30+
secrets = json.loads(stuff)
31+
# We are using the secrets to connect to the database
32+
# Although this is very convenient, it is not secure
33+
# If you are building something serious, use a more secure method
34+
# Like a .env file
35+
# The creds.json is only being used to make the code more readable
36+
connection = psycopg2.connect(user=str(secrets["username"]),
37+
password=str(secrets["password"]),
38+
host=str(secrets["host"]),
39+
port=str(secrets["port"]),
40+
database=str(secrets["database"]))
41+
42+
# Get the info of the connection using the get_dsn_parameters() method
43+
info = connection.get_dsn_parameters()
44+
except:
45+
console.print(
46+
":red_circle:Sorry, something went wrong! Maybe a wrong credential? Check stuff.json's info correctly")
47+
48+
# Define a new cursor object by calling the cursor() method
49+
# This is the object we use to interact with the database
50+
# Think of it as a pointer to a specific place in the database
51+
cursor = connection.cursor()
52+
53+
print(
54+
f'{info["user"]} is connected to PostgreSQL server on {info["host"]}:{info["port"]}')
55+
56+
# Generate hash for assigning to a entry
57+
def hash_gen():
58+
lower = string.ascii_lowercase
59+
upper = string.ascii_uppercase
60+
digits = string.digits
61+
whole = lower + upper + digits
62+
hash_string = random.sample(whole, 8)
63+
hash = "".join(hash_string)
64+
return hash
65+
66+
# Check if a table exits, if not create one
67+
# We have the following columns in the table
68+
# name, url, author, hash, date
69+
# The name, url, author have to be filled
70+
# Rest is auto generated by the program and the database
71+
def check_table():
72+
cursor.execute("""
73+
CREATE TABLE IF NOT EXISTS dbview(
74+
name TEXT NOT NULL,
75+
url TEXT NOT NULL,
76+
author TEXT,
77+
hash TEXT NOT NULL,
78+
date TIMESTAMP
79+
);
80+
""")
81+
console.log(":green_circle: Connected to Table :man: dbview")
82+
83+
# Insert a new entry into the table
84+
# The hash is auto generated
85+
def insert(name, url, author):
86+
cursor.execute(f"""
87+
INSERT INTO dbview (name, url, author, hash, date) VALUES ('{name}', '{url}', '{author}', '{str(hash_gen())}', CURRENT_TIMESTAMP)
88+
""")
89+
search("name", name)
90+
91+
# View the table using a rich table
92+
def table():
93+
cursor.execute("SELECT * from dbview")
94+
records = cursor.fetchall()
95+
try:
96+
table = Table(title=Panel(
97+
f'Query DB [red]{info["host"]}:{info["port"]}'), show_header=True, header_style="bold green")
98+
table.add_column("Name", style="purple", width=20)
99+
table.add_column("Url", style="yellow")
100+
table.add_column("Author", style="cyan")
101+
table.add_column("Hash", style="dim")
102+
table.add_column("Date", no_wrap=True, style="dim blue")
103+
for record in records:
104+
table.add_row(
105+
record[0],
106+
record[1],
107+
record[2],
108+
record[3],
109+
str(record[4])
110+
)
111+
console.print(table)
112+
except:
113+
# Some terminals don't support rich
114+
print("Name\t\t | URL\t\t | Channel\t\t | Hash\t\t | Date")
115+
for record in records:
116+
print(
117+
f'{record[0]}\t\t | {record[1]}\t\t | {record[2]}\t\t | {record[3]}\t\t| {str(record[4])}\t\t')
118+
119+
# Edit a entry in the table
120+
def edit(key, value):
121+
console.print(
122+
"Enter The New Values to Update in the form of a name url channel")
123+
new_record = str(Prompt.ask("Enter Here: "))
124+
new_records = new_record.split()
125+
# Execute the query to update the table
126+
cursor.execute(
127+
f"UPDATE dbview SET name='{new_records[0]}', url='{new_records[1]}', author='{new_records[2]}' WHERE {key}='{value}'")
128+
console.log(":green_circle: Successfully Updated Database")
129+
table()
130+
131+
# Get the hash of a entry
132+
def hash():
133+
console.print("Enter A Key and Value to get Hash")
134+
record_key = str(Prompt.ask("Enter a Key", choices=[
135+
"name", "url", "author", "hash", "date"], default="name"))
136+
record_value = str(Prompt.ask("Enter a Value"))
137+
cursor.execute(
138+
f"SELECT * from dnview WHERE {record_key}='{record_value}'")
139+
records = cursor.fetchone()
140+
console.print(f"The Hash is {records[3]}")
141+
142+
# Delete a entry from the table
143+
def delete(key, value):
144+
table()
145+
cursor.execute(f"DELETE from dbview WHERE {key}='{value}'")
146+
console.log(":green_circle: Successfully Updated Database")
147+
table()
148+
149+
# Drop the table
150+
def drop():
151+
console.print(
152+
"Are you sure you want to Drop the database? This will delete the whole table")
153+
confirmation = Confirm.ask("Are you sure")
154+
if confirmation:
155+
cursor.execute("DROP table dbview;")
156+
console.print(
157+
"Dropped Table dbview: Restart the application to start a new main table")
158+
else:
159+
console.print("Skipped the drop operation")
160+
161+
# Search the table for a entry
162+
# We provide a lot of options to search
163+
# You can search by name, url, author, hash
164+
# This makes it easy to search for a entry
165+
def search(key, value):
166+
cursor.execute(f"SELECT * from dbview WHERE {key}='{value}'")
167+
records = cursor.fetchall()
168+
table = Table(title=Panel(
169+
f'Query DB [red]{info["host"]}:{info["port"]}'), show_header=True, header_style="bold green")
170+
table.add_column("Name", style="purple", width=20)
171+
table.add_column("Url", style="yellow")
172+
table.add_column("Channel", style="cyan")
173+
table.add_column("Hash", style="dim")
174+
table.add_column("Date", no_wrap=True, style="dim blue")
175+
for record in records:
176+
table.add_row(
177+
record[0],
178+
record[1],
179+
record[2],
180+
record[3],
181+
str(record[4])
182+
)
183+
console.print(table)
184+
loop = 1
185+
# 12 is a rather arbitrary number
186+
# I know. But after a while, you don't want the program to run forever
187+
while loop != 12:
188+
check_table()
189+
# Just the normal CRUD operations
190+
action = str(Prompt.ask("Enter your name", choices=[
191+
"view", "new", "edit", "delete", "drop", "quit", "hash"], default="view"))
192+
if action == "view":
193+
table()
194+
if action == "new":
195+
name = Prompt.ask("Enter the Name")
196+
url = Prompt.ask("Enter The URL")
197+
author = Prompt.ask("Enter The Author")
198+
insert(name, url, author)
199+
if action == "delete":
200+
record_key = str(Prompt.ask("Enter the key", choices=[
201+
"name", "url", "author", "hash", "date"], default="name"))
202+
record_value = str(Prompt.ask("Enter the Value"))
203+
delete(record_key, record_value)
204+
if action == "drop":
205+
drop()
206+
if action == "edit":
207+
record_key = str(Prompt.ask("Enter the key", choices=[
208+
"name", "url", "author", "hash", "date"], default="name"))
209+
record_value = str(Prompt.ask("Enter the Value"))
210+
edit(record_key, record_value)
211+
if action == "hash":
212+
hash()
213+
if action == "quit":
214+
loop = 12
215+
216+
# If something goes wrong, we catch the error
217+
except (Exception, Error) as error:
218+
print("Error while connecting to PostgreSQL", error)
219+
# We finally close the connection
220+
finally:
221+
if (connection):
222+
connection.commit()
223+
cursor.close()
224+
connection.close()
225+
console.print(":red_heart: Thanks for using db.py")
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
rich==13.5.3
2+
psycopg2==2.9.7

0 commit comments

Comments
 (0)