Minimal working example (Postgres)

Hello,

I would like to take data from a postgres database, transform it into a dataframe and list it in a table.

I confess to everyone that I managed to do this part, however I would like to enter postgres and make an insert via sql command and this information appears in the table without having to update the browser or restart the dash server.

Can anyone give me a minimal working example?

I did something as below with Postgres. Hope this help:

dcc.Store(id='store-data', data=[], storage_type='memory'), 
    dcc.Interval(id='update', n_intervals = 0, interval=1000*18000) # You can change interval value to change update time, I'm setting up at 30 min    
])
        
# Load data from PostgreSQL, you have to change database name, user and password.
@app.callback(Output('store-data','data'),
             [Input('update', 'n_intervals')])
def update_data(interval):
    global df
    conn = pg2.connect(database='...',user='...',password='...')
    cur = conn.cursor()
    cur.execute('''SELECT * FROM public."salesMVA_3"''')
    data = cur.fetchall()
    cols = []
    for elt in cur.description:
        cols.append(elt[0]) 
    df = pd.DataFrame(data=data, columns=cols)

So that I can better understand your code I will make some comments including the code, if you can answer I would appreciate it.

First: In this line below do you store the data in dcc.Store in the interval of 30 minutes?
It looks like NUB but how will I use this data in charts and tables?

dcc.Store(id='store-data', data=[], storage_type='memory'),
     dcc.Interval(id='update', n_intervals = 0, interval=1000*18000) # You can change interval value to change update time, I'm     setting up at 30 min
])

Second: There is a Global variable df, is this variable to store the data?
What part of the code above will it be used in?

Third: The update_data(interval): function should contain all my dataframe creation? I use peewee to connect to Postgres, the process is a little different but in the end I have my dataframe, and I was wondering if this should contain all my connection process and dataframe creation.

1 Like

@EliasCoutinho:
First: Yep after 30 minutes, dcc.Store will be updated with new data from Postgres.
Second: For the global I think it’s not important so you can use it or not.
Third: After have dataframe and store with dcc.Store, you will you it as Input for graphs or table. Something like below:

@app.callback(Output('line_chart', 'figure'),
             [Input('item_name', 'value'),
             Input('channel_type_2', 'value'),
             Input('store-data', 'data')])

def build_graph_2(itemname,channeltype_2,n):  
    if not itemname or 'Select' in itemname:
        df3 = df2.copy()
        df3 = df3[(df3['Channel']==channeltype_2)]
        df3 = pd.pivot_table(df3,('invoiceTotal'),index=['Date'],aggfunc=np.sum).reset_index()
        df3['itemName'] = 'All item'        
    else:       
        df3 = df2[df2['itemName'].isin(itemname)]
        df3 = df3[(df3['Channel'] == channeltype_2)]          
        df3 = pd.pivot_table(df3,('invoiceTotal'),index=['Date','itemName'],aggfunc=np.sum).reset_index()    
    line_chart = px.scatter(df3,
                  x=df3['Date'],
                  y=df3['invoiceTotal'],
                  color=df3['itemName'])
    
    line_chart.update_traces(mode='lines+markers') #line_shape='spline'
    line_chart.update_layout(plot_bgcolor='white')
    line_chart.update_yaxes(showline=False,showgrid=False,exponentformat="none",separatethousands=True)
    line_chart.update_xaxes(showline=False,showgrid=False,exponentformat="none",separatethousands=True)    
    line_chart.update_traces(mode='lines+markers',
                        text=df3['invoiceTotal'],
                        textposition='top center',
                        texttemplate='%{text:,}')
    return line_chart

What I intend to do is read this data, store it in
dcc.Store(id=‘store-data’, data=, storage_type=‘memory’),

And display in the table.

Every 10 seconds update dcc.Store and table

from datetime import date
import plotly.graph_objs as go
import dash
from dash import html, dcc, Input, Output, State, dash_table
import dash_bootstrap_components as dbc
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import pandas as pd

from dash_bootstrap_templates import ThemeSwitchAIO

# Importei meu dataframe do arquivo movimento_geral.py
from movimento_geral import df_dados_gerais

# Formatação das tabelas
formatted = {'specifier': ',.2f', 'locale': {'group': '.', 'decimal': ',', }}

# ================================================================== #
from flask import Flask

dbc_css = "https://cdn.jsdelivr.net/gh/AnnMarieW/dash-bootstrap-templates/dbc.min.css"

server = Flask(__name__)

app = dash.Dash(__name__, server=server, suppress_callback_exceptions=True,
                external_stylesheets=[dbc.themes.BOOTSTRAP, dbc_css])

# ============================Styles================================ #
tab_card = {'height': '100%'}

main_config = {
    'hovermode': 'x unified',
    'legend': {
        'yanchor': 'top',
        'y': 0.9,
        'xanchor': 'left',
        'x': 0.1,
        'title': {'text': None},
        'font': {'color': 'white'},
        'bgcolor': 'rgba(0,0,0,0.0)'},
    'margin': {'l': 0, 'r': 0, 't': 20, 'b': 0}
}


template_theme1 = 'cyborg'
template_theme2 = 'spacelab'
url_theme1 = dbc.themes.CYBORG
url_theme2 = dbc.themes.SPACELAB


# To dict - Para salvar no dcc.store
df_store = df_dados_gerais.to_dict()

