bokeh model does not exist - django

I have django bokeh project and I'm trying to add dateRangePicker slider but I get error in console
bokeh-0.12.16.min.js:1 Uncaught Error: Model 'DateRangeSlider' does not exist. This could be due to a widget
or a custom model not being registered before first usage.
This is code for that.
date_range_slider = DateRangeSlider(title="Date Range: ", start='2018-01-02', end='2018-06-09',
value=('2018-06-02', '2018-06-09'), step=1)
# l = layout(children=[[date_range_slider]], sizing_mode='fixed')
l = layout(children=[[p], [date_range_slider]], sizing_mode='fixed')
script, div = components(l)
print(div)
return render(request, 'index.html', {"the_script": script, "the_div": div})
Do I need to add anything in django model?

Update: note that starting with Bokeh 2.0, there are only JS components to load (no separate CSS files)
BokehJS is split into multiple pieces so that users who do not need, e.g. widgets, do not have to load the extra JS and CSS for them. If you use widgets and components, you need to explicitly include the extra JS and CSS for them as described in the documentation
<link
href="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-x.y.z.min.css"
rel="stylesheet" type="text/css">
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-x.y.z.min.js"></script>

Related

Stylized HTML fields in Django admin

I Have TextField to store HTML text. I want to colorize HTML tags. I use TinyMCE but didn't need an HTML editor like WordPress just simple to colorize like IDE, Search a lot but didn't find anything useful, So if you can help I appreciate it.
My field:
I want output like this but changeable:
Using a code editor in Django admin Textarea
The following is implemented with CodeMirror but you can use any other library (Monaco, etc).
Final result:
For all the libraries, we need to add some library specific javascript and stylesheet plus some custom code for integration.
Django has pretty flexible templating to enable this. We can override the entire template or specific blocks. Detailed documentation can be fond here.
To integration CodeMirror, we can override the admin/base.html 's extrahead block. We will include the required js/css, one extra css for theming and some js to find and apply CodeMirror.
Here, I have create a admin/base.html inside my project's configured template directory. This will apply to all the apps and models. There are ways to target an app or model individually - check the office docs.
This works for JSONField data. You will have to tweak a bit to format
html or any other language.
Inspect the page and find id of the textarea you want to target
<!-- tested with Django 3.2.9 -->
{% extends "admin/base.html" %}
{% block extrahead %}
{{ block.super }}
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.0/codemirror.min.js" crossorigin="anonymous"
referrerpolicy="no-referrer"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.0/codemirror.min.css"
crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.0/theme/oceanic-next.min.css"
crossorigin="anonymous" referrerpolicy="no-referrer" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.0/mode/javascript/javascript.min.js"
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script type="text/javascript">
window.addEventListener('load', (event) => {
const target = document.getElementById("id_data");
target.value = JSON.stringify(JSON.parse(target.value), null, ' ');
const config = {
mode: 'javascript',
theme: 'oceanic-next',
lineNumbers: true,
lineWrapping: true
}
const jsonCodeMirror = CodeMirror.fromTextArea(target, config);
jsonCodeMirror.setSize("100%", 600);
});
</script>
{% endblock %}
Multiple textarea inputs can be targeted as well with get element with classname and then looping over the result set and applying the same logic.
For html formatting,
you can use this https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.0/mode/htmlmixed/htmlmixed.min.js instead of javascript. Delete the json stringify statement. Change mode to htmlmixed.
Don't need to override any template
solved by integrating django-ace field and in this we have support for CSS, python, HTML etc also.
Simply use this in django forms and provide the form in django model admin class
admin file
from appname import forms
#admin.register(models.Table)
class TableModelAdmin(admin.ModelAdmin):
form = forms.TableModelForm
form file
from django import forms
from django_ace import AceWidget
class TableModelForm(forms.ModelForm):
class Meta:
model = models.Table
fields = '__all__'
widgets = {
'columns': AceWidget(
mode='json',
theme='None',
width='1200px',
height='500px',
fontsize='18px',
showprintmargin=False,
),
}

Prevent Django form inserting javascript that is loaded in head of page

I build an application with Django (version 2.2.1). All templates extend a base template, which loads jQuery as follows:
<script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.min.js' %}"></script>
In one template, the combination of an autocompleting input field from django-autocomplete-light (version 3.4.1) and a datepicker from django-bootstrap-datepicker-plus (version 3.0.5) caused the date picker not to work, due to the bootstrap datepicker not being able to initialize jQuery.
It turns out that I stumbled upon the same problem as mentioned in this question.
The problem is that the template contains a form, which contains a ModelSelect2 widget. This widget causes the form to load jQuery again, as can be seen in its media property:
#property
def media(self):
"""Return JS/CSS resources for the widget."""
extra = '' if settings.DEBUG else '.min'
i18n_name = self._get_language_code()
i18n_file = (
'%s%s.js' % (I18N_PATH, i18n_name),
) if i18n_name else ()
return forms.Media(
js=(
'admin/js/vendor/jquery/jquery%s.js' % extra,
'autocomplete_light/jquery.init.js',
'vendor/select2/dist/js/select2.full%s.js' % extra,
) + i18n_file + (
'autocomplete_light/autocomplete.init.js',
'autocomplete_light/forward.js',
'autocomplete_light/select2.js',
'autocomplete_light/jquery.post-setup.js',
),
css={...}, # omitted, because irrelevant for this question.
)
My question: is it somehow possible to prevent this double loading a javascript file?
I guess that a form must be able to render itself without knowing what else has been rendered in front of it. So is it then somehow possible to inform a Form about any javascript that has already been included in the template so that a Form.media() can exclude javascript that is already loaded outside the form?
Oh sweet irony: while reviewing this question I stumbled upon this question and now I discovered that in a newer version of django-autocomplete-light jQuery has been removed from the media, solving my problem... Nevertheless, I am still interested in an answer to this question, because the problem might occur again with a different combination of widgets provided by external packages.
Usually that's why sometimes you would have {% block js %} in the base of the template.
how to organize JS files in Django?

