How to call a function/method on Flask from the template - python

First of all, I'm quite new using flask, but this is something I haven't been able to find so far.
I'm working on my website with Flask and Jinja templates, using postgresql as a DB, I want to be able to call another function/method in my template.
Here I can get all my shares (posts)
#shares_app.route('/shares', methods=['GET'])
#login_required
def last_shares():
shares = fetch_last_shares()
form = ReusableForm(request.form)
return render_template('shares.html', form=form, shares=shares)
template
{% for share in shares %}
<li class="comment" style="border:1px solid black; padding:5px;" >
<a class="pull-left" href="#">
<img width="35" height="35" avatar="{{share[5]}}">
</a>
<div class="comment-body">
<div class="comment-heading">
<h4 class="user">{{share[5]}} ({{share[4]}}) </h4>
<h5 class="time">{{share[3]}} /</h5>
</div>
<p> <b>{{share[0]}} </b> / {{share[2]}}</p>
</div>
<!--comments here -->
Here I wanna be able to get all my comments related to shares, here its where I\'m no sure if I can call another function from my controller.
comments = fetch_last_comments(share[0])
{% for comment in comments %}
Show comments here
{% endfor %}
<!--comments here -->
{% endfor %}
Basically, I want to be calling this function
def fetch_comments_by_shares(share_id):
comments = db.query("""SELECT * FROM comments WHERE share_id = {} """.format(share_id)).getresult()
return comments
Thanks a lot.

Instead of making multiple DB queries for each and every share id you can get all the comments for all the shares in the backend and then pass comments while rendering the template.
like.
render_template('shares.html', form=form, shares=shares, comments=comments)
and if still, you want to call the python function from jinja template then you can follow this answer for the same.
Call a python function from jinja2

Related

Django, link to object in db

