sql >> Base de Datos >  >> NoSQL >> HBase

Creación de una aplicación web CRUD simple y un almacén de imágenes con Cloudera Operational Database y Flask

Cloudera Operational Database (COD) es una solución dbPaaS gestionada disponible como experiencia en Cloudera Data Platform (CDP). Ofrece acceso de cliente multimodal con clave-valor NoSQL mediante las API de Apache HBase y SQL relacional con JDBC (a través de Apache Phoenix). Este último hace que COD sea accesible para los desarrolladores que están acostumbrados a crear aplicaciones que usan MySQL, Postgres, etc. Los principales beneficios de COD incluyen:

  • Ajuste de escala automático:basado en la utilización de la carga de trabajo del clúster y pronto tendrá la capacidad de aumentar o reducir el clúster
  • Ajuste automático:mejor rendimiento dentro del espacio de infraestructura existente.
  • Reparación automática:resuelve problemas operativos automáticamente (próximamente).

En este blog, demostraré cómo COD se puede usar fácilmente como un sistema de back-end para almacenar datos e imágenes para una aplicación web simple. Para construir esta aplicación, usaremos Phoenix, uno de los componentes subyacentes de COD, junto con Flask. Para almacenar imágenes, usaremos una capacidad HBase (almacenamiento backend de Apache Phoenix) llamada MOB (objetos medianos). MOB nos permite leer/escribir valores de 100k-10MB rápidamente.

*Para facilitar el uso del desarrollo, también puede usar el servidor de consultas Phoenix en lugar de COD. El servidor de consultas es una pequeña compilación de phoenix diseñada solo para fines de desarrollo, y los datos se eliminan en cada compilación.

Todo el código está en mi repositorio de github.

Instrucciones:

1. Inicie sesión en Cloudera Management Console y seleccione la experiencia de la base de datos operativa

2. Elija su entorno y asigne un nombre a su base de datos

3. Una vez que la base de datos esté activa, tome la URL del cliente ligero JDBC

4. Establezca su contraseña de carga de trabajo de CDP

5. Clone el proyecto git repo y los requisitos de instalación:$ pip install -r requirements.txt

6. Vaya a la carpeta de la aplicación y ejecute "setup.py". Esto creará una tabla con 3 registros de usuarios y sus imágenes $ python setup.py

7. Ejecute el servidor web Flask para que se inicie la aplicación web:$ FLASK_APP=app.py python -m Flask run –port=8888 –host=127.0.0.1  –reload –with-threads –debugger

8. Vaya a http://localhost:8888/users en su navegador. ¡Deberías poder ver la aplicación ejecutándose! Tan simple como eso.

Pasando por El Código

1. La clase Schema, básicamente contiene los detalles de la conexión y los métodos de creación y eliminación de tablas. Como puede ver, la columna "foto" es de tipo VARBINARIO, que se traduce en un objeto MOB en HBase:

import phoenixdb
import phoenixdb.cursor
class Schema:
    def __init__(self):
        opts = {}
        opts['authentication'] = 'BASIC'
        opts['avatica_user'] = '<cod workload username>'
        opts['avatica_password'] = '<cod workload pw>'
        database_url = "<cod thin jdbc url>"
        self.TABLENAME = "users"
        self.conn = phoenixdb.connect(database_url, autocommit=True,**opts)
        self.curs = self.conn.cursor()

    def create_users_table(self):
        query = """
        CREATE TABLE IF NOT EXISTS """+self.TABLENAME+""" (
        username VARCHAR NOT NULL,
        firstname VARCHAR,
        lastname  VARCHAR,
        telephone VARCHAR,
        message VARCHAR,
        email VARCHAR,
        photo VARBINARY,
        photo_name VARCHAR,
        photo_type VARCHAR,
        photo_chars VARCHAR
        CONSTRAINT my_pk PRIMARY KEY (username))
        """
        self.curs.execute(query)

    def drop_users_table(self):
        query = "DROP TABLE "+self.TABLENAME
        self.curs.execute(query)

2 La clase de usuarios es responsable de todas las operaciones de la aplicación con Phoenix. Podemos actualizar/insertar (upsert en lenguaje fénix), eliminar, listar y manejar transacciones de imágenes:

import phoenixdb
from schema import Schema
import json
class UsersModel:
    TABLENAME = "users"

    def __init__(self):
        db = Schema()
        self.conn=db.conn
        self.curs=db.curs

    def upsert(self, params):

        sql = "upsert into " + self.TABLENAME + \
            " (username ,message,telephone,firstname,lastname,email) \
             values (?,?,?,?,?,?)"
        data = (params.get('username'),params.get('message'),\
            params.get('telephone'),params.get('firstname'),\
            params.get('lastname'),params.get('email'))
        results = self.curs.execute(sql,data)
        return results

    def upsert_photo(self, params):
        if params.get('photo') is None:
            photo = bytes('','utf-8')
        else:
            photo = params.get('photo')

        sql = "upsert into " + self.TABLENAME + \
            " (username, photo,photo_name) values (?,?,?)"

        data = (params.get('username'),photo, params.get('photo_name'))
        results = self.curs.execute(sql,data)
        return results

    def delete(self, username):
        query = f"DELETE from {self.TABLENAME} " \
                f"WHERE username = {username}"

        self.curs.execute(query)

    def list_items(self, where_clause="",format="json"):
        query = f"SELECT username ,email,message,telephone,firstname,\
            lastname,photo_name " \
            f"from {self.TABLENAME} WHERE  " + where_clause

        self.curs.execute(query)
        if format=="json":
            r = [dict((self.curs.description[i][0].lower(), value) \
                   for i, value in enumerate(row)) for row in \
                   self.curs.fetchall()]
            self.conn.close()
            data={'data': r }
            return json.dumps(data)

        result_set=self.curs.fetchall()
        result = [{column: row[i]
            for i, column in enumerate(result_set[0].keys())}
                for row in result_set]
        return result
    def get_image(self, username):
        query = f"SELECT photo,photo_name " \
                f"from {self.TABLENAME} WHERE  username='"+username+"'"

        self.curs.execute(query)
        row = self.curs.fetchone()
        return row

3. app.py es el enrutador principal de la aplicación. Contiene todo el manejo con las entradas del usuario y enrutarlas a los métodos de conexión. Separé el manejo de imágenes para facilitar su uso, y de esa manera puedo obtener una imagen específica para un usuario:

from flask import Flask, request, send_file ,jsonify,render_template
import phoenixdb
import io
from users import UsersModel
from schema import Schema
import json

app = Flask(__name__)

@app.after_request
def add_headers(response):
    response.headers['Access-Control-Allow-Origin'] = '*'
    response.headers['Access-Control-Allow-Headers'] =  \
        "Content-Type, Access-Control-Allow-Headers, Authorization, \
        X-Requested-With"
    response.headers['Access-Control-Allow-Methods']=  "POST, GET, PUT, \
    DELETE, OPTIONS"
    response.headers['Allow']=  "POST, GET, PUT, OPTIONS"
    return response

@app.route("/")
def hello():
    return "Hello World!"

@app.route("/users")
def return_form():
    return render_template("users.html")

@app.route("/handle_data",methods=['POST'])
def handle_data():
    if request.method == 'POST':
        username = request.form['username']
        firstname = request.form['firstname']
        lastname = request.form['lastname']
        email = request.form['email']
        telephone = request.form['telephone']
        message = request.form['message']
        photo = request.files['photo']
        photo_bytes = photo.read()
        model=Schema()
        usersmodel=UsersModel()
        data = {'username':f"{username}",'firstname':f"{firstname}",\
            'lastname':f"{lastname}",'telephone':f"{telephone}",\
            'message':f"{message}"}
        photo_data = {'username':f"{username}",\
            'photo':photo_bytes,\
            'photo_name':f"{photo.filename}"}
        usersmodel.upsert(data)
        usersmodel.upsert_photo(photo_data)
        return render_template('users.html')
    else:
        return render_template('users.html')

@app.route("/get_users",methods=['GET'])
def get_users():
    if request.method == 'GET':
        usersmodel=UsersModel()
        users = usersmodel.list_items("1=1")
        return users

@app.route("/get_image",methods=['GET'])
def get_image():
    if request.method == 'GET':
        username = request.args.get('username')
        usersmodel=UsersModel()
        imagedb = usersmodel.get_image(username)
        return send_file(io.BytesIO(imagedb[0]),mimetype='image/png', \
            attachment_filename=imagedb[1])

if __name__ == "__main__":
    Schema()
    app.run(debug=True, port=8888)

Próximos pasos, puede usar este repositorio de github para probar su aplicación.

Espero que lo encuentres útil, ¡¡Feliz codificación!!