how to embed standalone bokeh graphs into django templates

I want to display graphs offered by the bokeh library in my web application via django framework but I don't want to use the bokeh-server executable because it's not the good way. so is that possible? if yes how to do that?
Using the Embedding Bokeh Plots documentation example as suggested by Fabio Pliger, one can do this in Django:
in the views.py file, we put:
from django.shortcuts import render
from bokeh.plotting import figure
from bokeh.resources import CDN
from bokeh.embed import components
def simple_chart(request):
plot = figure()
plot.circle([1,2], [3,4])
script, div = components(plot, CDN)
return render(request, "simple_chart.html", {"the_script": script, "the_div": div})
in the urls.py file we can put :
from myapp.views import simple_chart
...
...
...
url(r'^simple_chart/$', simple_chart, name="simple_chart"),
...
...
in the template file simple_chart.html we'll have :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Experiment with Bokeh</title>
<script src="http://cdn.bokeh.org/bokeh/release/bokeh-0.8.1.min.js"></script>
<link rel="stylesheet" href="http://cdn.bokeh.org/bokeh/release/bokeh-0.8.1.min.css">
</head>
<body>
{{ the_div|safe }}
{{ the_script|safe }}
</body>
</html>
And it works.
You don't need to use bokeh-server to embed bokeh plots. It just means you'll not be using (and probably don't need) the extra features it provides.
In fact you can embed bokeh plots in many ways like generating standalone html, by generating bokeh standalone components that you can then embed in you django app when rendering templates or with the method we call "autoloading" which makes bokeh return a tag that will replace itself with a Bokeh plot. You'll find better details looking at the documentation.
Another good source of inspiration is the embed examples you can find in the repository.
It is also possible to have it work with AJAX requests. Let's say we have a page loaded and would like to show a plot on button click without reloading the whole page. From Django view we return Bokeh script and div in JSON:
from django.http import JsonResponse
from bokeh.plotting import figure
from bokeh.resources import CDN
from bokeh.embed import components
def simple_chart(request):
plot = figure()
plot.circle([1,2], [3,4])
script, div = components(plot, CDN)
return JsonResponse({"script": script, "div": div})
When we get AJAX response in JS (in this example Jquery is used) the div is first appended to the existing page and then the script is appended:
$("button").click(function(){
$.ajax({
url: "/simple_chart",
success: function(result){
var bokeh_data = JSON.parse(result);
$('#bokeh_graph').html(bokeh_data.div);
$("head").append(bokeh_data.script);
}});
});
It must put {{the_script|safe}} inside the head tag
Here's a flask app that uses jquery to interract with a bokeh plot. Check out the templates/ for javascript you can reuse. Also search for bokeh-demos on github.

I'm writing a custom widget for django-admin that requires some JavaScript, where do I put it?

It seems wrong to add
<script src="..."></script>
to the render() of the widget. Especially if I have 2 of them on the same page. Is there a way to add a single script include on the admin form pages, in a way where I don't edit core files?
Widgets have a Media inner class that allows you to specify scripts and styles that need to be loaded. See the documentation.
You can set external javascript libraries in the Media class:
class ArticleAdmin(admin.ModelAdmin):
class Media:
css = {
"all": ("my_styles.css",)
}
js = ("my_code.js",)

How do I use multiple style sheets in Django, where the style sheet is determined by a GET variable, without violating DRY?

I an writing a website that will have multiple skins. Each skin has its own style sheet. I would like the skin used to be determined by a GET variable, so that this URL:
whatever?skin=foo
will cause the page to be rendered containing this HTML in the header:
<link rel="stylesheet" type="text/css" href="/site_media/foo.css"/>
(Normally I want the skin to be determined by a user's preference, but I want this way of doing it as well, so a user can preview what a new skin will look like, as well as for making it easy for me while developing it.)
This is fairly easy to do in Django, for example you could use a template with this line:
<link rel="stylesheet" type="text/css" href="/site_media/{{skin}}.css"/>
And a view like this:
def whateverView(request):
""" called by URL /whatever """
skin = request.GET.get('skin', "default")
c = RequestContext(request, {'skin': skin})
html = whateverTemplate.render(c)
return HttpResponse(html)
But I don't want to have to do it like that, as I would have to add the same code to every single view, which would violate DRY.
So is there some way I can do it, such that it works on all my pages, while only writing the code once?
You can do this using Django's Context Processors: http://docs.djangoproject.com/en/dev/ref/templates/api/?#writing-your-own-context-processors. Or, you could enable django.core.conext_processors.request and access the request object in your template.