I am trying to make a DataTable
in a Bokeh server with optional columns that can be toggled on/off with a checkbox widget; something like:
Where clicking a button in the checkbox widget would add/remove the corresponding column from the table below. I was able to accomplish this example with some simple CustomJS
attached to the widget that explicitly changes the columns of the table:
from datetime import date
from random import randint
from bokeh.io import show
from bokeh.layouts import column
from bokeh.models import (
ColumnDataSource,
DataTable,
DateFormatter,
TableColumn,
CheckboxButtonGroup,
CustomJS
)
from bokeh.plotting import curdoc
data = dict(
dates=[date(2014, 3, i+1) for i in range(10)],
downloads=[randint(0, 100) for i in range(10)],
)
source = ColumnDataSource(data)
columns = [
TableColumn(field="dates", title="Date", formatter=DateFormatter()),
TableColumn(field="downloads", title="Downloads"),
]
data_table = DataTable(source=source, columns=columns, width=400, height=280)
column_choice = CheckboxButtonGroup(
labels=[
"Date",
"Downloads"
],
active=[0, 1],
)
column_choice.js_on_click(
CustomJS(
args=dict(
table=data_table, columns=columns
),
code="""
var visible_columns = []
for (var i = 0; i < this.active.length; i++) {
visible_columns.push(columns[this.active[i]])
}
table.columns = visible_columns;
""",
)
)
curdoc().add_root(column(column_choice, data_table))
But I notice that on every click of the widget buttons, we get a DeserializationError
trying to handle the patch event (though the columns of the table do toggle as desired):
2021-07-13 13:13:16,349 error handling message
message: Message 'PATCH-DOC' content: {'events': [{'kind': 'ModelChanged', 'model': {'id': '1007'}, 'attr': 'columns', 'new': [{'id': '1004'}]}], 'references': []}
error: DeserializationError("Instance(TableColumn) failed to deserialize reference to {'id': '1004'}")
Traceback (most recent call last):
File "/home/charlesbluca/miniconda3/envs/dask-distributed/lib/python3.8/site-packages/bokeh/server/protocol_handler.py", line 90, in handle
work = await handler(message, connection)
File "/home/charlesbluca/miniconda3/envs/dask-distributed/lib/python3.8/site-packages/bokeh/server/session.py", line 67, in _needs_document_lock_wrapper
result = func(self, *args, **kwargs)
File "/home/charlesbluca/miniconda3/envs/dask-distributed/lib/python3.8/site-packages/bokeh/server/session.py", line 261, in _handle_patch
message.apply_to_document(self.document, self)
File "/home/charlesbluca/miniconda3/envs/dask-distributed/lib/python3.8/site-packages/bokeh/protocol/messages/patch_doc.py", line 100, in apply_to_document
doc._with_self_as_curdoc(lambda: doc.apply_json_patch(self.content, setter))
File "/home/charlesbluca/miniconda3/envs/dask-distributed/lib/python3.8/site-packages/bokeh/document/document.py", line 1198, in _with_self_as_curdoc
return f()
File "/home/charlesbluca/miniconda3/envs/dask-distributed/lib/python3.8/site-packages/bokeh/protocol/messages/patch_doc.py", line 100, in <lambda>
doc._with_self_as_curdoc(lambda: doc.apply_json_patch(self.content, setter))
File "/home/charlesbluca/miniconda3/envs/dask-distributed/lib/python3.8/site-packages/bokeh/document/document.py", line 411, in apply_json_patch
patched_obj.set_from_json(attr, value, models=references, setter=setter)
File "/home/charlesbluca/miniconda3/envs/dask-distributed/lib/python3.8/site-packages/bokeh/core/has_props.py", line 412, in set_from_json
descriptor.set_from_json(self, json, models, setter)
File "/home/charlesbluca/miniconda3/envs/dask-distributed/lib/python3.8/site-packages/bokeh/core/property/descriptors.py", line 623, in set_from_json
return super().set_from_json(obj, self.property.from_json(json, models), models, setter)
File "/home/charlesbluca/miniconda3/envs/dask-distributed/lib/python3.8/site-packages/bokeh/core/property/container.py", line 70, in from_json
return self._new_instance([ self.item_type.from_json(item, models) for item in json ])
File "/home/charlesbluca/miniconda3/envs/dask-distributed/lib/python3.8/site-packages/bokeh/core/property/container.py", line 70, in <listcomp>
return self._new_instance([ self.item_type.from_json(item, models) for item in json ])
File "/home/charlesbluca/miniconda3/envs/dask-distributed/lib/python3.8/site-packages/bokeh/core/property/instance.py", line 93, in from_json
raise DeserializationError(f"{self} failed to deserialize reference to {json}")
bokeh.core.property.bases.DeserializationError: Instance(TableColumn) failed to deserialize reference to {'id': '1004'}
My questions here are:
- Is there something I should be doing when explicitly setting the columns of the table to prevent an error like this?
- If not, is there a recommended way to accomplish something like this? My first thought was to make changes to the fields of the CDS instead, but I’m not exactly sure how something like this could be done while also streaming data into the CDS.
For context, this issue is coming up through work on dask/distributed#4614.