SQLAlchemy

By . Latest revision .

SQLAlchemy är en “verktygslåda” för hantering av SQL. Delen vi ska titta närmare på är en så kallad Object Relational Mapper(ORM). Det hjälper oss att använda den fulla kapaciteten hos SQL på ett objektrelaterat sätt, där vi inte behöver använda oss utav en onödigt många och invecklade SQL satser vid större projekt.

#Förutsättning

Du kan grunderna i Python och du vet vad variabler, typer och funktioner innebär. Du har kännedom om SQL och databashantering.
Du har gått igenom artikeln “Kom igång med objekt”.

#Installera SQLAlchemy

I terminalen (linux):

sudo pip3 install sqlalchemy

I Cygwin eller cmd (windows), starta som administratör:

pip3 install sqlalchemy

#Skapa SQLite-filen

I artikeln används:
1. cars.sqlite (databasen)
2. main.py (användingen av SQLAlchemy och klassen Car)
3. car.py (klassen Car som mappas mot databas-tabellen)

cars.sqlite skapas genom Firefox addon “SQLite manager”.

Instruktion för SQLite manager.

Strukturen för tabellen cars ser ut som följande:

CREATE TABLE "cars" ("id" INTEGER PRIMARY KEY  AUTOINCREMENT  NOT NULL  UNIQUE , "model" VARCHAR, "price" FLOAT, "country" VARCHAR, "manufacturer" VARCHAR)

Vi ser att tabellen har 5 kolumner:

  • id
  • model
  • price
  • country
  • manufacturer

Vi fyller på med 3 bilar:

INSERT INTO cars (model, price, country, manufacturer) VALUES
    ("Mustang", 400000, "USA", "Ford"),
    ("XC60", 595000, "Sweden", "Volvo"),
    ("E-Class", 745000, "Germany", "Mercedes")

#Mappa en tabell till en klass

Tack vare att vi mappar tabellen till en klass kan vi arbeta mer objektorienterat och vi slipper de långa SQL-satserna. Tanken är att skapa en klass utifrån en bas-klass i SQLAlchemy som innehåller färdig information om hur den ska mappas. Vi behöver importera ett par moduler för att få det att fungera.

car.py:

#!/usr/bin/env python3

""" Mapping of class """

from sqlalchemy import Column, Float, String, Integer
from sqlalchemy.ext.declarative import declarative_base

...

Sedan skapar vi vår klass utifrån declarative_base:

...

Base = declarative_base()

class Car(Base):
    __tablename__ = "cars"

    id = Column(Integer, primary_key=True)
    model = Column(String)
    price = Column(Float)
    country = Column(String)
    manufacturer = Column(String)
    
    def __init__(self, model, price, country, manufacturer):
        """ init method """
        self.model = model
        self.price = price
        self.country = country
        self.manufacturer = manufacturer

    def __str__(self):
        return "Model: {m}, Price: {p}, Country: {c}, Manufacturer: {ma}".format(m=self.model, p=self.price, c=self.country, ma=self.manufacturer)

Har man fler tabeller man vill mappa använder man samma “Base”-klass.

#Användning av klassen

Nu har vi grunderna för att börja manipulera datan i tabellen. Vi skapar även en main.py, där vi kopplar upp oss mot databas-filen och använder den Vi behöver importera bland annat “sessionmaker”. Det är i Session som ORM:en hanterar databasen.
Kopplingen till filen sker i variabeln “engine” och behöver kopplas till Session efter den skapats, alternativt samtidigt:

main.py:

#!/usr/bin/env python3

""" Test SQLAlchemy """

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from car import Car

engine = create_engine("sqlite:///db/cars.sqlite")

Session = sessionmaker(bind=engine)
session = Session()

Nuså. Beroende på vad man vill göra, behöver man importera olika moduler från “sqlalchemy”.

#Lägga till i tabellen

För att lägga till rader i tabellen, skapar vi ett nytt objekt av klassen “Car”:

#!/usr/bin/env python3

""" Test SQLAlchemy """

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from car import Car

engine = create_engine("sqlite:///db/cars.sqlite")

Session = sessionmaker(bind=engine)
session = Session()

# Skapa ett objekt att lägga till utifrån klassen Car
aCar = Car(model="v40", price=150000, country="Sweden", manufacturer="Volvo")

# Lägg till i sessionen
session.add(aCar)

# Slutför med commit()
session.commit()

Vill man lägga till flera objekt på en gång, görs det med:

session.add_all([ obj1, obj2, obj3, ... ])

Det är inte förrän en commit() görs som datan faktiskt läggs in. Med session.new kan man se vilka operationer som är avvaktande, “pending”:

>>> bCar = Car(model="Corsa", price=56000, country="Germany", manufacturer="Opel")

