Sqlite in Multi-Page dash app

Hello,
I’m using sample codes shown on
Charming Data

but when I’m modifying CRUD app to be part of mutlipage app it doesn’t work.
callbacks are inactive. Could you advice how to fix this?
it doesn’t matter if I use @app.callback, or just @callback not creating the app on the page9 it still doesn’t work. Thank you in advance!
code for main app is:

from dash import html, dcc
import dash_bootstrap_components as dbc
import dash_auth
from flask_sqlalchemy import SQLAlchemy
from flask import Flask


USERNAME_PASSWORD_PAIRS = [
     ['JB', '13']
]


app = dash.Dash(__name__, use_pages=True, external_stylesheets=[dbc.themes.SPACELAB])

server = app.server


auth = dash_auth.BasicAuth(app,USERNAME_PASSWORD_PAIRS)



sidebar = dbc.Nav(
            [
                dbc.NavLink(
                    [
                        html.Div(page["name"], className="ms-2"),
                    ],
                    href=page["path"],
                    active="exact",
                )
                for page in dash.page_registry.values()
            ],
            vertical=True,
            pills=True,
            className="bg-light",
)

app.layout = dbc.Container([
    dbc.Row([
        dbc.Col(html.Div("Sales Report and Analysis",
                         style={'fontSize':50, 'textAlign':'center'}))
    ]),

    html.Hr(),

    dbc.Row(
        [
            dbc.Col(
                [
                    sidebar
                ], xs=4, sm=4, md=2, lg=2, xl=2, xxl=2),

            dbc.Col(
                [
                    dash.page_container
                ], xs=8, sm=8, md=10, lg=10, xl=10, xxl=10)
        ]
    )
], fluid=True)


if __name__ == "__main__":
    app.run(debug=False, port=8019)

code for page 9 is.

import dash
from dash import Dash, dash_table, dcc, html, Input, Output, callback, State
import plotly.express as px
import dash_bootstrap_components as dbc
import pandas as pd
import dash
from dash.dependencies import Input, Output, State
from dash import dash_table
from dash import dcc, html
import sqlite3
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go

from flask_sqlalchemy import SQLAlchemy
from flask import Flask


dash.register_page(__name__,
                   path='/page1',  
                   name='Targets',  
                   title='Targerts'  
)

server = Flask(__name__)
server.app_context().push()
server.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:///books.db"
# Optional: But it will silence the deprecation warning in the
app = dash.Dash(__name__, server=server, suppress_callback_exceptions=True)
app.server.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False




db = SQLAlchemy(app.server)

print(db.engine)
class Product(db.Model):
    __tablename__ = 'productlist'

    Phone = db.Column(db.String(40), nullable=False, primary_key=True)
    Version = db.Column(db.String(40), nullable=False)
    Price = db.Column(db.Integer, nullable=False)
    Sales = db.Column(db.Integer, nullable=False)

    def __init__(self, phone, version, price, sales):
        self.Phone = phone
        self.Version = version
        self.Price = price
        self.Sales = sales
db.create_all()

conn = sqlite3.connect('instance/books.db')

# Get all table names
tables = pd.read_sql_query("SELECT name from sqlite_master WHERE type='table'", conn)

# Print the table names
print(77777777777777777777777)
print(tables["name"])
print(tables)

df = pd.read_sql_table('productlist', con=db.engine)
print(df)
print(db.engine)
print(77777777777777777777777)
# ------------------------------------------------------------------------------------------------

layout = html.Div([
    html.Div([
        dcc.Input(
            id='adding-rows-name',
            placeholder='Enter a column name...',
            value='',
            style={'padding': 10}
        ),
        html.Button('Add Column', id='adding-columns-button', n_clicks=0)
    ], style={'height': 50}),

    dcc.Interval(id='interval_pg', interval=86400000*7, n_intervals=0),  # activated once/week or when page refreshed
    html.Div(id='postgres_datatable'),

    html.Button('Add Row', id='editing-rows-button', n_clicks=0),
    html.Button('Save to PostgreSQL', id='save_to_postgres', n_clicks=0),

    # Create notification when saving to excel
    html.Div(id='placeholder', children=[]),
    dcc.Store(id="store", data=0),
    dcc.Interval(id='interval', interval=1000),

    # dcc.Graph(id='my_graph')

])


# ------------------------------------------------------------------------------------------------
#
#
@callback(Output('postgres_datatable', 'children'),
              [Input('interval_pg', 'n_intervals')])
