Artigo
· Jun. 29 21min de leitura

Examplo de aplicação Flask com SQLAlchemy-IRIS - Parte 1

Índice

Parte 1

  • Introduzindo o Flask: uma rápida revisão da documentação do Flask, onde você vai achar tudo o que precisa saber para esse tutorial;
  • Conectando ao InterSystems IRIS: um passo a passo detalhado de como usar SQLAlchemy para conectar a uma instância IRIS.

Parte 2

  • Uma discussão sobre esse tipo de implementação: por que devemos usá-la e em quais situações é aplicável.
  • A aplicação do OpenExchange: se você não está interessado no desenvolvimento desse exemplo, pode ir diretamente a essa seção e aprender a usá-la como um template para sua aplicação flask-iris;
  • Bônus: alguns erros que eu encontrei enquanto desenvolvia e como resolvê-los.

 

Introduzindo o Flask

Flask é uma framework web escrit em Python e desenhada para facilitar a sua vida ao criar uma aplicação web. Ele usa  Werkzeug para lidar com o Web Server Gateway Interface (WSGI), que é a especificação padrão do Python para comunicação de servidores web. Também utiliza Jinja para manusear templates HTML e o Click CLI toolkit para administrar códigos na interface de comando.

 

Instalação

O primeiro passo é opcional. No entanto, é uma boa ideia definir um ambiente virtual antes de iniciar projetos. Você pode fazer isso do terminal mudando o diretório para a pasta que vai usar como raiz e digitando o comando abaixo.

> python -m venv .venv

Se você escolher usar um ambiente virtual, ative-o com o comando abaixo (no Windows) antes de instalar os pré-requisitos:

> .venv\Scripts\activate

Finalmente, instale os pacotes com pip (Python Install Package).

> pip install Flask
> pip install Flask-SQLAlchemy
> pip install sqlalchemy-iris

 

Começo rápido

Vamos seguir a estrutura apresentada nesse Tutorial,  adaptando as conexões para o IRIS. Isso deve minimizar seus problemas ao aprimorar sua aplicação, já que você poderá consultar a  Documentação Flask sem conflitos. Contudo, já que o Flask é bastante flexível, ele não demanda que você siga nenhum tipo de layout, então você pode considerar os passos a seguir como meras sugestões.

A abordagem aqui apresentada é a de usar as aplicações como pacotes e definir uma fábrica de apps para cobrir projetos maiores. Ainda assim, você poderia construir um app Flask inteiro com apenas cinco linhas de código, como demonstrado abaixo.

from flask import Flask

app = Flask(__name__)


@app.route('/')
def ola():
    return 'Olá, mundo!'

 

Uma aplicação de exemplo

Essa seção é um guia dos passos que você precisa seguir para criar a aplicação flask-iris. Você poderá entender esse tutorial mesmo se nunca tiver usado uma framework web antes. Entretanto, se quiser entender completamente a teoria desse tipo de fluxo de trabalho, dê uma olhada no meu artigo anterior sobre Exemplos para usar IRIS com Django, em particular na imagem do início. Você ficará feliz em notar que diferentes frameworks web têm lógicas bastante similares. Ainda assim, no Flask podemos definir as URLs e as views juntas como grupos, chamados blueprints.

Os requisitos são Flask, Flask-SQLAlchemy, e sqlalchemy-iris. Intuitivamente, a ponte entre  aplicação web a sua instância IRIS está construída!
 

O pacote

Primeiramente, crie ou selecione a pasta que deseja usar como raiz. Vamos chamá-la de flaskr-iris. Precisamos que o Python entenda essa pasta como um pacote, então vamos criar um arquivo __init__.py. É nele que vamos colocar nossa fábrica de app.

Passo 1 - Criando a fábrica de app

A fábrica de aplicação é nada mais nada menos que uma função. Dentro dela, temos que definir uma instância Flask() (o app), definir suas configurações, conectar à base de dados e registrar blueprints se decidirmos usá-las. Vamos explorar mais detalhes sobre as blueprints posteriormente, então por ora você não precisa se preocupar com elas.

A função deve ser chamada create_app(). Comece importando o Flask e criando uma instância com o argumento __name__ (veja a documentação para mais detalhes). Então, mapeie a propriedade config com uma chave secreta e a URI da base de dados. Use o formato "dialeto://usuário:senha@host:porta/NAMESPACE", seugindo as recomendações do SQLAlchemy. Nesse caso, o dialeto deve ser "iris", criado pela  CaretDev. O usuário e senha são os que você usa para se conectar à instância IRIS apontada na porta e host, e o namespace é autoexplicativo.

# __init__.py
from flask import Flask

def create_app():
    # crie e configure
    app - Flask(__name__, instance_relative_config=True)
    
    app.config.from_mapping(
        SECRET_KEY = "dev", # sobrescreva com uma chave de geração randômica quando fizer deploy
        SQLALCHEMY_DATABSE_URI = "iris://_SYSTEM:sys@localhost:1972/SAMPLE"
    )

As linhas a seguir da nossa fábrica requerem a base de dados, então vamos reservar esse arquivo por um minuto enquanto falamos de dados e modelos.

Passo 2 - Administrando dados

Vamos usar o SQLAlchemy para importar e administrar dados já que podemos usar o Flask-SQLAchemy e o SQLAlchemy-IRIS para resolver os problemas de conexão. Priemiro, crie o arquivo database.py, onde vamos importar a versão Flask do SQLAlchemy e instanciá-la. Esse passo não necessita a criação de um arquivo separado. No entanto, ela pode ser útil para o desenvolvimento posterior de métodos para lidar com a base de dados.

# database.py
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

A seguir, devemos definir os modelos que dirão ao Python como interpretar cada registro em uma tabela. Crie o arquivo models.py, importe a instância SQLAlchemy e os recursos que você necessita para tratar os dados. Seguindo o exemplo dado na documentação oficial do Flask, podemos criar um blog que precisa de um modelo para os usuários e outro para as postagens. Essa implementação cobre um número substancial de casos e te dá um bom avanço para começar.

Vamos usar Object Relational Mapping (ORM), como sugerido na versão SQLAlchemy 2.0, definindo colunas mapeadas e uma relação one-to-many (um para muitos). Você pode checar como modelar outros tipos de relações no guia SQLAlchemy.

# models.py
from .database import db
from sqlalchemy.orm import Mapped, mapped_column
from typing import List, Optional
import datetime

class User(db.Model):
    id: Mapped[int] = mapped_column(db.Integer, primary_key=True)
    username: Mapped[str] = mapped_column(db.String(1000), unique=True, nullable=False)
    password: Mapped[str] = mapped_column(db.String(1000), nullable=False)
    posts: Mapped[List["Post"]] =  db.relationship(back_populates="user")
    
class Post(db.Model):
    id: Mapped[int] = mapped_column(db.Integer, primary_key=True)
    author_id: Mapped[int] = mapped_column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    user: Mapped["User"] = db.relationship(back_populates="posts")
    created: Mapped[datetime.datetime] = mapped_column(db.DateTime, nullable=False, server_default=db.text('CURRENT_TIMESTAMP'))
    title: Mapped[str] = mapped_column(db.String(1000), nullable=False)
    body: Mapped[Optional[str]] = mapped_column(db.String(1000000))

O formato geral que você pode usar para definir as colunas no último lançamento do SQLAlchemy é:

nomeDaColuna: Mapped[type] = cmapped_column(db.Type, argumentos)

Já para os relacionamentos, no lado "many", você também precisará definir a chave estrangeira.

 

Obs.: os nomes de classes serão convertidos de "CamelCase" para "snake_case" ao criar as tabelas SQL.

Finalmente, podemos retornar para a fábrica em __init__.py e fazer as conexões. Importar a base de dados e os modelos, atribuir esse app a instância da base de dados db e criar todas as tabelas dentro do método create_app().

# __init__.py
from flask import Flask
from sqlalchemy.exc import DatabaseError

def create_app():
    # create and configure the app
    app = Flask(__name__)
    
    app.config.from_mapping(
        SECRET_KEY="dev", # sobrescrever com randomico ao fazer o deploy
        SQLALCHEMY_DATABASE_URI = "iris://_SYSTEM:sys@localhost:1972/SAMPLE"
    )
    
    # o flask inicializa o Alchemy com esse app
    from .database import db
    from .models import User, Post
    
    db.init_app(app)
    
    try:
        with app.app_context():
            db.create_all()
    except DatabaseError as err:
        if 'already exists' in err._sql_message():
            print("Databases already exist.")
        else:
            print(err) 

Em seguida, vamos criar as views.

Passo 3 - as views

Passo 3.1 - As blueprints: autenticação

Um objeto Blueprint é desenhado para organizar suas views em grupos e registrá-las na aplicação. Vamos criar uma Blueprint para as views que lidam com autenticação e outra para postar, editar e deletar, então essa parte do tutorial cobrirá CRUD e administração de sessões.

Começando com a blueprint de autenticação, crie o arquivo auth.py e importe utilities do Flask e Werkzeug, a base de dados e o modelo User. Então, instancie o objeto Blueprint, especificando seu nome e prefixo URL.

# auth.py
from flask import (
    Blueprint, request, g, redirect, url_for, flash, render_template, session
)
from werkzeug.security import generate_password_hash, check_password_hash
from .database import db
from .models import User
import functools

bp = Blueprint('auth', __name__, url_prefix='/auth')

Depois, podemos usar decoradores para definir rotas para registrar, fazer login e logout com cada método necessário. No nosso exemplo, vamos empregar GET e POST e começar com o cadastro.

O decorador é acessado como um método da nossa Blueprint e tem um argumento para URL e outro para os métodos que aceita. Logo na próxima linha, crie a função que diz ao app o que fazer quando recebe uma requisição com os parâmetros correspondentes. No exemplo a seguir, teremos acesso à função ao buscar (GET) ou postar (POST) a URL http://host:port/auth/register

# auth.py
@bp.route('/register', methods=('GET', 'POST'))
def register():
    if request.method=='POST':
        pass

Se o app receber um POST com referência a "/register", significa que um usuário quer criar uma conta. Acessar o usuário e senha com o objeto do conteúdo da requisição é manuseado pelo Flask. Em seguida, crie uma instância do modelo User e armazene em suas propriedades os valores recebidos. Você também pode usar os métodos de werkzeug.security para proteger conteúdo sensível. Então, utilize  a propriedade de sessão da nossa base de dados, provida pelo SQL ALchemy, para adicionar o novo usuário à tabela e commitar. Nesse ponto, a informação já foi enviada à tabela correspondente na instância IRIS referida no último passo. Finalmente, redirecione o usuário para a página de login, que vamos criar no próximo passo. Não se esqueça de tratar erros como entradas vazias, integridade de indexação e conexão à base de dados. Você pode usar flash() para exibir detalhes sobre o problema ao usuário. Também podemos usar a mesma função register() para renderizar o template para  página de registro se o GET for usado na requisição.

# auth.py
@bp.route('/register', methods=('GET', 'POST'))
def register():
    if request.method=='POST':
        username = request.form['username']
        password = request.form['password']
        error = None
        if not username:
            error = "Username is required."
        elif not password:
            error = "Password is required."
        if error is None:
            try:
                user = User(
                    username=username,
                    password=generate_password_hash(password)
                )
                
                db.session.add(user)
                db.session.commit()
            except Exception as err: # TODO: change this to sqlite3's db.IntegrityError equivalent
                error = str(err)
            else:
                return redirect(url_for("auth.login"))
        
        flash(error)
    
    return render_template('auth/register.html')

Em seguida, repetimos a mesma lógica para a página de login. Ao tratar um POST, primeiro recebemos um usuário e senha e então checamos se ele já existe na nossa tabela IRIS. Dessa vez não vamos usar a propriedade session (apesar de que poderíamos). Ao invés disso, vamos aplicar o método  one_or_404(). Devemos fazer isso porque o usuário deve ser único, como definimos no modelo. Além disso, se a consulta não retornar exatamente uma linha como resultado de nossa requisição, podemos tratá-lo como não encontrado. Dentro dessa função, vamos encadear comandos SQL para achar o resultado que buscamos, usando modelos como tabelas e suas propriedades como colunas. Finalmente, limpe a sessão Flask, adicione o usuário se o login foi efetivo e redirecione-o à pagina inicial. Se for um simples GET, vamos simplesmente enviar o usuário à pagina de login.

#auth.py
@bp.route('/login', methods=('GET', 'POST'))
def login():
    if request.method=="POST":
        username = request.form['username']
        password = request.form['password']
        error = None
        try:
            user = db.one_or_404(db.select(User).where(User.username==username))
            
            if not check_password_hash(user.password, password):
                error = "Incorrect password"
        except Exception as err: # TODO also check this error
            error = f"User {username} not found."
        if error is None:
            session.clear()
            session['user_id'] = user.id 
            return redirect(url_for('index'))
        
        flash(error)
        
    
    return render_template('auth/login.html')
    

Para fazer o logout, podemos simplesmente limpar a sessão e redirecionar à página de início.

# auth.py
@bp.route('/logout')
def logout():
    session.clear()
    return redirect(url_for('index'))

Nas funções a seguir, vamos tratar o usuário fazendo login em requisições diferentes, não necessariamente relacionadas a autenticação. O objeto 'g', do Flask, é único por requisição, o que significa que você pode usá-lo para definir o usuário atual da base de dados ao lidar com acessos possivelmente proibidos.

Por sorte, a Blueprint tem a propriedade before_app_request. Ela pode ser usada como um decorador seguido de uma função para determinar o que fazer ao receber uma requisição antes de lidar com ela. Para carregar o usuário logado, devemos fazer o login novamente, mas dessa vez, vamos buscar a informação da sessão e não da requisição.

# auth.py
@bp.before_app_request
def load_logged_in_user():
    user_id = session.get('user_id')
    
    if user_id is None:
        g.user = None
    else:
        g.user = db.one_or_404(
            db.select(User).where(User.id==user_id)
        )

Finalmente, para nossa última função de autenticação, podemos fazer algo que vai lidar com as views que devem ser proibidas se o usuário não fez login. Se não quiser simplesmente exibir um código 403, redirecione o usuário à página de login.

# auth.py
def login_required(view):
    @functools.wraps(view)
    def wrapped_view(**kwargs):
        if session.get('user_id') is None:
            return redirect(url_for('auth.login'))
        
        return view(**kwargs)
    
    return wrapped_view


Passo 3.2 - As Blueprints: blog (CRUD)

Outra vez, comece criando o arquivo (vamos chamá-lo blog.py) e importando tudo que vamos precisar. Pode parecer muito trabalho, mas cada uma dessas importações vai fazer sentido durante nossa programação. Além disso, crie outra instância de Blueprint.

# blog.py
from flask import (
    Blueprint, flash, g, redirect, render_template, request, url_for, session
)
from werkzeug.exceptions import abort
from .auth import login_required
from .database import db
from .models import Post, User
from sqlalchemy import desc

bp = Blueprint('blog', __name__)

Primeiro, devemos começar criando a URL default, apontando para a página de início. Aqui vamos mostrar as postagens armazenadas no IRIS, então podemos usar a propriedade session da base de dados (que é uma instância SQLAlchemy() do Flask-SQLAlchemy). Há muitas maneiras de executar uma consulta com o módulo SQLAlchemy. Eu optei por usar a função scalars() com a sentença SQL como um argumento, encadeada com a função all(). Você já deve ter visto um comportamento similar em outras linguagens e plugins como fetchall(). Seu próximo passo será renderizar o templata para a página inicial, passando a variável onde guardamos todas as postagens como um argumento para que possamos acessá-las nos arquivos HTML. Dê uma olhada nosso READ (cRud) abaixo:

# blog.py
@bp.route('/')
def index():
    posts = db.session.scalars(
        db.select(Post).order_by(desc(Post.created))
    ).all() 
    
    return render_template('blog/index.html', posts=posts)

Em seguida, a função para criar (Crud) uma nova postagem será muito similar a registrar um usuário. Vamos buscar as entradas do usuário na requisição, definir seus valores nas propriedades do modelo Post, usar o objeto session da base de dados para adicionar (equivalente à declaração insert do SQL), e fazer um commit. Aqui, vamos acessar o usuário atual da requisição pelo objeto g. Além disso, vamos usar um segundo decorador, referenciando a função login_required() para redirecionar o usuário à página de login se ele já não estiver logado.

# blog.py
@bp.route('/create', methods=('GET', 'POST'))
@login_required
def create():
    if request.method == 'POST':
        title = request.form['title']
        body = request.form['body']
        error = None
        if not title:
            error = 'Title is required.'
        if error is not None:
            flash(error)
        else:
            post = Post(
                title=title,
                body=body,
                author_id=g.user.id
            )
            db.session.add(post)
            db.session.commit()
            
            return redirect(url_for('blog.index'))
        
    return render_template('blog/create.html')

Para fazer uma atualização (Update - crUd), vamos usa uma abordagem muito semelhante. Entretanto, ao invés de criar uma nova instância de Post, vamos retorná-la a partir de uma consulta, já que ela já existe na base de dados. Podemos usar a URL para receber um ID desse Post. Uma vez mais, o login é necessário aqui.

# blog.py
@bp.route('/<int:id>/update', methods=('GET', 'POST'))
@login_required
def update(id):
    post = get_post(id)
    
    if request.method == 'POST':
        title = request.form['title']
        body = request.form['body']
        error = None
        if not title:
            error = 'Title is required.'
        if error is not None:
            flash(error)
        else:
            post.title = title
            post.body = body
            
            db.session.commit()
            
            return redirect(url_for('blog.index'))
    
    return render_template('blog/update.html', post=post)

Já que para deletar também precisamos acessar o Post pelo ID, podemos criar uma função get_post(id) e usá-la nas duas views. Semelhante às outras consultas que fizemos até agora, podemos encadear os comandos SQL para escrevê-la. Já que nosso modelo para User tem uma relação, podemos selecionar o Post e juntar com o usuário. Dessa maneira, poderemos acessar informações do usuário pelo objeto resultante também. Também podemos usar abort() para enviar respostas 404 (not found - não encontrado) e 403 (forbidden - proibido).

# blog.py
def get_post(id, check_author=True):
    post = db.one_or_404(
        db.select(Post).join(User).where(Post.id == id)
    )
    
    if post is None:
        abort(404, f"Post id {id} doesn't exist.")
        
    if check_author and post.author_id != session['user_id']:
        abort(403)
        
    return post

Por último, mas não menos importante, você pode deletar com nada além de uma função delete da sessão da base de dados.

Last but not least, you can perform the delete with nothing more than a delete function from the database session.

# blog.py
@bp.route('/<int:id>/delete', methods=('POST',))
@login_required
def delete(id):
    post = get_post(id)
    
    db.session.delete(post)
    db.session.commit()
    
    return redirect(url_for('blog.index'))

 

 

Passo 3.3 - Registrar suas Blueprints

Finalmente, podemos finalizar nossa fábrica de app registrando as blueprints e retornando o app, como mostrado abaixo:

# __init__.py
from flask import Flask
from sqlalchemy.exc import DatabaseError

def create_app():
    # criar e configurar o app
    app = Flask(__name__)
    
    app.config.from_mapping(
        SECRET_KEY="dev", # sobrescreva com randômico ao fazer o deploy
        SQLALCHEMY_DATABASE_URI = "iris://_SYSTEM:sys@localhost:1972/SAMPLE"
    )
    
    # o flask inicializa o Alchemy com esse app
    from .database import db
    from .models import User, Post
    db.init_app(app)
    try:
        with app.app_context():
            db.create_all()
    except DatabaseError as err:
        if 'already exists' in err._sql_message():
            print("Databases already exist.")
        else:
            print(err) 
            
    # registra Blueprints
    from . import auth
    app.register_blueprint(auth.bp) 
    
    from . import blog
    app.register_blueprint(blog.bp)
    app.add_url_rule('/', endpoint='index')
    
    return app

 

Passo 4 - Os templates

Você deve ter notado que cada view que finaliza com return redirect(), envia o usuário para uma outra view ou retorna render_template(), que renderiza um template. Essas funções recebem templates HTML como argumentos, junto com quaisquer outros objetos que você queira usar dentro deles. Isso significa que, nesse ponto do tutorial, você aprenderá como acessar sua base de dados IRIS de um arquivo HTML, que permite que você manuseie os dados com CSS e JavaScript, e evolua para qualquer outra ferramenta de desenvolvimento web.

O caminho default para sua aplicação Flask construída num pacote para procurar por templates é app_folder/templates. Dentro dela, é comum adicionar um arquivo chamado base.html. O padrão para esses arquivos é o do Jinja. Isso significa que você pode usar {% .. %} para adicionar declarações como blocos, "if's" e "for's", sempre os finalizando (por exemplo, com {% endblock %}). Você também pode usar {{ ... }} para expressões para acessar objetos que você passou na função de renderização do template. Por fim, você pode usar {# ... #} para comentários.

Além dos argumentos passados nas views, você pode acessar contexto como o objeto g, que é único por requisição, e o get_flashed_messages() para exibir argumentos passados em flash(). Já que nos últimos passos nós já vimos como fazer consultas e passar o resultado no contexto da requisição, o próximo exemplo para um arquivo base vai te mostrar como acessar os dados do IRIS usando objeto g. Além disso, também vai demonstrar como exibir as mensagens do flash().

<!-- templates/base.html -->
<!DOCTYPE html>
<title>{% block title %}{% endblock %} - Flaskr</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<nav>
    <h1>Flaskr</h1>
    <ul>
        {% if g.user %}
          <li><span>{{ g.user['username'] }}</span>
          <li><a href="{{ url_for('auth.logout') }}">Log Out</a>
        {% else %}
          <li><a href="{{ url_for('auth.register') }}">Register</a>
          <li><a href="{{ url_for('auth.login') }}">Log In</a>
        {% endif %}
    </ul>
</nav>
<section class="content">
    <header>
        {% block header %}{% endblock %}
    </header>
    {% for message in get_flashed_messages() %}
      <div class="flash">{{ message }}</div>
    {% endfor %}
    {% block content %}{% endblock %}
</section>

Com a base já definida, você finalmente pode criar outras páginas que sejam extensões desse primeiro arquivo e personalizar os blocos. Explore o exemplo a seguir para a página de Criar Posts

<!-- templates/blog/create.html -->
{% extends 'base.html' %}

{% block header %}
    <h1>{% block title %}New Post{% endblock %}</h1>
{% endblock %}

{% block content %}
    <form method="post">
        <label for="title">Title</label>
        <input name="title" id="title" value="{{ request.form['title'] }}" required>
        <label for="body">Body</label>
        <textarea name="body" id="body">{{ request.form['body'] }}</textarea>
        <input type="submit" value="Save">
    </form>
{% endblock %}

O parâmetro nome no input será a chave para o dicionário request.form, acessível nas views. As páginas de autenticação login e registro terão uma sintaxe muito similar, já que ambas precisam de apenas um formulário. No entanto, você pode dar uma olhada nelas no meu repositório GitHub se desejar.

Agora, vamos dar uma olhada na página de início, chamada index.html.

<!-- templates/blog/index.html -->
{% extends 'base.html' %}

{% block header %}
 <h1>{% block title %}Posts{% endblock %}</h1>
 {% if g.user %}
    <a class="action" href="{{ url_for('blog.create') }}">New</a>
 {% endif %}
{% endblock %}

{% block content %}
    {% for post in posts %}
        <article class="post">
            <header>
                <div>
                    <h1>{{ post['title'] }}</h1>
                    <div class="about">by {{ post.user.username }} on {{ post['created'].strftime('%Y-%m-%d') }}</div>
                </div>
                {% if g.user['id'] == post['author_id'] %}
                    <a class="action" href="{{ url_for('blog.update', id=post['id'] )}}">Edit</a>
                {% endif%}
            </header>
            <p class="body">{{ post['body'] }}</p>
        </article>
        {% if not loop.last %}
            <hr>
        {% endif %}
    {% endfor %}
{% endblock %}

Aqui nós temos alguns elementos <a/> que valem a pena dar uma olhada. O link para criar uma nova postagem só aparece quando um usuário g.user confirma como "true", o que significa que tem um usuário logado. Também há uma declaração "for", iterando pelos objetos Posts retornados por uma consulta na view para a URL vazia "\" e passada a esse arquivo como um argumento de render_template(). Para cada postagem, podemos acessar o nome do usuário com uma sintaxe simples (post.user.username), graças à db.relationship adicionada no modelo User. Uma maneira alternativa de acessar os dados é usar os posts como dicionários e exibir suas propriedades com sintaxe de índice (como post['title']). Note como o link para atualizar uma postagem passa seu ID como argumento e só aparece se o usuário do post é igual ao que estiver logado.

Finalmente, temos a página de update, simultaneamente se referindo à view de deletar. É análoga à página de Criar Posts, mas tem uma lógica curiosa para definir os valores default da base de dados. Vou deixar uma ilustração abaixo caso queira explorar.

<!-- templates/blog/udpate.html -->
{% extends 'base.html' %}

{% block header %}
    <h1>{% block title %}Edit "{{ post['title'] }}"{% endblock %}</h1>
{% endblock %}

{% block content %}
    <form method="post">
        <label for="title">Title</label>
        <input name="title" id="title" value="{{ request.form['title'] or post['title'] }}" required>
        <label for="body">Body</label>
        <textarea name="body" id="body">{{ request.form['body'] or post['body'] }}</textarea>
        <input type="submit" value="Save">
    </form>
    <hr>
    <form action="{{ url_for('blog.delete', id=post['id']) }}" method="post">
        <input type="submit" class="danger" value="Delete" onclick="return confirm('Are you sure?');">
    </form>
{% endblock %}

 

Parte 5 - Adicionando arquivos estáticos

Nesse ponto, já temos uma interface interativa que recebe informações desde o usuário até a base de dados e de volta, e temos objetos IRIS acessíveis em Python com a ajuda do SQLAlchemy e modelos feitos com sintaxe de classe. No último passo, também conseguimos transformar esses objetos no conteúdo de elementos HTML, o que significa que você pode facilmente personalizá-los e interagir com eles com CSS e JavaScript.

Olhando de novo a base HTML, repare na linha com um link referenciando a stylesheet (folha de estilo), logo no início:

<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">

O primeiro argumento da função url_for() dentro da expressão href é a pasta dentro da raiz onde você pode encontrar os estáticos. O segundo argumento é o nome do arquivo. De agora em diante, o design é de sua decisão! Crie o arquivo style.css e edite como desejar.

Tudo pronto!

Com todos esses arquivos que criamos, a pasta raiz deve parecer com a imagem abaixo:

Agora, vá para o diretório raiz no terminal e digite

flask --app flaskr-iris run --debug

Então, siga o link http://127.0.0.1:5000 para ver como tudo está funcionando.

 

Conclusão

Nesse artigo, vimos como o Flask torna possível e fácil trazer informações de uma instância IRIS em qualquer lugar para uma aplicação web, transformando num CRUD totalmente personalizável, junto com a opção de conectar a outras bases de dados e sistemas. Agora você pode facilmente criar, exibir, editar e deletar informações de uma base de dados IRIS via uma interface que não só permite que você customize sua aparência, mas também permita que você trate os dados com qualquer ferramenta Python sem dificuldades.

Você pode entrar em contato comigo para compartilhar quaisquer dúvidas ou ideias que tenha encontrado após a leitura! No próximo artigo, vou discutir mais a respeito da teoria dessa implementação e apresentar a aplicação do OpenExchange desenvolvida aqui. Em um bônus na última seção, vou revelar alguns erros que encontrei e as soluções que consegui encontrar.
 

Discussão (0)1
Entre ou crie uma conta para continuar