How to create a todo list using postgres queries
- create a database with
CREATE DATABASE
- create a table with
CREATE TABLE
- insert sample todos with
INSERT INTO
- read todos with
SELECT
- read filtered todos with
SELECT...WHERE
- update todo by id with
UPDATE...WHERE
- delete todo by id with
DELETE...WHERE
Bash
patricktran=# create database todo_app;
CREATE DATABASE
patricktran=# \c todo_app
You are now connected to database "todo_app" as user "patricktran".
todo_app=# CREATE TABLE todos (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
completed BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT NOW(),
completed_at TIMESTAMP
);
CREATE TABLE
todo_app=# INSERT INTO todos (title) VALUES
('Buy groceries'),
('Walk the dog'),
('Write Postgres tutorial');
INSERT 0 3
todo_app=# SELECT * FROM todos;
id | title | completed | created_at | completed_at
----+-------------------------+-----------+----------------------------+--------------
1 | Buy groceries | f | 2025-06-02 00:44:43.059965 |
2 | Walk the dog | f | 2025-06-02 00:44:43.059965 |
3 | Write Postgres tutorial | f | 2025-06-02 00:44:43.059965 |
(3 rows)
todo_app=# SELECT * FROM todos WHERE completed = FALSE;
id | title | completed | created_at | completed_at
----+-------------------------+-----------+----------------------------+--------------
1 | Buy groceries | f | 2025-06-02 00:44:43.059965 |
2 | Walk the dog | f | 2025-06-02 00:44:43.059965 |
3 | Write Postgres tutorial | f | 2025-06-02 00:44:43.059965 |
(3 rows)
todo_app=# UPDATE todos
SET completed = TRUE, completed_at = NOW()
WHERE id = 2;
UPDATE 1
todo_app=# SELECT * FROM todos;
id | title | completed | created_at | completed_at
----+-------------------------+-----------+----------------------------+----------------------------
1 | Buy groceries | f | 2025-06-02 00:44:43.059965 |
3 | Write Postgres tutorial | f | 2025-06-02 00:44:43.059965 |
2 | Walk the dog | t | 2025-06-02 00:44:43.059965 | 2025-06-02 00:44:54.555347
(3 rows)
todo_app=# SELECT * FROM todos WHERE completed = FALSE;
id | title | completed | created_at | completed_at
----+-------------------------+-----------+----------------------------+--------------
1 | Buy groceries | f | 2025-06-02 00:44:43.059965 |
3 | Write Postgres tutorial | f | 2025-06-02 00:44:43.059965 |
(2 rows)
todo_app=# DELETE FROM todos WHERE id = 1;
DELETE 1
todo_app=# SELECT * FROM todos WHERE completed = FALSE;
id | title | completed | created_at | completed_at
----+-------------------------+-----------+----------------------------+--------------
3 | Write Postgres tutorial | f | 2025-06-02 00:44:43.059965 |
(1 row)
todo_app=# SELECT * FROM todos;
id | title | completed | created_at | completed_at
----+-------------------------+-----------+----------------------------+----------------------------
3 | Write Postgres tutorial | f | 2025-06-02 00:44:43.059965 |
2 | Walk the dog | t | 2025-06-02 00:44:43.059965 | 2025-06-02 00:44:54.555347
You can use psql directly or create an interactive command line tool
Bash script that runs psql commands
- Save this to a file
run_todo.sh
- Allow the user to execute the script
$ chmod +x run_todo.sh
- Run the scripte
$ ./run_todo.sh
Bash
#!/bin/bash
# Ensure the database exists
createdb todo_app 2>/dev/null
# Create the todos table if it doesn't exist
psql todo_app <<EOF
CREATE TABLE IF NOT EXISTS todos (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
completed BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT NOW(),
completed_at TIMESTAMP
);
EOF
while true; do
echo
echo "1. View todos"
echo "2. Add todo"
echo "3. Toggle completed"
echo "4. Delete todo"
echo "5. Exit"
read -p "Choose an option: " option
case $option in
1)
psql todo_app -c "SELECT id, title, completed, created_at, completed_at FROM todos ORDER BY id;"
;;
2)
read -p "Enter title: " title
psql todo_app -c "INSERT INTO todos (title) VALUES ('$title');"
;;
3)
read -p "Enter ID to toggle: " id
psql todo_app <<EOF
DO \$\$
BEGIN
IF EXISTS (SELECT 1 FROM todos WHERE id = $id AND completed = FALSE) THEN
UPDATE todos SET completed = TRUE, completed_at = NOW() WHERE id = $id;
ELSIF EXISTS (SELECT 1 FROM todos WHERE id = $id AND completed = TRUE) THEN
UPDATE todos SET completed = FALSE, completed_at = NULL WHERE id = $id;
ELSE
RAISE NOTICE 'Todo with ID % not found.', $id;
END IF;
END
\$\$;
EOF
;;
4)
read -p "Enter ID to delete: " id
psql todo_app -c "DELETE FROM todos WHERE id = $id;"
;;
5)
echo "Goodbye!"
break
;;
*)
echo "Invalid option"
;;
esac
done
python script that runs psql commands
- Copy this script to
run_todo.py
- Run with
python todo_cli.py
Bash
import subprocess
def run_sql(query):
subprocess.run(["psql", "todo_app", "-c", query])
def run_sql_block(sql_block):
subprocess.run(["psql", "todo_app"], input=sql_block.encode())
def create_db():
subprocess.run(["createdb", "todo_app"], stderr=subprocess.DEVNULL)
def create_table():
run_sql("""
CREATE TABLE IF NOT EXISTS todos (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
completed BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT NOW(),
completed_at TIMESTAMP
);
""")
def view_todos():
run_sql("SELECT id, title, completed, created_at, completed_at FROM todos ORDER BY id;")
def add_todo():
title = input("Enter title: ")
run_sql(f"INSERT INTO todos (title) VALUES ('{title}');")
def toggle_todo():
id_ = input("Enter ID to toggle: ")
sql_block = f"""
DO $$
BEGIN
IF EXISTS (SELECT 1 FROM todos WHERE id = {id_} AND completed = FALSE) THEN
UPDATE todos SET completed = TRUE, completed_at = NOW() WHERE id = {id_};
ELSIF EXISTS (SELECT 1 FROM todos WHERE id = {id_} AND completed = TRUE) THEN
UPDATE todos SET completed = FALSE, completed_at = NULL WHERE id = {id_};
ELSE
RAISE NOTICE 'Todo with ID % not found.', {id_};
END IF;
END
$$;
"""
run_sql_block(sql_block)
def delete_todo():
id_ = input("Enter ID to delete: ")
run_sql(f"DELETE FROM todos WHERE id = {id_};")
def main():
create_db()
create_table()
while True:
print("\n1. View todos\n2. Add todo\n3. Toggle completed\n4. Delete todo\n5. Exit")
choice = input("Choose an option: ")
if choice == "1":
view_todos()
elif choice == "2":
add_todo()
elif choice == "3":
toggle_todo()
elif choice == "4":
delete_todo()
elif choice == "5":
print("Goodbye!")
break
else:
print("Invalid option.")
if __name__ == "__main__":
main()