using mongoDB collection in one page twice - python

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

Django - How to access elements in list by parent loop counter in template

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.

Django How can I cache properly to reuse data?(with using django-mptt package and redis module)

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!

Flask pagination example wont work missing "iter_pages"

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) }}

create templates on the fly / dynamically

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.

Django - Template with 'if x in list' not working

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 :.

Categories

Resources