app.layout = dbc.Container([

    dcc.Store(id='store-data', data=[], storage_type='memory'), 
    dcc.Interval(
                id='interval-component',
                interval=10*1000, # in milliseconds
                n_intervals=0
            ),     

    dbc.Row([
        dbc.Col([
            dash_table.DataTable(
                id='datatable-data',
                editable=True,
                fixed_rows={'headers': True},
                
                style_cell_conditional=[
                    {
                        'if': {'column_id': 'CODIGO'},
                        'textAlign': 'left'
                    },

                    {
                        'if': {'column_id': 'CODIGO'},
                        'width': '80px'
                    },

                    {
                        'if': {'column_id': 'ESTOQUE'},
                        'width': '130px'
                    },

                    {
                        'if': {'column_id': 'UNIDADE'},
                        'width': '80px'
                    },                

                    {
                        'if': {'column_id': 'DESCRICAO'},
                        'textAlign': 'left', 'width': '230px'
                    },                

                    {
                        'if': {'column_id': 'GRUPO'},
                        'textAlign': 'left'
                    },   

                    {
                        'if': {'column_id': 'FAMILIA'},
                        'textAlign': 'left'
                    },

                    {
                        'if': {'column_id': 'UNIDADE'},
                        'textAlign': 'left'
                    },                                                      
                ],

                style_data={
                    'color': 'black',
                    'backgroundColor': 'white'
                },

                style_data_conditional=[
                    {
                        'if': {'row_index': 'odd'},
                        'backgroundColor': 'rgb(220, 220, 220)',
                    }
                ],            
                #'backgroundColor': 'white',
                
                style_header={'textAlign': 'center', 
                            'backgroundColor': 'rgb(210, 210, 210)', 
                            'color': 'black', 
                            'fontWeight': 'bold'},

                style_table={'height': '1000px', 'overflowY': 'auto'},                          

                filter_action='native',
                sort_action="native",            
                page_size=1000,
            )
        ], sm=12, lg=12)
    ], className='g-2 my-auto', style={'margin-top': '7px'})

], fluid=True, style={'height': '100vh'})


# ======== Callbacks ========== #
# Na aba TABELAS, conteúdo da tabela.
@app.callback(
    Output('datatable-data', 'data'),
    Output('datatable-data', 'columns'),
    Input('store-data', 'data')
    )
    
def update_table(d):
    print('---------------------- DATA update_table n ----------------------')
    print(d)


    mask = (df_dados_gerais['ANO'] == 2022) & (df_dados_gerais['OPERACAO'] == 'V') &  \
            (df_dados_gerais['MES'].isin(11)) & (df_dados_gerais['CANCELADO'] != '*')

    print('---------------------- update_table MASK ----------------------')
    print(mask)

    df_pivot = pd.pivot_table(
        df_dados_gerais.loc[mask], index=['CODIGO', 'DESCRICAO',
                'ESTOQUE', 'GRUPO',
                'FAMILIA', 'UNIDADE',],
        values='QUANTIDADE',
        columns='MES',
        aggfunc=sum).reset_index().fillna(0)
    
    df_pivot = df_pivot.rename(
        {1: 'JAN', 2: 'FEV', 3: 'MAR', 4: 'ABR', 5: 'MAI', 6: 'JUN', 
        7: 'JUL', 8: 'AGO', 9: 'SET', 10: 'OUT', 11: 'NOV', 12: 'DEZ'}, axis=1)
    
    cols = []

    textCols = ['CODIGO', 'DESCRICAO', 'GRUPO', 'FAMILIA', 'UNIDADE']

    for i in df_pivot.columns:
        if i not in textCols:
            cols.append({"name": str(i),
                    "id": str(i),
                    "type": "numeric",
                    "format": formatted})
        else:
            cols.append({"name": str(i),
                        "id": str(i),
                        "type": "text"})



    return df_pivot.to_dict('records'), cols

# Load data from PostgreSQL, you have to change database name, user and password.
@app.callback(Output('store-data', 'data'),
            Input('interval-component', 'n_intervals'))
def update_data(n_intervals):

    print(f'---------------------- FUNÇÃO update_data {n_intervals} ----------------------')
    #print(df_dados_gerais.to_dict())
    
    return df_dados_gerais.to_dict()

if __name__ == '__main__':
    app.run_server(debug=True)

Hello @EliasCoutinho,

What is the df function that you are calling?

My dataframe is called
I have a file called movimento_geral.py with the code below:

#from multiprocessing.util import is_exiting
from ntpath import join
from peewee import fn, JOIN
from models import Detalhe, Produto, Grupo, Familia, Unidade, Pessoas, Estoque, Codigos, Docitem, Comitem, Documen
import pandas as pd

from datetime import datetime


''' ABAIXO CRIO MINHA CONSULTA PARA USAR NOS GRÁFICOS '''
# -------------------------------------------------------------------------------------------------------------
# Usarei as consultas 'subquery' e 'estoque' abaixo para unir com a consulta 'dados_gerais'
EstoqueAlias = Estoque.alias()  # Faco referencia a tabela Estoque

# Criamos uma subconsulta para listar o alor máximo para cada iddetalhe, ou seja, sempre o último
subquery_estoque = (EstoqueAlias
            .select(
                EstoqueAlias.iddetalhe,
                fn.MAX(EstoqueAlias.dtreferencia).alias('max_ts'))
            .group_by(EstoqueAlias.iddetalhe)
            .alias('post_max_subquery'))

# Consulta que unifica a subquery acima com estoque abaixo:
estoque = (Estoque
        .select(
            Estoque.qtestoque2,
            Estoque.qtestoque,
            Estoque.iddetalhe,
        )
        .switch(Estoque)
        .join(subquery_estoque, on=(
            (Estoque.dtreferencia == subquery_estoque.c.max_ts) &
            (Estoque.iddetalhe == subquery_estoque.c.iddetalhe))))


# -------------------------------------------------------------------------------------------------------------

df_estoque_completo = pd.DataFrame(list(estoque.dicts()))

