{"id":849,"date":"2020-05-30T15:07:37","date_gmt":"2020-05-30T19:07:37","guid":{"rendered":"http:\/\/www.aibistin.com\/?p=849"},"modified":"2023-03-12T15:59:16","modified_gmt":"2023-03-12T19:59:16","slug":"insert-data-wit-python-and-postgresql","status":"publish","type":"post","link":"https:\/\/www.aibistin.com\/?p=849","title":{"rendered":"Insert Data With Python and PostgreSQL"},"content":{"rendered":"\n<p>Getting started with Python and psycopg2, I wanted to create a funtion that would insert a table row and return the primary key id column value. <br>The id would be returned whether the row existed already or not. This could easily be achieved with a PostgreSQL function, but this time I wanted to do it with Python. <br>Here is my sample table.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: sql; gutter: false; title: ; notranslate\" title=\"\">\nDROP TABLE IF EXISTS crash.street;\n \nCREATE TABLE crash.street (\n    id           INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,\n    name         TEXT        NOT NULL,\n    borough_code VARCHAR (2) NOT NULL,\n    zip_code     VARCHAR (5),\n    create_date  timestamp with time zone DEFAULT now(),\n    update_date  timestamp with time zone DEFAULT now(),\nCONSTRAINT  s_nbz_u UNIQUE (name, borough_code, zip_code)\n);\n<\/pre><\/div>\n\n\n<p>I created a Python module, &#8216;collision_db.py&#8217;, which contains all the functions for interacting with my database.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\nimport psycopg2\nimport psycopg2.extras\nimport os, sys\n\n\ndef log_this(msg):\n    print(f'{os.path.basename(__file__)}: {sys._getframe().f_back.f_lineno}&gt; {msg}')\n\n\ndef get_db_connection():\n    c = None\n    my_schema = 'crash'\n\t# Set the search path so I don't have to specify \n\t# 'crash' schema in the SQL\n    search_path = f'-c search_path=pg_catalog,public,{my_schema}'\n\n    try:\n        c = psycopg2.connect(\n            database='nyc_data',\n            user='postgres',\n            password='xyz',\n            options=search_path, )\n    except psycopg2.DatabaseError as e:\n        log_this(f'Error {e})')\n        sys.exit(99)\n\n    # No need for commits and rollbacks in for this test\n    c.autocommit = True\n    return c\n\n\ndef insert_into_street(*, con, street):\n    street_id = None\n    cur = con.cursor(cursor_factory=psycopg2.extras.DictCursor)\n    cur.execute(\n        &quot;SELECT id FROM street WHERE name=%(name)s AND borough_code=%(borough_code)s AND zip_code=%(zip_code)s&quot;,\n        street)\n    log_this(f'Cursor status: {cur.statusmessage}')\n    log_this(f'Cursor desc: {cur.description}')\n\n    if cur.statusmessage == 'SELECT 0':\n        log_this(f&quot;Inserting new street {street}&quot;)\n        cur.execute(\n            &quot;INSERT INTO street (name, borough_code, zip_code) VALUES ( %(name)s, %(borough_code)s, %(zip_code)s) RETURNING id&quot;,\n            street)\n\n    street_id = cur.fetchone()&#x5B;0]\n    return street_id\n\n...\n\ndef delete_from_table(*,  con, table_name,  id):\n    cur = con.cursor()\n    sql = f&quot;DELETE FROM {table_name} WHERE id = %s&quot;\n    cur.execute(sql, (id,))\n    return cur.rowcount\n\n<\/pre><\/div>\n\n\n<p><br>I will focus on the &#8216;insert_into_street&#8217; function.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\ndef insert_into_street(*, con, street):\n    street_id = None\n    cur = con.cursor(cursor_factory=psycopg2.extras.DictCursor)\n    cur.execute(\n        &quot;SELECT id FROM street WHERE name=%(name)s AND borough_code=%(borough_code)s AND zip_code=%(zip_code)s&quot;,\n        street)\n    log_this(f'Cursor status: {cur.statusmessage}')\n    log_this(f'Cursor desc: {cur.description}')\n\n    if cur.statusmessage == 'SELECT 0':\n        log_this(f&quot;Inserting new street {street}&quot;)\n        cur.execute(\n            &quot;INSERT INTO street (name, borough_code, zip_code) VALUES ( %(name)s, %(borough_code)s, %(zip_code)s) RETURNING id&quot;,\n            street)\n\n    street_id = cur.fetchone()&#x5B;0]\n    return street_id\n\n\n<\/pre><\/div>\n\n\n<p>The &#8216;insert_into_street&#8217; is given a dictionary of street data, for example:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\n    expect_street = {\n        'name': 'Dead End Street',\n        'borough_code': 'q',\n        'zip_code': '11111'}\n<\/pre><\/div>\n\n\n<p>The function first checks if the street exists already. If it returns 0 rows, it inserts the the new street data using the PostgreSQL &#8220;RETURNING id&#8221;.<br>The cursor will now contain the &#8216;id&#8217; value either from the select or the insert. This id will be returned by the function.<\/p>\n<p>To test this out I created a Pytest function that calls the insert function twice with the same data.<br>Both times it should return with the same row id. It should be noted that the &#8216;get_db_connection&#8217; function<br>sets the Auto-commit to true.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    c.autocommit = True\n    return c<\/code><\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: python; gutter: false; title: ; notranslate\" title=\"\">\nimport psycopg2\nimport pytest\n\nfrom collision_db import delete_from_table, get_db_connection, insert_into_street\nCON = None\n...\ndef setup_function():\n    global CON\n    CON = get_db_connection()\n    log_this(&quot;In setup!&quot;)\n\ndef teardown_function():\n    global CON\n    CON.close()\n    log_this(&quot;In teardown!&quot;)\n...\ndef test_insert_into_street_returns_street_id():\n    global CON\n    test_street = {\n        'name': 'dead_end',\n        'borough_code': 'm',\n        'zip_code': '11111'}\n    street_id_1 = insert_into_street(con=CON, street=test_street)\n    assert street_id_1 &gt; 0\n    street_id_2 = insert_into_street(con=CON, street=test_street)\n    assert street_id_1 == street_id_2\n    row_ct = delete_from_table(con=CON, table_name='street', id=street_id_2)\n    assert row_ct == 1\n\n<\/pre><\/div>\n\n\n<p>I run this test in noisy mode with -v and -s<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\n\u03bb pytest test_collision_db_1.py::test_insert_into_street_returns_street_id -v -s\n==================================================================== test session starts ==================================================================== platform win32 -- Python 3.7.2, pytest-5.4.2, py-1.8.1, pluggy-0.13.1 -- c:\\python37\\python.exe\ncachedir: .pytest_cache\nrootdir: C:\\Users\\ak1\\Apps\\Python\\collision\\tests\ncollected 1 item\n\ntest_collision_db_1.py::test_insert_into_street_returns_street_id test_collision_db_1.py: 25&gt; In setup!\ncollision_db.py: 45&gt; Cursor status: SELECT 0\ncollision_db.py: 46&gt; Cursor desc: (Column(name='id', type_code=23),)\ncollision_db.py: 49&gt; Inserting new street {'name': 'dead_end', 'borough_code': 'm', 'zip_code': '11111'}\ncollision_db.py: 45&gt; Cursor status: SELECT 1\ncollision_db.py: 46&gt; Cursor desc: (Column(name='id', type_code=23),)\nPASSED\ntest_collision_db_1.py: 30&gt; In teardown!\n<\/pre><\/div>\n\n\n<p>Or quiet mode.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; gutter: false; title: ; notranslate\" title=\"\">\n===================================================================== 1 passed in 0.19s =====================================================================\n\n#######################################\n\n#######################################\n\u03bb pytest test_collision_db_1.py::test_insert_into_street_returns_street_id                                                                                   \n==================================================================== test session starts ====================================================================\nplatform win32 -- Python 3.7.2, pytest-5.4.2, py-1.8.1, pluggy-0.13.1                                                                                        \nrootdir: C:\\Users\\ak1\\Apps\\Python\\collision\\tests                                                                                                            \ncollected 1 item                                                                                                                                             \n                                                                                                                                                             \ntest_collision_db_1.py .                                                                                                                               &#x5B;100%]\n                                                                                                                                                             \n===================================================================== 1 passed in 0.14s =====================================================================\n                                                                                                                                                             \n\n<\/pre><\/div>\n\n\n<p>Outstanding!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Getting started with Python and psycopg2, I wanted to create a funtion that would insert a table row and return the primary key id column value. The id would be returned whether the row existed already or not. This could easily be achieved with a PostgreSQL function, but this time I wanted to do it [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[97,77,100,98],"tags":[81,91,82,68,88],"class_list":["post-849","post","type-post","status-publish","format-standard","hentry","category-postgresql","category-psycopg2-python","category-python","category-sql","tag-postgresql","tag-psycopg2","tag-pytest","tag-python","tag-sql"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.aibistin.com\/index.php?rest_route=\/wp\/v2\/posts\/849","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.aibistin.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.aibistin.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.aibistin.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.aibistin.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=849"}],"version-history":[{"count":9,"href":"https:\/\/www.aibistin.com\/index.php?rest_route=\/wp\/v2\/posts\/849\/revisions"}],"predecessor-version":[{"id":859,"href":"https:\/\/www.aibistin.com\/index.php?rest_route=\/wp\/v2\/posts\/849\/revisions\/859"}],"wp:attachment":[{"href":"https:\/\/www.aibistin.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=849"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.aibistin.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=849"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.aibistin.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=849"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}