Wednesday, November 28, 2012

SQLAlchemy y SAP HANA

Este Lunes 26 de Noviembre participe en el Montréal-Python 33: Qualified Quasar un realmente interesante meetup organizado por mis amigos del Montreal Python Community. No fui solo como asistente, pero tambien como speaker y hable sobre "Python y SAP" (Voy a escribir un blog apenas los videos esten listos)...durante mi presentacion, alguien me pregunto sobre SQLAlchemy y SAP HANA.

Hasta ese dia, nunca habia utilizado SQLAlchemy y realmente no sabia si algo asi existia para SAP HANA, pero algo...llamenlo premonicion...me dijo que alguien estaba trabajando en eso...asi que le respondi..."No lo se, pero estoy casi seguro de que alguien esta trabajando en eso...yo les aviso apenas tenga alguna noticia".

El Martes, comence a revisar algo de SQLAlchemy pero no lo logre captar que es lo grandioso que tiene...y de pronto, Srdjan Boskovic me dijo Martin Stein habia estado trabajando en...adivinen que? SQLAlchemy para SAP HANA...me pregunto si podia jugar un poco con eso y quizas escribir un blog...ya se podran imaginar lo feliz y emocionado que estaba...una nueva tecnologia para aprender...horas de romperme la cabeza tratando de entenderlo...levantarme temprano y acostarme tarde solo para escribir un blog en el SCN...estaba el paraiso sin duda alguna...pero...ya estaba ocupado con varias cosas, asi que les prometi a ambos que le daria una mirada al dia siguiente...

Hoy dia, me levante muy temprano...mas temprano de lo normal y comence a analizar el codigo y los ejemplos...lei un par de tutoriales y luego comenze a trabajar por primera vez con SQLAlchemy...con un poco de gran ayuda de Martin...finalmente termine mi aplicacion...puedo decir que me tomo cerca de 15 horas sin pausa...pero cuando amas tu trabajo...simplemente no puedes parar...

En fin...todavia esta en "Beta"...no disponible por el momento, asi que...les voy a mostrar como usarlo como se ve una aplicacion utilizandolo...por supuesto...regrese a mi micro framework favorito de Python...el asombroso Bottle.

Asi que, para este proyecto necesitamos las librerias de Bottle, PyODBC y SQLAlchemy_HANA. Tambien utilice una conexion ODBC con mi servidor SAP HANA en AWS.

Veamos el codigo fuente...

Band_Model.py
from sqlalchemy import Column, Integer, String
from meta import Base


class Singer(Base):
    __tablename__ = 'SINGERS'

    singer_id = Column(Integer, primary_key=True)
    first_name = Column(String(20))
    last_name = Column(String(20))
    band = Column(String(20))

    def __init__(self, singer_id, first_name, last_name, band):
        self.singer_id = singer_id
        self.first_name = first_name
        self.last_name = last_name
        self.band = band

    def __repr__(self):
        return "<Single('{first_name}', '{last_name}'\
                ,'{band}')>".format(**self.__dict__)

Como no tenia ningun idea en la cual basar mi aplicacion, decidi crear algo para organizar a algunos de mis cantantes favoritos de Punk Rock...el primer script Band_Model.py se encargara de crear la tabla SINGERS en SAP HANA.

Band_App.py
from bottle import get, post, request, run, redirect
from meta import Base, engine, Session
from sqlalchemy import *
from sqlalchemy.orm.exc import NoResultFound
from Band_Model import Singer

def connect():
    Base.metadata.drop_all(engine)
    Base.metadata.create_all(engine)

@get('/show')
def show_form():
    output = "<div align='center'>"
    output += "<h1>SQLAlchemy on SAP HANA</h1>"
    output += "<a href='/add_singer'>Add Singer</a>"
    output += "<table border=1>"
    singers = Session.query(Singer).all()
    output += "<tr><th>Id</th><th>First Name</th>"\
              "<th>Last Name</th><th>Band</th>"\
              "<th>Update</th><th>Delete</th></tr>"
    for singer in singers:
        update = "/update_singer?singer_id=" + str(singer.singer_id)
        delete = "/delete_singer?singer_id=" + str(singer.singer_id)
        output += "<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td>"\
                  "<td><a href='%s'>Update</a></td><td>"\
                  "<a href='%s'>Delete</a></td></tr>"\
        % (singer.singer_id, singer.first_name, singer.last_name,
           singer.band, update, delete)
    output += "</table></div>"
    return output

