Django - passing data with 'include' - python

I'm a newbie in Django and here's what I want to do: I want to have a base.html that includes a Navigation.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>title</title>
</head>
<body>
{% include 'nav.html' %}
{% block content %}{% endblock %}
</body>
</html>
pretty simple, but: The navigation should render it's own content flexible, as i want to add a cms later.
<nav class="nav">
{% for item in sites %}
<a class="nav__item" href="{{ item.href.value }}">{{ item.label }}</a>
{% endfor %}
</nav>
I have a render method of index.html (which just extends base.html and adds an h1 tag for testing purposes) like so
def home_view(request, *args, **kwargs):
opts = {
'sites': [
{
'href': {
'value': '/someurl'
},
'label': 'Some Label'
},
{
'href': {
'value': '/lorem'
},
'label': 'Lorem Ipsum'
},
{
'href': {
'value': '/contact'
},
'label': 'Contakt'
}
]
}
return render(request, 'index.html', opts)
but if I run my local server my content does not get passed.

You should use {% include "nav.html" %} inside the {% block content %} tag.
Otherwise it won't be shown.

Related

Retrieve individual values in tuple without brackets

I'm using a database to retrieve project numbers and put them in a search for a user in a form. I am using an api (ajax google api) for autocompletion on the search.
When putting it as autocomplete in search field it contains brackets and a comma.
I need it just to be like:
24403
24429
not like:
(24403,)
(24429,)
I am unsure how to do this. My current code is:
app.py
class Element(db.Model):
__tablename__ = 'elements'
ProjectNumber = db.Column(db.Integer, primary_key=True)
#app.route("/", methods=["POST", "GET"])
def basicTemplate():
project_number = Element.query.with_entities(Element.ProjectNumber).distinct()
if request.method == "GET":
return render_template('search.html', navigation=project_number)
if __name__ == '__main__':
app.run()
base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="/static\style.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.js"></script>
<link href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/themes/ui-lightness/jquery-ui.css" rel="stylesheet" type="text/css" />
{% block head %}
<title>{% block title %}{% endblock %}- base</title>
{% endblock %}
</head>
<body>
{% block body %}
{% block project_number %} {% endblock %}
{% endblock %}
</body>
</html>
search.html
{% extends 'base.html' %}
{% block head %}
{% endblock %}
{% block project_number %}
<label for="project_code">Project Number</label><input type="text" id="project_code" name="project_code">
<input type="submit">
<script>
$( function() {
var project = [
{% for test in navigation %}
"{{test}}",
{% endfor %}
];
$( "#project_code" ).autocomplete({
source: project
});
} );
</script>
{% endblock %}
The command with_entities always returns a list of tuples.
You can combine multiple jinja filters to get a list of strings that can be passed directly to javascript.
First you extract the first element of all tuples and convert them into strings. You convert the received generator into a list and output it in a javascript-compatible manner.
$(function() {
const projects = {{ navigation | map('first') | map('string') | list | tojson }};
$('#project_code').autocomplete({
source: projects
});
});
Of course you can also perform the extraction and conversion to the string in the endpoint.
Depending on how many projects are stored in your database, I recommend getting the data via ajax.
$(function() {
$('#project_code').autocomplete({
source: '/search',
});
});
#app.route('/search')
def search():
term = request.args.get('term', '')
projects = Element.query.filter(Element.ProjectNumber.ilike(f'%{term}%')).all()
return jsonify([str(p.ProjectNumber) for p in projects])

Global variable doesn't work inside django views

I wanted to use some dummy data in my Django views to work with templates. When the posts variable is outside the home function all I'm getting is an empty body. Although when I move it inside everything displays as it should.
from django.shortcuts import render
posts = [
{
'author': 'Kamil',
'title' : 'Post 1',
},
{
'author': 'Tomek',
'title' : 'Post 2',
},
]
def home(request):
context = {
'posts' : posts
}
return render(request, 'blog_app/home.html', context)
Here is also my html
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
{% for post in posts %}
<h1>{{ post.title }}</h1>
<p>By {{ post.author }}</p>
{% endfor %}
</body>
</html>
Make posts a global variable
global posts
posts = [
{
'author': 'Kamil',
'title' : 'Post 1',
},
{
'author': 'Tomek',
'title' : 'Post 2',
},
]

Am i missing something on templates on django