df_estoque = pd.DataFrame()
df_estoque['iddetalhe'] = df_estoque_completo.iddetalhe
df_estoque['qtestoque'] = df_estoque_completo.qtestoque
df_estoque['qtestoque2'] = df_estoque_completo.qtestoque2
# -------------------------------------------------------------------------------------------------------------
# Crio um dataframecom pessoas
pessoas = (Pessoas(Pessoas.idpessoa, Pessoas.nmpessoa).select())

df_pessoa_completo = pd.DataFrame(list(pessoas.dicts()))

df_pessoa = pd.DataFrame()
df_pessoa['idpessoa'] = df_pessoa_completo.idpessoa
df_pessoa['nmpessoa'] = df_pessoa_completo.nmpessoa
df_pessoa['nmendereco'] = df_pessoa_completo.nmendereco
df_pessoa['nmbairro'] = df_pessoa_completo.nmbairro
df_pessoa['nmcidade'] = df_pessoa_completo.nmcidade
df_pessoa['iduf'] = df_pessoa_completo.iduf


# Base de dados relacionada inteiramente com LEFT_OUTER, ou seja, mostra os dados do produto e se tiver movimento ele lista.
# sem a quantidade em estoque
# .limit(10) *** LIMITA QUANTIDADE DE LINHAS
dados_gerais = (Detalhe().select(
                                Detalhe.iddetalhe, Detalhe.cdprincipal, Detalhe.dsdetalhe, Detalhe.vlprecocusto, Detalhe.vlprecovenda,
                                Docitem.iddocumento, Docitem.dtreferencia, Docitem.qtitem, Docitem.tpoperacao, Docitem.tpdevolucao, Docitem.vlunitario, Docitem.vldesconto,
                                Documen.idpessoa.alias('idcliente'), Documen.stdocumentocancelado, Documen.nrdocumento, Documen.vldesconto.alias('desconto_global'),
                                Familia.dsfamilia,
                                Grupo.nmgrupo,

                                Unidade.dsunidade,
                                Comitem.idpessoa.alias('idvendedor'), Comitem.vlbasecomissao, Comitem.alcomissao).
                                join(Produto, JOIN.LEFT_OUTER, on=(Detalhe.idproduto == Produto.idproduto)).
                                join(Unidade, JOIN.LEFT_OUTER, on=(Produto.idunidade == Unidade.idunidade)).                                
                                join(Grupo, JOIN.LEFT_OUTER, on=(Produto.idgrupo == Grupo.idgrupo)).
                                join(Familia, JOIN.LEFT_OUTER, on=(Detalhe.idfamilia == Familia.idfamilia)).
                                join(Docitem, JOIN.LEFT_OUTER, on=(Detalhe.iddetalhe == Docitem.iddetalhe)).
                                join(Documen, JOIN.LEFT_OUTER, on=(Docitem.iddocumento == Documen.iddocumento)).
                                join(Comitem, JOIN.LEFT_OUTER, on=(Docitem.iddocumentoitem == Comitem.iddocitem))                     
)

# Transformando uma consulta peewee em uma lista, consequentemente em um datagrame do pandas.
df_dados_gerais = pd.DataFrame(
    list(dados_gerais.dicts()))


# Unificando df_dados_gerais e df_estoque pelo campo 'iddetalhe' comum em ambos
df_dados_gerais = pd.merge(df_dados_gerais, df_estoque, on='iddetalhe', how='left')

# **************************** TENTAR USAR CONCAT EM TUDO E DESCOBRIR VALOR UNITARIO EM COCITEM **************************** 
df_dados_gerais = pd.merge(df_dados_gerais, df_pessoa, left_on='idcliente', right_on='idpessoa', how='left')

df_dados_gerais = pd.merge(df_dados_gerais, df_pessoa, left_on='idvendedor', right_on='idpessoa', how='left')

df_dados_gerais = df_dados_gerais.drop(columns=['iddetalhe', 'iddocumento', 'idcliente', 'idvendedor' ,'idpessoa_x'])

df_dados_gerais = df_dados_gerais.drop(columns=['iduf_y', 'nmendereco_y', 'nmbairro_y', 'nmcidade_y', 'idpessoa_y'])

df_dados_gerais.rename(columns={'nmpessoa_y': 'VENDEDOR', 
                                'nmpessoa_x': 'CLIENTE_FORNECEDOR', 
                                'iduf_x':'UF', 
                                'nmendereco_x':'LOGRADOURO', 
                                'nmbairro_x':'BAIRRO', 
                                'nmcidade_x':'CIDADE',
                                'qtitem':'QUANTIDADE',
                                'dtreferencia':'DTREFERENCIA',
                                'vlprecovenda':'VENDA',
                                'vlprecocusto':'CUSTO',
                                'dsdetalhe':'DESCRICAO',
                                'cdprincipal':'CODIGO',
                                'tpdevolucao':'DEVOLUCAO',
                                'tpoperacao':'OPERACAO',
                                'stdocumentocancelado': 'CANCELADO',
                                'dsfamilia':'FAMILIA',
                                'nmgrupo':'GRUPO',
                                'dsunidade':'UNIDADE',
                                'qtestoque':'PRATELEIRA',
                                'qtestoque2':'DEPOSITO',
                                'nrdocumento':'NRDOCUMENTO',
                                'vlbasecomissao': 'VLBASECOMISSAO',
                                'alcomissao': 'ALCOMISSAO',
                                'vldesconto': 'DESCONTO_ITEM',
                                'desconto_global': 'DESCONTO_GLOBAL'}, inplace=True)

# df_dados_gerais.to_excel('C:/Users/EliasPai/Desktop/confere.xlsx')
# Verificando nome das colunas para renomear.
#df_dados_gerais.info()

