Django newbie here: my aim is to integrate Folium to an html page. so what I have at the moment:
polls/views.py
def show_map(request):
#creation of map comes here + business logic
m = folium.Map([51.5, -0.25], zoom_start=10)
test = folium.Html('<b>Hello world</b>', script=True)
popup = folium.Popup(test, max_width=2650)
folium.RegularPolygonMarker(location=[51.5, -0.25], popup=popup).add_to(m)
context = {'my_map': m}
return render(request, 'polls/show_folium_map.html', context)
polls/urls.py
urlpatterns = [
path('show_my_map', views.show_map, name='show_map'),
]
and show_folium_map.html
<h1>map result comes here</h1>
{{ my_map }}
problem is that I get the 'to_string' value of the map (I promise you I saw that coming). So how can I integrate the map in such way that I can actually see the map and also define the size?
In order to really include folium into custom django template you have to render your figure first before adding it to context (This will recursivly load all parts of the map into the figure). Afterwards in your template, you have to access header, html and script parts of figure seperatly by calling their render function. Additionally, those parts have to marked as "safe" by django template tag in order to allow html insertion. See example below.
Example:
views.py:
import folium
from django.views.generic import TemplateView
class FoliumView(TemplateView):
template_name = "folium_app/map.html"
def get_context_data(self, **kwargs):
figure = folium.Figure()
m = folium.Map(
location=[45.372, -121.6972],
zoom_start=12,
tiles='Stamen Terrain'
)
m.add_to(figure)
folium.Marker(
location=[45.3288, -121.6625],
popup='Mt. Hood Meadows',
icon=folium.Icon(icon='cloud')
).add_to(m)
folium.Marker(
location=[45.3311, -121.7113],
popup='Timberline Lodge',
icon=folium.Icon(color='green')
).add_to(m)
folium.Marker(
location=[45.3300, -121.6823],
popup='Some Other Location',
icon=folium.Icon(color='red', icon='info-sign')
).add_to(m)
figure.render()
return {"map": figure}
templates/folium_app/map.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
{{map.header.render|safe}}
</head>
<body>
<div><h1>Here comes my folium map:</h1></div>
{{map.html.render|safe}}
<script>
{{map.script.render|safe}}
</script>
</body>
</html>
You can try the below way. I also had faced the same issue and it worked great for me.
views.py
def show_map(request):
#creation of map comes here + business logic
m = folium.Map([51.5, -0.25], zoom_start=10)
test = folium.Html('<b>Hello world</b>', script=True)
popup = folium.Popup(test, max_width=2650)
folium.RegularPolygonMarker(location=[51.5, -0.25], popup=popup).add_to(m)
m=m._repr_html_() #updated
context = {'my_map': m}
return render(request, 'polls/show_folium_map.html', context)
show_folium_map.html
{{ my_map|safe }}
You can get the html as a string by triggering the rendering on the (internal) parent of Map:
m = folium.Map()
html: str = m.get_root().render()
Note that this returns a full html page, so you may need to put it in an iframe.
Alternatively, you can render the head, body and script parts separately. That way you can put each part on your page where it belongs and you don't need an iframe:
m = folium.Map()
html_head: str = m.get_root().header.render()
html_body: str = m.get_root().html.render()
html_script: str = m.get_root().script.render()
Related
I make a web app using Django, Folium. I have a navbar and a Folium map on the web page. It works fine om computers and landscape screen devices, but on portrait screen devices the map has a free space.
My code for map:
current_map = folium.Map(location=start_location, zoom_start=6)
fig = branca.element.Figure(height="100%")
fig.add_child(current_map)
context = {"current_map": current_map._repr_html_()}
return render(request, template_name="index.html", context=context)
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
{{ current_map | safe }}
</body>
</html>
How do I fill it?
Well, I might to use Figure and modify folium package.
current_map = folium.Map(location=(48.51, 32.25), zoom_start=6)
map_container = branca.element.Figure(height="100%")
map_container.add_child(current_map)
...
context = {"current_map": map_container.render(), "form": form}
return render(request, template_name="hub/index.html", context=context)
I downloaded folium-0.12.1.post1.tar.gz, and from folium/folium.py removed mentions about old versions of Bootstrap from lists _default_js and _default_css. But I use Bootstrap in base.html.
And in requirements.txt I use this local modified distr:
./distr/folium-0.12.1.post1.tar.gz
You could use
current_map._repr_html_()
That give you representation for jupitier notebook.
And use it in template by
{{current_map|safe}}
In teality this is a syring content tat easely could be modified by
.replace()
and you coud add height, or change proportion.
Also you could use
f=folium.Figure(height="100%"
current_map.add(f)
And in this case
current_map._repr_html_()
will return
Sou you could manage size of follium window.
I also had this similar problem while back
I solved it by changing the style of the map where i rendered it position:fixed;
Try this:
context = {'map': map.get_root().render()}
return render(request, template_name="index.html", context=context)
index.html:
<html>
{{map|safe}}
</html>
In my django project I have a model with a field that contain html code as text:
html_test = models.TextField()
for example in this field could be:
<html>
<header><title>This is title</title></header>
<body>
Hello world
</body>
</html>
I want to retrive this code and render dynamically showing the corresponding html page without create a file or save it on disk.
How can i render an html code in memory for display page and then destroy it?
I'm still not sure what your actual question is. You retrieve the model from the db and return the data to the user, just like anything else. For example, a view might look like this:
def my_view(request, pk):
obj = MyModel.objects.get(pk=pk)
return HttpResponse(obj.html_test)
Using jinja2 as the templating engine,
class YourClassName(generic.TemplateView):
template_name = 'your_template.jinja'
def get_context_data(self, **kwargs):
kwargs['html_data'] = MyModel.objects.get(pk=pk).html_test
return super(YourClassName, self).get_context_data(**kwargs)
In your your_template.jinja
<html>
<header><title>This is title</title></header>
<body>
{{html_data}}
</body>
i am a beginner with Django and i encountered a problem. I wrote a code which allows me to add different products and categories from admin, and then display them on a html page. However, when i click on any of the displayed products i want my product description to be displayed in another html page. The first page is working just fine( product_list.html), however i am having trouble with the second one (product_detail.html).
urls.py ( without the imports):
urlpatterns = [
url(r'^category/(?P<categoryid>\d+)/$', views.product_list, name='product_list_by_category'),
#url(r'^list/(?P<id>\d+)/$', views.product_detail, name='product_detail'),
re_path('^$', views.product_list, name = 'product_list'),
url(r'^list/(?P<id>\d+)/$', views.product_detail, name='product_detail'),
]
views.py
def product_list(request, categoryid = 1): *this one is working*
categories = Category.objects.all()
products = Product.objects.all().filter(category_id=categoryid)
context = {'categories': categories,
'products': products
}
return render(request, 'shop/product_list.html', context)
def product_detail(request, id=1): *this one is not displaying anything*
productt = Product.objects.all().filter(id = id)
return render(request, 'shop/product_detail.html', {'product': productt})
product_detail.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{{product.description}}
</body>
</html>
Do you have any idea why the page rendered by product_detail does not display anything?
#DanielRoseman is right, but to prevent an error in case there's no Product with that id, you have 3 options. The first one is to do a queryset and get the first element, then check if there is a result, like this:
productt = Product.objects.filter(id=id).first()
if productt is None:
# do something to let the user know the product doesn't exist
The second option is to wrap your get method arround a try except:
try:
productt = Product.objects.get(id=id)
except Product.DoesNotExist:
# do something to let the user know the product doesn't exist
The final option is to use get_object_or_404, which:
Calls get() on a given model manager, but it raises Http404 instead of the model’s DoesNotExist exception.
from django.shortcuts import get_object_or_404
productt = get_object_or_404(Product, id=id)
filter returns a queryset. Querysets don't have id or description attributes.
You need to use get to get an instance:
productt = Product.objects.get(id=id)
I have just installed FusionCharts Suite XT v3.13.4 to use in my (Python) Django application. I have done the Getting Started Guide (https://www.fusioncharts.com/dev/getting-started/django/your-first-chart-using-django#installation-2), but I can't seem to get it to work. I don't get an error, but my page remains completely empty. I don't know what I did wrong, I followed the tutorial exactly.
dash.html
<!-- Filename: app_name/templates/index.html -->
<!DOCTYPE html>
<html>
<head>
<title>FC-python wrapper</title>
{% load static %}
<script type="text/javascript" src="{% static "https://cdn.fusioncharts.com/fusioncharts/latest/fusioncharts.js" %}"></script>
<script type="text/javascript" src="{% static "https://cdn.fusioncharts.com/fusioncharts/latest/themes/fusioncharts.theme.fusion.js" %}"></script>
</head>
<body>
<div id="myFirstchart-container">{{ output|safe }}</div>
</body>
</html>
views.py
from django.shortcuts import render
from django.http import HttpResponse
from collections import OrderedDict
# Include the `fusioncharts.py` file that contains functions to embed the charts.
#from fusioncharts import FusionCharts
from vsdk.dashboard.fusioncharts import FusionCharts
def myFirstChart(request):
#Chart data is passed to the `dataSource` parameter, like a dictionary in the form of key-value pairs.
dataSource = OrderedDict()
# The `chartConfig` dict contains key-value pairs of data for chart attribute
chartConfig = OrderedDict()
chartConfig['caption'] = 'Countries With Most Oil Reserves [2017-18]'
chartConfig['subCaption'] = 'In MMbbl = One Million barrels'
chartConfig['xAxisName'] = 'Country'
chartConfig['yAxisName'] = 'Reserves (MMbbl)'
chartConfig['numberSuffix'] = 'K'
chartConfig['theme'] = 'fusion'
# The `chartData` dict contains key-value pairs of data
chartData = OrderedDict()
chartData['Venezuela'] = 290
chartData['Saudi'] = 260
chartData['Canada'] = 180
chartData['Iran'] = 140
chartData['Russia'] = 115
chartData['UAE'] = 100
chartData['US'] = 30
chartData['China'] = 30
dataSource['chart'] = chartConfig
dataSource['data'] = []
# Convert the data in the `chartData`array into a format that can be consumed by FusionCharts.
#The data for the chart should be in an array wherein each element of the array
#is a JSON object# having the `label` and `value` as keys.
#Iterate through the data in `chartData` and insert into the `dataSource['data']` list.
for key, value in chartData.items():
data = {}
data['label'] = key
data['value'] = value
dataSource['data'].append(data)
# Create an object for the column 2D chart using the FusionCharts class constructor
# The chart data is passed to the `dataSource` parameter.
column2D = FusionCharts("column2d", "ex1" , "600", "400", "chart-1", "json", dataSource)
return render(request, 'dash.html', {'output' : column2D.render(), 'chartTitle': 'Simple Chart Using Array'})
urls.py
from django.shortcuts import render
from django.urls import path
from vsdk.dashboard.fusioncharts import FusionCharts
from . import views
from django.conf.urls import url, include
urlpatterns = [
url(r'^$', views.myFirstChart, name = 'demo'),
]
The instructions in the "Getting Started Guide" are a bit confusion and there are some errors. Here is my working version of the Fusionchart example.
Fusionchart example
You can use FusionCharts to render charts in html. I have based the example on the Getting Started Guide and tweaked here and there to make it work. To replicate just do the following:
copy my github code, this will create an new project directory fusionchart_example.
git clone https://github.com/bvermeulen/fusionchart_example
The tree structure should look like:
go to this folder and create a virtual environment for Django, note I work with Python 3.6.8, but likely other python 3.6 or 3.7 would work ok as well.
python -m venv ./venv
activate the environment (Linux)
source ./venv/bin/activate
(or Windows)
./venv/scripts/activate
with the virtual environment enabled install Django
pip install django==2.2.3
You can now run the app
python manage.py runserver
and view the result in your browser at 127.0.0.1:8000 that should look like:
You can review the source code when you have cloned my github, especially settings.py, but I give urls.py, views.py and chart.html below as first reference.
urls.py:
from django.urls import path
from render_graph import views
urlpatterns = [
path('', views.chart, name='chart'),
]
views.py:
from django.shortcuts import render
from django.http import HttpResponse
from collections import OrderedDict
# Include the `fusioncharts.py` file that contains functions to embed the charts.
from fusioncharts import FusionCharts
from pprint import pprint
def chart(request):
#Chart data is passed to the `dataSource` parameter, like a dictionary in the form of key-value pairs.
dataSource = OrderedDict()
# The `chartConfig` dict contains key-value pairs of data for chart attribute
chartConfig = OrderedDict()
chartConfig["caption"] = "Countries With Most Oil Reserves [2017-18]"
chartConfig["subCaption"] = "In MMbbl = One Million barrels"
chartConfig["xAxisName"] = "Country"
chartConfig["yAxisName"] = "Reserves (MMbbl)"
chartConfig["numberSuffix"] = "K"
chartConfig["theme"] = "fusion"
# The `chartData` dict contains key-value pairs of data
chartData = OrderedDict()
chartData["Venezuela"] = 290
chartData["Saudi"] = 260
chartData["Canada"] = 180
chartData["Iran"] = 140
chartData["Russia"] = 115
chartData["UAE"] = 100
chartData["US"] = 30
chartData["China"] = 30
dataSource["chart"] = chartConfig
dataSource["data"] = []
# Convert the data in the `chartData`array into a format that can be consumed by FusionCharts.
#The data for the chart should be in an array wherein each element of the array
#is a JSON object# having the `label` and `value` as keys.
#Iterate through the data in `chartData` and insert into the `dataSource['data']` list.
for key, value in chartData.items():
dataSource["data"].append({'label':key, 'value': value})
# print the datasource to see what will be rendered
pprint(dataSource)
# Create an object for the column 2D chart using the FusionCharts class constructor
# The chart data is passed to the `dataSource` parameter.
column2D = FusionCharts("column2d", "Oil_Reserves", "600", "400", "Oil_Reserves-container", "json", dataSource)
context = {'output': column2D.render(), }
return render(request, 'chart.html', context)
chart.html:
<!DOCTYPE html>
<html>
<head>
<title>Oil Reserves</title>
{% load static %}
<script type="text/javascript" src="{% static 'fusioncharts/types/fusioncharts.js' %}"></script>
<script type="text/javascript" src="{% static 'fusioncharts/themes/fusioncharts.theme.fusion.js' %}"></script>
<link rel="icon" href="data:,">
</head>
<body>
<div id="Oil_Reserves-container">{{ output|safe }}</div>
</body>
</html>
Fusioncharts says this is a trial version, so better check if any costs are involved. Let me know if you need any more info. Good luck...
Bruno Vermeulen
bruno.vermeulen#hotmail.com
8 July 2019
I have a script which takes uploaded data, munges it together, turns it into a plot (using Bokeh) and then exports it to a directory as JSON.
At some point in the future, a user can hit the right URL and the appropriate plot should be displayed to the user as part of the HTML template.
I can generate the plot. I can save it as JSON. I can get the URL to retrieve it as JSON, but I cannot get the JSON plot to render within the template.
I've had a dig around the Bokeh documentation and examples, but they all seem to use a flask app to serve the pages.
I think I'm on the right track, using views.py to find and return JSON as part of a render() response, and then have Bokeh.embed.embed_items() do the work in the template to make it look right, but it's not working out - everything but the plot is displayed.
1) Create the plot and puts it in the directory for later use (app/results/1)
create plot.py
import os
import json
from django.conf import settings
from bokeh.embed import json_item
from bokeh.plotting import figure
x=[1,2,3,4,5]
y=[0,-1,-2,3,4]
p=figure(title="test_example")
p.line(x, y)
#json_export = json_item(p, "result")
json_export = json_item(p)
with open(os.path.join(settings.RESULTS_DIR,"1", "test.json"), 'w') as fp:
fp.write(json.dumps(json_export))
2) Set up the url
urls.py
urlpatterns=[
path('result/<int:pk>', views.resultdetailview, name='result-detail'),
]
3) Take the request, use the pk to find the plot json and render it all in the appropriate template.
views.py
def resultdetailview(request, pk):
results=str(pk)
with open(os.path.join(settings.RESULTS_DIR, results, "test.json"), 'r') as fp:
#data=json.load(fp)
data=json.loads(fp.read())
#context={'result':data}
#return render(request, 'app/sandbox_detail.html', context)
return render(request=request,
context={'json_object':data, 'resources':CDN.render()})
NB: If I instead use return JsonResponse(data, safe=False) then the url returns the json successfully ...
I think therefore that the issue is in the template.
4) Show the wonderous plot to the user
sandbox_detail.html
<header>
<link rel="stylesheet" href="http://cdn.bokeh.org./bokeh/release/bokeh-0.11.1.min.css" type="text/css" >
<script type="text/javascript" src="http://cdn.bokeh.org./bokeh/release/bokeh-0.11.1.min.js"> </script>
</header>
<h1> Title: Test </h1>
<div>Test</div>
<body>
<div id="result"></div>
<script>
Bokeh.embed.embed_item({{json_object}}, "result");
</script>
</body>
This template renders everything but the 'result' div.
What have I missed?
This is what I see so far:
FIRST: You are mixing 2 methods for injecting plot json data into the page.
According to documentation you can do it using either of these two methods:
1) specify the div directly:
Python: json_data = json.dumps(json_item(p, "myplot"))
JavaScript: Bokeh.embed.embed_item(item);
2) specify the div in embed_item function:
Python: json_data = json.dumps(json_item(p))
JavaScript: Bokeh.embed.embed_item(item, "myplot");
But not both of them at the same time. Could this be the problem?
SECOND: Preferably don't insert Bokeh resources by hand: rather use CDN.render() or INLINE.render() to automatically include all that your script needs:
import json
from bokeh.resources import CDN
return render(request = request,
template_name = 'app/sandbox_detail.html',
context = { json_object = json.loads(json_string),
resources = CDN.render() } )
sandbox_detail.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
{{ resources }}
</head>
<body>
<div id="result"></div>
<script>
Bokeh.embed.embed_item({{ json_object }}, "result");
</script>
</body>
</html>
THIRD: Make sure what you embed in the page is json object not a string (see variable naming above)
It helps when you debug your rendered template in your browser's debug tool.
I tried a very similar approach and found one large flaw: My browser noted that it could not find the None object. The reason here is that python stores the empty value as None, while JavaScript expects a null object.
The solution? Python already translates None to null, when you run json.dumps. To keep it that way, read the json string as a string. So instead of your data=json.loads(fp.read()) use data=fp.read().