Mapowanie w SQLAlchemy

Mapowanie obiektów SQLAlchemy na tabele w bazie danych i ich wykorzystanie w skryptach Pythona. Dzięki mapowaniu tabel na obiekty możemy operować na danych w bazie danych bez wykorzystania języka SQL

Ten artykuł opisuje przestarzałą wersję biblioteki!
Oto przykład:
from sqlalchemy import *

db = create_engine('sqlite://')

db.echo = True

metadata = BoundMetaData(db)

users = Table('users', metadata,
    Column('user_id', Integer, primary_key=True),
    Column('name', String(40)),
    Column('age', Integer),
)
users.create()

emails = Table('emails', metadata,
    Column('email_id', Integer, primary_key=True),
    Column('address', String),
    Column('user_id', Integer, ForeignKey('users.user_id')),
)
emails.create()

i = users.insert()
i.execute(
    {'name': 'Mary', 'age': 30},
    {'name': 'John', 'age': 42},
    {'name': 'Susan', 'age': 57},
    {'name': 'Carl', 'age': 33}
)
i = emails.insert()
i.execute(
    {'address': 'mary@example.com', 'user_id': 1},
    {'address': 'john@nowhere.net', 'user_id': 2},
    {'address': 'john@example.org', 'user_id': 2},
    {'address': 'carl@nospam.net', 'user_id': 4},
)
# Puste klasy, które zostaną zmapowane
class User(object):
    pass
class Email(object):
    pass

# mapujemy (Klasa, obiekt_tabeli)
usermapper = mapper(User, users)
emailmapper = mapper(Email, emails)

# tworzymy sesję
session = create_session()

mary = session.query(User).selectfirst(users.c.name=='Mary')
mary.age += 1

session.flush()

fred = User()
fred.name = 'Fred'
fred.age = 37

print "About to flush() without a save()..."
session.flush()  # nie zapisze danych Freda

session.save(fred)
print "Just called save(). Now flush() will actually do something."
session.flush()  # Teraz zapisze

session.delete(fred)
session.flush()
Objaśnienie: ORM (object relational mapper) SQLAlchemy pozwala połączyć tabele z klasami pythona w "mapy", na których można wykonywać zapytania. Zaczynamy od stworzenia pustych klas, na które zostaną zmapowane dane:
class User(object):
    pass
class Email(object):
    pass
Następnie je mapujemy:
usermapper = mapper(User, users)
emailmapper = mapper(Email, emails)
Funkcja mapper przyjmuje (minimum) dwa parametry - klasę do mapowania i jako drugi - obiekt tabeli. Podane obiekty dostaną atrybuty takie jak nazwy pól w tabeli. "User" będzie posiadał: User.user_id, User.name, User.age i User.password. "Email": Email.email_id, Email.address i Email.user_id.
session = create_session()
SQLAlchemy poprzez obiekt session jest w stanie śledzić zmiany zachodzące w mapowanych obiektach.
mary = session.query(User).selectfirst(users.c.name=='Mary')
Powyższy kod tworzy obiekt "mary", który pobiera określone dane z tabeli użytkowników (klasa Users). Na tym obiekcie można operować jak na zwykłych obiektach SQLAlchemy. Można dodawać czy zmieniać dane lecz do bazy zmiany zapisane zostaną dopiero po wywołaniu:
session.flush()

fred = User()
fred.name = 'Fred'
fred.age = 37
Tutaj tworzymy nowy obiekt "fred" klasy "Users". Wywołanie session.flush() nie zapisze tych danych, gdyż obiekt ten nie jest powiązany z czynną sesją. Dopiero:
session.save(fred)
"przypisze" go do działającej sesji i flush() uwzględni ten obiekt przy zapisie.
session.delete(fred)
session.flush()
Powyższy kod usuwa obiekt "fred", czyli dodany wcześniej wpis w bazie danych. Samo "session.delete" nie usunie wpisu, stanie się to dopiero przy wywołaniu "session.flush()"

Ręczne zarządzanie transakcjami

Można przejąć ręczną kontrolę nad transakcjami. Oto przykład:
transaction = session.create_transaction()
try: 
    # tutaj trochę operacji
    session.flush()
    # kolejna porcja
    session.flush()
    # wszystko się udało! zapisujemy
    transaction.commit()
except:
    # coś nie wyszło, cofamy niepełne zmiany
    transaction.rollback()
    # ... wypada wygenerować wyjątek
    raise

Obiekt Query

Wykorzystane wcześniej session.query(klasa_lub_mapper) zwraca obiekt Query. Obiekt ten ma sporo ciekawych opcji:
# select z WHERE i AND między warunkami
result = query.select_by(name='john', street='123 green street')
# pobranie 1 określonego wiersza
result = query.get_by(id=12)

#select_by_* i get_by_* gdzie * to nazwa kolumny, a argument to wartość
result = query.select_by_name('fred')
u = query.get_by_name('fred')
blog comments powered by Disqus

Kategorie

Strony