This is my code on html file on a django project. I am using this code on this function
def home(request):
context = {
'post': posts
}
return render(request,'blog/home.html',context
It does not display anything when I see page source it's just bacis html code head and body empty
<html>
<head>
<title> </title>
</head>
<body>
{% for post in posts %}
<h1>{{post.title}}</h1>
<p>By {{post.author}} on {{post.date_posted}}</p>
<p>{{post.content}}</p>
{% endfor %}
</body>
</html>```
It is a typo. You need to change the context in your view from:
context = { 'post': posts }
to
context = { 'posts': posts }
^^^^^^^

CSS fails when passing a list to render(request)

I am trying to follow a tutorial online and for some reason I cannot get css to work correctly with my views.py file and django 2.2. when I remove the "My_context" from return render(request, 'blog/home.html', My_context)
and pass something else like
return render(request, 'blog/home.html', {'title': 'blah') it seems to work just fine.
I have tried restarting the server and and clearing the page cache. I am new to django and not sure what else to try.
views.py
from django.shortcuts import render
posts = [
{
'author': 'xxxxx',
'title': 'Blog Post 1',
'Content': 'My First Post Content',
'date_posted': 'August 27, 2019'
},
{
'author': 'xxxxx',
'title': 'Blog Post 2',
'Content': 'My Second Post Content',
'date_posted': 'August 27, 2019'
}
]
# This function fails to load css correctly
def home(request):
My_context = {
'posts': posts
}
return render(request, 'blog/home.html', My_context)
#This function works fine
def about(request):
return render(request, 'blog/about.html', {'title': 'About'})
home.html
{% extends "blog/base.html" %}
{% block content %}
{% for post in posts %}
<h1>{{ post.title }}</h1>
<p>By {{ post.author }} on {{ post.date_posted }}</p>
<p>{{ post.Content }}</p>
{% endfor %}
{% endblock content %}
about.html
{% extends "blog/base.html" %}
{% block content %}
<h1>About Page</h1>
{% endblock content %}
The major difference between home() and about() is the template you use.
So you should check your home.html contains the path to your css file.
You can also check your console for any error message and share it with us.

Generating a recursive sitemap with relative href links

I'm using Flask to expose a local directory of HTML files on a web page.
I am also using a jinja2 to generate the sitemap in the lefthand div of my main endpoint.
I am unable to correctly specify the URL to the endpoint of my subfolders.
As mentioned in the code below, how would I dynamically build a relative link from /docs (i.e. /docs/folder1/subfolder1/SubFolder1Page.html)?
The way I am currently setting the value for href obviously does not work.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Docs Demo</title>
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
</head>
<body>
<div id="container">
<div class="left_frame">
<h1>{{ tree.name }}</h1>
<ul>
{%- for item in tree.children recursive %}
<!-- How would I build a relative link from /docs/ i.e. /docs/folder1/subfolder1/SubFolder1Page.html -->
<li><a href="docs/{{ item.name }}" target="iframe1">{{ item.name }}
{%- if item.children -%}
<ul>{{ loop(item.children) }}</ul>
{%- endif %}</a></li>
{%- endfor %}
</ul>
</div>
<div class="right_frame">
<iframe name="iframe1"></iframe>
</div>
</div>
</body>
</html>
Folder structure example:
How it looks overall displaying the contents of file1.html:
So I figured out a satisfying way of solving my own issue.
I managed to get this very functional result:
Do note that my template is only good for files with .html extension, though it can be easily enhanced to support other file extensions.
Here is my finalized templates\template.html file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Docs Demo</title>
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
</head>
<body>
<div id="container">
<div class="left_frame">
<h1>{{ tree.name }}</h1>
<ul>
{%- for item in tree.children recursive %}
{% if '.html' in item.name %}
<li><a href="docs/{{ item.name }}" target="iframe1">
{{ item.name.split('/')[-1:][0] }}
{%- if item.children -%}
<ul>{{ loop(item.children) }}</ul>
{%- endif %}</a></li>
{% else %}
<li>{{ item.name }}
{%- if item.children -%}
<ul>{{ loop(item.children) }}</ul>
{%- endif %}</li>
{% endif %}
{%- endfor %}
</ul>
</div>
<div class="right_frame">
<iframe name="iframe1"></iframe>
</div>
</div>
</body>
</html>
You can refer to King Reload's answer for an analysis of what I've changed in the template.html file to make this work correctly.
And here is the demo_app.py script that serves my document HTML files via Flask:
import threading
import os
import webbrowser
from flask import Flask, render_template, send_from_directory
app = Flask(__name__, static_folder='static')
ROOT = os.path.dirname(os.path.abspath(__file__))
DOCS_ROOT = os.path.join(app.static_folder, 'docs')
#app.route('/')
def docs_tree():
return render_template('template.html', tree=make_tree(DOCS_ROOT))
#app.route('/docs/<path:filename>')
def send_docs(filename):
return send_from_directory(directory=DOCS_ROOT, 'docs'), filename=filename)
def make_tree(path):
tree = dict(name=os.path.basename(path), children=[])
try:
lst = os.listdir(path)
except OSError:
pass # ignore errors
else:
for name in lst:
fn = os.path.join(path, name)
if os.path.isdir(fn):
tree['children'].append(make_tree(fn))
else:
np = os.path.join(path.replace(DOCS_ROOT, ''), name).replace('\\', '/')
if np.startswith('/'):
np = np[1:]
tree['children'].append(dict(name=np))
return tree
if __name__ == '__main__':
host = 'localhost'
port = '8888'
url = 'http://{h}:{p}'.format(h=host, p=port)
threading.Timer(3, lambda: webbrowser.open(url)).start()
app.run(host=host, port=port, debug=False)
Most notable changes in demo_app.py since asking my original question were the following:
After initializing app, I set DOCS_ROOT using app.static_folder;
In the function send_docs(), I changed the send_from_directory()'s directory argument to use DOCS_ROOT;
Inside of make_tree(), inside the else block of the for loop, I added:
np = os.path.join(path.replace(DOCS_ROOT, ''), name).replace('\\', '/')
if np.startswith('/'):
np = np[1:]
All this does is take the absolute path of name, remove what matches DOCS_ROOT, leaving only the relative path (and then replacing the \\ for /), resulting in a simple relative path from static/docs. If the relative path starts with a /, I remove it (since there is a trailing / from docs in template.html.
For anyone interested in the simplistic stylesheet (static\styles.css) I used (along with some updated enhancements):
html {
min-height:100%;
position:relative;
}
body {
overflow:hidden;
}
.container {
width:100%;
overflow:auto;
}
.left_frame {
float:left;
background:#E8F1F5;
width:25%;
height:100vh;
}
.right_frame {
float:right;
background:#FAFAFA;
width:75%;
height:100vh;
}
.right_frame iframe {
display:block;
width:100%;
height:100%;
border:none;
}
To add onto the solution of #HEADLESS_0NE:
He added a few more if statements in the for loop, like so:
{%- for item in tree.children recursive %}
-> {% if '.html' in item.name %}
<li><a href="docs/{{ item.name }}" target="iframe1">
-> {{ item.name.split('/')[-1:][0] }}
{%- if item.children -%}
<ul>{{ loop(item.children) }}</ul>
{%- endif %}</a></li>
-> {% else %}
-> <li>{{ item.name }}
-> {%- if item.children -%}
-> <ul>{{ loop(item.children) }}</ul>
-> {%- endif %}</li>
-> {% endif %}
{%- endfor %}
Everything with an -> has been changed in the html, I couldn't find what he added in his python or css, but in short:
An if to check if there's a .html in the item.name.
A split on the item.name, so the / gets removed.
An else statement if there's no .html in the item.name.
This basically adds the ul's and the li's in the correct format.
For a more detailed explanation I hope HEADLESS_0NE could provide us of more information what he might've changed in the python script.
With flask, I have built a site map using the following
from flask import url_for
def get_flask_resources():
verbs = ["POST","GET","PUT","DELETE"]
resources = {}
for rule in app.url_map.iter_rules():
if has_no_empty_params(rule):
resource = url_for(rule.endpoint, **(rule.defaults or {}))
if resource not in resources:
resources[resource] = {}
for verb in verbs:
if verb in rule.methods:
resources[resource][verb] = {
'function':rule.endpoint,
'docs':app.view_functions[rule.endpoint].__doc__
}
else:
resource = rule.rule
if resource not in resources:
resources[resource] = {}
for verb in verbs:
if verb in rule.methods:
resources[resource][verb] = {
'function':rule.endpoint,
'docs':app.view_functions[rule.endpoint].__doc__
}
return resources
This function returns a dictionary like this
{
"/endpoint1": {
"GET": {
"docs": "",
"function": "endpoint1"
}
},
"/endpoint2": {
"GET": {
"docs": "",
"function": "endpoint2"
}
},
"/endpoint1/something": {
"POST": {
"docs": "",
"function": "endpoint1_something"
}
},
}
I had an endpoint return this data and then formatted it on the front end. The dictionary keys are the URIs that you would want to use in the links.
This does assume that you will have a flask route set up for each HTML document, which may not be the case.
One benefit of using this would be that it is dynamic if you add any more HTML documents/Flask routes.

Categories