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).

Avatar for Gonz Saavedra

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.