def populate_datatable(n_intervals):
    df = pd.read_sql_table('productlist', con=db.engine)
    print(90)
    print(df)
    return [
        dash_table.DataTable(
            id='our-table',
            columns=[{
                         'name': str(x),
                         'id': str(x),
                         'deletable': False,
                     } if x == 'Sales' or x == 'Phone'
                     else {
                'name': str(x),
                'id': str(x),
                'deletable': True,
            }
                     for x in df.columns],
            data=df.to_dict('records'),
            editable=True,
            row_deletable=True,
            filter_action="native",
            sort_action="native",  # give user capability to sort columns
            sort_mode="single",  # sort across 'multi' or 'single' columns
            page_action='none',  # render all of the data at once. No paging.
            style_table={'height': '300px', 'overflowY': 'auto'},
            style_cell={'textAlign': 'left', 'minWidth': '100px', 'width': '100px', 'maxWidth': '100px'},
            style_cell_conditional=[
                {
                    'if': {'column_id': c},
                    'textAlign': 'right'
                } for c in ['Price', 'Sales']
            ]

        ),
    ]
#
#
@callback(
    Output('our-table', 'columns'),
    [Input('adding-columns-button', 'n_clicks')],
    [State('adding-rows-name', 'value'),
     State('our-table', 'columns')],
    prevent_initial_call=True)
def add_columns(n_clicks, value, existing_columns):
    if n_clicks > 0:
        existing_columns.append({
            'name': value, 'id': value,
            'renamable': True, 'deletable': True
        })
    return existing_columns

#
@callback(
    Output('our-table', 'data'),
    [Input('editing-rows-button', 'n_clicks')],
    [State('our-table', 'data'),
     State('our-table', 'columns')],
    prevent_initial_call=True)
def add_row(n_clicks, rows, columns):
    if n_clicks > 0:
        rows.append({c['id']: '' for c in columns})
    return rows
#
#
# @app.callback(
#     Output('my_graph', 'figure'),
#     [Input('our-table', 'data')],
#     prevent_initial_call=True)
# def display_graph(data):
#     # df_fig = pd.DataFrame(data)
#     # fig = px.bar(df_fig, x='Phone', y='Sales')
#
#     pg_filtered = db.session.query(Product.Phone, Product.Sales)
#     phone_c = [x.Phone for x in pg_filtered]
#     sales_c = [x.Sales for x in pg_filtered]
#     fig = go.Figure([go.Bar(x=phone_c, y=sales_c)])
#
#     return fig
#
#
@callback(
    [Output('placeholder', 'children'),
     Output("store", "data")],
    [Input('save_to_postgres', 'n_clicks'),
     Input("interval", "n_intervals")],
    [State('our-table', 'data'),
     State('store', 'data')],
    prevent_initial_call=True)
def df_to_csv(n_clicks, n_intervals, dataset, s):
    output = html.Plaintext("The data has been saved to your PostgreSQL database.",
                            style={'color': 'green', 'font-weight': 'bold', 'font-size': 'large'})
    no_output = html.Plaintext("", style={'margin': "0px"})

    input_triggered = dash.callback_context.triggered[0]["prop_id"].split(".")[0]

    if input_triggered == "save_to_postgres":
        s = 6
        pg = pd.DataFrame(dataset)
        pg.to_sql("productlist", con=db.engine, if_exists='replace', index=False)
        return output, s
    elif input_triggered == 'interval' and s > 0:
        s = s - 1
        if s > 0:
            return output, s
        else:
            return no_output, s
    elif s == 0:
        return no_output, s

# @app.callback(Output('placeholder', 'children'),
#               [Input('save_to_postgres', 'n_clicks')],
#               [State('our-table', 'data')])
# def save_to_db(n_clicks, data):
#     if n_clicks > 0:
#         # Create the table if it doesn't exist
#         db.create_all()
#         for row in data:
#             new_product = Product(Phone=row['Phone'], Version=row['Version'], Price=row['Price'], Sales=row['Sales'])
#             db.session.add(new_product)
#             db.session.commit()
#     return 'Data saved to database'

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

Hello @drongo007,

Not sure why you are trying to init a dash app on a page, but it doesnt allow you do.

You should only have one running, and then register the page like you are. Take the sqlite connection and setup to the main app, and I think this will work fine.

