Creating and displaying pyvis graph from django app running sql backend - python-3.x

This is moreso a question about pyvis graphs, but also involves a django server running with a sqlite3 backend. One of my views needs to produce an interactive pyvis graph and display it in the clients browser. I can do this without the django app with the following code:
from pyvis.network import Network
network = Network()
network.show('map.html')
As you can see with this method, pyvis creates an html file and save it to disk first. nework.show() simply opens the file in the browser. Because I will be running this on a django webapp, I would rather create the graph's html without saving it to disk, then just return it as a string in the HttpResponse for the view. Is it possible to do this, or is there another way that is more appropriate?

One way to do that is to save the pyvis graph into a file within the templates directory of Django. Instead of using network.show(), use network.save_graph(path_to_templates_dir/some_pyvis_file.html) and then include that file inside the template where you want to show the results using the Django tags {% include ‘pvis_graph_file.html’ %}
views.py
from pyvis.network import Network
from django.conf import settings
network = Network()
...
network.save_graph(str(settings.BASE_DIR)+'app/templates/pvis_graph_file.html')
templates/showReport.html
{% extends 'base.html' %}
{% block body %}
Content of Report with pyVis Graphs
{% include 'pvis_graph_file.html' %}
{% endblock %}

Related

Do I need a generator, maybe SQLAlchemy for flask loop when querying SQLite?

I am trying to query SQLite and return image paths that meet a certain condition in a flask for loop but I am getting a broken image icon. The image_path is saved into the SQLite table which I thought was a good solution here. The code I am using is below. The first set is where I am having trouble and the second set works when the images are saved to a static directory. Do I need to create a generator for this or I am just blundering somewhere else? This is what the SQLite table looks like,
Edit 1
I think it may help to explain what I want here. I want index.html to show a Recent Ad section which would display recently posted ads, like on Adverts
app.py*
#app.route("/")
def index():
sqliteConnection = sqlite3.connect('finance.db')
cursor = sqliteConnection.cursor()
ads = cursor.execute("SELECT image_path FROM food WHERE active = 'Y'")
ads = cursor.fetchall()
print(ads)
return render_template("index.html", ads=ads)
This is the output for print(ads)
`[('/home/ubuntu/final/freeood/dan.jpg',), ('/home/ubuntu/final/freeood/dan3.jpg',)]'
and this next is the results for the code
for row in ads:
print(*row, sep='\t')
/home/ubuntu/final/freeood/dan.jpg
/home/ubuntu/final/freeood/dan3.jpg
index.html
{% for ad in ads %}
<img src={{ad}}>
{% endfor %}
This yields the broken icon image. I have also tried adding `ads.
I am able to display images when jpegs are saved to the static directory with the follow code, but I am having a problem with saving to the static folder so I am trying to figure out a work around just in case.
app.py
ads = os.listdir(os.path.join(app.static_folder, "images"))
return render_template("index.html", ads=ads)
index.html
{% for ad in ads %}
<img src='/static/images/{{ad}}'/>
{% endfor %}
Image files should be in the static folder of your website. This is because all file paths are relative to the root of your website.
There is, however, a work around if you really need to access files outside of your website using send_from_directory.
Add this function to app.py:
from flask import send_from_directory
#app.route('/uploads/<path:img_path>')
def download_file(img_path):
directory, filename = img_path.rsplit('/', 1)
return send_from_directory(directory, filename, as_attachment=True)
Then you can invoke this function in your template index.html:
{% for ad in ads %}
<img src="{{ url_for('download_file', img_path=ad) }}"/>
{% endfor %}

Render Jinja Markup in Passed Variables

I am moving my blog over from Jekyll to Flask. In my Jekyll blog I use Liquid includes to process overhead heavy items like youtube videos and images and to ensure uniformity so that they can all be updated in the same place if there's a style or code change to the site.
In Flask is there a way to have the Jinja2 template process Liquid variables in the body of the blog article when it's passed to render_template()?
Example:
#route('/article')
def article():
blog_body = "<h1>My Video</h1>{% include '_media.html' type='youtube' id='a2343lj' %}"
return render_template('home.html.j2', content = blog_body)
And then in home.html.j2:
{% block page_content %}
{{ content }}
{% endblock %}
When testing, the template renders the literal string and does not process the include in the variable. Is there any way to get the Jinja template to render the Liquid syntax in the variable passed to it?
While there does not appear to be any way to render a template string using Jinja2 syntax, there is a workaround. At the time the variable is set, you can process it using render_template_string from Flask.
Example:
from flask import render_template, render_template_string
#route('/article')
def article():
blog_body = render_template_string("<h1>My Video</h1>{% include '_media.html' type='youtube' id='a2343lj' %}")
return render_template('home.html.j2', content = blog_body)
Then at least the variable will be rendered at the time it is set, even if not whenever the template is rendered.

Load extends after blocks

So I have been working with twig now, and I use a function manner to render all of my html. My question is, is there a way to render the blocks (with functions) first, and then the {% extends X %} afterwards?
example:
{% extends 'base.html.twig' %}
{% block body %}
{{ resource('script', 'google.com') }}
{% endblock %}
I would want the resource function to trigger, before the file is extended, in some way.
Reason why:
In the base I define another function which loads a head with a base configuration, it also gives easy options to load certain libraries such as bootstrap etc. But I want the user able to later, externally in a body add more resources and make them pop up in the head instead of in the body. I can give more code if needed but I think this will suffice since this is more a twig question than a php question.

How to push values to array in templates and partials to output them in layout?

