License. Veri Tabanı Sistemleri. Konular. Uygulama Geliştirme Veri Tabanı Arayüzleri. uygulama kodunda veri işlemleri nasıl yapılacak?

Benzer belgeler
License. Veri Tabanı Sistemleri. Konular büyük miktarda verinin etkin biçimde tutulması ve işlenmesi. Problem Kayıt Dosyaları

License. Veri Tabanı Sistemleri. Konular. Bağıntı Modeli Dr. E. F. Codd, 1970 veri bağıntılar şeklinde modellenir: α A B C...

Lisans. Deskriptif Önermeler

License. Veri Tabanı Sistemleri. Konular. Kapalılık. Bağıntı Cebri Katma. kapalılık: bütün işlemlerin girdileri de çıktıları da bağıntı

License. Veri Tabanı Sistemleri. Konular. Hareket Özellikleri. Tanım hareket: bir işin mantıksal bir birimi

Üst Düzey Programlama

Veri Tabanı SQL Server ve Management Studio kurulum linkleri:

Lisans. Ayrık Matematik Tanıtlama. Kaba Kuvvet Yöntemi. Konular. Temel Kurallar

Yukarıdakilerden hangileri DML (Data Manipulation Language) ile gerçekleştirilir?

MOBİL UYGULAMA GELİŞTİRME

Kullanıldığı Başlıca Siteler

SQL Komutları (2) Uzm. Murat YAZICI

License. Alan Adları

5 SQL- Yapısal Sorgulama Dili. Veritabanı 1

ACCESS PLATFORMUNDA SQL

Lisans. Ayrık Matematik Yüklemler ve Kümeler. Konular. Tanım. Tanım çalışma evreni: U izin verilen seçenekler kümesi örnekler:

VERĐTABANI YÖNETĐM SĐSTEMLERĐ

JDBC kütüphanesi, her görev için genellikle veritabanı kullanımı ile ilişkili API leri içerir:

Data Programming SQL Language. Elbistan Meslek Yüksek Okulu Bahar Yarıyılı

Cybersoft Bilişim Teknolojileri Sunucu Tarafı Programlaması Kursu Final soruları. Tarih: 27 Kasım 2010 Saat: 13:30 Süre: 3 saat

Veritabanına Giriş. Oğuzhan Ceylan. 19 Eylül 2011

PostgreSQL ve PL/pgSQL

Her Yönüyle SQL Enjeksiyonu. OWASP-Türkiye Bünyamin Demir

SQL e Giriş. Uzm. Murat YAZICI

MySqlConnection connection; MySqlCommand command; MySqlDataReader reader; MySqlDataAdapter adapter; DataTable table;

-- işareti tek satırlık açıklamalarda kullanılır. Açıklama olarak yazılan satırın önüne konulması yeterlidir.

SORGULAR. Öğr.Gör.Volkan Altıntaş

Veri Tabanı ve Yönetimi

PostgreSQL ve PL/pgSQL

BÖLÜM- 9: KULLANICI ERİŞİMLERİNİ YÖNETMEK

Veritabanı Yönetim Sistemleri (Veritabanı Tasarımı) SQL (Structured Query Language)

Veritabanı Tasarımı. Kullanıcı Erişimini Kontrol Etme

Veritabanlarına ve SQL'e Giriş. Devrim GÜNDÜZ. Teknoloji Destek Merkezi --

SQL TRIGGERS (Tetikleyiciler)

Bölüm 4: DDL Veri Tanımlama Dili

Python Web 2.0 Python ve Web 2.0 Son. Python ve Web 2.0. Gökmen GÖKSEL, 3 Mayıs 2007

Genel Kavramlar. Bilgisayar ortamında işlenebilecek durumda bulunan kayıtlar. Birbiri ile ilişkili veriler topluluğu ve veriler arası ilişkiler

Mysql Veritabanı Komutları

VERİ TABANI YÖNETİM SİSTEMLERİ II. 5. SQL PROGRAMLAMADA CURSOR (İMLEÇ) ve TRIGGERS (TETİKLEMELER)

BÖLÜM -6: VERİLERİ DEĞİŞTİRMEK

SQL Kod ile Tablo Oluşturma

Veri Tabanı Hafta Dersi

Oracle da kullanılan veri tipleri:

Swing ve JDBC ile Database Erişimi

Veri Tabanı Sistemleri

EXISTS VE NOT EXISTS fonksiyonları

2- Total de 8000 byte yer tutup 4000 karakter olarak kullanabildiğimiz tip aşağıdakilerden hangisidir?

8 Oracle da tablo yapısı içinde otomatik artan kolon yoktur. (identity kolon

Lisans. Cebirsel Yapı

EBE-368 Veri Tabanı Yönetim Sistemleri İlişkisel Model (The Relational Model)

VERİTABANI Veritabanı Yönetimi

Yaptığımız web sitelerinin daha kullanışlı olması için veritabanı sistemleri ile bağlantı kurup ihtiyaca göre verileri okuyup yazmasını isteriz.

BLM401 Mobil Cihazlar için ANDROID İşletim Sistemi. SQLite Veritabanı. BLM401 Dr.Refik SAMET

Veritabanına Uygulanması

Lisans. Meslek Ahlakı

SAKLI YORDAM (Stored Procedure) Sibel Somyürek

20461C Querying Microsoft SQL Server Modül Seviye Belirleme Testi

Fonksiyonlar istenilen deger tipinde dönüs yapabilir. INT, VARCHAR deger döndürebileceğiniz gibi bir tablo da döndürebilirsiniz.

VERİTABANI. SQL (Structured Query Language)

BÖLÜM -7: TABLOLARI OLUŞTURMA VE YÖNETME

Aşağıdaki tabloyu inceleyin. Sorgulama işlemlerini bu tabloya göre yapacağız.

«BM364» Veritabanı Uygulamaları

Liquibase ile Veri Tabanı Değişiklik Yönetimi

SQL'e Giriş. SELECT Deyimi. SQL Komutları. Yardımcı Deyimler

Bu işleçlerin dışında, aşağıda belirtilen karşılaştırma işleçlerinden de yararlanılır.

Upgrading Internet Technology skills of Information and Communication Technologies (ICT) Professionals

SQL Deyimleri. Öğr.Gör.Volkan ALTINTAŞ Volkanaltintas.com

İNTERNET PROGRAMCILIĞI HAFTA. MYSQL ile VERİTABANI İŞLEMLERİ - 1. Hazırlayan Fatih BALAMAN. İçindekiler. Hedefler. Veritabanı Oluşturma, Silme

Veritabanı Tasarımı. Sütun Değerlerini Güncelleme ve Satırları Silme

Aşağıdaki tabloyu inceleyin. Yeni kayıt girme, var olan bir kaydı silme veya güncelleme işlemlerini bu tabloya göre yapacağız.

PostgreSQL Veritabanı Sunucusu. 8.2 neler getiriyor?

TRIGGER. Trigger lar, tablo üzerinde tanımlanabilen ve bu tablo üzerinde bir işlem gerçekleştiğinde tetiklenen programlama ögeleridir.

Veritabanı Tasarımı. Tablo Oluşturma

YAPISAL SORGULAMA DİLİ (SQL)

DML işlemleri. Elbistan Meslek Yüksek Okulu Bahar Yarıyılı May Öğr. Gör. Murat KEÇECĠOĞLU

EBE-368 Veri Tabanı Yönetim Sistemleri SQL

Veri Tabanı Programlamaya Giriş

Bölüm 24. Java Ağ Uygulamaları 24.1 Java Appletleri. Bir Applet in Yaşam Döngüsü:

İNTERNET PROGRAMLAMA 2 A S P. N E T. Marmara Teknik Bilimler MYO / Hafta 5 Veri Tabanı İşlemleri

SP_RENAMEDB eski_isim, yeni_isim VEYA SP_RENAMEDB 'eski isim', 'yeni isim'

Vbnet Vbne access bağ ba lant lan ı t s ı ı s, ekleme, güncelleme,,silme

TESİ. indeks. söylenebilir?? bir ilişkidir d) Hiçbiri. veya somutlaştırılmış. düzeyidir? sağlayabilir? sına. d) Hepsi. olabilir? c) Verilerin d) Hepsi

2 PYTHON A GIRIŞ 13 PyCharm İle Python Projesi Oluşturma 15 Projenin Çalıştırılması 18 İlk Python Programımız 19 Açıklama Satırları 21