# Renomeando cada coluna.
df_dados_gerais.columns = ['CODIGO', 'DESCRICAO', 'CUSTO', 'VENDA', 'DTREFERENCIA', 'QUANTIDADE', 'OPERACAO', 'DEVOLUCAO', 'VLUNITARIO', 'DESCONTO_ITEM', 'CANCELADO', 'NUMERO',  
                        'DESCONTO_GLOBAL', 'FAMILIA', 'GRUPO',  'UNIDADE', 'VLBASECOMISSAO', 'ALCOMISSAO', 'QT_PRATELEIRA', 'QT_DEPOSITO',  'CLIENTE_FORNECEDOR',  'LOGRADOURO', 
                        'BAIRRO', 'CIDADE', 'UF', 'VENDEDOR']

# -------------------------------------------------------------------------------------------------------------
# **** PREENCHENDO COLUNAS ****
# Aplicando valores a campos em branco ou com valores que não deveriam aparecer (Incluo ou substituo)

df_dados_gerais['OPERACAO'] = df_dados_gerais['OPERACAO'].fillna('X')
df_dados_gerais['FAMILIA'] = df_dados_gerais['FAMILIA'].fillna('NAO_PREENCHIDO')
df_dados_gerais['CLIENTE_FORNECEDOR'] = df_dados_gerais['CLIENTE_FORNECEDOR'].fillna('NAO_PREENCHIDO')
# Erro linha de cima.
df_dados_gerais['DTREFERENCIA'] = df_dados_gerais['DTREFERENCIA'].fillna('1900-01-01 00:00:01')
df_dados_gerais['CODIGO'] = df_dados_gerais['CODIGO'].fillna('000000')
df_dados_gerais['DESCRICAO'] = df_dados_gerais['DESCRICAO'].fillna('DESCONSIDERE')
df_dados_gerais['QUANTIDADE'] = df_dados_gerais['QUANTIDADE'].fillna(0)
df_dados_gerais['CUSTO'] = df_dados_gerais['CUSTO'].fillna(0)
df_dados_gerais['VENDA'] = df_dados_gerais['VENDA'].fillna(0)
#df_dados_gerais['ATIVO'] = df_dados_gerais['ATIVO'].fillna('N')
df_dados_gerais['GRUPO'] = df_dados_gerais['GRUPO'].fillna('NAO_PREENCHIDO')
df_dados_gerais['UNIDADE'] = df_dados_gerais['UNIDADE'].fillna('NAO_PREENCHIDO')
df_dados_gerais['QT_PRATELEIRA'] = df_dados_gerais['QT_PRATELEIRA'].fillna(0)
df_dados_gerais['QT_DEPOSITO'] = df_dados_gerais['QT_DEPOSITO'].fillna(0)
df_dados_gerais['VENDEDOR'] = df_dados_gerais['VENDEDOR'].fillna('NAO_PREENCHIDO')
df_dados_gerais['LOGRADOURO'] = df_dados_gerais['LOGRADOURO'].fillna('NAO_PREENCHIDO')
df_dados_gerais['BAIRRO'] = df_dados_gerais['BAIRRO'].fillna('NAO_PREENCHIDO')
df_dados_gerais['CIDADE'] = df_dados_gerais['CIDADE'].fillna('NAO_PREENCHIDO')
df_dados_gerais['UF'] = df_dados_gerais['UF'].fillna('NAO_PREENCHIDO')
df_dados_gerais['DESCONTO_ITEM'] = df_dados_gerais['DESCONTO_ITEM'].fillna(0)
df_dados_gerais['DESCONTO_GLOBAL'] = df_dados_gerais['DESCONTO_GLOBAL'].fillna(0)



# -------------------------------------------------------------------------------------------------------------
# Crio a coluna 'ANO' para filtros futuros.
df_dados_gerais['ANO'] = pd.DatetimeIndex(df_dados_gerais.DTREFERENCIA).year

# Crio uma nova coluna 'MES' e faço a tradução simultanea.
df_dados_gerais['MES'] = pd.DatetimeIndex(df_dados_gerais.DTREFERENCIA).month

# Renomeio novamente as colunas.
df_dados_gerais.columns = ['CODIGO', 'DESCRICAO', 'CUSTO', 'VENDA', 'DTREFERENCIA', 'QUANTIDADE', 'OPERACAO', 'DEVOLUCAO', 
                        'VLUNITARIO', 'DESCONTO_ITEM', 'CANCELADO', 'NUMERO', 'DESCONTO_GLOBAL', 'FAMILIA', 'GRUPO',  'UNIDADE', 'VLBASECOMISSAO', 'ALCOMISSAO', 
                        'QT_PRATELEIRA', 'QT_DEPOSITO',  'CLIENTE_FORNECEDOR',  'LOGRADOURO', 'BAIRRO', 'CIDADE', 'UF', 'VENDEDOR', 
                        'ANO', 'MES']
                        

# Removo dos meus dados toda linha que o código for igual a 000000
df_dados_gerais = df_dados_gerais[df_dados_gerais['CODIGO'] != '000000']

# Faço um cálculo com algumas colunas e crio uma nova 'TOTAL_OPERACAO' e 'ESTOQUE' com o resultado..
df_dados_gerais['TOTAL_OPERACAO'] = (df_dados_gerais['VLUNITARIO'] * df_dados_gerais['QUANTIDADE']) - df_dados_gerais['DESCONTO_ITEM']
df_dados_gerais['ESTOQUE'] = df_dados_gerais['QT_DEPOSITO'] + df_dados_gerais['QT_PRATELEIRA']
df_dados_gerais['VLBASECOMISSAO'] = df_dados_gerais['VLBASECOMISSAO'].fillna(df_dados_gerais['TOTAL_OPERACAO'])


# Todas as cidades para maiúsculo.
df_dados_gerais['CIDADE'] = df_dados_gerais['CIDADE'].str.upper()
df_dados_gerais['CIDADE'] = df_dados_gerais['CIDADE'].str.normalize('NFKD').str.encode('ascii', errors='ignore').str.decode('utf-8')
df_dados_gerais.sort_values(by='DTREFERENCIA', ascending=False, inplace=True)

#df_dados_gerais.to_excel('C:/Users/EliasPai/Desktop/df_dados_gerais.xlsx')

Your dataframe needs to be something where it gets refreshed by a function.

Right now, it’s just static. That’s why you aren’t seeing updates.

Something like this will need to be your df file:

# from multiprocessing.util import is_exiting
from ntpath import join
from peewee import fn, JOIN
from models import Detalhe, Produto, Grupo, Familia, Unidade, Pessoas, Estoque, Codigos, Docitem, Comitem, Documen
import pandas as pd

from datetime import datetime
def df_dados_gerais_func():
    ''' ABAIXO CRIO MINHA CONSULTA PARA USAR NOS GRÁFICOS '''
    # -------------------------------------------------------------------------------------------------------------
    # Usarei as consultas 'subquery' e 'estoque' abaixo para unir com a consulta 'dados_gerais'
    EstoqueAlias = Estoque.alias()  # Faco referencia a tabela Estoque
    
    # Criamos uma subconsulta para listar o alor máximo para cada iddetalhe, ou seja, sempre o último
    subquery_estoque = (EstoqueAlias
                        .select(
        EstoqueAlias.iddetalhe,
        fn.MAX(EstoqueAlias.dtreferencia).alias('max_ts'))
                        .group_by(EstoqueAlias.iddetalhe)
                        .alias('post_max_subquery'))
    
    # Consulta que unifica a subquery acima com estoque abaixo:
    estoque = (Estoque
               .select(
        Estoque.qtestoque2,
        Estoque.qtestoque,
        Estoque.iddetalhe,
    )
               .switch(Estoque)
               .join(subquery_estoque, on=(
            (Estoque.dtreferencia == subquery_estoque.c.max_ts) &
            (Estoque.iddetalhe == subquery_estoque.c.iddetalhe))))
    
    # -------------------------------------------------------------------------------------------------------------
    
    df_estoque_completo = pd.DataFrame(list(estoque.dicts()))
    
    df_estoque = pd.DataFrame()
    df_estoque['iddetalhe'] = df_estoque_completo.iddetalhe
    df_estoque['qtestoque'] = df_estoque_completo.qtestoque
    df_estoque['qtestoque2'] = df_estoque_completo.qtestoque2
    # -------------------------------------------------------------------------------------------------------------
    # Crio um dataframecom pessoas
    pessoas = (Pessoas(Pessoas.idpessoa, Pessoas.nmpessoa).select())
    
    df_pessoa_completo = pd.DataFrame(list(pessoas.dicts()))
    
    df_pessoa = pd.DataFrame()
    df_pessoa['idpessoa'] = df_pessoa_completo.idpessoa
    df_pessoa['nmpessoa'] = df_pessoa_completo.nmpessoa
    df_pessoa['nmendereco'] = df_pessoa_completo.nmendereco
    df_pessoa['nmbairro'] = df_pessoa_completo.nmbairro
    df_pessoa['nmcidade'] = df_pessoa_completo.nmcidade
    df_pessoa['iduf'] = df_pessoa_completo.iduf
    
    # Base de dados relacionada inteiramente com LEFT_OUTER, ou seja, mostra os dados do produto e se tiver movimento ele lista.
    # sem a quantidade em estoque
    # .limit(10) *** LIMITA QUANTIDADE DE LINHAS
    dados_gerais = (Detalhe().select(
        Detalhe.iddetalhe, Detalhe.cdprincipal, Detalhe.dsdetalhe, Detalhe.vlprecocusto, Detalhe.vlprecovenda,
        Docitem.iddocumento, Docitem.dtreferencia, Docitem.qtitem, Docitem.tpoperacao, Docitem.tpdevolucao,
        Docitem.vlunitario, Docitem.vldesconto,
        Documen.idpessoa.alias('idcliente'), Documen.stdocumentocancelado, Documen.nrdocumento,
        Documen.vldesconto.alias('desconto_global'),
        Familia.dsfamilia,
        Grupo.nmgrupo,
    
        Unidade.dsunidade,
        Comitem.idpessoa.alias('idvendedor'), Comitem.vlbasecomissao, Comitem.alcomissao).
                    join(Produto, JOIN.LEFT_OUTER, on=(Detalhe.idproduto == Produto.idproduto)).
                    join(Unidade, JOIN.LEFT_OUTER, on=(Produto.idunidade == Unidade.idunidade)).
                    join(Grupo, JOIN.LEFT_OUTER, on=(Produto.idgrupo == Grupo.idgrupo)).
                    join(Familia, JOIN.LEFT_OUTER, on=(Detalhe.idfamilia == Familia.idfamilia)).
                    join(Docitem, JOIN.LEFT_OUTER, on=(Detalhe.iddetalhe == Docitem.iddetalhe)).
                    join(Documen, JOIN.LEFT_OUTER, on=(Docitem.iddocumento == Documen.iddocumento)).
                    join(Comitem, JOIN.LEFT_OUTER, on=(Docitem.iddocumentoitem == Comitem.iddocitem))
                    )
    
    # Transformando uma consulta peewee em uma lista, consequentemente em um datagrame do pandas.
    df_dados_gerais = pd.DataFrame(
        list(dados_gerais.dicts()))
    
    # Unificando df_dados_gerais e df_estoque pelo campo 'iddetalhe' comum em ambos
    df_dados_gerais = pd.merge(df_dados_gerais, df_estoque, on='iddetalhe', how='left')
    
    # **************************** TENTAR USAR CONCAT EM TUDO E DESCOBRIR VALOR UNITARIO EM COCITEM **************************** 
    df_dados_gerais = pd.merge(df_dados_gerais, df_pessoa, left_on='idcliente', right_on='idpessoa', how='left')
    
    df_dados_gerais = pd.merge(df_dados_gerais, df_pessoa, left_on='idvendedor', right_on='idpessoa', how='left')
    
    df_dados_gerais = df_dados_gerais.drop(columns=['iddetalhe', 'iddocumento', 'idcliente', 'idvendedor', 'idpessoa_x'])
    
    df_dados_gerais = df_dados_gerais.drop(columns=['iduf_y', 'nmendereco_y', 'nmbairro_y', 'nmcidade_y', 'idpessoa_y'])
    
    df_dados_gerais.rename(columns={'nmpessoa_y': 'VENDEDOR',
                                    'nmpessoa_x': 'CLIENTE_FORNECEDOR',
                                    'iduf_x': 'UF',
                                    'nmendereco_x': 'LOGRADOURO',
                                    'nmbairro_x': 'BAIRRO',
                                    'nmcidade_x': 'CIDADE',
                                    'qtitem': 'QUANTIDADE',
                                    'dtreferencia': 'DTREFERENCIA',
                                    'vlprecovenda': 'VENDA',
                                    'vlprecocusto': 'CUSTO',
                                    'dsdetalhe': 'DESCRICAO',
                                    'cdprincipal': 'CODIGO',
                                    'tpdevolucao': 'DEVOLUCAO',
                                    'tpoperacao': 'OPERACAO',
                                    'stdocumentocancelado': 'CANCELADO',
                                    'dsfamilia': 'FAMILIA',
                                    'nmgrupo': 'GRUPO',
                                    'dsunidade': 'UNIDADE',
                                    'qtestoque': 'PRATELEIRA',
                                    'qtestoque2': 'DEPOSITO',
                                    'nrdocumento': 'NRDOCUMENTO',
                                    'vlbasecomissao': 'VLBASECOMISSAO',
                                    'alcomissao': 'ALCOMISSAO',
                                    'vldesconto': 'DESCONTO_ITEM',
                                    'desconto_global': 'DESCONTO_GLOBAL'}, inplace=True)
    
    # df_dados_gerais.to_excel('C:/Users/EliasPai/Desktop/confere.xlsx')
    # Verificando nome das colunas para renomear.
    # df_dados_gerais.info()
    
    # Renomeando cada coluna.
    df_dados_gerais.columns = ['CODIGO', 'DESCRICAO', 'CUSTO', 'VENDA', 'DTREFERENCIA', 'QUANTIDADE', 'OPERACAO',
                               'DEVOLUCAO', 'VLUNITARIO', 'DESCONTO_ITEM', 'CANCELADO', 'NUMERO',
                               'DESCONTO_GLOBAL', 'FAMILIA', 'GRUPO', 'UNIDADE', 'VLBASECOMISSAO', 'ALCOMISSAO',
                               'QT_PRATELEIRA', 'QT_DEPOSITO', 'CLIENTE_FORNECEDOR', 'LOGRADOURO',
                               'BAIRRO', 'CIDADE', 'UF', 'VENDEDOR']
    
    # -------------------------------------------------------------------------------------------------------------
    # **** PREENCHENDO COLUNAS ****
    # Aplicando valores a campos em branco ou com valores que não deveriam aparecer (Incluo ou substituo)
    
    df_dados_gerais['OPERACAO'] = df_dados_gerais['OPERACAO'].fillna('X')
    df_dados_gerais['FAMILIA'] = df_dados_gerais['FAMILIA'].fillna('NAO_PREENCHIDO')
    df_dados_gerais['CLIENTE_FORNECEDOR'] = df_dados_gerais['CLIENTE_FORNECEDOR'].fillna('NAO_PREENCHIDO')
    # Erro linha de cima.
    df_dados_gerais['DTREFERENCIA'] = df_dados_gerais['DTREFERENCIA'].fillna('1900-01-01 00:00:01')
    df_dados_gerais['CODIGO'] = df_dados_gerais['CODIGO'].fillna('000000')
    df_dados_gerais['DESCRICAO'] = df_dados_gerais['DESCRICAO'].fillna('DESCONSIDERE')
    df_dados_gerais['QUANTIDADE'] = df_dados_gerais['QUANTIDADE'].fillna(0)
    df_dados_gerais['CUSTO'] = df_dados_gerais['CUSTO'].fillna(0)
    df_dados_gerais['VENDA'] = df_dados_gerais['VENDA'].fillna(0)
    # df_dados_gerais['ATIVO'] = df_dados_gerais['ATIVO'].fillna('N')
    df_dados_gerais['GRUPO'] = df_dados_gerais['GRUPO'].fillna('NAO_PREENCHIDO')
    df_dados_gerais['UNIDADE'] = df_dados_gerais['UNIDADE'].fillna('NAO_PREENCHIDO')
    df_dados_gerais['QT_PRATELEIRA'] = df_dados_gerais['QT_PRATELEIRA'].fillna(0)
    df_dados_gerais['QT_DEPOSITO'] = df_dados_gerais['QT_DEPOSITO'].fillna(0)
    df_dados_gerais['VENDEDOR'] = df_dados_gerais['VENDEDOR'].fillna('NAO_PREENCHIDO')
    df_dados_gerais['LOGRADOURO'] = df_dados_gerais['LOGRADOURO'].fillna('NAO_PREENCHIDO')
    df_dados_gerais['BAIRRO'] = df_dados_gerais['BAIRRO'].fillna('NAO_PREENCHIDO')
    df_dados_gerais['CIDADE'] = df_dados_gerais['CIDADE'].fillna('NAO_PREENCHIDO')
    df_dados_gerais['UF'] = df_dados_gerais['UF'].fillna('NAO_PREENCHIDO')
    df_dados_gerais['DESCONTO_ITEM'] = df_dados_gerais['DESCONTO_ITEM'].fillna(0)
    df_dados_gerais['DESCONTO_GLOBAL'] = df_dados_gerais['DESCONTO_GLOBAL'].fillna(0)
    
    # -------------------------------------------------------------------------------------------------------------
    # Crio a coluna 'ANO' para filtros futuros.
    df_dados_gerais['ANO'] = pd.DatetimeIndex(df_dados_gerais.DTREFERENCIA).year
    
    # Crio uma nova coluna 'MES' e faço a tradução simultanea.
    df_dados_gerais['MES'] = pd.DatetimeIndex(df_dados_gerais.DTREFERENCIA).month
    
    # Renomeio novamente as colunas.
    df_dados_gerais.columns = ['CODIGO', 'DESCRICAO', 'CUSTO', 'VENDA', 'DTREFERENCIA', 'QUANTIDADE', 'OPERACAO',
                               'DEVOLUCAO',
                               'VLUNITARIO', 'DESCONTO_ITEM', 'CANCELADO', 'NUMERO', 'DESCONTO_GLOBAL', 'FAMILIA', 'GRUPO',
                               'UNIDADE', 'VLBASECOMISSAO', 'ALCOMISSAO',
                               'QT_PRATELEIRA', 'QT_DEPOSITO', 'CLIENTE_FORNECEDOR', 'LOGRADOURO', 'BAIRRO', 'CIDADE', 'UF',
                               'VENDEDOR',
                               'ANO', 'MES']
    
    # Removo dos meus dados toda linha que o código for igual a 000000
    df_dados_gerais = df_dados_gerais[df_dados_gerais['CODIGO'] != '000000']
    
    # Faço um cálculo com algumas colunas e crio uma nova 'TOTAL_OPERACAO' e 'ESTOQUE' com o resultado..
    df_dados_gerais['TOTAL_OPERACAO'] = (df_dados_gerais['VLUNITARIO'] * df_dados_gerais['QUANTIDADE']) - df_dados_gerais[
        'DESCONTO_ITEM']
    df_dados_gerais['ESTOQUE'] = df_dados_gerais['QT_DEPOSITO'] + df_dados_gerais['QT_PRATELEIRA']
    df_dados_gerais['VLBASECOMISSAO'] = df_dados_gerais['VLBASECOMISSAO'].fillna(df_dados_gerais['TOTAL_OPERACAO'])
    
    # Todas as cidades para maiúsculo.
    df_dados_gerais['CIDADE'] = df_dados_gerais['CIDADE'].str.upper()
    df_dados_gerais['CIDADE'] = df_dados_gerais['CIDADE'].str.normalize('NFKD').str.encode('ascii',
                                                                                           errors='ignore').str.decode(
        'utf-8')
    df_dados_gerais.sort_values(by='DTREFERENCIA', ascending=False, inplace=True)

    # df_dados_gerais.to_excel('C:/Users/EliasPai/Desktop/df_dados_gerais.xlsx')
    
    return df_dados_gerais

