Hi2all!
I have:
1) mongo collection:
[{_id:ObjectId("5b0d5fb624d22e1b4843c06b")
collectionName:"collection0"
collectionCaption:"caption1"}
{_id:ObjectId("5b0d5fb824d22e1b4843d4c1")
collectionName:"collection1"
collectionCaption:"caption1"}
{_id:ObjectId("5b0d5fb924d22e1b4843d74a")
collectionName:"collection2"
collectionCaption:"caption1"}
{_id:ObjectId("5b0d5fb924d22e1b4843d7b0")
collectionName:"collection3"
collectionCaption:"caption1"}]
2) flask app with the view:
def index():
a = mongo.db.collectionsNames.find()
return render_template('index.html', collectionsNames=a)
3) templates: index.html witch extend base.html.
base.html:
{% extends "bootstrap/base.html" %}
{% block content %}
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
{% for asd in collectionsNames %}
<a class="dropdown-item" href="/{{ asd["collectionName"] }}">{{ asd["collectionCaption"] }}</a>
{% endfor %}
</div>
{% block contentBase %} {% endblock %}
{% endblock %}
index.html:
{% extends "base.html" %}
{% block contentBase %}
{% for zxc in collectionsNames %}
{{ zxc["collectionName"] }}
{% endfor %}
{% endblock %}
The question is: why if base and index using the same variable of collection, index template show nothing?
But if in view code is:
def index():
a = mongo.db.collectionsNames.find()
b = mongo.db.collectionsNames.find()
return render_template('index.html', collectionsNames1=a, collectionsNames2=b)
and in templates i am using the different variables, the index template show me the data.
mongo.db.collectionsNames.find() returns a cursor: print(mongo.db.collectionsNames.find()) gives <pymongo.cursor.Cursor object at 0x7fd3854d5710>.
To make it easy (but kind of wrong) a cursor is a specific kind of instance that fetch data from the database chunk by chunk so if you want the 1 000 000 first items in db you don't actually store 1M items in RAM, you iterate 100 by 100 items (for example). Cursor handle that in a magic way.
Whatever, you cannot loop several time on the same cursor and you should never caste a cursor to list like list(cursor_instance) (because if your query ask for 1M product, doing this add all these products in RAM).
So, now, how can you handle this. Most of time I would say it's better to call the method when you need it, twice if needed.
But here you are in a Jinja environment and if I'm right calling methods within a Jinja template is impossible.
A way to do that is to use properties.
class LazyCollection(object):
#property
def collections_names(self):
return mongo.db.collectionsNames.find()
lazycoll = LazyCollection()
return render_template('index.html', collectionsNames=lazycoll)
Then, in your template:
{% for asd in collectionsNames.collections_names %}
<p>{{ asd.foo }}</p>
{% endfor %}
Related
I have this code
{% for time in listOfTimes %}
{% for booking in someOtherList.forloop.parentloop.counter0 %}
{{ booking }}
{% endfor %}
{% endfor %}
The booking variable does not get printed. I think this was because I cannot access someOtherList by using the forloop counter. How would I get the booking value?
Assuming your data is as follows:
listOfTimes = ['time1', 'time2']
someOtherList = [['booking1', 'booking2'], ['booking3', 'booking4']]
Then in template you can do this:
{% for time in listOfTimes %}
{% for booking in someOtherList|get_index:forloop.counter0 %}
{{ booking }}
{% endfor %}
{% endfor %}
Notice the get_index filter in above code, you need to write this custom filter in your app templatetags:
from django import template
register = template.Library()
#register.filter
def get_index(l, i):
return l[i]
Note: Your two list should be of same size, otherwise an IndexError might be raised.
I am making kind of dynamic menu. when you click menu on the top, it show sub menu on the left side. I searched with keyword 'dynamic menu' from stackoverflow and google. I got idea to build that kind of menu. I made it like below.
1) render data(menu list) in context to template by custom context processor.
2) using custom template tag which is provided by django-mptt package.
3) show top menu in base template.
4) move to another template to show sub menu according to what top menu you click
I made custom context_processor to use menu in context in every template.
context_processor.py
from manager.models import Menu
def menu(request):
menu_list = list(Menu.objects.all())
return {'menu':menu_list}
template.py(example)
{% load mptt_tags %}
<nav id="{{ menu_id }}" class="tree-menu">
<ul>
{% recursetree menu %}
<li class="menu
{% if node.is_root_node %}root{% endif %}
{% if node.is_child_node %}child{% endif %}
{% if node.is_leaf_node %}leaf{% endif %}
{% if current_menu in node.get_descendants %}open{% else %}closed{% endif %}
">
{{ node.menu_name }}
{% if not node.is_leaf_node %}
<ul class="children">
{{ children }}
</ul>
{% endif %}
{% if node.items and node.items.exists %}
<ul class="items">
{% for item in node.items.all %}
{% if item_template %}
{% include item_template %}
{% else %}
{% include "menu/tree-item.html" %}
{% endif %}
{% endfor %}
</ul>
{% endif %}
</li>
{% endrecursetree %}
</ul>
</nav>
mptt_tags.py
#register.tag
def recursetree(parser, token):
"""
Iterates over the nodes in the tree, and renders the contained block for each node.
This tag will recursively render children into the template variable {{ children }}.
Only one database query is required (children are cached for the whole tree)
Usage:
<ul>
{% recursetree nodes %}
<li>
{{ node.name }}
{% if not node.is_leaf_node %}
<ul>
{{ children }}
</ul>
{% endif %}
</li>
{% endrecursetree %}
</ul>
"""
bits = token.contents.split()
if len(bits) != 2:
raise template.TemplateSyntaxError(_('%s tag requires a queryset') % bits[0])
queryset_var = template.Variable(bits[1])
template_nodes = parser.parse(('endrecursetree',))
parser.delete_first_token()
return RecurseTreeNode(template_nodes, queryset_var)
My Question
If you see django manual about QuerySet, it says that "Each QuerySet contains a cache to minimize database access". It is obvious that, if you query same data in certain rule, it doesn't seem hit database again but return result from cache. Then I am querying Menu.objects.all() in custom context processor. This result(menu_list = Menu.objects.all()) will be in context every time, you can use menu data on every template repeately. So does it reuse the result from cache without hitting database again?
If menu_list = Menu.objects.all() in custom context processor hit database every time whenever template load this menu list, Does it work in this way to reuse menu data from cache without hitting database everytime?
context_processors.py
from manager.models import Menu
from django.core.cache import cache
def menu(request):
menu_list = cache.get_or_set('menu_list', list(Menu.objects.all()))
return {'menu':menu_list, 'redis':"Food"}
Lastly, I don't know if there are many people using django-mptt package. I guess just a few people have experience using it in person. It says "Only one database query is required (children are cached for the whole tree)" so does it mean if I use django-mptt package and get menu from it on template, it automatically cache its data?
Well, I am not clear about django cache system.
It would be really appreciate if you can give me answer and insight for my questions. Thanks for reading!
I am working on a small web app to view some logfiles. But the
queries I issue to the database use to get very big.
I wanted to implement some pagination following this example pagination.
I put the class into a single file which gets loaded in the Flask view. Next I implemented
my pagination view like this:
#app.route('/index/', defaults={'page':1})
#app.route('/index/page/<int:page>')
def index(page):
count = db_session.execute("select host,facility,level,msg from messages").rowcount
tblqry = db_session.execute("select host,facility,level,msg from messages").fetchmany(size=1000)
if not tblqry and page != 1:
abort(404)
pagination = Pagination(page, PER_PAGE, count)
return render_template('index.html', pagination=pagination, tblqry=tblqry)
after that I created a macro file named _pagination_helper.html with the macro contents from the macro example. Then I imported the pagination_helper macro with:
{% from "_pagination_helper.html" import render_pagination %}
but when I then try to do something like this:
{{ render_pagination(host[0]) }}
flask claims:
UndefinedError: 'str object' has no attribute 'iter_pages'
so why does flask fails to find the 'iter_pages' because I included the pagination class in the views file?
And also I am not really sure where to put the URL Generation Helper from the How To.
Edit:
This is what my pagination_helper looks like:
{% macro render_pagination(pagination) %}
<div class=pagination>
{% for page in pagination.iter_pages() %}
{% if page %}
{% if page != pagination.page %}
{{ page }}
{% else %}
<strong>{{ page }}</strong>
{% endif %}
{% else %}
<span class=ellipsis>…</span>
{% endif %}
{%- endfor %}
{% if pagination.has_next %}
Next »
{% endif %}
</div>
{% endmacro %}
You are expected to pass a Pagination object to the macro, not a string. host[0] is a string, not the pagination value you created in your view function.
Use:
{{ render_pagination(pagination) }}
I am currently writing a django app around a rather complex data model.
For many use cases, I need to build similar, but slightly differing templates (incl. graphviz etc..).
Now I wonder if there is a way to follow DRY and create the templates "on the fly", e.g. based on a nested tuple (of which one gets defined per use-case -> template).
Practically speaking I'd like to stop writing these:
static_template_case1.html
{% if program %}
{{ program.name }}
{% for process in program.process_set.all %}
{{ process.name }}
{% for step in process.step_set.all %}
{{ step.name }}
{% endfor %}
{% for control in process.control_set.all %}
{{ control.name }}
{% endfor %}
{% endfor %}
{% endif %}
and replace it with something in the following direction:
template_generator.py
structure_case1 = ("program"("process"("step","control")))
def onTheFlyTemplate(structure):
# iterate through structure
# build template dynamically
# return template
onTheFlyTemplate(structure_case1)
I was not able to find something similar and also don't know yet where to start generating templates on the fly, nor how to integrate it.
Within the views feels like a good starting point, (instead of loader.get_template('static_template_case1.html')).
Thanks for any hint sending me into the right direction as well as your thoughts if DRY is being "overdone" here.
Ralph
Edit
I got a step closer, doing the following:
In the view:
from django.template import Template
def templateGen():
return Template("Program: {{program.count}}")
#login_required
def test(request):
program = Program.objects.all()
t = templateGen()
c = RequestContext(request,locals())
return HttpResponse(t.render(c))
So far this is only a proof of concept. The real work will be to write a smart "templateGen()" listening to the nested tuple.
How about an inclusion tag?
#register.inclusion_tag('<your-template.html>')
def get_program(program_id):
return {'program': <some logic to return your Program object>}
Your main template:
{%load my-template-tags%}
<div id="my-div">
{%get_program program_id %}
</div>
And your template file:
{{ program.name }}
{% for process in program.process_set.all %}
{{ process.name }}
{% for step in process.step_set.all %}
{{ step.name }}
{% endfor %}
{% for control in process.control_set.all %}
{{ control.name }}
{% endfor %}
{% endfor %}
You'd have to pass in your program_id to the main template.
I have a Django template that looks something like this:
{% if thing in ['foo', 'bar'] %}
Some HTML here
{% else %}
Some other HTML
{% endif %}
The problem is it comes back empty. If I switch to this:
{% if thing == 'foo' or thing == 'bar' %}
Some HTML here
{% else %}
Some other HTML
{% endif %}
it works fine. Is there some reason you can't use x in list in Django templates?
You can. But you can't use a list literal in templates. Either generate the list in the view, or avoid using if ... in ....
I got it working with the help of this answer. We could use split to generate a list inside the template itself. My final code is as follows (I want to exclude both "user" and "id")
{% with 'user id' as list %}
{% for n, f, v in contract|get_fields %}
{% if n not in list.split %}
<tr>
<td>{{f}}</td>
<td>{{v}}</td>
</tr>
{% endif %}
{% endfor %}
{% endwith %}
Send the list from the context data in the view.
Views.py:
class MyAwesomeView(View):
...
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['list'] = ('foo', 'bar')
...
return context
MyTemplate.html:
{% if thing in list %}
Some HTML here
{% else %}
Some other HTML
{% endif %}
Tested on Django version 3.2.3.
There is a possibility to achieve that also by creating a custom filter:
Python function in your_tags.py:
from django import template
register = template.Library()
#register.filter(name='is_in_list')
def is_in_list(value, given_list):
return True if value in given_list else False
and passing your list to html django template:
{% load your_tags %}
{% if thing|is_in_list:your_list %}
Some HTML here
{% else %}
Some other HTML
{% endif %}
or without passing any list - by creating a string containing your list's values (with a filter you still can't use list literal), e.g.:
{% load your_tags %}
{% if thing|is_in_list:'foo, bar' %}
Some HTML here
{% else %}
Some other HTML
{% endif %}
[TIP] Remember not to insert space after :.