Bokeh+Django使用row合并图表时报Models must be owned by only a single document错误
错误触发原因
这个错误是Bokeh的核心设计限制导致的:Bokeh的所有可视化模型(包括Figure、数据源、布局组件等)同一时间只能属于一个Document文档对象。
你单独渲染单个图表时,components函数只会处理单个Figure对象,自动将其绑定到临时生成的文档,处理完成后自动解绑,所以不会报错。
当你把两个已实例化的Figure对象放入row布局再传入components时,老版本Bokeh的处理逻辑存在缺陷:要么是你生成的两个Figure已经被隐式绑定到了全局默认的curdoc()上,要么是components处理布局时重复给子Figure对象绑定新文档,最终触发了重复绑定的异常。
解决方案
你可以根据自己的需求任选以下一种方案:
方案1:手动解绑Figure的文档绑定(最快捷)
在构造row布局前,手动清除两个图表已有的文档绑定,代码修改如下:def get(self, request, *args, **kwargs): sample_plot = create_barchart_all_sites() variant_plot = create_variant_barchart() # 手动清除已有文档绑定 sample_plot.document = None variant_plot.document = None # 再构造布局生成嵌入代码 script, div = components(row(sample_plot, variant_plot, sizing_mode='scale_width')) self.context['script'] = script self.context['div'] = div return render(request, self.template, self.context)方案2:用前端布局替代Bokeh的row布局(兼容性最好)
完全不使用Bokeh的布局组件,单独生成每个图表的嵌入代码,在Django模板中用Bootstrap的栅格系统实现横向排列,完全绕开Bokeh的文档绑定冲突:
视图代码修改:def get(self, request, *args, **kwargs): sample_plot = create_barchart_all_sites() variant_plot = create_variant_barchart() # 单独生成每个图表的嵌入代码 script_sample, div_sample = components(sample_plot) script_variant, div_variant = components(variant_plot) self.context.update({ 'script_sample': script_sample, 'div_sample': div_sample, 'script_variant': script_variant, 'div_variant': div_variant }) return render(request, self.template, self.context)模板代码修改:
<div class="row"> <div class="col mb-3"> <div id='outstanding_samples' class='card'> <div class="card-header bg-light"> <h6><b>HODB Overview</b></h6> </div> <div class='container-fluid card-body'> <!-- 用栅格实现横向排列 --> <div class="row"> <div class="col-6">{{div_sample|safe}}</div> <div class="col-6">{{div_variant|safe}}</div> </div> {{script_sample|safe}} {{script_variant|safe}} </div> </div> </div> </div>方案3:升级Bokeh版本
如果你使用的Bokeh版本低于2.0,可升级到Python3.6支持的最高Bokeh版本2.4.3,该版本已经修复了布局组件导致的重复绑定文档的bug:pip install bokeh==2.4.3
内容的提问来源于stack exchange,提问作者trouselife
相关产品推荐
相关产品推荐