Then in your app, this should work:

from datetime import date
import plotly.graph_objs as go
import dash
from dash import html, dcc, Input, Output, State, dash_table
import dash_bootstrap_components as dbc
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import pandas as pd

from dash_bootstrap_templates import ThemeSwitchAIO

# Importei meu dataframe do arquivo movimento_geral.py
from movimento_geral import df_dados_gerais_func

# Formatação das tabelas
formatted = {'specifier': ',.2f', 'locale': {'group': '.', 'decimal': ',', }}

# ================================================================== #
from flask import Flask

dbc_css = "https://cdn.jsdelivr.net/gh/AnnMarieW/dash-bootstrap-templates/dbc.min.css"

server = Flask(__name__)

app = dash.Dash(__name__, server=server, suppress_callback_exceptions=True,
                external_stylesheets=[dbc.themes.BOOTSTRAP, dbc_css])

# ============================Styles================================ #
tab_card = {'height': '100%'}

main_config = {
    'hovermode': 'x unified',
    'legend': {
        'yanchor': 'top',
        'y': 0.9,
        'xanchor': 'left',
        'x': 0.1,
        'title': {'text': None},
        'font': {'color': 'white'},
        'bgcolor': 'rgba(0,0,0,0.0)'},
    'margin': {'l': 0, 'r': 0, 't': 20, 'b': 0}
}