Veri tabanı içinde bulunan tablolardakibazı sütunlarda bulunan bilgilerin, herkes tarafından görülmesi istenmeyebilir.

SQL'e Giriş 2. CREATE TABLE tabloadı (kolon isimleri ve veri türleri) (BOLUM_NO NUMBER, BOLUM_ADI CHAR(10));

Kepware Veritabanı Ürünleri. Teknolojiye Genel Bir Bakış

Tavsiye Edilen Önhazırlık Temel veritabanı kavramlar hakkında bilgi sahibi olmak. Hedefler Temel veritabanı güvenlik işlemlerini gerçekleştirebilmek

SORGULAR VE ÇEŞİTLERİ II

Microsoft SQL Server Sorgulama

«BM364» Veritabanı Uygulamaları

ile Python gibi kod yazma

T-SQL NEDİR? Microsoft T-SQL Transact-SQL

Aligram Documentation

Veri Bütünlüğü ve Constraint ler. Veritabanı 1

Sorgudan elde edilen değerin değişkenlere aktarılmasını sağlar. Sorgudan tek satır dönmesi gerekir. Çok satır dönerse hata verir.

Nesneler üzerinde değişiklik yapmak için kullanılır. Veri tabanındaki nesnelerin oluşturulabilmesi için CREATE komutu kullanılır.

Veritabanı Tasarımı. Veritabanı Hareketleri

SQL PROGRAMLAMA. Bir batch, bir arada bulunan bir dizi SQL deyimidir. Batch ayıracı GO deyimidir.

NETBEANS GUI İLE MS SQL İŞLEMLERİ

Transkript:

License Veri Tabanı Sistemleri Uygulama Geliştirme H. Turgut Uyar Şule Öğüdücü 2002-2016../license You are free to: c 2002-2016 T. Uyar, Ş. Öğüdücü Share copy and redistribute the material in any medium or format Adapt remix, transform, and build upon the material Under the following terms: Attribution You must give appropriate credit, provide a link to the license, and indicate if changes were made. NonCommercial You may not use the material for commercial purposes. ShareAlike If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original. For more information: https://creativecommons.org/licenses/by-nc-sa/4.0/ Read the full license: https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode 1 / 58 2 / 58 Konular Giriş Veri Tabanı Arayüzleri Giriş İşlemler Hata Denetimi Komutlar Nesne/Bağıntı Eşleştirmesi Giriş SQLAlchemy Sorgulamalar Dış Anahtarlar uygulama kodunda veri işlemleri nasıl yapılacak? veri tabanı sunucusuna bağlan sunucu, veri tabanı, kullanıcı adı, parola işlemleri yürüt sonuçları uyarla bağlantıyı kopar 3 / 58 4 / 58

Amaçlar Python DBAPI kod belirli bir ürüne bağlı olmamalı başka bir ürüne kolayca taşınabilmeli soyutlama katmanları performans problemlerine yol açıyor örneğin ODBC standart ama yavaş sürücüler dil standart arayüzlerine uyarak gerçeklenir Java: JDBC, Python: DBAPI sürücüyü yükle başka sürücülere kolay uyarlamak için adını değiştir örnek import psycopg2 as dbapi2 # import sqlite3 as dbapi2 5 / 58 6 / 58 Bağlantı Bağlantı Örneği bağlantı bilgileri: kullanıcı adı, parola, sunucu, veri tabanı veri kaynağı adı (DSN): user=.. password=.. host=.. port=.. dbname=.. tekdüzen kaynak tanımlayıcısı (URI): protocol://user:password@host:port/dbname örnekler user= vagrant password= vagrant host= localhost port=5432 dbname= itucsdb dsn = """user= vagrant password= vagrant host= localhost port=5432 dbname= itucsdb """ connection = dbapi2.connect(dsn) # database operations connection.close() postgres://vagrant:vagrant@localhost:5432/itucsdb 7 / 58 8 / 58

