Using a jinja variable in JS script - python

I'm new to Flask and looking for a way to parse one or n values of a jinja variable.
The app route hands an object over to the html template.
`selection2 = db.execute("SELECT user.id, bikes.id, bikes.name, bikeshopname, price, city, img, enddate FROM user LEFT JOIN bikes ON user.id = bikes.userid LEFT JOIN renthistory ON bikes.id = renthistory.bikeid WHERE user.city = :city AND price NOT NULL", city=select_city)
return render_template("/bikes.html", selection2=selection2)`
In the template I implemented it like this:
{% for row in selection2 %}
<p id="enddate" value="{{row["enddate"]}}"></p>
{% endfor %}
In the js function I get the value (a certain date) of that particular paragraph. So it works for the first item.
function getBikeRentEndDate() {
let enddate = (document.getElementById("enddate").innerHTML);
document.getElementById('showdate').innerHTML = enddate;
}
But what I want is to look in all the created once the template is rendered. And then show a message if a condition is true. Something like "if date is => today - show a message.
I know there is a way but after researching and trail and erroring for quite a while, I hope someone can point it out.

To achieve the desired result, you have to first cast the jinja2 variable to js.
To do that in your template:
<script>
const listselection2 = JSON.parse('{{ selection2 | tojson | safe }}');
//do your stuff with listselection2.
</script>

Related

Passing information using HTML and Flask

I'm working on a uni project that involves logging films, sort of like Letterboxd. I've made a search page where users enter a keyword when looking for a movie, this then prints the results supplied by the tmdb api. Below is the code in my routes.py file for the search and results page:
#app.route('/search-movie', methods=['GET', 'POST'])
def m_search():
form = MovieSearch()
if form.validate_on_submit():
user_search = urllib.parse.quote(form.movieName.data)
complete_url = search_url + user_search + "&page=1"
conn = urllib.request.urlopen(complete_url)
json_data = json.loads(conn.read())
return render_template('search_results.html', results=json_data["results"], term=form.movieName.data)
return render_template('movie_search.html', form=form)
The following is the code in the html file for that page:
{% block content %}
<h1> results for "{{term}}"</h1>
<h2> click movie name to log it</h2>
<div class="movie-list-container">
{% for movie in results %}
<div class="movie-card">
<img src="https://image.tmdb.org/t/p/w500/{{movie.poster_path}}" alt="">
<!-- <h3> {{movie.title}} ({{movie.release_date[:4]}})</h3> -->
<h3><a class="log-link" href="{{ url_for('log_movie', movieid=movie.id) }}"> {{movie.title}} ({{movie.release_date[:4]}}) </a></h3>
</div>
{% endfor %}
</div>
{% endblock %}
As you can see in the line I commented out, previously it would just display the movie title and release year. However I wanted to change is so that if the user presses the movie name, they are taken to a page where they add information through a form such as their rating, the date they watched the movie, and a review.
This is how it's done on Letterboxd I wanted mine to be pretty much the same.
I want to be able to show the movie name, release date and poster for the movie they pressed on the logging page, and I tried this in the h3 by passing through movieid=movie.id . From there in my routes.py file I wrote the following code
#app.route('/log-movie', methods=['GET', 'POST'])
def log_movie(movieid):
log_url = info_url + movieid + "?api_key=" + api_key
conn = urllib.request.urlopen(log_url)
json_data = json.loads(conn.read())
form = LogMovie()
if form.validate_on_submit():
log_data = Diary(date_watched=form.dateWatched.data, movie_name=mname, release_date=myear, user_rating=form.movieRating.data,
rewatch=form.movieRewatch.data, review=form.movieReview.data, logger=current_user)
db.session.add(log_data)
db.session.commit()
return redirect(url_for('home'))
return render_template('log_movie.html', form=form, results=json_data["results"])
My idea was to simply get the movieid so that I can then request the information from the api again, so that I can pass it through like I did when displaying the results in the html code I added above.
When storing to the database, I have the variables mname, myear. These were from a previous attempt where I wished to pass in the movie year and release date from the HTML without needing to call upon the api again in routes.py. When I couldn't get the multiple variables to pass, that's when I changed it to just movieid. I forgot to change these back but should I manage to pass the information from the HTML, I may go back to this version.
I keep getting an error TypeError: log_movie() missing 1 required positional argument: 'movieid' and I can't seem to find an answer on google. I was wondering if anyone knew why, or a better way of achieving what I want?
I've never asked a question before so let me know if I need to provide more information.
Many Thanks! :)
#app.route('/log-movie', methods=['GET', 'POST'])
def log_movie(movieid):
I'm just pointing out that. This is where your error is. If you want to get the movieid here, you're route should be
#app.route('/log-movie/<movieid>', methods=['GET', 'POST'])
In that way, if you GET /log-movie/12345, the movieid will be 12345.
Edit :
Just to be clear, i just pointed out where your error is, if you need more help about this, you can still make a comment, i'll edit my answer to other question :)