template_theme1 = 'cyborg'
template_theme2 = 'spacelab'
url_theme1 = dbc.themes.CYBORG
url_theme2 = dbc.themes.SPACELAB

# To dict - Para salvar no dcc.store
df_dados_gerais = df_dados_gerais_func()
df_store = df_dados_gerais.to_dict()

app.layout = dbc.Container([

    dcc.Store(id='store-data', data=[], storage_type='memory'),
    dcc.Interval(
        id='interval-component',
        interval=10 * 1000,  # in milliseconds
        n_intervals=0
    ),

    dbc.Row([
        dbc.Col([
            dash_table.DataTable(
                id='datatable-data',
                editable=True,
                fixed_rows={'headers': True},

                style_cell_conditional=[
                    {
                        'if': {'column_id': 'CODIGO'},
                        'textAlign': 'left'
                    },

                    {
                        'if': {'column_id': 'CODIGO'},
                        'width': '80px'
                    },

                    {
                        'if': {'column_id': 'ESTOQUE'},
                        'width': '130px'
                    },

                    {
                        'if': {'column_id': 'UNIDADE'},
                        'width': '80px'
                    },

                    {
                        'if': {'column_id': 'DESCRICAO'},
                        'textAlign': 'left', 'width': '230px'
                    },

                    {
                        'if': {'column_id': 'GRUPO'},
                        'textAlign': 'left'
                    },

                    {
                        'if': {'column_id': 'FAMILIA'},
                        'textAlign': 'left'
                    },

                    {
                        'if': {'column_id': 'UNIDADE'},
                        'textAlign': 'left'
                    },
                ],

                style_data={
                    'color': 'black',
                    'backgroundColor': 'white'
                },

                style_data_conditional=[
                    {
                        'if': {'row_index': 'odd'},
                        'backgroundColor': 'rgb(220, 220, 220)',
                    }
                ],
                # 'backgroundColor': 'white',

                style_header={'textAlign': 'center',
                              'backgroundColor': 'rgb(210, 210, 210)',
                              'color': 'black',
                              'fontWeight': 'bold'},

                style_table={'height': '1000px', 'overflowY': 'auto'},

                filter_action='native',
                sort_action="native",
                page_size=1000,
            )
        ], sm=12, lg=12)
    ], className='g-2 my-auto', style={'margin-top': '7px'})

], fluid=True, style={'height': '100vh'})