thank you for reply!
"Not sure why you are trying to init a dash app on a page, " I’m new with Dash, I think you mean that on pg9 I defined app= and it was mistaken? I did it just trying all possibilities.
If I set SQL to main (in my case app.py) how should I then process with pg9 to create functionality?
importing db from app from main folder? how if yes, then how should I do this? multupage app requires paged to be in separate directory and I couldn’t import db to it.
thank you very much for helping!

Nope, this will lead to circular import.

You should be able to reference the sqlite server with the page in the same fashion as you were trying.

Take this to the app.py and leave everything else there.

As pages get registered with the app, they actually end up being able to reference things like they are the app.

Should I transfer class Product(db.Model): to main page too?
I did but page isn’t working. it only displays title on the first page. no navbar etc.

Is it giving any errors?

nope,

I seperated code this way. (I had to take class to app.py other wise in page 10 couldn’t work with db.)
Now other pages work, but the main DB doesn’t.
callbacks don’t work.


server = Flask(__name__)
server.app_context().push()
server.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:///books.db"
# Optional: But it will silence the deprecation warning in the
app = dash.Dash(__name__, server=server, use_pages=True, suppress_callback_exceptions=True, external_stylesheets=[dbc.themes.SPACELAB])
app.server.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app.server)


class Product(db.Model):
    __tablename__ = 'productlist'

    Phone = db.Column(db.String(40), nullable=False, primary_key=True)
    Version = db.Column(db.String(40), nullable=False)
    Price = db.Column(db.Integer, nullable=False)
    Sales = db.Column(db.Integer, nullable=False)

    def __init__(self, phone, version, price, sales):
        self.Phone = phone
        self.Version = version
        self.Price = price
        self.Sales = sales

db.create_all()


auth = dash_auth.BasicAuth(app,USERNAME_PASSWORD_PAIRS)

and
another page

import dash_bootstrap_components as dbc
from dash import Input, Output, html
import dash
from dash import html, dcc
import dash_bootstrap_components as dbc
from dash import Dash, dash_table, dcc, html, Input, Output, callback, State
from flask_sqlalchemy import SQLAlchemy
from flask import Flask

import pandas as pd
import dash_pivottable


# db = SQLAlchemy(app.server)
# print(db.engine)



dash.register_page(__name__,
                   name='DB')

layout = html.Div(

dbc.Container([
html.Div([
        dcc.Input(
            id='adding-rows-name',
            placeholder='Enter a column name...',
            value='',
            style={'padding': 10}
        ),
        html.Button('Add Column', id='adding-columns-button', n_clicks=0)
    ], style={'height': 50}),

    dcc.Interval(id='interval_pg', interval=86400000*7, n_intervals=0),  # activated once/week or when page refreshed
    html.Div(id='postgres_datatable'),

    html.Button('Add Row', id='editing-rows-button', n_clicks=0),
    html.Button('Save to PostgreSQL', id='save_to_postgres', n_clicks=0),

    # Create notification when saving to excel
    html.Div(id='placeholder', children=[]),
    dcc.Store(id="store", data=0),
    dcc.Interval(id='interval', interval=1000),

    dcc.Graph(id='my_graph')





    ])
)

@callback(Output('postgres_datatable', 'children'),
              [Input('interval_pg', 'n_intervals')])
def populate_datatable(n_intervals):
    df = pd.read_sql_table('productlist', con=db.engine)
    return [
        dash_table.DataTable(
            id='our-table',
            columns=[{
                         'name': str(x),
                         'id': str(x),
                         'deletable': False,
                     } if x == 'Sales' or x == 'Phone'
                     else {
                'name': str(x),
                'id': str(x),
                'deletable': True,
            }
                     for x in df.columns],
            data=df.to_dict('records'),
            editable=True,
            row_deletable=True,
            filter_action="native",
            sort_action="native",  # give user capability to sort columns
            sort_mode="single",  # sort across 'multi' or 'single' columns
            page_action='none',  # render all of the data at once. No paging.
            style_table={'height': '300px', 'overflowY': 'auto'},
            style_cell={'textAlign': 'left', 'minWidth': '100px', 'width': '100px', 'maxWidth': '100px'},
            style_cell_conditional=[
                {
                    'if': {'column_id': c},
                    'textAlign': 'right'
                } for c in ['Price', 'Sales']
            ]

        ),
    ]
