I have a FastAPI app which takes any endpoint foo/{page} and uses the page variable to decide what template to render.
It looks kind of like this:
#api_router.get("/foo/{page}/")
def foo_router(page: str, request: Request):
return TEMPLATES.TemplateResponse(
f"{page}.html",
{
"request": request,
'page_title':page,
'schema':schema[page],
}
)
The templates contain buttons that are created from a schema that has button text and link destination as key/value pairs.
Something like this:
schema = {
"index": {
"Look At Our Menu":"foo/menu",
"Find Our Store":"foo/location",
}
}
And the Jinja template looks like this:
<div class="form_wrapper">
{% for k in buttons %}
<a href="foo/{{buttons[k]}}/">
<div class="button_text full_button">
{{ k.upper() }}
</div>
</a>
{% endfor %}
</div>
My problem is if I have a link within foo/menu/ that I want to direct to foo/drinks, it tries to load foo/menu/foo/drinks. How do I reset the path so it doesn't nest?
OK I figured it out using request to get the base path
<div class="form_wrapper">
{% for k in buttons %}
<a href="{{ request.base_url.url + 'foo/' + buttons[k] }}/">
<div class="button_text full_button">
{{ k.upper() }}
</div>
</a>
{% endfor %}
</div>
I'm not sure this is the best way but it does work so far!
Related
views.py
def get(self, request, *args, **kwargs):
domains = Domain.objects.all()
context['domains'] = domains
domain_dict = {}
# ..........
# ..........some codes here for domain_dict dictionary
print(domain_dict)
context['domain_dict'] = domain_dict
return render(request, self.response_template, context)
Output after printing the domain_dict
{4: '', 3: '', 1: '', 5: '', 7: '', 2: '', 6: 'Are you a candidate for the engineering admission '}
Now the domain_dict is sent to template through context.
templates.html
<div class="col-md-8">
<div class="tab-content">
{% for domain in domains %}
{% with domain_id=domain.id %}
<div class="tab-pane container p-0 {% if forloop.first %} active {% endif %}" id="services{{domain.id}}">
<div class="img" style="background-image: url('static/counsellor/images/service-1.png');">
</div>
<h3>Name: {{domain.name}} ID: {{domain_id}}</h3>
<p>{{domain_dict.6}}</p>
</div>
{% endwith %}
{% endfor %}
</div>
</div>
In the above template I use <p>{{domain_dict.6}}</p>. domain_dict.6 to find the value of key 6. It returns perfectly.
Outputs: Are you a candidate for the engineering admission.
But in the below
<div class="col-md-8">
<div class="tab-content">
{% for domain in domains %}
{% with domain_id=domain.id %}
<div class="tab-pane container p-0 {% if forloop.first %} active {% endif %}" id="services{{domain.id}}">
<div class="img" style="background-image: url('static/counsellor/images/service-1.png');">
</div>
<h3>Name: {{domain.name}} ID: {{domain_id}}</h3>
<p>{{domain_dict.domain_id}}</p>
</div>
{% endwith %}
{% endfor %}
</div>
</div>
In the above template I use <p>{{domain_dict.domain_id}}</p>. domain_dict.domain_id to find the value of key domain_id. It returns null. Here domain_id = 4,3,1,6,5,7,2
Outputs: null
How can I return the value of a key of the dictionary here?
I've recently run into the same problem. Here is example of the solution.
Say, we have tuple
people = ('Vasya', 'Petya', 'Masha', 'Glasha')
and dictionary with their status:
st = {
'Vasya': 'married',
'Petya': 'divorced',
'Masha': 'married',
'Glasha': 'unmarried'
}
We transfer these to our template as context of corresponding view:
context['people'] = people
context['st'] = st
If we write in template
{% for person in people %}
{{ person }} is {{ st.person }}
{% endfor %}
then it won't work. Well, 'person' will be displayed, but data from dictionary will not. Because :
Note that bar in a template expression like {{ foo.bar }} will be
interpreted as a literal string and not using the value of the
variable bar, if one exists in the template context.
The solution of the problem is either to use another data structure or to use custom filter in the template. The idea of the latter is to add a filter to your dictionary in the template, which takes current value of person and returns corresponding value of the dictionary.
To do this,
create in the folder with your app new folder templatetags,
write the path to this new folder in settings.py,
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
...,
os.path.join(BASE_DIR, 'your_app/templatetags'),
],
...
}
inside the templatetags create new file, say, filter.py with our new filter called dict_value:
from django import template
register = template.Library()
#register.filter
def dict_value(d, key):
return d[key]
go back to your template and load newly created filter somewhere in first lines:
{% load filter %}
rewrite you template code like this:
{% for person in people %}
{{ person }} is {{ st|dict_value:person }}
{% endfor %}
Now it works as needed.
In my view, I return html if a form is valid:
if form_valid():
return render(request, 'home.html', context=context)
else:
return HttpResponse(status=204)
I'm submitting multiple forms via ajax and want to render the response, if the status code is not 204:
$.ajax({
data: $(this).serialize(),
type: $(this).attr('method'),
url: $(this).attr('action'),
success: function (response, status, jqXHR) {
if (jqXHR.status !== 204) {
document.write(response); // this works, but I lose some functionality. Some buttons stop working.
// How can I render the response correctly?
}
}
});
EDIT: The buttons that don't work anymore. It's a form using bootstrap collapse with some workarounds
main.html
<form action="." method="post">
{% csrf_token %}
{% include 'collapse_form.html' with form=mysql_form %}
{% include 'collapse_form.html' with form=postgres_form %}
{% include 'collapse_form.html' with form=sqlite_form %}
<input type="hidden" name="databases-section"/>
<a id="download-btn" class="btn btn-success">Download</a>
<a id="hidden-download-link" class="js-scroll-trigger" href="#download"></a>
</form>
collapse_form.html
{% load crispy_forms_filters %}
<div class="collapseForm">
<div class="collapseFormButton">
<button id="collapseBtn" class="d-none btn btn-check p-0 m-0" type="button" data-toggle="collapse"
data-target="#{{ form.prefix }}-collapseTarget"
aria-expanded="false">
</button>
{{ form.active|as_crispy_field }}
</div>
<div class="collapse" id="{{ form.prefix }}-collapseTarget">
<div class="card card-body">
{{ form|as_crispy_errors }}
{% for field in form.visible_fields|slice:"1:" %}
{{ field|as_crispy_field }}
{% endfor %}
</div>
</div>
</div>
js
$('.collapseFormButton').find("input").change(function () {
toggleCollapseForm()
});
function toggleCollapseForm() {
let collapseForm = $(".collapseForm");
collapseForm.each(function () {
let collapseCheckbox = $(this).find("input[id*='active']");
let collapseTarget = $(this).find("div[id*='collapseTarget']");
if (collapseCheckbox.is(':checked')) {
collapseTarget.collapse('show');
} else {
collapseTarget.collapse('hide');
}
});
}
instead of document.write there is more functions like append, you could use that to append the rendered htmls cooming to ajax as a response to a specific element selected by id to avoid any problem like lossing the documents html tags(html, body, header) .
and there is big point!
javascript has different behavior regarding dynamically added elements!
to be more clear, I think js and/or jQuery has different behavior for keword this which doesn't work on dynamically added elements. so you sould use another query-selector like $(document).on('click', element, function (){....} and this would work as expected.
I checked multiple posts and solutions but can't make it happen.
I have a Python object returned within a view. I now want to use data from that dict to render it using Django template tags.
Somehow nothing shows up though...
View:
def render_Terminal(request, template="Terminal.html"):
account_information = AccountInformation.objects.all()
account_information_dict = {
'account_information': account_information
}
return render(request, template, (account_information_dict))
HTML
<div id="oneTwo" class="columnsOne">
{{ account_information.pk }}
</div>
Using just account_information within the tag, I get:
<QuerySet [<AccountInformation: AccountInformation object (30e61aec-0f6e-4fa0-8c1b-eb07f9347c1f)>]>
Where is the issue?
AccountInformation.objects.all() is a QuerySet with a all() filter. A QuerySet is iterable, and it executes its database query the first time you iterate over it. You can show the id for all items in your list using:
{% for item in account_information %}
<div id="some-id-{{ forloop.counter }}" class="some-class">
{{ item.pk }}
</div>
{% endfor %}
Do like this
def render_Terminal(request, template="Terminal.html"):
account_information = AccountInformation.objects.all()
account_information_dict = {
'account_information': [a for a in account_information]
}
return render(request, template, (account_information_dict))
and
<div id="oneTwo" class="columnsOne">
{{ account_information.0.pk }}
</div>
however you can only recover the first item that comes
a better solution might be
account_information = AccountInformation.objects.get(pk=`you id`)
return render(request, template, (account_information_dict))
and then
<div id="oneTwo" class="columnsOne">
{{ account_information.pk }}
</div>
Using just account_information within the tag, I get:
inside the html code you have to put the item in "for" if you take the integer value
{% for a in account_information %}
<div>
{{ a.pk }}
</div>
{% endfor %}
I'm building a Netflix like website for my Devops course. I made a Python list of dictionaries (Mockfilms) to define my films, and want to populate a database (Ratings) with reviews in preparation for sending data in the format :filmid: :userid: :rating: to a recommendation engine.
My index page is a list of film images with a link to a review form under each one. I want each review form to appear on a different url (/review/ID where ID is saved in mockfilms as oid). In order to do this I want to access mockfilms.oid, then pass it to the view function to make the url for the form. Once the form is complete I then want to add this ID to the Ratings database. Here is what I have so far:
Index:
{% extends "base.html" %}
{% block content %}
<h1>Hello, {{ current_user.username }}! Welcome to our extensive video library:</h1>
{% for film in mockfilms %}
{% set ID = film.oid %}
<div>
<a href = {{ film.video }}>
<img src = {{ film.image }} alt = "doh" style = "width:200px;height:200px;border:0;">
</a>
</div>
<div>
">Leave a review here!
{% endfor %}
{% endblock %}
Route:
#app.route('/review/<ID>', methods = ['GET', 'POST'])
#login_required
def review(ID):
form = ReviewForm()
if form.validate_on_submit():
review = Ratings(User_id = current_user.id, Score_given = form.score.data, Film_id = ID)
db.session.add(review)
db.session.commit()
flash('Thanks for your review')
return redirect(url_for('index'))
return render_template('review.html', title='Review Page', form=form)
The following error is what I get when I run it:
File "/home/jc/Desktop/Lokal/DevopsAssig/microblog/Kilfinnan/lib/python3.5/site-packages/werkzeug/routing.py", line 1768, in build
raise BuildError(endpoint, values, method, self)
werkzeug.routing.BuildError: Could not build url for endpoint 'review'. Did you forget to specify values ['ID']?
From this I assume that the issue is with the ID variable within this template. My searchings and learnings led me to believe that {% set %} in the index template would let me declare the ID variable and then use it in the dynamic.
Try this:
{% block content %}
<h1>
Hello, {{ current_user.username }}!
Welcome to our extensive video library:
</h1>
{% for film in mockfilms %}
<div>
<a href="{{ film.video }}">
<img src="{{ film.image }}" alt="doh" style="width:200px;height:200px;border:0;" />
</a>
</div>
<div>
<a href="{{ url_for('review', ID=film.oid) }}">
Leave a review here!
</a>
</div>
{% endfor %}
{% endblock %}
Ultimately your solution was quite close, but it is not necessary to use the Jinja set command when you need to pass the variable into url_for() function using the keyword for the parameter. You could still do it using {% set ID = film.oid %} but it would be a bit superfluous.
Try to provide key=value arguments into your url_for function.
Something like this
">Leave a review here!
Also Flask have a great documentation, Flask docs
I got the following html snippet from my page template.html.
<ul class='nav'>
<li class="active"><a href='/'>Home</a></li>
<li><a href='/lorem'>Lorem</a></li>
{% if session['logged_in'] %}
<li>Account</li>
<li>Projects
<li>Logout</li>
{% endif %}
{% if not session['logged_in'] %}
<li>Login</li>
<li>Register</li>
{% endif %}
</ul>
As you can see on line 2, there's the class active. This highlights the active tab with the twitter bootstrap css file. Now, this will work fine if I would visit www.page.com/ but not when I would visit www.page.com/login for example. It would still highlight the home link as the active tab.
Of course, I could easily do this with Javascript/jQuery but I'd rather not use that in this situation.
There's already a working solution for ruby on rails but I don't know how to convert that into python/jinja (I'm rather new to jinja/flask, never worked with ruby at all)
Have you looked at this ? https://jinja.palletsprojects.com/en/3.0.x/tricks/#highlighting-active-menu-items
Highlighting Active Menu Items
Often you want to have a navigation bar with an active navigation item. This is really simple to achieve. Because assignments outside of blocks in child templates are global and executed before the layout template is evaluated it’s possible to define the active menu item in the child template:
{% extends "layout.html" %}
{% set active_page = "index" %}
The layout template can then access active_page. Additionally it makes sense to define a default for that variable:
{% set navigation_bar = [
('/', 'index', 'Index'),
('/downloads/', 'downloads', 'Downloads'),
('/about/', 'about', 'About')
] -%}
{% set active_page = active_page|default('index') -%}
...
<ul id="navigation">
{% for href, id, caption in navigation_bar %}
<li{% if id == active_page %} class="active"{% endif
%}>{{ caption|e }}
</li>
{% endfor %}
</ul>
Here is another simpler way if you have menus distributed all over the page. This way uses inline if statements to print out the class active.
<ul>
<li class="{{ 'active' if active_page == 'menu1' else '' }}">
Link 1
</li>
<li class="{{ 'active' if active_page == 'menu2' else '' }}">
Link 2
</li>
</ul>
Class active is for highlighting
You still need to set the variable on every page to mark them
{% extends "master.html" %}
{% set active_page = "menu1" %}
or
{% set active_page = "menu2" %}
For jinja/flask/bootstrap users:
If you define your nav like they did in the blog example http://getbootstrap.com/examples/blog/
simply assign ids to your links that match your url_for arguments and you just need to modify the layout-template, the rest just works #magic.
<nav class="blog-nav">
<a id="allposts" class="blog-nav-item" href="{{ url_for('allposts')}}">All Posts</a>
<a id="index" class="blog-nav-item" href="{{ url_for('index')}}">Index</a>
<a id="favorites" class="blog-nav-item" href="{{ url_for('favorites')}}">Favorites</a>
</nav>
At the bottom of your base/layout template just add this
<script>
$(document).ready(function () {
$("#{{request.endpoint}}").addClass("active"); })
</script>
and the right elements will be set active.
EDIT:
If you have a layout with elements in a list, like this:
<nav class="blog-nav">
<ul class="nav navbar-nav">
<li>
<a id="allposts" class="blog-nav-item" href="{{ url_for('allposts')}}">All Posts</a>
</li>
<li>
<a id="index" class="blog-nav-item" href="{{ url_for('index')}}">Index</a>
</li>
<li>
<a id="favorites" class="blog-nav-item" href="{{ url_for('favorites')}}">Favorites</a>
</li>
</ul>
</nav>
use the parent() function to get the li element instead of the link.
<script>
$(document).ready(function () {
$("#{{request.endpoint}}").parent().addClass("active"); })
</script>
we can make class active by using jinja if statements
<ul class="nav navbar-nav">
<li class="{% if request.endpoint=='home' %}active{%endif %}">home</li>
<li class="{% if request.endpoint=='add_client' %}active{%endif %}">Add Report</li>
</li>
</ul>
I liked #philmaweb's approach, but there's really no reason to require duplicating the endpoint in the id of each element.
base.js:
$(document).ready(function () {
var scriptElement = $('#baseScript')[0];
var path = scriptElement.getAttribute('data-path');
$('a[href="'+path+'"]').addClass("active");
});
base.html
<script id="baseScript" src="{{ url_for('static', filename='js/base.js') }}"
data-path="{{ request.path }}"></script>
Why not just put this script inline? You could, of course, but allowing inline JS is a security nightmare. You should be using a CSP on your site (e.g. Flask-Talisman) which will not allow inline JS. With data-* attributes, it's not hard to do this in a secure way.
NB: If you have multiple links leading to the same, current page and you want only ONE of them to be marked "active"—then this approach may not work for you.
I tried different solution for this for the solution 1st by Codegeek didn't work as I have multiple Ul and li under it so I just include my navbar in template.html
{% include 'sidebar.html' %}
then in Navbar file in the li class you can set active with help of "request.endpoint" but then again it will return you entire route instead use split and take last route name and set active if same for exmaple
<li class="{% if request.endpoint.split('.')[1] == 'index' %} active {% else %} {% endif %}">
request.endpoint.split('.')[1] will return the route eg localhost/example. You will get example which you can compare and use. If you won't split and use request.endpoint than you will get 'file.example' (entire route).
Add the following CSS somewhere on your page:
a[href $= {{ page_name|default("'/'"|safe) }}]{ [INSERT YOUR ACTIVE STYLING HERE] }
Now, on each template define page_name, for example:
{% extends "template.html" %}
{% set page_name = "gallery" %}
This seems much simpler and easier to build on, than other options.
EDIT:
Almost 1 year later I'm returning to make this a much simpler fix, because setting the page name on every page is pretty inefficient.
Instead create a function like so:
#app.context_processor
def context_processor():
out = {}
out['request'] = request # Make sure you import request from flask
return out
This will allow you to pass variables implicitly to jinja, in this case we are passing the request for access to request.url_rule which contains the route the user is accessing. In the previous version, we just change {{ page_name|default("'/'"|safe) }} to "{{ request.url_rule|safe }}". Much cleaner.
I did not want to have to define the ID in the child pages, as many of the links I have do not have a specific child template.
Using the request.base_url and if it matches the _external url_for the route, then render that nav item as active.
{% set nav_items = [
("public.home", "Home"),
("public.downloads", "Downloads"),
("public.about", "About")
("account.login", "Login"),
]
-%}
...
<ul class="navbar-nav mr-auto">
{% for route, display_text in nav_items %}
<li class={% if request.base_url == url_for(route, _external=True) %}"nav-item active"{% else %}"nav-item"{% endif %}>
<a class="nav-link" href="{{ url_for(route) }}">{{ display_text }}
{% if request.base_url == url_for(route, _external=True) %}<span class="sr-only">(current)</span>{% endif %}
</a>
</li>
{% endfor %}
</ul>