>>> session.add(bCar)

>>> print(session.new)

IdentitySet([Model: Corsa, Price: 56000, Country: Germany, Manufacturer: Opel])

Vi kan även se att “bCar” inte har fått ett id än:

>>> print(bCar.id)
None

Gör vi en commit() och kollar på id:t igen så ser vi att den är tillagd i tabellen:

>>> session.commit()
>>> print( bCar.id )
5

Så länge frågan ligger i sessionen kan man även göra session.rollback() för att återställa sessionen vid eventuella problem.

#Hämta från databasen

Nu har vi några bilar i tabellen och vi har lärt oss hur man lägger till. För att hämta data använder vi oss utav session.query(), där det finns ett flertal alternativ.

Vill vi hämta allt ställer vi en fråga till “session”:

>>>for row in session.query(Car):
...    print(row)

Model: Mustang, Price: 400000.0, Country: USA, Manufacturer: Ford
Model: XC60, Price: 595000.0, Country: Sweden, Manufacturer: Volvo
Model: E-Class, Price: 795000.0, Country: Germany, Manufacturer: Mercedes
Model: v40, Price: 195000.0, Country: Sweden, Manufacturer: Volvo
Model: Corsa, Price: 56000.0, Country: Germany, Manufacturer: Opel

Varje rad i tabellen hämtas och __str__()-funktionen används på dem. Vill man däremot välja vilka kolumner man vill ha anger man det i frågan:

>>> for model, price in session.query(Car.model, Car.price):
...    print("Model: {mo}, Price: {p}\n".format(mo=model, p=price))

Model: Mustang, Price: 400000.0
Model: XC60, Price: 595000.0
Model: E-Class, Price: 795000.0
Model: v40, Price: 195000.0
Model: Corsa, Price: 56000.0

Det finns en funktion, filter(), som hjälper oss att filtrera resultatet. Låt säga att vi glömt hur ordet “Volvo” stavas och vill hämta allt, vars kolumn “manufacturer”, har med “vo” att göra:

>>> for row in session.query(Car).filter(Car.manufacturer.like("%vo%")):
...     print(row)

Model: XC60, Price: 595000.0, Country: Sweden, Manufacturer: Volvo
Model: v40, Price: 195000.0, Country: Sweden, Manufacturer: Volvo

För att använda fler filter lägger vi bara på en filter() på filtret. Vi lägger på ett filter som kollar om priset är under 200.000kr:

>>> for row in session.query(Car).filter(Car.manufacturer.like("%vo%")).filter(Car.price < 200000):
...    print(row)

Model: v40, Price: 195000.0, Country: Sweden, Manufacturer: Volvo

#Filter operatorer

Det finns en uppsättning operatorer vi kan tillgå för att förenkla våra databasfrågor, till exempel:

  • and_()
  • or_()
  • in_()
  • med flera.

and_():

>>> for row in session.query(Car).filter(and_(Car.country == "Germany", Car.manufacturer == "Mercedes")):
...    print(row)

Model: E-Class, Price: 795000.0, Country: Germany, Manufacturer: Mercedes

or_():

>>> for row in session.query(Car).filter(or_(Car.country == "Germany", Car.country == "USA")):
...    print(row)

Model: Mustang, Price: 400000.0, Country: USA, Manufacturer: Ford
Model: E-Class, Price: 795000.0, Country: Germany, Manufacturer: Mercedes
Model: Corsa, Price: 56000.0, Country: Germany, Manufacturer: Opel

in_():

>>> for row in session.query(Car).filter(Car.model.in_(["v40", "Amazon", "s60", "s40"])):
...    print(row)

Model: v40, Price: 195000.0, Country: Sweden, Manufacturer: Volvo

För fler exempel, kika i SQLAlchemys tutorial

#Ta bort data från tabellen

För att ta bort rader ur tabellen använder vi oss utav .delete(). Vi tar bort bilen med id 2:

>>> session.query(Car).filter(Car.id == 2).delete()
>>> session.commit()

#Resultat som lista

Vi kan även få tillbaka resultatet som olika datatyper. För att få det som en lista använder vi oss utav .all():

>>> print(session.query(Car).filter(Car.price > 250000).all())
[Model: Mustang, Price: 400000.0, Country: USA, Manufacturer: Ford, Model: XC60, Price: 595000.0, Country: Sweden, Manufacturer: Volvo, Model: E-Class, Price: 795000.0, Country: Germany, Manufacturer: Mercedes]

#Överkurs

Gå igenom tutorialen för SQLAlchemy.

#Avslutningsvis

Det här var lite om grunderna i SQLAlchemy och hur man kommer igång med det. Läs gärna på mer om tillfälle ges.

#Revision history

  • 2017-01-27: (A, lew) First version.

Document source.

Category: oopython.