#
#
@callback(
    Output('our-table', 'columns'),
    [Input('adding-columns-button', 'n_clicks')],
    [State('adding-rows-name', 'value'),
     State('our-table', 'columns')],
    prevent_initial_call=True)
def add_columns(n_clicks, value, existing_columns):
    if n_clicks > 0:
        existing_columns.append({
            'name': value, 'id': value,
            'renamable': True, 'deletable': True
        })
    return existing_columns

#
@callback(
    Output('our-table', 'data'),
    [Input('editing-rows-button', 'n_clicks')],
    [State('our-table', 'data'),
     State('our-table', 'columns')],
    prevent_initial_call=True)
def add_row(n_clicks, rows, columns):
    if n_clicks > 0:
        rows.append({c['id']: '' for c in columns})
    return rows


if __name__ == "__main__":
    app.run_server(debug=False, port=8009)

Take that out of the page.

took,
other pages work normal but code for DB page doesn’t/
it worked however when I used it as separate page. but as a part of multi-page it doesn’t see callbacks I think.

I think it has something to do with

df = pd.read_sql_table('productlist', con=db.engine)
in my populate_datatable callback.

db is defined on main page. but what should I do?

Use this, I think it should work.

I have this line on app.py,
but on pg10.py (where populate_datatable, add_row etc.) there is no db variable.

You need to declare it on that page, the same way I did above.

You should be able to ignore the warning about app not being a variable in the page. :slight_smile:

I did, but then it doesn’t launch.
I got error like this.

  File "/Users/zaurguliyev/Documents/PyTraining/PyCharm/projects/salesreport7/pages/pg10.py", line 17, in <module>
    db = SQLAlchemy(app.server)
NameError: name 'app' is not defined

You should be running the app.py, not the page’s.

I’m running app.py but it returns error for the pg10.

Ok, can you give me what both of your files are?

of course.

app.py

import dash
from dash import html, dcc
import dash_bootstrap_components as dbc
import dash_auth
from flask_sqlalchemy import SQLAlchemy
from flask import Flask


USERNAME_PASSWORD_PAIRS = [
    ['sales', '2022b'],['AS', '1'], ['JB', '13'],['TA', '2']
]

# # was here
# app = dash.Dash(__name__, use_pages=True, external_stylesheets=[dbc.themes.SPACELAB])
# #was here
# server = app.server


server = Flask(__name__)
server.app_context().push()
server.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:///books.db"
# Optional: But it will silence the deprecation warning in the
app = dash.Dash(__name__, server=server, use_pages=True, suppress_callback_exceptions=True, external_stylesheets=[dbc.themes.SPACELAB])
app.server.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app.server)


class Product(db.Model):
    __tablename__ = 'productlist'

    Phone = db.Column(db.String(40), nullable=False, primary_key=True)
    Version = db.Column(db.String(40), nullable=False)
    Price = db.Column(db.Integer, nullable=False)
    Sales = db.Column(db.Integer, nullable=False)

    def __init__(self, phone, version, price, sales):
        self.Phone = phone
        self.Version = version
        self.Price = price
        self.Sales = sales

db.create_all()


auth = dash_auth.BasicAuth(app,USERNAME_PASSWORD_PAIRS)



sidebar = dbc.Nav(
            [
                dbc.NavLink(
                    [
                        html.Div(page["name"], className="ms-2"),
                    ],
                    href=page["path"],
                    active="exact",
                )
                for page in dash.page_registry.values()
            ],
            vertical=True,
            pills=True,
            className="bg-light",
)

app.layout = dbc.Container([
    dbc.Row([
        dbc.Col(html.Div("Sales Report and Analysis",
                         style={'fontSize':50, 'textAlign':'center'}))
    ]),

    html.Hr(),

    dbc.Row(
        [
            dbc.Col(
                [
                    sidebar
                ], xs=4, sm=4, md=2, lg=2, xl=2, xxl=2),

            dbc.Col(
                [
                    dash.page_container
                ], xs=8, sm=8, md=10, lg=10, xl=10, xxl=10)
        ]
    )
], fluid=True)


if __name__ == "__main__":
    app.run(debug=False, port=8069)

pg10.py

import dash_bootstrap_components as dbc
from dash import Input, Output, html
import dash
from dash import html, dcc
import dash_bootstrap_components as dbc
from dash import Dash, dash_table, dcc, html, Input, Output, callback, State
from flask_sqlalchemy import SQLAlchemy
from flask import Flask
import pandas as pd
import dash_pivottable