We are using Twig as our templating engine but I'm stuck with a problem of the rendering order now. See - https://straightupcraft.com/articles/twig-processing-order
We are using amp for our new site and with amp and web components in general it is necessary to import the components (once) to use them. So the idea was to create a Twig Extension to accomplish exactly this. Everytime a component is used it is called via this twig function call. The function is checking the component, adding it to a stack and you can use it from now on. The stack then is transformed into a list of imports for the head of the page.
The problem here is Twigs top to bottom rendering. When I call said function in a partial or a template the rendering of "layout.twig" already took place and all function calls below/after are not affecting the stack.
Is there an option to change the rendering to inside out, so that the rendering is first using the lowest partial and "bubbles" up to templates and finally layout without them rendering before?
Another way to get the values in layout would nice as well.
Desired behavior:
Layout.twig
{{ getAllComponents() }}
{# should output component1 and component2 #}
{% block content %}
Page.twig
{% extends Layout.twig %}
{{ addComponent('component1') }}
{% include partials.twig %}
<component1>...</component1>
partials.twig
{{ addComponent('component2') }}
<component2>...</component2>
Actual behavior:
Layout.twig
{{ getAllComponents() }}
{# is empty #}
There is no option to change this behavior. However you can use a trick to archive this:
Render your content to a variable before you call getAllComponents()
layout.twig
{% set mainContent %}
{% block content %}{% endblock %}
{% endset %}
{{ getAllComponents() }}
{{ mainContent }}
There actually is a magical solution to this problem -- some genius wrote this Deferred Twig Extension which lets you run some code (output a variable, call a function, etc) in a block near the top of your layout, but wait until the rest of the twig templates have parsed before executing them. There's an example on that github page showing how you can achieve this.
As far as I know, no you cannot switch the rendering of a TWIG template.
However, I would recommend possibly looking at the {% embed %} or {{ render }} tags.
Embed allows specific TWIG files to be placed within others with variables, so you could pass each component into their relevant partials.twig file.
Render allows the use of embedding a separate controller action, so you create a ControllerAction that took in a component variable and renders its own TWIG file.
Documentation on Embed: https://twig.sensiolabs.org/doc/2.x/tags/embed.html
Documentation containing Render: https://twig.sensiolabs.org/doc/2.x/api.html

Symfony Server crashes when extending FOSUserBundle's default template layout.html.twig

I am trying to get the basic user login running from the FOSUserBundle.
I am using Symfony 3.0.6.
I followed the description to setup everything from the FOSUserBundle:
https://symfony.com/doc/master/bundles/FOSUserBundle/index.html
DB is up and running everything seems fine except I cant figure out how to override the layout.html.twig from the FOSUserBundle.
I followed this description for achieving that:
https://symfony.com/doc/master/bundles/FOSUserBundle/overriding_templates.html
I now have a file "layout.html.twig" in the folder "Resources/FOSUserBundle/views" with the content being the same as in the last link provided above.
This leads to the following error:
Unable to find template "layout.html.twig" (looked into:
[somePathInfo]) in FOSUserBundle::layout.html.twig at line 1.
Now I changed the first line in the "layout.html.twig" Template to be
{% extends 'FOSUserBundle::layout.html.twig' %}
And this then leads to the symfony server to crash stating
>php bin/console server:run -v
[OK] Server running on http://127.0.0.1:8000
// Quit the server with CONTROL-C.
RUN "C:\xampp\php\php.exe" "-S" "127.0.0.1:8000" "[PATH]\myProject\vendor\symfony\symfony\src\Symfony\Bundle\FrameworkBundle\Resources\config\router_dev.php"
RES -1073741571 Command did not run successfully
[ERROR] Built-in server terminated unexpectedly.
I am stuck here...
Any ideas are very welcome.
EDIT: The FOSUserBundle installed by the composer (which I use through the current PHP-Storm plugin) is installed at the path:
[projectPath]\vendor\friendsofsymfony\user-bundle\Resources\views\layout.html.twig
In the docu however allways "FOSUserBundle" only is mentioned and I don't know how to figure out if that mapping fits to the path in my project.
Any hints for this issue are very wellcome as well.
When you override standart FOSUser layout you need to place your layout into app/Resources/FOSUserBundle/views/layout.html.twig. great, you did this. it's just a layout, it should not extend standart FOSUser layout, so remove line {% extends 'FOSUserBundle::layout.html.twig' %}. But usually developers make one base layout, in my case it is \app\Resources\views\base.html.twig, so if I want to override fosuser layout I will have in app/Resources/FOSUserBundle/views/layout.html.twig something like this
{% extends 'base.html.twig' %}
{% block title %}User Management{% endblock %}
{% block content %}
{% block fos_user_content %}{% endblock %}
{% endblock %}
In first line you extend your base layout not FOSUser. You may not extend something, maybe you have separate complete layout for this template.
The crash does make sense.
When you write:
{% extends 'FOSUserBundle::layout.html.twig' %}
The Symfony will first try to load app/Resources/FOSUserBundle/views/layout.html.twig. Failing to find the file will revert to similar path but inside the vendor directory. And if you are trying to extend FOS's template from within your FOS overriden template, that would create recursive loop:
app/Resource/FOSUserBundle/views/layout.html.twig
^^ extends
app/Resource/FOSUserBundle/views/layout.html.twig
^^ extends
app/Resource/FOSUserBundle/views/layout.html.twig
....
and so on...
So, this is not a way to solve the problem.
Make sure that your template is well placed in your app directory and not your bundle, as Denis Alimov suggested in a comment.

Resources