# ======== Callbacks ========== #
# Na aba TABELAS, conteúdo da tabela.
@app.callback(
    Output('datatable-data', 'data'),
    Output('datatable-data', 'columns'),
    Input('store-data', 'data')
)
def update_table(d):
    print('---------------------- DATA update_table n ----------------------')
    print(d)

    mask = (df_dados_gerais['ANO'] == 2022) & (df_dados_gerais['OPERACAO'] == 'V') & \
           (df_dados_gerais['MES'].isin(11)) & (df_dados_gerais['CANCELADO'] != '*')

    print('---------------------- update_table MASK ----------------------')
    print(mask)

    df_pivot = pd.pivot_table(
        df_dados_gerais.loc[mask], index=['CODIGO', 'DESCRICAO',
                                          'ESTOQUE', 'GRUPO',
                                          'FAMILIA', 'UNIDADE', ],
        values='QUANTIDADE',
        columns='MES',
        aggfunc=sum).reset_index().fillna(0)

    df_pivot = df_pivot.rename(
        {1: 'JAN', 2: 'FEV', 3: 'MAR', 4: 'ABR', 5: 'MAI', 6: 'JUN',
         7: 'JUL', 8: 'AGO', 9: 'SET', 10: 'OUT', 11: 'NOV', 12: 'DEZ'}, axis=1)

    cols = []

    textCols = ['CODIGO', 'DESCRICAO', 'GRUPO', 'FAMILIA', 'UNIDADE']

    for i in df_pivot.columns:
        if i not in textCols:
            cols.append({"name": str(i),
                         "id": str(i),
                         "type": "numeric",
                         "format": formatted})
        else:
            cols.append({"name": str(i),
                         "id": str(i),
                         "type": "text"})

    return df_pivot.to_dict('records'), cols


# Load data from PostgreSQL, you have to change database name, user and password.
@app.callback(Output('store-data', 'data'),
              Input('interval-component', 'n_intervals'))
def update_data(n_intervals):
    print(f'---------------------- FUNÇÃO update_data {n_intervals} ----------------------')
    # print(df_dados_gerais.to_dict())
    df_dados_gerais = df_dados_gerais_func()
    return df_dados_gerais.to_dict()


if __name__ == '__main__':
    app.run_server(debug=True)

Hello, I am very happy to have people like you helping others.
This way I had already tried.

I will send you two links:
1 - Containing the complete code:

I have some observations for this code to work:
a) Install the libraries highlighted in the teste.py file before the imports.
b) The database access configuration I made in a file called pg_config.json, just edit and inform your bank credentials.

2 - Containing the Postgres database:

@EliasCoutinho,

I’m not really for that, however, I think making a change here will work:

# ======== Callbacks ========== #
# Na aba TABELAS, conteúdo da tabela.
@app.callback(
    Output('datatable-data', 'data'),
    Output('datatable-data', 'columns'),
    Input('store-data', 'data')
)
def update_table(d):
    print('---------------------- DATA update_table n ----------------------')
    print(d)

    df_dados_gerais = pd.DataFrame(d)

    mask = (df_dados_gerais['ANO'] == 2022) & (df_dados_gerais['OPERACAO'] == 'V') & \
           (df_dados_gerais['MES'].isin(11)) & (df_dados_gerais['CANCELADO'] != '*')

    print('---------------------- update_table MASK ----------------------')
    print(mask)

    df_pivot = pd.pivot_table(
        df_dados_gerais.loc[mask], index=['CODIGO', 'DESCRICAO',
                                          'ESTOQUE', 'GRUPO',
                                          'FAMILIA', 'UNIDADE', ],
        values='QUANTIDADE',
        columns='MES',
        aggfunc=sum).reset_index().fillna(0)

    df_pivot = df_pivot.rename(
        {1: 'JAN', 2: 'FEV', 3: 'MAR', 4: 'ABR', 5: 'MAI', 6: 'JUN',
         7: 'JUL', 8: 'AGO', 9: 'SET', 10: 'OUT', 11: 'NOV', 12: 'DEZ'}, axis=1)

    cols = []

    textCols = ['CODIGO', 'DESCRICAO', 'GRUPO', 'FAMILIA', 'UNIDADE']

    for i in df_pivot.columns:
        if i not in textCols:
            cols.append({"name": str(i),
                         "id": str(i),
                         "type": "numeric",
                         "format": formatted})
        else:
            cols.append({"name": str(i),
                         "id": str(i),
                         "type": "text"})

    return df_pivot.to_dict('records'), cols