From 822d67675b32f426edd943d26899f3dc17e8f6a6 Mon Sep 17 00:00:00 2001 From: slashtechno <77907286+slashtechno@users.noreply.github.com> Date: Tue, 4 Jun 2024 11:18:04 -0500 Subject: [PATCH] order, group, and join tables use SQLAlchemy --- pdm.lock | 56 ++++++++++++++++++- pyproject.toml | 3 +- .../{__main__.py => datacamp_tutorial.py} | 51 +++++++++++++++-- 3 files changed, 104 insertions(+), 6 deletions(-) rename src/hello_postgres/{__main__.py => datacamp_tutorial.py} (57%) diff --git a/pdm.lock b/pdm.lock index 2f81d85..bda5681 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["cross_platform", "inherit_metadata"] lock_version = "4.4.1" -content_hash = "sha256:2be337f3deba8cffea31cdbd43024c3db7bed53a688b484b32a3b92c27c530cf" +content_hash = "sha256:3c4bf81bc71a0e1f56d0f71f725ba2668ff4346e236ae89f946e0ee388a47e83" [[package]] name = "asttokens" @@ -42,6 +42,26 @@ files = [ {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"}, ] +[[package]] +name = "greenlet" +version = "3.0.3" +requires_python = ">=3.7" +summary = "Lightweight in-process concurrent programming" +groups = ["default"] +marker = "platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\"" +files = [ + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + [[package]] name = "icecream" version = "2.1.3" @@ -112,3 +132,37 @@ files = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] + +[[package]] +name = "sqlalchemy" +version = "2.0.30" +requires_python = ">=3.7" +summary = "Database Abstraction Library" +groups = ["default"] +dependencies = [ + "greenlet!=0.4.17; platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\"", + "typing-extensions>=4.6.0", +] +files = [ + {file = "SQLAlchemy-2.0.30-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5a79d65395ac5e6b0c2890935bad892eabb911c4aa8e8015067ddb37eea3d56c"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9a5baf9267b752390252889f0c802ea13b52dfee5e369527da229189b8bd592e"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cb5a646930c5123f8461f6468901573f334c2c63c795b9af350063a736d0134"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:296230899df0b77dec4eb799bcea6fbe39a43707ce7bb166519c97b583cfcab3"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c62d401223f468eb4da32627bffc0c78ed516b03bb8a34a58be54d618b74d472"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3b69e934f0f2b677ec111b4d83f92dc1a3210a779f69bf905273192cf4ed433e"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-win32.whl", hash = "sha256:77d2edb1f54aff37e3318f611637171e8ec71472f1fdc7348b41dcb226f93d90"}, + {file = "SQLAlchemy-2.0.30-cp312-cp312-win_amd64.whl", hash = "sha256:b6c7ec2b1f4969fc19b65b7059ed00497e25f54069407a8701091beb69e591a5"}, + {file = "SQLAlchemy-2.0.30-py3-none-any.whl", hash = "sha256:7108d569d3990c71e26a42f60474b4c02c8586c4681af5fd67e51a044fdea86a"}, + {file = "SQLAlchemy-2.0.30.tar.gz", hash = "sha256:2b1708916730f4830bc69d6f49d37f7698b5bd7530aca7f04f785f8849e95255"}, +] + +[[package]] +name = "typing-extensions" +version = "4.12.1" +requires_python = ">=3.8" +summary = "Backported and Experimental Type Hints for Python 3.8+" +groups = ["default"] +files = [ + {file = "typing_extensions-4.12.1-py3-none-any.whl", hash = "sha256:6024b58b69089e5a89c347397254e35f1bf02a907728ec7fee9bf0fe837d203a"}, + {file = "typing_extensions-4.12.1.tar.gz", hash = "sha256:915f5e35ff76f56588223f15fdd5938f9a1cf9195c0de25130c627e4d597f6d1"}, +] diff --git a/pyproject.toml b/pyproject.toml index 40678b8..885c419 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,6 +9,7 @@ dependencies = [ "psycopg2-binary>=2.9.9", "python-dotenv>=1.0.1", "icecream>=2.1.3", + "SQLAlchemy>=2.0.30", ] requires-python = ">=3.12" readme = "README.md" @@ -19,7 +20,7 @@ requires = ["pdm-backend"] build-backend = "pdm.backend" [project.scripts] -hello-postgres = "hello_postgres.__main__:main" +datacamp-postgres-tutorial = "hello_postgres.datacamp_tutorial:main" [tool.pdm] diff --git a/src/hello_postgres/__main__.py b/src/hello_postgres/datacamp_tutorial.py similarity index 57% rename from src/hello_postgres/__main__.py rename to src/hello_postgres/datacamp_tutorial.py index 60e0f05..3b86605 100644 --- a/src/hello_postgres/__main__.py +++ b/src/hello_postgres/datacamp_tutorial.py @@ -1,6 +1,5 @@ -# https://www.freecodecamp.org/news/postgresql-in-python/ # https://www.datacamp.com/tutorial/tutorial-postgresql-python -# docker compose down && docker compose up -d && hello-postgres +# docker compose down && docker compose up -d && datacamp-postgres-tutorial import psycopg2 import dotenv import os @@ -8,6 +7,7 @@ import os def main(): + dotenv.load_dotenv() print("Connecting to the PostgreSQL database...") conn = connect() cursor = conn.cursor() @@ -45,13 +45,56 @@ def main(): print("Deleting \"Introduction to Statistics in R\"...") cursor.execute("DELETE from datacamp_courses WHERE course_name = 'Introduction to Statistics in R';") conn.commit() + print("Sorting table by instructor...") + cursor.execute('SELECT * FROM datacamp_courses ORDER BY course_instructor') + rows = cursor.fetchall() + for row in rows: + print(row) + print("Fetching the number of courses each instructor teaches...") + cursor.execute('SELECT course_instructor, COUNT(*) FROM datacamp_courses GROUP BY course_instructor') + for row in cursor.fetchall(): + print(row) + print("Creating the new table for language rankings...") + # https://www.w3schools.com/sql/sql_primarykey.ASP + cursor.execute(""" + CREATE TABLE programming_languages ( + language_id INT PRIMARY KEY, + language_name TEXT NOT NULL, + course_number INT NOT NULL, + tiobe_ranking INT NOT NULL + ) +""") + cursor.execute(""" + INSERT INTO programming_languages (language_id, language_name, course_number, tiobe_ranking) + VALUES + (1, 'SQL', 31, 8), + (2, 'Python', 157, 1), + (3, 'R', 132, 16), + (4, 'Julia', 2, 33), + (5, 'Scala', 1, 38) +""") + conn.commit() + # datacamp: "We will use an INNER JOIN to get only the information of the programming languages that appear in the datacamp_course table. " + print("Getting courses, their instructors, the topic, and the tiobe ranking of the language...") + cursor.execute("""SELECT course_name, course_instructor, topic, tiobe_ranking +FROM datacamp_courses +INNER JOIN programming_languages +ON datacamp_courses.topic = programming_languages.language_name""") + for row in cursor.fetchall(): + print(row) cursor.close() conn.close() - # cursor.execute("SELECT * FROM DB_table WHERE id = 1") + print("Now using SQLAlchemy to interact with the database...") + import sqlalchemy + engine = sqlalchemy.create_engine(f"postgresql://{os.getenv('POSTGRES_USER')}:{os.getenv('POSTGRES_PASSWORD')}@{os.getenv('DB_HOST')}:{os.getenv('DB_PORT')}/{os.getenv('POSTGRES_DB')}") + conn = engine.connect() + print("Fetching all the available rows...") + output = conn.execute(sqlalchemy.text("SELECT * FROM datacamp_courses")) + print(output.fetchall()) + conn.close() def connect(): - dotenv.load_dotenv() conn = psycopg2.connect( database=os.getenv('POSTGRES_DB'), host=os.getenv('DB_HOST'),