db = SQLAlchemy(app.server)

dash.register_page(__name__,
                   name='DBase')

layout = html.Div(

dbc.Container([
html.Div([
        dcc.Input(
            id='adding-rows-name',
            placeholder='Enter a column name...',
            value='',
            style={'padding': 10}
        ),
        html.Button('Add Column', id='adding-columns-button', n_clicks=0)
    ], style={'height': 50}),

    dcc.Interval(id='interval_pg', interval=86400000*7, n_intervals=0),  # activated once/week or when page refreshed
    html.Div(id='postgres_datatable'),

    html.Button('Add Row', id='editing-rows-button', n_clicks=0),
    html.Button('Save to PostgreSQL', id='save_to_postgres', n_clicks=0),

    # Create notification when saving to excel
    html.Div(id='placeholder', children=[]),
    dcc.Store(id="store", data=0),
    dcc.Interval(id='interval', interval=1000),

    dcc.Graph(id='my_graph')





    ])
)

@callback(Output('postgres_datatable', 'children'),
              [Input('interval_pg', 'n_intervals')])
def populate_datatable(n_intervals):
    df = pd.read_sql_table('productlist', con=db.engine)
    return [
        dash_table.DataTable(
            id='our-table',
            columns=[{
                         'name': str(x),
                         'id': str(x),
                         'deletable': False,
                     } if x == 'Sales' or x == 'Phone'
                     else {
                'name': str(x),
                'id': str(x),
                'deletable': True,
            }
                     for x in df.columns],
            data=df.to_dict('records'),
            editable=True,
            row_deletable=True,
            filter_action="native",
            sort_action="native",  # give user capability to sort columns
            sort_mode="single",  # sort across 'multi' or 'single' columns
            page_action='none',  # render all of the data at once. No paging.
            style_table={'height': '300px', 'overflowY': 'auto'},
            style_cell={'textAlign': 'left', 'minWidth': '100px', 'width': '100px', 'maxWidth': '100px'},
            style_cell_conditional=[
                {
                    'if': {'column_id': c},
                    'textAlign': 'right'
                } for c in ['Price', 'Sales']
            ]

        ),
    ]
#
#
@callback(
    Output('our-table', 'columns'),
    [Input('adding-columns-button', 'n_clicks')],
    [State('adding-rows-name', 'value'),
     State('our-table', 'columns')],
    prevent_initial_call=True)
def add_columns(n_clicks, value, existing_columns):
    if n_clicks > 0:
        existing_columns.append({
            'name': value, 'id': value,
            'renamable': True, 'deletable': True
        })
    return existing_columns

#
@callback(
    Output('our-table', 'data'),
    [Input('editing-rows-button', 'n_clicks')],
    [State('our-table', 'data'),
     State('our-table', 'columns')],
    prevent_initial_call=True)
def add_row(n_clicks, rows, columns):
    if n_clicks > 0:
        rows.append({c['id']: '' for c in columns})
    return rows

this happens when I run app.py

Ok, so, I think I got it working, what you need to do is split the flask app into its own thing:

server.py:

import flask
from flask import Flask

server = Flask(__name__)
server.app_context().push()
server.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:///books.db"
server.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

app.py:

import dash
from dash import html, dcc
import dash_bootstrap_components as dbc
import dash_auth
from flask_sqlalchemy import SQLAlchemy
from flask import Flask
from server import server

USERNAME_PASSWORD_PAIRS = [
    ['sales', '2022b'],['AS', '1'], ['JB', '13'],['TA', '2']
]

# # was here
# app = dash.Dash(__name__, use_pages=True, external_stylesheets=[dbc.themes.SPACELAB])
# #was here
# server = app.server

# Optional: But it will silence the deprecation warning in the
app = dash.Dash(__name__, server=server, use_pages=True, suppress_callback_exceptions=True, external_stylesheets=[dbc.themes.SPACELAB])
db = SQLAlchemy(app.server)


class Product(db.Model):
    __tablename__ = 'productlist'

    Phone = db.Column(db.String(40), nullable=False, primary_key=True)
    Version = db.Column(db.String(40), nullable=False)
    Price = db.Column(db.Integer, nullable=False)
    Sales = db.Column(db.Integer, nullable=False)

    def __init__(self, phone, version, price, sales):
        self.Phone = phone
        self.Version = version
        self.Price = price
        self.Sales = sales

db.create_all()