Güncelleme İşlemleri Güncelleme Örneği güncelleme işlemleri için (insert, delete, update, create,... ) bağlantı için bir imleç tanımla sorguları çalıştır bekleyen değişikliklerı işle imleci kapat connection = dbapi2.connect(dsn) cursor = connection.cursor() statement = """CREATE TABLE PERSON ( ID SERIAL PRIMARY KEY, NAME VARCHAR(40) UNIQUE NOT NULL )""" connection.commit() cursor.close() connection.close() 9 / 58 10 / 58 Sorgulama İşlemleri Sorgulama Örneği sorgulama işlemleri için (select) bağlantı için bir imleç tanımla sorguları çalıştır imleçle döngü içinde satırlar üzerinde dolaş (her satır bir çoklu) imleci kapat connection = dbapi2.connect(dsn) cursor = connection.cursor() statement = """SELECT TITLE, SCORE FROM MOVIE WHERE (YR = 1999)""" for row in cursor: title, score = row print( {}: {}.format(title, score)) cursor.close() connection.close() 11 / 58 12 / 58

Sorgulama Örneği Sorgulama Örnekleri çoklu ataması ile daha basit işlem for row in cursor: title, score = row print( {}: {}.format(title, score)) for title, score in cursor: print( {}: {}.format(title, score)) filmler ve yönetmenleri statement = """SELECT TITLE, NAME FROM MOVIE JOIN PERSON ON (MOVIE.DIRECTORID = PERSON.ID)""" for title, name in cursor: print( {}: {}.format(title, name)) 13 / 58 14 / 58 Hata Denetimi Şablon veri tabanı işlemleri ile ilgili hatalar hata durumunda işlemi geri al (except) açık bütün kaynakları kapat (finally) try: connection = dbapi2.connect(dsn) cursor = connection.cursor() connection.commit() cursor.close() except dbapi2.databaseerror: if connection: connection.rollback() finally: if connection: connection.close() 15 / 58 16 / 58

Bağlantı Bağlam Yöneticileri Bağlantı Bağlam Yöneticisi Örneği bazı sürücülerde bağlantılar bağlam yöneticileridir: with automatic commit (try), rollback (except), close (finally) şablon: with dbapi2.connect(dsn) as connection: cursor = connection.cursor() cursor.close() with dbapi2.connect(dsn) as connection: cursor = connection.cursor() statement = """CREATE TABLE MOVIE ( ID SERIAL PRIMARY KEY, TITLE VARCHAR(80), YR NUMERIC(4), SCORE FLOAT, VOTES INTEGER DEFAULT 0, DIRECTORID INTEGER REFERENCES PERSON (ID) )""" cursor.close() 17 / 58 18 / 58 İmleç Bağlam Yöneticileri İmleç Bağlam Yöneticisi Örneği bazı sürücülerde imleçler de bağlam yöneticileridir otomatik kapama template: with dbapi2.connect(dsn) as connection: with connection.cursor() as cursor: with dbapi2.connect(dsn) as connection: with connection.cursor() as cursor: statement = """CREATE TABLE CASTING ( MOVIEID INTEGER REFERENCES MOVIE (ID), ACTORID INTEGER REFERENCES PERSON (ID), ORD INTEGER, PRIMARY KEY (MOVIEID, ACTORID) )""" 19 / 58 20 / 58

Komutlar SQL Enjeksiyonu Örneği komutları katar formatlama ile oluşturmak güvenli değil dış kaynaklardan yapılan girdilere asla güvenme SQL enjeksiyonu saldırıları kötü örnek name = input( What is your name? ) statement = """INSERT INTO Students (Name) VALUES ( %s )""" % name INSERT INTO Students (Name) VALUES ( Robert ); DROP TABLE Students;-- ) INSERT INTO Students (Name) VALUES ( Robert ); DROP TABLE Students;-- ) http://xkcd.com/327/ 21 / 58 22 / 58 Yer Tutucular Yer Tutucu Örnekleri çoklu kullanarak: değerler için yer tutucular farklı sürücülerin farklı biçimde: %s,?,... gerçek parametreler çoklu veya sözlük ile sağlanır statement = """INSERT INTO MOVIE (TITLE, YR) VALUES (%s, %s)""" cursor.execute(statement, (title, year)) sözlük kullanarak: statement = """INSERT INTO MOVIE (TITLE, YR) VALUES (%(title)s, %(year)s)""" cursor.execute(statement, { year : year, title : title}) 23 / 58 24 / 58