How do I count hits of each element in a list in Django?

So I have a page where multiple articles are listed. (To be precise, TITLES that are outlinked to the articles written on Notion template.) And I want to have a filed in my model that counts the number of clicks of each article. (I don't want to use django-hitcount library).
Let me first show you my code. models.py
class Article(models.Model):
number = models.IntegerField(primary_key=True)
title = models.CharField(max_length=20, default="")
url = models.URLField(max_length=100, default="")
hits = models.IntegerField(default=0)
template
...
<div class="col text-center">
{% for q in allArticles %}
<h2 id={{q.number}}>{{q.title}}</h2>
{% endfor %}
</div>
...
I was thinking of using onclick() event in JavaScript, but then passing data from JavaScript to Django seemed too challenging to me at the moment.
I'd very much appreciate your help. Thanks.
Well, when you dont take up new challenges you stop learning !
The onclick method looks like the best imo, lets see what others suggest.
honestly, using JS and AJAX to communicate with your django server might be dauting at first but it is quite easy really.
if you know how to create a function in your views.py and know a bit of JS, it's just like any other classic functionnality.
Set up your urls.py for the view function that will add a click to the counter:
path('ajax/add_click', views.add_click name="add_click"),
Then, create your view function (pseudo code):
def add_click(request):
# retrieve the article
article_id = request.GET.get("articleId", None)
# then retrieve the object in database, add 1 to the counter save and return a response
Now the "complicated" part, the ajax request:
function add_one_click(articleId) {
$.ajax({
type: "GET",
url: '/ajax/add_click', // you also can use {% url "app_name:add_click" %}
data: {
'articleId': articleId,
},
success: function() {
console.log("hit added to article");
}
});
}
You need to add JS and Ajax lib to your html template for it to works.
Also you need to pass in the onclick attribute the name of the function + the id of the article
onclick="add_one_click({{article.id}})"
One more thing, this type of view, if not protected can lead to get false results.
Instead of having q.url have a new URL(/article_count?id=q.id) which you will define on your Django project
def article_count(req):
_id = req.GET.get('id', '')
# Query Aritcle and get object
q = Article.objects.get(id=_id)
# update the fields for clicks
q.hits += 1
q.save()
# redirect the page
return redirect(q.url)
Edit:
Create a new url that would handle your article click, lets say-
path('article/clicked/<article_number>', views.click_handler, name='click_counter')
Now, in your template use this url for all the article
<div class="col text-center">
{% for q in allArticles %}
<h2 id={{q.number}}>{{q.title}}</h2>
{% endfor %}
</div>
and in your views.py create a new controller
def click_handler(request, article_number):
article = Article.objects.get(number=article_number)
article.hits += 1
article.save()
# now redirect user to the outer link
return redirect(article.url)

How do I filter a model object and update a view in django?

I have a a template with an html table in django. At the top of the view is a small form for input. Something like:
{% csrf_token %}
<form method="post" name="some_name" action="/myApp/">
search button here
...
</form>
<table>
loop through data and make table here
</table>
I when I access the webpage, I initialize data into the table. I have a backend sqlite database with > 25 million rows. I'd like to use the user input from the form to filter the data. I've already tried to get the data from the form, but when I try to apply objects.all.filter(some_condition), I don't see the table update. What am I doing wrong? Or has anyone else come up with a solution to this type of problem? I know this should be simple, but I've had a time figuring it out. Thanks!
UPDATE:
views.py
def showTable(request):
if request.method == 'POST':
#I have a table of aircraft entries.The EntriesTable has a ForeignKey
#referencing an AircraftTable that has the numbers of aircraft
#some_name will reference the number of the aircraft
aircraft = request.POST.get('some_name', None)
query_results = EntriesTable.objects.all().filter(aircraft__exact = filterable)
template=loader.get_template('myApp/showTable.html')
context=RequestContext(request, {'query_results': query_results,})
return HttpResponse(template.render(context))
else:
query_results = EntriesTable.objects.all().filter(start_time__range(start, stop)) #Assume that I have correctly filtered on start/stop times. I am able to render that so far without issue.
context= RequestContext(request, {'query_results': query_results,})
return HttpResponse(template.render(context))
query_results = EntriesTable.objects.filter(aircraft__exact = filterable)
You have an error in your view, the syntax should me:
NameOfModelClass.objects.filter(conditions)
I believe that will fix the problem

Passing multiple template variables to template tags in django

My problem occurs when I try to pass 3 variables to my template tag which I guess takes one or 2 inputs. So any ideas on how to pass 3 template variables to my template tag and here is my code:
views.py:
from shoghlanah.models import *
register = template.Library()
#register.simple_tag
def get_messages(sender_id,receiver_id,task_id):
sender = UserProfile.objects.get(id = sender_id)
receiver =UserProfile.objects.get(id = receiver_id)
task = Task.objects.get(id=task_id)
return messages
message.html :
the url.id and the task_id are template variables
{{ get_messages request.user.id usr.id task_id }}
it gives me an error : Could not parse the remainder: ' request.user.id usr.id task_id' from 'get_messages request.user.id usr.id task_id'
For a django tag, you need to use {% %}, not the double curly brackets. The double braces signify outputting a value.
See the docs for more.
(Just as a note, I presume that is an extract, but you will also need to {% load %} your tag too.)

Django - Paginating in Template using multiple GET parameters

I am using a Django Paginator and I want to have multiple available get parameters, such as:
page=1
sort_by=price
However, in my template tags I have:
Showing items sorted by {{ SORT_PARAM }}.
Showing {{ ITEMS_PER_PAGE }} items per page.
{% if has_prev %}
Previous |
{% endif %}
However, this does not preserve the other GET variables. What I mean is, if I'm viewing
page/?page=1&sort_by=price
and I click the link in the template fragment above, I will go to
page=2
instead of
page=2&sort_by=price
What I mean is, the a href does not preserve the other GET parameters.
One solution is I could type all the possible GET parameters in the a href, such as
Previous
but this will become less scalable the more arguments I want to add to my browsing. I'm guessing there should be an automated way to obtain all GET parameters, and then pass those and one more?
This one http://djangosnippets.org/snippets/1592/ looks cleaner
You can create a 'parameter-string'. Let's supose that in your code you have:
my_view( request, page, options):
sort_choices = {P:'price',N:'name', ...}
n_item_choices = {'S':5, 'L':50, 'XL':100)
ascending_descending_choices = {'A':'', 'D':'-'}
...
then you can concatenat options as:
options='P-S-D' #order by price, 5 items per page, descending order
encode opions as:
Previous
then, in urls.py capture options and in view:
my_view( request, page, options):
... #choides ....
try:
optionsArray = options.split('-')
sort_by = sort_choices[ optionsArray[0] ]
n_ites_page = n_item_choices[ optionsArray[1] ]
asc_or_desc = ascending_descending_choices[ optionsArray[2] ]
...
except:
somebody is playing ....
with this method you are free to add more paginations options without modify urls.py, all you need is to append options at the end of string options . This has advantages but also some dangers: I hope you can identify risks.
With Django's Pagination - preserving the GET params is simple.
First copy the GET params to a variable (in view):
GET_params = request.GET.copy()
and send it to the template in via context dictionary:
return render_to_response(template,
{'request': request, 'contact': contact, 'GET_params':GET_params}, context_instance=RequestContext(request))
Second thing you need to do is use it specify it in the url calls (href) in the template - an example (extending the basic pagination html to handle extra param condition):
{% if contacts.has_next %}
{% if GET_params %}
next
{% else %}
next
{% endif %}
{% endif %}
Source - Posted same answer.

Categories