auth = dash_auth.BasicAuth(app,USERNAME_PASSWORD_PAIRS)



sidebar = dbc.Nav(
            [
                dbc.NavLink(
                    [
                        html.Div(page["name"], className="ms-2"),
                    ],
                    href=page["path"],
                    active="exact",
                )
                for page in dash.page_registry.values()
            ],
            vertical=True,
            pills=True,
            className="bg-light",
)

app.layout = dbc.Container([
    dbc.Row([
        dbc.Col(html.Div("Sales Report and Analysis",
                         style={'fontSize':50, 'textAlign':'center'}))
    ]),

    html.Hr(),

    dbc.Row(
        [
            dbc.Col(
                [
                    sidebar
                ], xs=4, sm=4, md=2, lg=2, xl=2, xxl=2),

            dbc.Col(
                [
                    dash.page_container
                ], xs=8, sm=8, md=10, lg=10, xl=10, xxl=10)
        ]
    )
], fluid=True)


if __name__ == "__main__":
    app.run(debug=False, port=8069)

pg10.py:

import dash_bootstrap_components as dbc
from dash import Input, Output, html
import dash
from dash import html, dcc
import dash_bootstrap_components as dbc
from dash import Dash, dash_table, dcc, html, Input, Output, callback, State
from flask_sqlalchemy import SQLAlchemy
from flask import Flask
import pandas as pd
from server import server




db = SQLAlchemy(server)

dash.register_page(__name__,
                   name='DBase')

layout = html.Div(

dbc.Container([
html.Div([
        dcc.Input(
            id='adding-rows-name',
            placeholder='Enter a column name...',
            value='',
            style={'padding': 10}
        ),
        html.Button('Add Column', id='adding-columns-button', n_clicks=0)
    ], style={'height': 50}),

    dcc.Interval(id='interval_pg', interval=86400000*7, n_intervals=0),  # activated once/week or when page refreshed
    html.Div(id='postgres_datatable'),

    html.Button('Add Row', id='editing-rows-button', n_clicks=0),
    html.Button('Save to PostgreSQL', id='save_to_postgres', n_clicks=0),

    # Create notification when saving to excel
    html.Div(id='placeholder', children=[]),
    dcc.Store(id="store", data=0),
    dcc.Interval(id='interval', interval=1000),

    dcc.Graph(id='my_graph')





    ])
)

@callback(Output('postgres_datatable', 'children'),
              [Input('interval_pg', 'n_intervals')])
def populate_datatable(n_intervals):
    df = pd.read_sql_table('productlist', con=db.engine)
    return [
        dash_table.DataTable(
            id='our-table',
            columns=[{
                         'name': str(x),
                         'id': str(x),
                         'deletable': False,
                     } if x == 'Sales' or x == 'Phone'
                     else {
                'name': str(x),
                'id': str(x),
                'deletable': True,
            }
                     for x in df.columns],
            data=df.to_dict('records'),
            editable=True,
            row_deletable=True,
            filter_action="native",
            sort_action="native",  # give user capability to sort columns
            sort_mode="single",  # sort across 'multi' or 'single' columns
            page_action='none',  # render all of the data at once. No paging.
            style_table={'height': '300px', 'overflowY': 'auto'},
            style_cell={'textAlign': 'left', 'minWidth': '100px', 'width': '100px', 'maxWidth': '100px'},
            style_cell_conditional=[
                {
                    'if': {'column_id': c},
                    'textAlign': 'right'
                } for c in ['Price', 'Sales']
            ]

        ),
    ]
#
#
@callback(
    Output('our-table', 'columns'),
    [Input('adding-columns-button', 'n_clicks')],
    [State('adding-rows-name', 'value'),
     State('our-table', 'columns')],
    prevent_initial_call=True)
def add_columns(n_clicks, value, existing_columns):
    if n_clicks > 0:
        existing_columns.append({
            'name': value, 'id': value,
            'renamable': True, 'deletable': True
        })
    return existing_columns

#
@callback(
    Output('our-table', 'data'),
    [Input('editing-rows-button', 'n_clicks')],
    [State('our-table', 'data'),
     State('our-table', 'columns')],
    prevent_initial_call=True)
def add_row(n_clicks, rows, columns):
    if n_clicks > 0:
        rows.append({c['id']: '' for c in columns})
    return rows

thankS! that worked! so pity that had to bother you so much, but couldn’t find any relevant information in documentation!