Sonuçların Alınması Sonuç Alma Örneği yönetmenler ve yönettikleri filmler imleçle döngü içinde dolaşmak yerine sonuçların alınması:.fetchall().fetchone() statement = """SELECT ID, NAME FROM PERSON""" people = cursor.fetchall() for person_id, name in people: statement = """SELECT TITLE FROM MOVIE WHERE (DIRECTORID = %s)""" cursor.execute(statement, (person_id,)) directed = cursor.fetchall() print( {}:.format(name)) for (title,) in directed: print( {}.format(title)) 25 / 58 26 / 58 Referanslar Problem Yardımcı Kaynak Python Database API Specification v2.0: https://www.python.org/dev/peps/pep-0249/ veri modeli ile yazılım modeli arasında uyumsuzluk veri tabanı bağıntılar şeklinde: bağıntı, çoklu, dış anahtar,... yazılım nesneye dayalı: nesne, yöntem,... 27 / 58 28 / 58

Model Farkı Örneği Model Farkı Örneği filme oyuncu ekleme: SQL tanımları CREATE TABLE MOVIE (ID INTEGER PRIMARY KEY, TITLE VARCHAR(80) NOT NULL) CREATE TABLE PERSON (ID INTEGER PRIMARY KEY, NAME VARCHAR(40) NOT NULL) CREATE TABLE CASTING ( MOVIEID INTEGER REFERENCES MOVIE (ID), ACTORID INTEGER REFERENCES PERSON (ID), PRIMARY KEY (MOVIEID, ACTORID) ) filme oyuncu ekleme: SQL işlemleri INSERT INTO MOVIE (ID, TITLE) VALUES (110, Sleepy Hollow ) INSERT INTO PERSON (ID, NAME) VALUES (26, Johnny Depp ) INSERT INTO CASTING (MOVIEID, ACTORID) VALUES (110, 26) 29 / 58 30 / 58 Model Farkı Örneği Model Farkı Örneği filme oyuncu ekleme: Python tanımları class Person: def init (self, name): self.name = name class Movie: def init (self, title): self.title = title self.cast = [] filme oyuncu ekleme: Python işlemleri movie = Movie( Sleepy Hollow ) actor = Person( Johnny Depp ) movie.add_actor(actor) def add_actor(self, person): self.cast.append(person) 31 / 58 32 / 58

Nesne/Bağıntı Dönüşümü SQLAlchemy yazılım bileşenleri veri tabanı bileşenleriyle eşleştirilir nesne arayüzünü SQL komutlarına çeviriyor model SQL software relation table class tuple row object (instance) attribute column attribute nesne-bağıntı dönüştürücü bir Python sınıfı SQL tablo tanımı dönüştürücü sınıfı tabloya dönüştürür 33 / 58 34 / 58 Bağlantı Örneği Sınıf Örneği from sqlalchemy import create_engine uri = postgres://vagrant:vagrant@localhost:5432/itucsdb engine = create_engine(uri, echo=true) from sqlalchemy import MetaData metadata = MetaData() class Movie: def init (self, title, year=none, score=none, votes=none): self.title = title self.yr = year self.score = score self.votes = votes 35 / 58 36 / 58

Tablo Örneği Dönüştürücü Örneği from sqlalchemy import Column, Table from sqlalchemy import Float, Integer, String movie_table = Table( Movie, metadata, Column( id, Integer, primary_key=true), Column( title, String(80), nullable=false), Column( yr, Integer), Column( score, Float) Column( votes, Integer) ) from sqlalchemy.orm import mapper mapper(movie, movie_table) 37 / 58 38 / 58 Tablo Yaratma Oturumlar metadata.create_all(bind=engine) CREATE TABLE "Movie" ( id SERIAL NOT NULL, title VARCHAR(80) NOT NULL, yr INTEGER, score FLOAT, votes INTEGER, PRIMARY KEY (id) ) veri işlemleri oturumlar içinde yürütülür sonlandırma veya geri alma ile sonlandırılır oturumlar değişikliğe uğrayan veya yeni nesneleri izler 39 / 58 40 / 58

Oturum Örneği Oturum Örneği: Güncelleme from sqlalchemy.orm import sessionmaker Session = sessionmaker(bind=engine) session = Session() movie.votes = 23283 session.commit() UPDATE "Movie" SET votes=%(votes)s WHERE "Movie".id = %(Movie_id)s { Movie_id : 1, votes : 23283} 41 / 58 42 / 58 Oturum Örneği: Silme Sorgulama Örnekleri session.delete(movie) session.commit() DELETE FROM "Movie" WHERE "Movie".id = %(id)s { id : 1} session.query(movie) SELECT "Movie".id AS "Movie_id", "Movie".title AS "Movie_title", "Movie".yr AS "Movie_yr", "Movie".score AS "Movie_score", "Movie".votes AS "Movie_votes" FROM "Movie" 43 / 58 44 / 58