I've got a django test-project going with a postgresql db with a table called customers.
Rendering all the customers in /customers/ is fine but I'm looking for a way to render a single customer like this: /customer/{{customer.id}}. Not really sure what I'm looking for, anyone that can guide me to the correct documentation or something?
Much appreciated.
List all customers and open given customer when clicking on the div:
<div class="container">
{% for customerz in custs_all %}
<br>
<div class="row shadow-sm d-flex w-1000 justify-content-between" onclick="location.href='/customers/{{customerz.id}}';" style="cursor: pointer;">
<div class="col-xl-4"><h5 class="mb-1">{{ customerz.name }}</h5></div>
<div class="col-xl-4">Contact person: {{ customerz.contactperson }}</div>
<div class="col-xl-4">Email: {{ customerz.email }}</div>
<div class="col-xl-4">Active: <span class="badge badge-light">{{ customerz.active }}</span></div>
</div>
{% endfor %}
</div>
So this is what I dont understand, what doc do I need to read to setup this to work?
onclick="location.href='/customers/{{customerz.id}}';"
I dont know what I'm looking for, so its hard to ask the right questions here.. Sorry
views.py
def customer_detail(request, pk):
customer = get_object_or_404(Customer, id=pk)
return render (request, template, {'customer': customer}
Here pk is primary key so you are getting a customer from your model Customer(or whatever you have named it).
urls.py
path('customer/<int:pk>/', views.customer_detail, name='customer-detail')
And in cutomer list template you can add link to add you customer detail template like cutomer detail
I think you are looking for this. You can pass an ID in the URL request and use it to get your desired object from the database.
urls.py
path('customer/<int:pk>/', views.customer_detail, name='customer-detail'),
views.py
def customer_detail(request, pk):
customer = Customers.objects.get(id=pk)
return render (request, 'customer.html', {'customer': customer})
customer.html
{% extends "base.html" %}
{% block content %}
<br>
<div class="container row">
<div class="col-xl-12"><h5 class="mb-1">{{ customer.name }}</h5></div>
<div class="col-xl-12">Active: <span class="badge badge-light">{{ customer.active }}</span><hr></div>
<div class="col-xl-12">Description: {{ customer.desc }}<hr></div>
<div class="col-xl-12">Contact person: {{ customer.contactperson }}</div>
<div class="col-xl-12">Email: {{ customer.email }}</div>
<div class="col-xl-12">Address: {{ customer.addr }}</div>
<div class="col-xl-12">Last updated: {{ customer.modified }}</div>
</div>
<br>
{% endblock %}
This is currently doing exactly what i want :)
The previous answer was correct, in action doe ; you could do something like this :
-- you could grab the customer id through request.form; if you use form within your table; i guess this is the best approach to do so.
-- than you can set something with (url_for), and give it a parameter as {{ customer.id}} , within your .html .
-- than handle the logic when returning this object.
You can check this project, go through it , you might find something similar to this :
[https://github.com/Nouamanezh909/BLog_repo][1]

How to access variables within html template url_for

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

How To Print Descendants Of GAE NDB onto Jinja2?

So I have been tinkering for a while with jinja2 and google app engine. I am just writing a small toy app on my spare time; the app has a webpage that displays the ten most recent posts along with its comments.
All of the blog posts print fine onto the page by using the following within the google data store, after the Post object is created and stored in the database of course. I use the following query to get the ten posts to be displayed.
recent_blog_posts = ndb.gql("SELECT * FROM Posts ORDER BY created_at
DESC LIMIT 10;")
The blogpage.html code below:
{% block content %}
{% for post in recent_blog_posts %}
<div>
<h3>{{post.title}}</h3>
<pre>
<p style="max-width: 100%;">{{post.post}}</p>
</pre>
<p>By: {{post.by_user}}</p>
<!-- this is where I want the comments to go (explained below)-->
<h4>Leave A Comment:</h4>
<form method="post">
<textarea name="comment" value="{{comment}}" style="height: 50px; width: 200px;"></textarea>
<input type="hidden" name="post_key" value="{{post.key}}">
<br>
<button>Comment</button>
</form>
</div>
<hr>
{% endfor %}
{% endblock %}
I just iterated over the ten objects in the query above to print all of the blog posts. However, this is where it gets tricky for me.
I create a new Comment instance with the following:
new_comment = Comments(comment = comment,
user = user.name, parent = ndb.Key(Posts, int(post_key)))
new_comment_key = new_comment.put()
When I print the new Comment instances onto the screen, just to see, they all print out correctly with the right parent and their own ids.
Now this is where I am not sure on how to take each Comment instance and print it with its corresponding post. How can I accomplish that?
I have searched everywhere, and even added this to the html template above. (In place of the comment from the html template above)
{% for comment in comment_query %}
{{comment.comment}}
{% endfor %}
With the query below:
recent_comments = Comments.query(ancestor=ndb.Key(Posts, int(new_string))).order(-Comments.created_at).fetch(limit=3)
This obviously just prints out all of the Comments instances for all of the Posts instances on the page.
Thanks In Advance
Just form the output list in the backend itself.
recent_blog_posts = ndb.gql("SELECT * FROM Posts ORDER BY created_at
DESC LIMIT 10;")
posts_with_comments = []
for post in recent_blog_posts:
recent_comments = Comments.query(ancestor=post.key).order(-Comments.created_at).fetch(limit=3)
posts_with_comments.append([post, recent_commmnets])
Then iterate over posts_with_comments in the template like
{% for post,comments in posts_with_comments %}
<div>
<h3>{{post.title}}</h3>
<pre>
<p style="max-width: 100%;">{{post.post}}</p>
</pre>
<p>By: {{post.by_user}}</p>
<p> Comments: </p>
{% for commnet in comments %}
{{ comment }}
{% endfor %}
{% endfor %}

"TemplateSyntaxError: Invalid filter:"; custom django template filter based on django docs broken, but template tags working

I have a template filter based on the django docs at https://docs.djangoproject.com/en/1.8/howto/custom-template-tags/. For the life of me I can't see any difference in my usage and theirs, and am slowly going insane. I have a working tag I got on a forum as such:
myproject/index/templatetags/add_get_parameter.py:
from django.template import Library, Node, resolve_variable
register = Library()
class AddGetParameter(Node):
def __init__(self, values):
self.values = values
def render(self, context):
req = resolve_variable('request', context)
params = req.GET.copy()
for key, value in self.values.items():
params[key] = value.resolve(context)
return '?%s' % params.urlencode()
#register.tag
def add_get(parser, token):
pairs = token.split_contents()[1:]
values = {}
for pair in pairs:
s = pair.split('=', 1)
values[s[0]] = parser.compile_filter(s[1])
return AddGetParameter(values)
This one, add_get on lines 8-9, works, whereas shorten_title on line 4 doesn't work:
myproject/templates/index/silo.html:
{% load bootstrap add_get_parameter extras %}
...other stuff...
{% for article in articles %}
<div class="col-md-4 article-link">
<div class="panel panel-default hover">
<div class="panel-heading"><h4 class="url-link">{{ article.title|shorten_title }}</h4></div>
<div class="panel-body">
<p> <span class="url-text">{{ article.url }}</span></p>
<div class="article_button">Edit</div>
<div class="article_button"><p>Archive</p></div>
<div class="article_button">Delete</div>
<div style="margin-top:8px;">
{% for tag in article.tags.all %}
<p class="tag">{{ tag.name }}</p>
{% endfor %}
</div>
</div>
</div>
</div>
{% endfor %}
Here's the villian:
from django.template import Library
register = Library()
#register.filter
def shorten_title(title):
length = len(title)
new = title
if length > 65:
new = title[0:65] + "..."
return new
register.filter('shorten_title', shorten_title)
He's been so rude I double registered him, just to see what happens (he doesn't work registered once as a decorator or afterward, and doesn't work registered twice).
{{ article.title }} works, but {{ article.title|shorten_title }} breaks the page with:
django.template.base.TemplateSyntaxError
TemplateSyntaxError: Invalid filter: 'shorten_title'
'Index' is definitely registered and working, and the page works when I delete the filter from that article.title tag.
Usually when I get a stubborn error I missed something small, but following the docs word for word has me baffled (I've written several working filters before). Is this filter bad, or is there maybe something else in my page that causes the issue? Thanks
You need to make sure you import the file with register.filter('shorten_title', shorten_title) before you render the template. Since that call happens outside of any functions it is run when you import the module. This has the side effect of registering it so that it will be available in your templates afterwards.

Replace part of template with contents of html file in Django

I have a problem with replacing part of website built in django with another content.
I have a code like
<section id='main_page'>
<div id="main_div">
<--! include kek.html content here -->
</div>
</section>
and I'd like to replace this comment with html placed in another html file.
I tried
def home(request):
return HttpResponse(render_to_string(
'index.html',{'bla':kek.html},
))
while in in index.html was
<section id='main_page'>
<div id="main_div">
{{ bla }}
</div>
</section>
but, it doesn't seem to work that way. I'm kinda new to django and python and I can't find any solution.
No, that wouldn't even run (kek.html is not defined in the view, so it's a NameError).
You can use the {% include %} tag in the template for this:
<section id='main_page'>
<div id="main_div">
{% include 'kek.html' %}
</div>
</section>
You should really do the Django tutorial, which explains how templates work (among other things).
Edit after comment
In that case you should treat the name of the include as a variable and pass it from the view:
{% include template_to_include %}
...
return render_to_response(
'index.html', {'template_to_include': 'kek.html'},
)
Note the quotes.

Categories

Resources