Садржај
3.0 SQL: Промена садржаја базе
3.2 SQL: Ажурирање података у табелама
3.3 SQL: Брисање података из табела
7.0 Библиотека Flask - пројектни задатак

Упити са параметрима

Често се јавља потреба да упит зависи од уноса корисника. На пример, наредни програм исписује све ученике чије је презиме и чији је разред једнак ономе што је корисник унео.

import os
import sqlite3

prezime = input("Унеси презиме: ")
razred = int(input("Унеси разред: "))
db_conn = sqlite3.connect(os.path.join(os.getcwd(), 'dnevnik.db'))
cur = db_conn.cursor()
res = cur.execute("SELECT ime, prezime FROM ucenik WHERE prezime=? AND razred=?",
                  (prezime, razred))
for ime, prezime in res:
    print(ime, prezime)
db_conn.close()

На почетку скрипта корисник уноси презиме које се смешта у променљиву prezime и разред који се смешта у променљиву razred. Упит који се формира садржи два параметра означена упитницима: "SELECT ime FROM ucenik WHERE prezime=? AND razred=?". Метода execute након упита прима вредности тих параметара (у облику уређене торке) и параметри се аутоматски замењују наместо упитника, водећи рачуна о коректности и безбедности.

Још један начин да се постигне исти ефекат је да се користе именовани параметри (уместо безимених).

res = cur.execute("SELECT ime, prezime FROM ucenik WHERE prezime=:prezime AND razred=:razred",
                  {"prezime": prezime, "razred": razred});

У овом случају се уместо упитника наводе називи параметара (у овом случају то су називи :prezime и :razred), док се вредности параметара не наводе у облику уређене торке, већ у облику речника који називе параметара пресликава у њихове вредности.

Ако постоји само један безимени параметар, тада се вредност назива у оквиру „уређене једнорке”, што звучи помало апсурдно, а и ствара одређене проблеме. Наиме, ако се нека вредност наведе у заградама, она се не сматра елементом „једнорке”, већ се заграде просто занемарују. Да би се нагласило да је једна вредност део торке, потребно је навести је у наредном облику (vrednost, ). На пример, ако желимо да вршимо селекцију само на основу унетог презимена, то се може урадити на следећи начин.

res = cur.execute("SELECT ime, prezime FROM ucenik WHERE prezime=?", (prezime,))

Важно. Могло би се помислити да би замена параметара у упиту могла да се изврши коришћењем механизама за баратање нискама у језику Python. На пример, могло би се урадити овако нешто.

res = cur.execute("SELECT ime, prezime FROM ucenik WHERE prezime='" + prezime + "'")

Иако ово ради исправно, оно представља велики безбедносни пропуст (познат под називом SQL injection). Наиме, корисник вашег Python програма може уместо презимена да унесе и неку ниску која садржи и SQL кôд. На пример ' OR True; --. Тиме се добија наредни упит:

SELECT ime, prezime FROM ucenik WHERE prezime='' OR True; --'

Извршавањем упита добија се следећи резултат:

ime

prezime

Петар

Петровић

Милица

Јовановић

Лидија

Петровић

Петар

Миловановић

Ана

Пекић

То значи да су приказана имена свих ученика из табеле, чиме се можда откривају неки поверљиви подаци. Зашто ово ради? Па услов OR True је увек тачан, па се на овај начин заправо не врши никаква рестрикција. Коментари у језику SQL се наводе након две цртице (све након -- се сматра коментаром), па су две цртице додате да би неутралисале завршни апостроф.

Због свега наведеног, параметризовани упити би увек требало да буду реализовани коришћењем подршке коју метода execute пружа (безимени или именовани параметри).

Више упита

Понекад је потребно извршити више упита који се разликују само по параметрима. У том случају је могуће позвати методу executemany и проследити јој листу торки које чувају различите вредности параметара. На пример, наредни скрипт убацује у табелу податке о неколико ученика.

import os
import sqlite3

ucenici = [ ("Јована", "Ђорђевић", 3, 1),
            ("Мајда", "Зорановић", 2, 3),
            ("Лазар", "Табаковић", 4, 1),
            ("Мирна", "Ђерић", 3, 3) ]
db_conn = sqlite3.connect(os.path.join(os.getcwd(), 'dnevnik.db'))
cur = db_conn.cursor()
cur.executemany("INSERT INTO ucenik (ime, prezime, razred, odeljenje)" +
                "VALUES (?, ?, ?, ?)", ucenici)
db_conn.commit()
db_conn.close()
(Created using Swinx, RunestoneComponents and PetljaDoc)
© 2022 Petlja
A- A+