34. SQLite Python Intro - MantsSk/CA_PTUA14 GitHub Wiki

Darbas su SQL Python aplinkoje

Python aplinkoje darbas su duomenų bazėmis vyksta importavus atitinkamos duomenų bazės tvarkyklę, pvz:

  • Postrgres - psycopg2
  • MySQL - mysql-connector-python
  • SQLite - sqlite3

Pythone SQLite tvarkyklės papildomai diegti nereikia, ji ateina jau integruota kartu su Python.

Parsisiųskite duomenų bazę su kuria dirbsime ir įsidėkite šalia jūsų paleidžiamojo Python failo (pvz į PyCharm projektą) - https://github.com/robotautas/kursas/blob/master/DB/db_python/zmones.db

Įrašymas

Užklausa

query = '''
INSERT INTO draugai (f_name, l_name, email) 
VALUES ("Jonas", "Viršaitis", "[email protected]");
'''

Tam, kad po kiekvienos užklausos nereikėtų uždarinėti prisijungimo, galime naudoti context manager'į:

import sqlite3

conn = sqlite3.connect("duomenu_baze.db")
c = conn.cursor()

with conn:
    c.execute("INSERT INTO draugai VALUES ('Domantas', 'Rutkauskas', '[email protected]')")
    c.execute("INSERT INTO draugai VALUES ('Rimas', 'Radzevičius', '[email protected]')")

Įrašų paieška

.fetchone():

with conn:
    c.execute("SELECT * From darbuotojai WHERE pavarde='Rutkauskas'")
    print(c.fetchall())

# ('Domantas', 'Rutkauskas', '[email protected]')

kitas pvz.:

with conn:
    c.execute("SELECT * From draugai WHERE l_name LIKE 'R%'")
    print(c.fetchone())
# ('Domantas', 'Rutkauskas', '[email protected]')

Jeigu rezultatų daugiau, negu vienas, .fetchone() mums spausdina pirmą rezultatą. Norint gauti juos visus, turėtumem naudoti .fetchall():

with conn:
    c.execute("SELECT * From draugai WHERE l_name LIKE 'R%'")
    print(c.fetchall())

Įrašų keitimas ir trynimas

with conn:
    c.execute("UPDATE draugai SET email='[email protected]' WHERE l_name='Radzevičius'")
with conn:
    c.execute("DELETE from draugai WHERE l_name='Rutkauskas'")

Įrašų atnaujinimui ir trynimui nereikia naudoti jokių specifinių metodų.

Dinaminės užklausos

Jeigu norėtumėm užklausoje panaudoti kintamuosius iš kodo, vienas iš galimų variantų būtų:

import sqlite3

conn = sqlite3.connect("zmones.db")
c = conn.cursor()

vardas = input('Įveskite vardą: ')
with conn:
    c.execute(f"SELECT * From draugai WHERE f_name = '{vardas}'")
    res = c.fetchall()
if res:
    print(res)
else:
    print('nėra tokio vardo!')
Įveskite vardą: Domantas
[('Domantas', 'Rutkauskas', '[email protected]')]

Tarkime, vardas suveikia kaip slaptažodis ir vartotojas gali matyti savo duomenis (labai primityvus pavyzdys :) ).

pamėginkime dar kartą:

Įveskite vardą: 'OR 1=1--
[('Jonas', 'Viršaitis', '[email protected]'), ('Jurgis', 'Vagelis', '[email protected]'), ('Domantas', 'Rutkauskas', '[email protected]'), ('Rimas', 'Radzevičius', '[email protected]')]

Atsitiko taip, kad mes pratęsėme SQL užklausą ir ji tapo tokia:

SELECT * From draugai WHERE f_name = '' OR 1=1--'

Tai yra vadinama SQL Injection ataka, kuri veikia, kuomet vartotojui yra palikta galimybė pratęsti SQL užklausą. Niekada nekelkite savo kintamųjų tiesiai į užklausas! :)

Saugus būdas

sqlite3 (kaip ir kitos DB tvarkyklės) turi integruotą sistemą, saugiam kintamųjų naudojimui užklausose:

with conn:
    c.execute("SELECT * From draugai WHERE f_name =?", (vardas,))
    res = c.fetchall()

Šiuo atveju klaustukas užklausoje yra pakeičiamas antrame parametre nurodytomis vertėmis. Jis turi būti pateiktas tuple formatu. Dabar viskas veikia, kaip priklauso:

Įveskite vardą: Domantas
[('Domantas', 'Rutkauskas', '[email protected]')]
Įveskite vardą: OR 1=1--
nėra tokio vardo!

Užklausoje galime naudoti ir daugiau klaustukų:

vardas = 'Algimantas'
pavarde = 'Guobys'
email = '[email protected]'

with conn:
    c.execute("INSERT INTO draugai VALUES(?,?,?)", (vardas, pavarde, email))

excecutemany()

Galime įterpti daug įrašų į lentelę vienu kartu, svarbu jas paduoti tinkamu formatu (list of tuples):

draugai = [
    ('Jonas', 'Jonaitis', '[email protected]'),
    ('Petras', 'Miltelis', '[email protected]'),
    ('Inga', 'Guobytė', '[email protected]')
]

with conn:
    c.executemany("INSERT INTO draugai VALUES(?,?,?)", draugai)

Rowid

SQLite turi ypatumą - ID stulpelį sukuria automatiškai, todėl kuriant lentelę nebūtina tuo rūpintis. Stulpelis vadinasi rowid:

ids = (1, 3, 5)

with conn:
    c.execute("SELECT * FROM draugai WHERE rowid IN (?,?,?)", ids)
    print(c.fetchall())

Kitose duomenų bazėse reikėtų kurti atskirą ID, PostgreSQL pvz:

CREATE TABLE lentele(
    id SERIAL NOT NULL PRIMARY KEY
....

Užduotis:

Duombazę parsisiųsti iš čia - https://github.com/robotautas/kursas/blob/master/DB/uzduotis_masinos/cars.db

Parašykite programą, kuri leistų vartotojui per konsolę:

  • įvesti naują eilutę (automobilį su visais parametrais)
  • ieškoti įrašų pagal visus duomenų bazės lentelės stulpelius. Vartotojas gali pasirinkti kuriuos parametrus paieškoje praleisti. Metus ir kainą vartotojas turėtų nurodinėti nuo-iki.