Przekazywanie nazwy tabeli oraz wartości jako parametrów przy pomocy psycopg2

0

Cześć!

Cały dzień próbuję znaleźć optymalne rozwiązanie, żeby przekazać jednocześnie nazwę tabeli oraz wartości do tej tabeli jako parametry, żeby wykonać wpis do bazy danych.
Podążałem za instrukcjami jak tutaj:
https://stackoverflow.com/que[...]me-as-a-parameter-in-psycopg2
https://www.psycopg.org/docs/sql.html#module-psycopg2.sql

Mój kod wygląda następująco:

new_dict = {
    "Product": (('chleb', 'pszenny', 10.5), ('mąka', 'żytnia', 4.50)),
    "Orders": (('zamówienie 1',), ('zamówienie 2',)),
    "Client": ((1, 'Zygmunt', 'Wachlarz'),(2, 'olek', 'kolek'))
}

def insert_into_table(table_inserts, db_name):
    cnx = connection(db_name)
    cur = cnx.cursor()
    table_names = table_inserts.keys()
    for table_name in table_names:
        for i in range(len(table_inserts[table_name])):
            cur.execute(sql.SQL("INSERT INTO {} values %s").format(sql.Identifier(table_name)),
                        (table_inserts[table_name][i],))
    cur.close()
    cnx.close()

insert_into_table(new_dict, "exercises_db")

Otrzymuję następujący błąd:

  • psycopg2.errors.UndefinedTable: relation "Product" does not exist
    LINE 1: INSERT INTO "Product" values ('chleb', 'pszenny', 10.5)

Co ciekawe gdy wykonam print:

print(sql.SQL("INSERT INTO {} values (%s)").format(sql.Identifier(table_name)),
                  (table_inserts[table_name][i],))

otrzymuję

  • Composed([SQL('INSERT INTO '), Identifier('Product'), SQL(' values (%s)')]) (('chleb', 'pszenny', 10.5),)

Gdy wykonam print:

print(sql.SQL("INSERT INTO {} values (%s)").format(sql.Identifier('Product')),
                  (table_inserts[table_name][i],))

Wynik wydaje się być taki sam

  • Composed([SQL('INSERT INTO '), Identifier('Product'), SQL(' values (%s)')]) (('chleb', 'pszenny', 10.5),)

I teraz coś czego nie rozumiem. Na stackowerflow, a później także w dokumentacji jest mowa o przekazywaniu parametru, jednak za każdym przykładem (tutaj dla nazwy tabeli) parametr ten jest przekazywany jako string w pojedynczym cudzysłowie (jak drugi print), co dla mnie oznacza tyle, że został wprowadzony ręcznie, a nie jako parametr, który został przekazany do funkcji.

Co do wyników printów - wydają się identyczne.
Jak podstawiałem do inserta nazwę tabeli jako parametr table_name wyskoczył error, a nazwa tabeli była przekazywana w cudzysłowie. Natomiast gdy próbowałem zrobić insert przekazując parametr wpisując go ręcznie jako 'Product', operacja powiodła się i wpis został dodany do bazy danych.
Czy ktoś mógłby mi naświetlić co się tutaj dzieje i czy w ogóle da się przekazać nazwę tabeli jako parametr?

0

W treści błędu

psycopg2.errors.UndefinedTable: relation "Product" does not exist
LINE 1: INSERT INTO "Product" values ('chleb', 'pszenny', 10.5)

wyraźnie widzę nazwę tabeli w podwójnym cudzysłowie.

Uruchom sobie taki kod i sprawdź co otrzymasz:

print(sql.SQL(', ').join([sql.Identifier('Product'), sql.Identifier(table_name)]).as_string(cur))
0

@Haskell:
Obawiam się, że nie rozumiem czego nie rozumiem. Po uruchomieniu kodu, który podałeś w obu przypadkach nazwa tabeli ląduje cudzysłowie, jednak:

  1. Czytam dokumentację w kontekście as_string i nie jestem pewny czy metoda ta jest tak naprawdę uruchamiana za każdym razem gdy przekazuję zapytanie do bazy danych, wydaje mi się, że sugeruje to ten fragment dokumentacji:

    • as_string(context)
      Return the string value of the object.
      Parameters: context (connection or cursor) – the context to evaluate the string into.
      The method is automatically invoked by execute(), executemany(), copy_expert() if a Composable is passed instead of the query string.
  2. Jeśli tak się dziejej to dlaczego ten fragment kodu:

    cur.execute(sql.SQL("INSERT INTO {} values %s").format(sql.Identifier('Product')),
                        (table_inserts[table_name][i],))

    Przekazuje do bazy danych parametr Product bez cudzysłowów i poprawnie dodaje do tabeli przekazane wartości?

  3. Nadal nie wiem czy da się jednocześnie przekazać nazwę tabeli oraz wartości do tej tabeli jako parametry.

0

Moim zdaniem, nazwa podana w apostrofach trafia do zapytania bez cudzysłów, a ze zmiennej trafia w cudzysłowach stąd różnica w działaniu.

Nazwa podana bez cudzysłów jest przez PostgreSQL czytana bez zwracania uwagi na wielkość znaków czyli SELECT 1 FROM Product zadziała zawsze nawet jeżeli tabela w bazie nosi nazwę product, PRODUCT czy pRODUCT. Z kolei nazwa podana w cudzysłowie jest brana taka jak ją podano i wielkość liter ma znaczenie czyli SELECT 1 FROM "Product" zadziała tylko i wyłącznie wtedy gdy tabela w bazie nosi nazwę rozpoczynającą się wielką literą, a pozostałe litery są małe.

0

@Haskell:
Ok to pomogło. Nie zdawałem sobie sprawy, że tworząc tabele, przekazywałem je do bazy bez cudzysłowów, więc psql zmieniał nazwy na pisane małą literą.
Oczywiście moje materiały do nauki nic nie wspominają na ten temat, więc straciłem półtora dnia nauki na takiej głupocie, ale już mi się to powinno wryć w pamięć :)
Dzięki za pomoc.

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0