Upgrade to Pro — share decks privately, control downloads, hide ads and more …

SQLAlchemy - Stepping up

SQLAlchemy - Stepping up

Slides for my presentation on SQLAlchemy basics for the tryomeetup (spanish).

Gonz Saavedra

July 31, 2013
Tweet

More Decks by Gonz Saavedra

Other Decks in Technology

Transcript

  1. 2 Introducción • “The main goal of SQLAlchemy is to

    change the way you think about databases and SQL!”. • El lenguaje SQL es: popular, controversial y... considerablemente rechazado por sus propios usuarios. • Las bases de datos relacionales son una gran herramienta. • SQLAlchemy es un tookit.
  2. SQLAlchemy 3 • “SQLAlchemy is the Python SQL toolkit and

    Object Relational Mapper that gives application developers the full power and flexibility of SQL.” • Liberado en febrero del 2005 • Autor Mike Bayer (aka zzzeek) • Maduro, eficiente, modular, no-opinionado. Acerca del proyecto
  3. 4 SQLAlchemy • Incorporar el uso de múltiples bases de

    datos y adapters en una interfaz tan consistente como sea posible... Filosofía • ...pero exponer los diferentes comportamientos y características de cada backend. • Proveer automatización y DRY. • Permitir expresar las tareas a realizar utilizando patrones declarativos.
  4. 6 TOC • Level 0 - Python DBAPI • Level

    1- SQLAlchemy Engine • Level 2 - SQLAlchemy SQL Expressions • Level 3 - SQLAlchemy Object relational mapper
  5. 8

  6. Python DBAPI • Python Database API Specification 2.0 (PEP 249)

    • Es una API spec definida para mejorar la consistencia, portabilidad y alcance entre los paquetes python usados para acceder a bases de datos, comúnmente llamados database drivers o adapters. • Muchas implementaciones, ej: • PostgreSQL: psycopg2, pyPgSQL, PyGreSQL, ... • MySQL: mysql-python, pyMySQL, myconnpy, ... • ... 9
  7. Python DBAPI 10 Ejemplo: Psycopg2 import  psycopg2 conn  =  psycopg2.connect("postgres://gonz@localhost/test")

    cur  =  conn.cursor() cur.execute("CREATE  TABLE  registry  (key  varchar,  value  varchar)") cur.executemany("INSERT  INTO  registry  (key,  value)  VALUES  (%s,  %s)",                                [("key1",  "value1"),  ("key2",  "value2")])                       cur.execute('SELECT  key,  value  FROM  registry') for  key,  value  in  cur.fetchall():        print  key,  value conn.commit()   cur.close() conn.close()
  8. Python DBAPI 11 • Componer strings para formar SQL válido

    es una tarea engorrosa. • Si es necesario hablar con mas de una base, es necesario usar mas de un adapter y queda de nuestro lado reusar el código en común y manejar sus diferencias. • Los bound parameters de queries tienen pitfalls. • Debuggear y mantener código se hace muy difícil a medida que la interacción entre la aplicación y la base crece.
  9. 13

  10. 14 Engine • Una engine es un wrapper liviano entorno

    a un adapter. • Usa una Connection Pool para reusar conexiones existentes del adapter a la base de datos. • Usa un Dialect que se encarga de hacer la traducción entre la engine y los diferentes adapters.
  11. 15 Engine from  sqlalchemy  import  create_engine   engine  =  create_engine("postgres://gonz@localhost/test")

      conn  =  engine.connect()   rows  =  conn.execute("SELECT  key,  value  FROM  registry") for  key,  value  in  rows:        print  key,  value   conn.close()
  12. 16 Engine from  sqlalchemy  import  create_engine   engine  =  create_engine("postgres://gonz@localhost/test")

      rows  =  engine.execute("SELECT  key,  value  FROM  registry") for  key,  value  in  rows:        print  key,  value
  13. 18

  14. 19 SQL Expression Language & Schema/Types • Representación de las

    estructuras de la base de datos, expresiones SQL y tipos de datos usando objetos y demás estructuras de python. • Modelado para parecerse tanto como sea posible al lenguaje de la base de datos en uso. • Abstracción mínima para unificar las diferencias y proveer una API consistente. • No implementa solamente el mínimo común denominador, las features específicas del backend son soportadas.
  15. 20 Metadata • Almacena la descripción de la estructura de

    la base de datos (o parte de ella) mediante objetos python (Tablas, columnas, constraints, etc...). • Puede emitir DDL statements, ej. crear todas las tablas modeladas en la base. • Extremadamente útil cuando es necesario inspeccionar la schema de la base, ej. encontrar todas las claves primarias en cada tabla. • Es posible también generar toda la metadata desde una estructura existente en la base de datos, esto es llamado reflection.
  16. 21 from  sqlalchemy  import  *   metadata  =  MetaData()  

    users  =  Table('users',  metadata,        Column('user_id',  Integer,  primary_key  =  True),        Column('user_name',  String(16),  nullable  =  False),        Column('email_address',  String(60)), ) engine  =  create_engine('sqlite:///:memory:') metadata.create_all(engine) Metadata, Tablas y Columnas
  17. 22 metadata  =  MetaData() user_reflected  =  Table('users',  metadata,  autoload=True,  

    autoload_with=engine)   print  user_reflected.__repr__() #Table('users',  MetaData(bind=None), #            Column(u'user_id',  INTEGER(),  table=<users>, #                          primary_key=True,  nullable=False), #            Column(u'user_name',  VARCHAR(length=16), #                          table=<users>,  nullable=False), #            Column(u'email_address',  VARCHAR(length=60), #                          table=<users>),  schema=None) metadata.reflect(engine) Reflection
  18. 23 SQL Expressions • Objetos python que pueden pueden ser

    compuestos y generar SQL (strings) con el objetivo de ser ejecutadas por la base de datos, vía la engine y finalmente el adapter. • Estos objetos y las strings SQL que generan representan unidades de SQL, ej: una comparación, conjunciones, un update statement, etc... • Estas strings se pueden obtener fácilmente a partir de los objetos, lo cual es muy útil para debugging.
  19. 24 SQL Expressions >>>  conn  =  engine.connect()   >>>  str(users.insert())

    'INSERT  INTO  users  (user_id,  user_name,  email_address)                VALUES  (:user_id,  :user_name,  :email_address)'   >>>  conn.execute(users.insert(),  [ >>>          {'user_name':  'valarm',  'email':  '[email protected]'}, >>>          {'user_name':  'aryas',  'email':  '[email protected]'} >>>  ])
  20. 25 SQL Expressions >>>  users.c.user_name Column('user_name',  String(length=16),  table=<users>,  nullable=False)  

    >>>  conj  =  (users.c.user_name  ==  'valar')   >>>  conj <sqlalchemy.sql.expression.BinaryExpression  object  at  0x105f62d90>   >>>  str(conj) 'users.user_name  =  :user_name_1'   >>>  str((users.c.user_name  ==  'valar')  |  (users.c.user_name  ==  None)) 'users.user_name  =  :user_name_1  OR  users.user_name  IS  NULL'
  21. 26 SQL Expressions >>>  s  =  (select([users.c.user_name,  addresses.c.email_address])    

                 .select_from(users.outerjoin(addresses)))   >>>  print  s SELECT  users.user_name,  addresses.email_address    FROM  users    LEFT  OUTER  JOIN  addresses  ON  users.id  =  addresses.user_id
  22. 27 SQL Expressions >>>  expr  =  (users.c.user_name  ==  'valar')  

    >>>  expr.left Column('user_name',  String(length=16),  table=<users>,  nullable=False) >>>  expr.right BindParameter(u'%(4395010384  user_name)s',  'valar',  type_=String(length=16)) >>>  expr.operator <function  operator.eq>   >>>  from  sqlalchemy.dialects  import  mysql,  postgresql   >>>  print  expr.compile(dialect=mysql.dialect()) users.user_name  =  %s   >>>  print  expr.compile(dialect=postgresql.dialect()) users.user_name  =  %(user_name_1)s   >>>  compiled  =  expr.compile(dialect=postgresql.dialect())   >>>  compiled <sqlalchemy.dialects.postgresql.psycopg2.PGCompiler_psycopg2  at  0x105b56990>   >>>  compiled.params {u'user_name_1':  'valar'}
  23. 29

  24. 30 Object relational mapper • “The SQLAlchemy Object Relational Mapper

    presents a method of associating user-defined Python classes with database tables, and instances of those classes (objects) with rows in their corresponding tables.” • Visión de la data en el ORM enfocada en el domain model.
  25. 31 ORM • El proceso de mapping generalmente consta de

    definir tablas y clases (llamadas normalmente modelos) y asociarlos. • Los ORMs modernos integran ambos pasos en uno. La tabla se genera a partir de la definición del modelo, sus atributos por ej. son convertidos en columnas • SQLAlchemy permite hacer todo explícitamente, crear una tabla como vimos anteriormente, un modelo y luego definir un mapper para asociar cada tabla con su modelo. • Y también provee una extensión, llamada declarative, que realiza esto automáticamente por nosotros.
  26. 32 from  sqlalchemy.ext.declarative  import  declarative_base from  sqlalchemy  import  Column,  Integer,

     String   Base  =  declarative_base()   class  User(Base):        __tablename__  =  'users'          id  =  Column(Integer,  primary_key=True)        username  =  Column(String(16))        email  =  Column(String(60))          def  __repr__(self):                return  "<User('%s')>"  %  self.username ORM
  27. 33 >>>  Base.metadata MetaData(bind=None)   >>>  User.metadata MetaData(bind=None) >>>  User.__table__

    Table('users',  MetaData(bind=None),            Column('id',  Integer(),  table=<users>,  primary_key=True,                          nullable=False),            Column('username',  String(length=16),  table=<users>),            Column('email',  String(length=60),  table=<users>),                          schema=None)   >>>  User.__mapper__ <Mapper  at  0x10a35eed0;  User> ORM
  28. 34 Sessions • SQLAlchemy se basa en un patrón Data

    Mapper, no Active Record. • Los detalles de la persistencia y el objeto a persistir se manejan de forma separada. >>>  from  sqlalchemy.orm  import  Session   >>>  s  =  Session()   >>>  user  =  User(username='gonz',  email='[email protected]') >>>  s.add(user)   >>>  user2  =  s.query(User).filter(User.username  ==  'gonz').one() >>>  user2.email  =  '[email protected]' >>>  s.add(user2)   >>>  user  is  user2 True   >>>  s.commit()
  29. 35 Sessions y Unit of Work • La sesión lleva

    cuenta de todo cuanto se hace en una transacción. • Cuando llega la hora de hacer un commit, organiza inserts/updates/deletes pendientes (Unit of work). • Esto le permite ordenar automáticamente statements según la dependencia topológica de las tablas y agrupar statements redundantes. • Mediante IdentityMaps cada instancia de un modelo en memoria es único, esto no solo ahorra memoria sino también consultas innecesarias a la base.