@get('/add_singer')
def add_form():
    query = ""
    singer_id = 0
    try:
        query = Session.query(Singer.singer_id).\
                    order_by(desc(Singer.singer_id)).first()
        singer_id = int(query[0]) + 1
    except NoResultFound:
        singer_id = 1
    except TypeError:
        singer_id = 1
    output = "<DIV ALIGN='CENTER'><TABLE>"
    output += "<FORM METHOD='POST'><TR><TD>"
    output += "<INPUT TYPE='HIDDEN' NAME='Singer_Id'"\
              "value='%s'></TD></TR>" % (singer_id)
    output += "<TR><TD>First Name</TD><TD>"
    output += "<INPUT TYPE='TEXT' NAME='First_Name'></TD></TR>"
    output += "<TR><TD>Last Name</TD><TD>"
    output += "<INPUT TYPE='TEXT' NAME='Last_Name'></TD></TR>"
    output += "<TR><TD>Band</TD>"
    output += "<TD><INPUT TYPE='TEXT' NAME='Band'></TD></TR>"
    output += "<TR><TD COLSPAN='2' ALIGN='CENTER'>"
    output += "<INPUT TYPE='SUBMIT' value='Add Singer' NAME='Add_Singer'>"
    output += "<INPUT TYPE='RESET' value='Clear'></TD></TR>"
    output += "</FORM><TABLE></DIV>"
    return output

@post('/add_singer')
def create():
    Singer_Id = request.forms.get('Singer_Id')
    Singer_Id = int(Singer_Id)
    First_Name = request.forms.get('First_Name')
    Last_Name = request.forms.get('Last_Name')
    Band = request.forms.get('Band')
    singer = Singer(Singer_Id, First_Name, Last_Name, Band)
    Session.add(singer)
    Session.commit()
    redirect("/show")

@get('/update_singer')
def update_form():
    query = ""
    singer_id = 0
    singer_id = int(request.query.singer_id)
    query = Session.query(Singer.first_name, Singer.last_name, Singer.band).\
                          filter_by(singer_id=singer_id).first()
    first_name = query[0]
    last_name = query[1]
    band = query[2]
    output = "<DIV ALIGN='CENTER'><TABLE>"
    output += "<FORM METHOD='POST'><TR><TD>"
    output += "<INPUT TYPE='HIDDEN' NAME='Singer_Id'"\
              "value='%s'></TD></TR>" % singer_id
    output += "<TR><TD>First Name</TD><TD>"
    output += "<INPUT TYPE='TEXT' NAME='First_Name'"\
              "value='%s'></TD></TR>" % first_name
    output += "<TR><TD>Last Name</TD><TD>"
    output += "<INPUT TYPE='TEXT' NAME='Last_Name'"\
              "value='%s'></TD></TR>" % last_name
    output += "<TR><TD>Band</TD>"
    output += "<TD><INPUT TYPE='TEXT' NAME='Band'"\
              "value='%s'></TD></TR>" % band
    output += "<TR><TD COLSPAN='2' ALIGN='CENTER'>"
    output += "<INPUT TYPE='SUBMIT' value='Update Singer'"\
              "NAME='Update_Singer'>"
    output += "<INPUT TYPE='RESET' value='Clear'></TD></TR>"
    output += "</FORM><TABLE></DIV>"
    return output

@post('/update_singer')
def update():
    Singer_Id = request.forms.get('Singer_Id')
    Singer_Id = int(Singer_Id)
    First_Name = request.forms.get('First_Name')
    Last_Name = request.forms.get('Last_Name')
    Band = request.forms.get('Band')
    Session.query(Singer).filter_by(singer_id=Singer_Id).\
    update({'first_name': First_Name, 'last_name': Last_Name,
           'band': Band})
    Session.commit()
    redirect("/show")

@get('/delete_singer')
def delete():
    singer_id = 0
    singer_id = int(request.query.singer_id)
    Session.query(Singer).filter_by(singer_id=singer_id).delete()
    Session.commit()
    redirect("/show")

connect()
run(host='localhost', port=8080)


El script Band_App.py se encargara de crear la aplicacion Bottle, agregar nuevos cantantes, modificar los existentes y tambien borrarlos. Como pueden ver...no hay ni un solo SELECT, puesto que SQLAlchemy maneja las tablas como clases y provee metodos para manejar todo. Ahora finalmente entiendo toda la emocion acerca de SQLAlquemy...es realmente impresionante...y facil de usar...luego de que has gastado  15 horas trabajando con el...

Veamos las imagenes...


Por supuesto, la primera vez que ejecutamos la aplicacion, la tabla va a estar vacia...acabamos de crearla...



Podemos insertar nuestro primer registro...o quizas mas para que sea mejor...


Oh...parece que Mr. Ness no tiene una banda...no hay problema...podemos actualizar su registro...


Podemos revisar si todo esta OK o no...


Si, ahora todo se ve bien...sin embargo...tenemos que demostrar que en enlace para borrar tambien funciona...asi que...lastima por los muchachos...


Bueno...eso es todo...me diverti mucho trabajando en esto...y es otra prueba de que SAP HANA es realmente versatil y puede ser usado en cualquier lugar. Hasta la proxima -:)

Saludos,

Blag.

No comments: