Como Usar um Banco de Dados PostgreSQL em uma Aplicação Flask

O autor selecionou o Fundo de Código Aberto e Gratuito para receber uma doação como parte do programa Escreva para Doações.

Introdução

Nas aplicações web, geralmente é necessário um banco de dados, que é uma coleção organizada de dados. Você usa um banco de dados para armazenar e manter dados persistentes que podem ser recuperados e manipulados de forma eficiente. Por exemplo, em uma aplicação de mídia social, você tem um banco de dados onde os dados do usuário (informações pessoais, postagens, comentários, seguidores) são armazenados de forma que possam ser manipulados eficientemente. Você pode adicionar dados a um banco de dados, recuperá-los, modificá-los ou excluí-los, dependendo de diferentes requisitos e condições. Em uma aplicação web, esses requisitos podem ser um usuário adicionando uma nova postagem, excluindo uma postagem ou excluindo sua conta, o que pode ou não excluir suas postagens. As ações que você realiza para manipular dados dependerão de recursos específicos em sua aplicação. Por exemplo, você pode não querer que os usuários adicionem postagens sem títulos.

Flask é um framework web leve em Python que fornece ferramentas úteis e recursos para criar aplicações web na linguagem Python. PostgreSQL, ou Postgres, é um sistema de gerenciamento de banco de dados relacional que fornece uma implementação da linguagem de consulta SQL. É compatível com padrões e possui muitos recursos avançados, como transações confiáveis e concorrência sem travamentos de leitura.

Neste tutorial, você construirá uma pequena aplicação web de revisão de livros que demonstra como usar a biblioteca psycopg2, um adaptador de banco de dados PostgreSQL que permite interagir com seu banco de dados PostgreSQL em Python. Você o usará com o Flask para realizar tarefas básicas, como conectar-se a um servidor de banco de dados, criar tabelas, inserir dados em uma tabela e recuperar dados de uma tabela.

Pré-requisitos

Passo 1 — Criando o Banco de Dados e Usuário do PostgreSQL

Neste passo, você criará um banco de dados chamado flask_db e um usuário de banco de dados chamado sammy para sua aplicação Flask.

Ao instalar o Postgres, um usuário do sistema operacional chamado postgres foi criado para corresponder ao usuário administrativo do PostgreSQL, postgres. Você precisa usar este usuário para realizar tarefas administrativas. Você pode usar o sudo e passar o nome de usuário com a opção -iu.

Faça login em uma sessão interativa do Postgres usando o seguinte comando:

  1. sudo -iu postgres psql

Você receberá um prompt do PostgreSQL onde poderá configurar seus requisitos.

Primeiro, crie um banco de dados para o seu projeto:

  1. CREATE DATABASE flask_db;

Nota: Cada declaração do Postgres deve terminar com um ponto e vírgula, então certifique-se de que seu comando termine com um se estiver enfrentando problemas.

Em seguida, crie um usuário de banco de dados para nosso projeto. Certifique-se de selecionar uma senha segura:

  1. CREATE USER sammy WITH PASSWORD 'password';

Depois, conceda a este novo usuário acesso para administrar seu novo banco de dados:

  1. GRANT ALL PRIVILEGES ON DATABASE flask_db TO sammy;

Para confirmar se o banco de dados foi criado, obtenha a lista de bancos de dados digitando o seguinte comando:

  1. \l

Você verá flask_db na lista de bancos de dados.

Quando terminar, saia do prompt do PostgreSQL digitando:

  1. \q

O Postgres está agora configurado para que você possa conectar-se e gerenciar suas informações de banco de dados via Python usando a biblioteca psycopg2. Em seguida, você instalará esta biblioteca juntamente com o pacote Flask.

Passo 2 — Instalando Flask e psycopg2

Neste passo, você instalará o Flask e a biblioteca psycopg2 para que possa interagir com seu banco de dados usando Python.

Com seu ambiente virtual ativado, use o pip para instalar o Flask e a biblioteca psycopg2:

  1. pip install Flask psycopg2-binary

Uma vez que a instalação for concluída com sucesso, você verá uma linha semelhante à seguinte no final da saída:

Output
Successfully installed Flask-2.0.2 Jinja2-3.0.3 MarkupSafe-2.0.1 Werkzeug-2.0.2 click-8.0.3 itsdangerous-2.0.1 psycopg2-binary-2.9.2

Agora você tem os pacotes necessários instalados em seu ambiente virtual. Em seguida, você se conectará e configurará seu banco de dados.

Passo 3 — Configurando um Banco de Dados

Neste passo, você criará um arquivo Python no diretório do projeto flask_app para conectar ao banco de dados flask_db, criar uma tabela para armazenar livros e inserir alguns livros com avaliações nele.

Primeiro, com o seu ambiente de programação ativado, abra um novo arquivo chamado init_db.py no seu diretório flask_app.

  1. nano init_db.py

Este arquivo abrirá uma conexão com o banco de dados flask_db, criará uma tabela chamada books e populará a tabela usando dados de exemplo. Adicione o seguinte código a ele:

flask_app/init_db.py
import os
import psycopg2

conn = psycopg2.connect(
        host="localhost",
        database="flask_db",
        user=os.environ['DB_USERNAME'],
        password=os.environ['DB_PASSWORD'])

# Abra um cursor para realizar operações no banco de dados
cur = conn.cursor()

# Execute um comando: isso cria uma nova tabela
cur.execute('DROP TABLE IF EXISTS books;')
cur.execute('CREATE TABLE books (id serial PRIMARY KEY,'
                                 'title varchar (150) NOT NULL,'
                                 'author varchar (50) NOT NULL,'
                                 'pages_num integer NOT NULL,'
                                 'review text,'
                                 'date_added date DEFAULT CURRENT_TIMESTAMP);'
                                 )

# Insira dados na tabela

cur.execute('INSERT INTO books (title, author, pages_num, review)'
            'VALUES (%s, %s, %s, %s)',
            ('A Tale of Two Cities',
             'Charles Dickens',
             489,
             'A great classic!')
            )


cur.execute('INSERT INTO books (title, author, pages_num, review)'
            'VALUES (%s, %s, %s, %s)',
            ('Anna Karenina',
             'Leo Tolstoy',
             864,
             'Another great classic!')
            )

conn.commit()

cur.close()
conn.close()

Salve e feche o arquivo.

Neste arquivo, você primeiro importa o módulo os que você usará para acessar as variáveis de ambiente onde armazenará seu nome de usuário e senha do banco de dados para que não estejam visíveis no seu código-fonte.

Você importa a biblioteca psycopg2. Em seguida, você abre uma conexão com o banco de dados flask_db usando a função psycopg2.connect(). Você especifica o host, que é o localhost neste caso. Você passa o nome do banco de dados para o parâmetro database.

Você fornece seu nome de usuário e senha via objeto os.environ, que lhe dá acesso a variáveis de ambiente que você definiu em seu ambiente de programação. Você irá armazenar o nome de usuário do banco de dados em uma variável de ambiente chamada DB_USERNAME e a senha em uma variável de ambiente chamada DB_PASSWORD. Isso permite que você armazene seu nome de usuário e senha fora do seu código-fonte, para que suas informações sensíveis não sejam expostas quando o código-fonte for salvo no controle de origem ou enviado para um servidor na internet. Mesmo que um invasor ganhe acesso ao seu código-fonte, eles não terão acesso ao banco de dados.

Você cria um cursor chamado cur usando o método connection.cursor(), que permite que o código Python execute comandos PostgreSQL em uma sessão do banco de dados.

Você usa o método execute() do cursor para excluir a tabela books se ela já existir. Isso evita a possibilidade de outra tabela chamada books existir, o que pode resultar em um comportamento confuso (por exemplo, se ela tiver colunas diferentes). Isso não é o caso aqui, porque você ainda não criou a tabela, então o comando SQL não será executado. Note que isso irá excluir todos os dados existentes sempre que você executar este arquivo init_db.py. Para nossos propósitos, você só executará este arquivo uma vez para iniciar o banco de dados, mas talvez queira executá-lo novamente para excluir quaisquer dados que você inseriu e começar com os dados de exemplo iniciais novamente.

Em seguida, você usa CREATE TABLE books para criar uma tabela chamada books com as seguintes colunas:

  • id: Um ID do tipo serial, que é um inteiro de incremento automático. Esta coluna representa uma chave primária que você especifica usando as palavras-chave PRIMARY KEY. O banco de dados atribuirá um valor único a esta chave para cada entrada.
  • title: O título do livro do tipo varchar, que é um tipo de caractere de comprimento variável com um limite. varchar(150) significa que o título pode ter até 150 caracteres de comprimento. NOT NULL significa que esta coluna não pode ficar vazia.
  • author: O autor do livro, com um limite de 50 caracteres. NOT NULL significa que esta coluna não pode ficar vazia.
  • pages_num: Um inteiro representando o número de páginas que o livro possui. NOT NULL significa que esta coluna não pode ficar vazia.
  • revisão: A revisão do livro. O tipo de texto significa que a revisão pode ser texto de qualquer tamanho.
  • data_adicionada: A data em que o livro foi adicionado à tabela. DEFAULT define o valor padrão da coluna como CURRENT_TIMESTAMP, que é o momento em que o livro foi adicionado ao banco de dados. Assim como id, você não precisa especificar um valor para esta coluna, pois será preenchido automaticamente.

Após criar a tabela, você utiliza o método execute() do cursor para inserir dois livros na tabela, A Tale of Two Cities de Charles Dickens, e Anna Karenina de Leo Tolstoy. Você utiliza o marcador %s para passar os valores para a declaração SQL. O psycopg2 trata a inserção em segundo plano de forma a prevenir ataques de Injeção SQL.

Depois de terminar de inserir os dados do livro na tabela, você utiliza o método connection.commit() para confirmar a transação e aplicar as mudanças ao banco de dados. Em seguida, você limpa as coisas fechando o cursor com cur.close(), e a conexão com conn.close().

Para a conexão com o banco de dados ser estabelecida, defina as variáveis de ambiente DB_USERNAME e DB_PASSWORD executando os seguintes comandos. Lembre-se de usar seu próprio nome de usuário e senha:

  1. export DB_USERNAME="sammy"
  2. export DB_PASSWORD="password"

Agora, execute seu arquivo init_db.py no terminal usando o comando python:

  1. python init_db.py

Uma vez que o arquivo terminar a execução sem erros, uma nova tabela books será adicionada ao seu banco de dados flask_db.

Acesse uma sessão interativa do Postgres para verificar a nova tabela books.

  1. sudo -iu postgres psql

Conecte-se ao banco de dados flask_db usando o comando \c:

  1. \c flask_db

Em seguida, use uma instrução SELECT para obter os títulos e autores dos livros da tabela books:

  1. SELECT title, author FROM books;

Você verá uma saída como a seguinte:

        title         |      author
----------------------+------------------
 A Tale of Two Cities | Charles Dickens
 Anna Karenina        | Leo Tolstoy

Encerre a sessão interativa com \q.

Em seguida, você criará um pequeno aplicativo Flask, conectará ao banco de dados, recuperará as duas resenhas de livros que inseriu no banco de dados e as exibirá na página inicial.

Passo 4 — Exibindo Livros

Neste passo, você criará um aplicativo Flask com uma página de índice que recuperará os livros que estão no banco de dados e os exibirá.

Com seu ambiente de programação ativado e o Flask instalado, abra um arquivo chamado app.py para edição dentro do seu diretório flask_app:

  1. nano app.py

Este arquivo configurará sua conexão com o banco de dados e criará uma única rota Flask para usar essa conexão. Adicione o seguinte código ao arquivo:

flask_app/app.py
import os
import psycopg2
from flask import Flask, render_template

app = Flask(__name__)

def get_db_connection():
    conn = psycopg2.connect(host='localhost',
                            database='flask_db',
                            user=os.environ['DB_USERNAME'],
                            password=os.environ['DB_PASSWORD'])
    return conn


@app.route('/')
def index():
    conn = get_db_connection()
    cur = conn.cursor()
    cur.execute('SELECT * FROM books;')
    books = cur.fetchall()
    cur.close()
    conn.close()
    return render_template('index.html', books=books)

Salve e feche o arquivo.

Aqui, você importa o módulo os, a biblioteca psycopg2 e a classe Flask e a função render_template() do pacote flask. Você cria uma instância de aplicativo Flask chamada app.

Você define uma função chamada get_db_connection(), que abre uma conexão com o banco de dados flask_db usando o usuário e a senha armazenados em suas variáveis de ambiente DB_USERNAME e DB_PASSWORD. A função retorna o objeto de conexão conn que você usará para acessar o banco de dados.

Então você cria uma rota principal / e uma função de visualização index() usando o decorador app.route(). Na função de visualização index(), você abre uma conexão de banco de dados usando a função get_db_connection(), cria um cursor e executa a instrução SQL SELECT * FROM books; para obter todos os livros que estão no banco de dados. Você usa o método fetchall() para salvar os dados em uma variável chamada books. Em seguida, você fecha o cursor e a conexão. Por último, você retorna uma chamada para a função render_template() para renderizar um arquivo de modelo chamado index.html, passando a ele a lista de livros que você buscou do banco de dados na variável books.

Para exibir os livros que você tem em seu banco de dados na página inicial, primeiro você criará um modelo base, que terá todo o código HTML básico que outros modelos também usarão para evitar repetição de código. Em seguida, você criará o arquivo de modelo index.html que será renderizado em sua função index(). Para saber mais sobre modelos, consulte Como Usar Modelos em uma Aplicação Flask.

Crie um diretório templates, depois abra um novo modelo chamado base.html:

  1. mkdir templates
  2. nano templates/base.html

Adicione o seguinte código dentro do arquivo base.html:

flask_app/templates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %} {% endblock %}- FlaskApp</title>
    <style>
        nav a {
            color: #d64161;
            font-size: 3em;
            margin-left: 50px;
            text-decoration: none;
        }

        .book {
            padding: 20px;
            margin: 10px;
            background-color: #f7f4f4;
        }

        .review {
                margin-left: 50px;
                font-size: 20px;
        }

    </style>
</head>
<body>
    <nav>
        <a href="{{ url_for('index') }}">FlaskApp</a>
        <a href="#">About</a>
    </nav>
    <hr>
    <div class="content">
        {% block content %} {% endblock %}
    </div>
</body>
</html>

Salve e feche o arquivo.

Este modelo base tem todo o boilerplate HTML que você precisará reutilizar em seus outros modelos. O bloco title será substituído para definir um título para cada página, e o bloco content será substituído pelo conteúdo de cada página. A barra de navegação tem dois links, um para a página inicial onde você usa a função auxiliar url_for() para vincular à função de visualização index(), e o outro para uma página Sobre se você optar por incluir uma em sua aplicação.

Em seguida, abra um modelo chamado index.html. Este é o modelo que você referenciou no arquivo app.py:

  1. nano templates/index.html

Adicione o seguinte código a ele:

flask_app/templates/index.html

{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Books {% endblock %}</h1>
    {% for book in books %}
        <div class='book'>
            <h3>#{{ book[0] }} - {{ book[1] }} BY {{ book[2] }}</h3>
            <i><p>({{ book[3] }} pages)</p></i>
            <p class='review'>{{ book[4] }}</p>
            <i><p>Added {{ book[5] }}</p></i>
        </div>
    {% endfor %}
{% endblock %}

Salve e feche o arquivo.

Neste arquivo, você estende o modelo base e substitui o conteúdo do bloco content. Você usa um cabeçalho <h1> que também serve como título.

Você usa um loop for do Jinja na linha {% for book in books %} para percorrer cada livro na lista books. Você exibe o ID do livro, que é o primeiro item usando book[0]. Em seguida, você exibe o título do livro, autor, número de páginas, resenha e a data em que o livro foi adicionado.

Enquanto estiver no diretório flask_app com seu ambiente virtual ativado, informe ao Flask sobre a aplicação (app.py neste caso) usando a variável de ambiente FLASK_APP. Em seguida, defina a variável de ambiente FLASK_ENV como development para executar a aplicação no modo de desenvolvimento e obter acesso ao depurador. Para obter mais informações sobre o depurador do Flask, consulte Como Lidar com Erros em uma Aplicação Flask. Use os seguintes comandos para fazer isso:

  1. export FLASK_APP=app
  2. export FLASK_ENV=development

Certifique-se de definir as variáveis de ambiente DB_USERNAME e DB_PASSWORD se ainda não tiver feito:

  1. export DB_USERNAME="sammy"
  2. export DB_PASSWORD="password"

Em seguida, execute a aplicação:

  1. flask run

Com o servidor de desenvolvimento em execução, visite a seguinte URL usando seu navegador:

http://127.0.0.1:5000/

Você verá os livros que adicionou ao banco de dados na primeira inicialização.

Você exibiu os livros em seu banco de dados na página de índice. Agora você precisa permitir que os usuários adicionem novos livros. Você adicionará uma nova rota para adicionar livros no próximo passo.

Passo 5 — Adicionando Novos Livros

Neste passo, você criará uma nova rota para adicionar novos livros e avaliações ao banco de dados.

Você adicionará uma página com um formulário web onde os usuários inserem o título do livro, o autor do livro, o número de páginas e a avaliação do livro.

Deixe o servidor de desenvolvimento em execução e abra uma nova janela de terminal.

Primeiro, abra seu arquivo app.py:

  1. nano app.py

Para lidar com o formulário web, você precisará importar algumas coisas do pacote flask:

  • O objeto global request para acessar os dados enviados.
  • A função url_for() para gerar URLs.
  • A função redirect() para redirecionar os usuários para a página inicial após adicionar um livro ao banco de dados.

Adicione essas importações na primeira linha do arquivo:

flask_app/app.py

from flask import Flask, render_template, request, url_for, redirect

# ...

Então adicione a seguinte rota no final do arquivo app.py:

flask_app/app.py

# ...


@app.route('/create/', methods=('GET', 'POST'))
def create():
    return render_template('create.html')

Salve e feche o arquivo.

Nesta rota, você passa a tupla ('GET', 'POST') para o parâmetro methods para permitir tanto requisições GET quanto POST. As requisições GET são usadas para recuperar dados do servidor. As requisições POST são usadas para enviar dados para uma rota específica. Por padrão, apenas as requisições GET são permitidas. Quando o usuário solicita pela primeira vez a rota /create usando uma requisição GET, um arquivo de modelo chamado create.html será renderizado. Você editará posteriormente esta rota para lidar com requisições POST quando os usuários preencherem e enviarem o formulário da web para adicionar novos livros.

Abra o novo modelo create.html:

  1. nano templates/create.html

Adicione o seguinte código a ele:

flask_app/templates/create.html
{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Add a New Book {% endblock %}</h1>
    <form method="post">
        <p>
            <label for="title">Title</label>
            <input type="text" name="title"
                   placeholder="Book title">
            </input>
        </p>

        <p>
            <label for="author">Author</label>
            <input type="text" name="author"
                   placeholder="Book author">
            </input>
        </p>

        <p>
            <label for="pages_num">Number of pages</label>
            <input type="number" name="pages_num"
                   placeholder="Number of pages">
            </input>
        </p>
        <p>
        <label for="review">Review</label>
        <br>
        <textarea name="review"
                  placeholder="Review"
                  rows="15"
                  cols="60"
                  ></textarea>
        </p>
        <p>
            <button type="submit">Submit</button>
        </p>
    </form>
{% endblock %}

Salve e feche o arquivo.

Você estende o modelo base, define um título como cabeçalho e usa uma tag <form> com o atributo method definido como post para indicar que o formulário enviará uma requisição POST.

Você tem um campo de texto com o nome title, que será usado para acessar os dados do título em sua rota /create.

Você tem um campo de texto para o autor, um campo de número para o número de páginas e uma área de texto para a resenha do livro.

Por último, você tem um botão Enviar no final do formulário.

Agora, com o servidor de desenvolvimento em execução, use seu navegador para acessar a rota /create:

http://127.0.0.1:5000/create

Você verá uma página Adicionar um Novo Livro com um campo de entrada para o título do livro, um para seu autor, e outro para o número de páginas que o livro possui, uma área de texto para a resenha do livro, e um botão Enviar.

Se você preencher o formulário e enviá-lo, enviando uma solicitação POST para o servidor, nada acontece porque você não tratou as solicitações POST na rota /create.

Abra o arquivo app.py para lidar com a solicitação POST que o usuário envia:

  1. nano app.py

Edite a rota /create para ficar como segue:

flask_app/app.py

# ...

@app.route('/create/', methods=('GET', 'POST'))
def create():
    if request.method == 'POST':
        title = request.form['title']
        author = request.form['author']
        pages_num = int(request.form['pages_num'])
        review = request.form['review']

        conn = get_db_connection()
        cur = conn.cursor()
        cur.execute('INSERT INTO books (title, author, pages_num, review)'
                    'VALUES (%s, %s, %s, %s)',
                    (title, author, pages_num, review))
        conn.commit()
        cur.close()
        conn.close()
        return redirect(url_for('index'))

    return render_template('create.html')

Salve e feche o arquivo.

Você trata as solicitações POST dentro da condição if request.method == 'POST'. Você extrai o título, autor, número de páginas e a avaliação que o usuário envia do objeto request.form.

Você abre um banco de dados usando a função get_db_connection() e cria um cursor. Em seguida, executa uma instrução SQL INSERT INTO para inserir o título, autor, número de páginas e avaliação que o usuário enviou na tabela books.

Você confirma a transação e fecha o cursor e a conexão.

Por último, redireciona o usuário para a página de índice, onde ele pode ver o novo livro adicionado abaixo dos livros existentes.

Com o servidor de desenvolvimento em execução, use seu navegador para navegar até a rota /create:

http://127.0.0.1:5000/create

Preencha o formulário com alguns dados e envie-o.

Você será redirecionado para a página de índice, onde verá sua nova avaliação de livro.

Em seguida, você adicionará um link para a página de criação na barra de navegação. Abra base.html:

  1. nano templates/base.html

Edite o arquivo para ficar como segue:

flask_app/templates/base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %} {% endblock %} - FlaskApp</title>
    <style>
        nav a {
            color: #d64161;
            font-size: 3em;
            margin-left: 50px;
            text-decoration: none;
        }

        .book {
            padding: 20px;
            margin: 10px;
            background-color: #f7f4f4;
        }

        .review {
                margin-left: 50px;
                font-size: 20px;
        }

    </style>
</head>
<body>
    <nav>
        <a href="{{ url_for('index') }}">FlaskApp</a>
        <a href="{{ url_for('create') }}">Create</a>
        <a href="#">About</a>
    </nav>
    <hr>
    <div class="content">
        {% block content %} {% endblock %}
    </div>
</body>
</html>

Salve e feche o arquivo.

Aqui, você adiciona um novo link <a> à barra de navegação que aponta para a página Create.

Atualize sua página de índice e você verá o novo link na barra de navegação.

Agora você tem uma página com um formulário da web para adicionar novas resenhas de livros. Para mais informações sobre formulários da web, consulte Como Usar Formulários da Web em uma Aplicação Flask. Para um método mais avançado e seguro de gerenciar formulários da web, consulte Como Usar e Validar Formulários da Web com Flask-WTF.

Conclusão

Você construiu uma pequena aplicação web para resenhas de livros que se comunica com um banco de dados PostgreSQL. Você tem funcionalidades básicas de banco de dados em sua aplicação Flask, como adicionar novos dados ao banco de dados, recuperar dados e exibi-los em uma página.

Se você gostaria de ler mais sobre Flask, confira os outros tutoriais na série Flask.

Source:
https://www.digitalocean.com/community/tutorials/how-to-use-a-postgresql-database-in-a-flask-application