Sorgulama Örnekleri: Sütunları Seçme SQLAlchemy Örneği: Sıralama session.query(movie).order_by(movie.yr) session.query(movie.title, Movie.score) SELECT "Movie".title AS "Movie_title", "Movie".score AS "Movie_score" FROM "Movie" SELECT "Movie".id AS "Movie_id", "Movie".title AS "Movie_title", "Movie".yr AS "Movie_yr", "Movie".score AS "Movie_score", "Movie".votes AS "Movie_votes" FROM "Movie" ORDER BY "Movie".yr 45 / 58 46 / 58 SQLAlchemy Örneği: Satırları Seçme SQLAlchemy Örneği: Yüklemle Satırları Seçme session.query(movie).filter_by(yr=1999) SELECT "Movie".id AS "Movie_id", "Movie".title AS "Movie_title", "Movie".yr AS "Movie_yr", "Movie".score AS "Movie_score", "Movie".votes AS "Movie_votes" FROM "Movie" WHERE "Movie".yr = %(yr_1)s { yr_1 : 1999} session.query(movie).filter(movie.yr < 1999) SELECT "Movie".id AS "Movie_id", "Movie".title AS "Movie_title", "Movie".yr AS "Movie_yr", "Movie".score AS "Movie_score", "Movie".votes AS "Movie_votes" FROM "Movie" WHERE "Movie".yr < %(yr_1)s { yr_1 : 1999} 47 / 58 48 / 58

Dış Anahtarlar Dış Anahtar Örneği tablo tanımlarına dış anahtar sütunlarını ekle dönüştürücüye relationship özelliği ekle özellik ismi başvuran tablodaki nitelik ismi backref parametresi başvurulan tablodaki nitelik ismi class Person: def init (self, name): self.name = name person_table = Table( Person, metadata, Column( id, Integer, primary_key=true), Column( name, String(40), nullable=false, unique=true) ) mapper(person, person_table) 49 / 58 50 / 58 Dış Anahtar Örneği Dış Anahtar Örneği from sqlalchemy import ForeignKey movie_table = Table( Movie, metadata, Column( id, Integer, primary_key=true), Column( title, String(80)), Column( yr, Integer), Column( score, Float), Column( votes, Integer), Column( directorid, Integer, ForeignKey( Person.id )) ) from sqlalchemy.orm import relationship mapper(movie, movie_table, properties={ director : relationship(person, backref= directed ) }) 51 / 58 52 / 58

Dış Anahtar Örneği Backref Örneği person = movie.director print(person.name) SELECT "Person".id AS "Person_id", "Person".name AS "Person_name" FROM "Person" WHERE "Person".id = %(param_1)s { param_1 : 8} for movie in person.directed: print(movie.title) SELECT "Movie".id AS "Movie_id", "Movie".title AS "Movie_title",... "Movie".directorid AS "Movie_directorid" FROM "Movie" WHERE %(param_1)s = "Movie".directorid { param_1 : 8} 53 / 58 54 / 58 Dış Anahtar Örneği Dış Anahtar Örneği ikinci bir tablo kullanarak casting_table = Table( Casting, metadata, Column( movieid, Integer, ForeignKey( Movie.id ), primary_key=true), Column( actorid, Integer, ForeignKey( Person.id ), primary_key=true), Column( ord, Integer) ) mapper(movie, movie_table, properties={ director : relationship(person, backref= directed ), cast : relationship(person, backref= acted, secondary=casting_table) }) 55 / 58 56 / 58

Dış Anahtar Örneği Kaynaklar for movie in session.query(movie): print( {}:.format(movie.title)) for person in movie.cast: print( {}.format(person.name)) for person in session.query(person): print( {}:.format(person.name)) for movie in person.acted: print( {}.format(movie.title)) Yardımcı Kaynak SQLAlchemy Documentation: http://docs.sqlalchemy.org/ 57 / 58 58 / 58