How can you change Django loop body using javascript? - javascript

i was wondering if there is a way i can change the body of for loop in django using javascript each time i press a button.
in my case i want to display matches this week and when i press next i want to change the list using javascript and then pass it django template in the regroup part, i want to change the matches list.
i know how to write the code to make the new list and the previous and next buttons using javascript but i don't know how to pass it to django template
or maybe another way could be to write django code in javascript, anyone can help with either way?
in views.py , matches return a list of dictionaries from today to 6 days later
def home(request):
start = datetime.now().date()
end = today + timedelta(6)
matches = request_games(today, after_week)
return render(request, "sporty/home.html",{
"matches": matches,
"start" : start,
"end": end
})
in home.html
{% extends "sporty/layout.html" %}
{% load static %}
{% block body %}
<div class="box">
{{start}},{{end}}
{% regroup matches by date as date_list %}
{% for date in date_list %}
<div class="the_date">
{{date.grouper}}
</div>
{% for match in date.list %}
<div class="match_container">
<div class="status">
{% if match.status_code == 1%}
{{match.minute}}'
{% elif match.status_code == 11%}
HT
{% elif match.status_code == 3 %}
Finished
{% endif %}
</div>
<div class="match">
<div class="home">
{{match.home_name}} <img src="{{match.home_logo}}">
</div>
<div class="score">
{% if match.status_code == 0 %}
{{match.time}}
{% elif match.status_code == 17 %}
TBD
{% elif match.status_code == 1%}
{{match.home_score}} : {{match.away_score}}
{% elif match.staus_code == 11%}
{{match.home_score}} : {{match.away_score}}
{% elif match.status_code == 3 %}
{{match.home_score}} : {{match.away_score}}
{% endif %}
</div>
<div class="away">
<img src="{{match.away_logo}}">
{{match.away_name}}
</div>
</div>
</div>
{% endfor %}
{% endfor %}
</div>
{% endblock %}
def request_games(start, end):
params = (
("season_id","1511"),
("date_from",start.strftime("%Y-%m-%d")),
("date_to",end.strftime("%Y-%m-%d"))
);
headers = {
"apikey": //my api key
}
response = requests.get('https://app.sportdataapi.com/api/v1/soccer/matches', headers=headers, params=params)
r = response.json()
data = r["data"]
number = len(data)
matches = []
for i in range(number):
match = {}
match['status'] = data[i]["status"]
match['status_code'] = data[i]["status_code"]
match['minute'] = data[i]['minute']
full_date = data[i]["match_start"]
dt = datetime.strptime(full_date, '%Y-%m-%d %H:%M:%S')
match['date'] = dt.date()
match['time'] = dt.time()
match['start'] = full_date
home_team = data[i]["home_team"]
match['home_name'] = home_team['name']
match['home_logo'] = home_team['logo']
away_team = data[i]["away_team"]
match['away_name'] = away_team['name']
match['away_logo'] = away_team['logo']
stats = data[i]['stats']
match['home_score'] = stats['home_score']
match['away_score'] = stats['away_score']
matches.append(match)
matches.sort(key = lambda x: datetime.strptime(x['start'], '%Y-%m-%d %H:%M:%S'))
return matches

First thing first, once you decide to start working on pages that do not refresh but still query the server and change, one of your solutions is to start using AJAX calls to query the server for different data.
This would add a bit of code to your project.
For example, your home.html template will need to be split into two. Make sure to also add jQuery to your head.
sporty/home.html
{% extends "sporty/layout.html" %}
{% load static %}
{% block body %}
<div id="matchweek" class="box">
{% include "sporty/matchweek.html" %}
</div>
<button type="button" value="p" onclick="weekControl(this)">Previous</button>
<button type="button" value="n" onclick="weekControl(this)">Next</button>
{% comment %}
Either place weekupdate.js in /static/ or give the path within static,
best would be to have a folder for javascript in static and use 'js/weekupdate.js'
{% endcomment %}
<script src="{% static 'js/weekupdate.js' %}"></script>
{% endblock %}
and sporty/matchweek.html
{{start}},{{end}}
{% regroup matches by date as date_list %}
{% for date in date_list %}
<div class="the_date">
{{date.grouper}}
</div>
{% for match in date.list %}
<div class="match_container">
<div class="status">
{% if match.status_code == 1%}
{{match.minute}}'
{% elif match.status_code == 11%}
HT
{% elif match.status_code == 3 %}
Finished
{% endif %}
</div>
<div class="match">
<div class="home">
{{match.home_name}} <img src="{{match.home_logo}}">
</div>
<div class="score">
{% if match.status_code == 0 %}
{{match.time}}
{% elif match.status_code == 17 %}
TBD
{% elif match.status_code == 1%}
{{match.home_score}} : {{match.away_score}}
{% elif match.staus_code == 11%}
{{match.home_score}} : {{match.away_score}}
{% elif match.status_code == 3 %}
{{match.home_score}} : {{match.away_score}}
{% endif %}
</div>
<div class="away">
<img src="{{match.away_logo}}">
{{match.away_name}}
</div>
</div>
</div>
{% endfor %}
{% endfor %}
Because you will need to update the entire content of matchweek div every time.
views.py
def home(request):
if request.is_ajax():
template = 'matchweek.html'
direction = request.GET.get('dir')
if direction == 'n':
request.session['weekoffset'] += 1
elif direction == 'p':
request.session['weekoffset'] -= 1
else:
template = 'home.html'
request.session['weekoffset'] = 0
offset = request.session['weekoffset']
start = datetime.now().date() + timedelta(days=7*offset)
end = start + timedelta(days=6)
matches = request_games(start, end)
return render(request, f"sporty/{template}",{
"matches": matches,
"start": start,
"end": end
})
What you see in the function is a check if the request is coming from the AJAX call or not and determination on what to do from there.
/static/js/weekupdate.js
function weekControl(id) {
var value = id.value;
$.ajax({
url: '', // The url suffix that leads to your home function, example: '/home/'
type: "GET", // Http method
data: {'dir': value}, // The data to be sent to the server.
success: function (htmlres) { // What to do on success and response reaching back
$("#matchweek").html(htmlres);
}
});
}
What you see here is the AJAX get call to your django view with the data of 'dir' and its value. When it is returned, jQuery will change the content of the matchweek div with the new content.
I haven't tested this but let me know if this works and if you have any questions.

Related

Merging Paginator and export html to xlsx and large amount of data

I'm trying to create a way to export the HTML table to xlsx, but I have a large amount of data in the queries. So I need to use pagination with Paginator so the browser doesn't load the data all at once and end up causing TimeOut. But when applying the Paginator and exporting, it only exports what is on the current page. Any suggestions to improve this code, such as creating a loop so that it can export all pages?
View function:
def export_project_data(request, pk):
if str(request.user) != 'AnonymousUser': # só vai ter acesso se for diferente de AnonymousUser
individuals_list = Individual.objects.filter(Project=pk)
traps = Sample_unit_trap.objects.filter(Project=pk)
page = request.GET.get('page', 1)
paginator = Paginator(individuals_list, 1000)
try:
individuals = paginator.page(page)
except PageNotAnInteger:
individuals = paginator.page(1)
except EmptyPage:
individuals = paginator.page(paginator.num_pages)
path_info = request.META['PATH_INFO']
context = {
'individuals': individuals,
'pk': pk,
'traps': traps,
'header': 'export_project_data',
'path_info': path_info
}
return render(request, 'inv/index.html', context)
HTML paginator code:
<div class="table-div">
{% if individuals.has_other_pages %}
<ul class="pagination pagination-sm">
{% if individuals.has_previous %}
<li>«</li>
{% else %}
<li class="disabled"><span>«</span></li>
{% endif %}
{% for i in individuals.paginator.page_range %}
{% if individuals.number == i %}
<li class="active"><span>{{ i }} <span class="sr-only">(current)</span></span></li>
{% else %}
<li>{{ i }}</li>
{% endif %}
{% endfor %}
{% if individuals.has_next %}
<li>»</li>
{% else %}
<li class="disabled"><span>»</span></li>
{% endif %}
</ul>
{% endif %}
</div>
{% endif %}
Javascript html to xlsx code:
<script>
function html_table_to_excel(type)
{
var data = document.getElementById('data');
var file = XLSX.utils.table_to_book(data, {sheet: "Dados_Ecológicos"});
XLSX.write(file, { bookType: type, bookSST: true, type: 'base64' });
XLSX.writeFile(file, 'file.' + type);
}
const export_button = document.getElementById('export_button');
export_button.addEventListener('click', () => {
html_table_to_excel('xlsx');
});
</script>

infinite scroll working but not populating all data because of javascript call

In my web site I want to show the user ratings - for that I used the infinite scroll but I am facing one problem.
When it first loads the data before calling the <a class="infinite-more-link" href="?page={{ ratings.next_page_number }}"></a> it is showing the star with the count of vote,but when after calling the <a class="infinite-more-link" href="?page={{ ratings.next_page_number }}"></a> it is not showing the star.
my views.py
#login_required
def ratings_user(request,pk):
ratings = VoteUser.objects.filter(the_user_id=pk).order_by('-pk')
paginator = Paginator(ratings, 1)
page = request.GET.get('page')
try:
posts = paginator.page(page)
except PageNotAnInteger:
posts = paginator.page(1)
except EmptyPage:
posts = paginator.page(paginator.num_pages)
return render(request,request.session['is_mobile']+'profile/ratings.html',{'ratings':posts})
html
{% extends 'mobile/profile/base.html' %}
{% block title %}
Ratings
{% endblock %}
{% block leftcontent %}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css" rel="stylesheet">
{% endblock %}
{% block middlecontent %}
<div class="infinite-container">
{% for i in ratings %}
<div class="infinite-item">
<div class="w3-container w3-card w3-white w3-round w3-margin">
<img src="{{ i.the_user.profile.avatar.url }}" alt="Avatar" class="w3-left w3-circle w3-margin-right" style="width:40px;height:40px;border-radius:50%;">
{% with user=i.the_user.profile %}{{ user.prenom|title|truncatewords:2 }} {{ user.nom|title|truncatewords:1 }}{% endwith %}
<br>
<span class="stars" data-rating="{{ i.vote.vote }}" data-num-stars="5" ></span>
<hr class="w3-clear">
<p>
{{ i.commentaire|linebreaksbr }}
</p>
<span class="glyphicon glyphicon-user"></span> {% with user=i.the_sender.profile %}{{ user.prenom|title|truncatewords:2 }} {{ user.nom|title|truncatewords:1 }}{% endwith %}
</div>
</div>
{% endfor %}
</div>
{% if ratings.has_next %}
<a class="infinite-more-link" href="?page={{ ratings.next_page_number }}"></a>
{% endif %}
{% endblock %}
{% block rightcontent %}
{% endblock %}
{% block js %}
<script>
var infinite = new Waypoint.Infinite({
element: $('.infinite-container')[0]
});
</script>
<script>
//ES5
$.fn.stars = function() {
return $(this).each(function() {
var rating = $(this).data("rating");
var fullStar = new Array(Math.floor(rating + 1)).join('<i class="fas fa-star"></i>');
var halfStar = ((rating%1) !== 0) ? '<i class="fas fa-star-half-alt"></i>': '';
var noStar = new Array(Math.floor($(this).data("numStars") + 1 - rating)).join('<i class="far fa-star"></i>');
$(this).html(fullStar + halfStar + noStar);
});
}
//ES6
$.fn.stars = function() {
return $(this).each(function() {
const rating = $(this).data("rating");
const numStars = $(this).data("numStars");
const fullStar = '<i class="fas fa-star"></i>'.repeat(Math.floor(rating));
const halfStar = (rating%1!== 0) ? '<i class="fas fa-star-half-alt"></i>': '';
const noStar = '<i class="far fa-star"></i>'.repeat(Math.floor(numStars-rating));
$(this).html(`${fullStar}${halfStar}${noStar}`);
});
}
</script>
<script>
$(function(){
$('.stars').stars();
});
</script>
{% endblock %}
I have tried to put the <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css" rel="stylesheet"> inside the class="infinite-item" but it does not help.what might be the reason for that ? Thanks.
Since yesterday I am on this question I tried everything.
This is another user that has tried to help me here https://stackoverflow.com/a/69930878/15042684 but I did not really understand could you please help me to understand it with some code.
this is his answer:
It doesn't look like .stars() will look for new elements being added to the DOM. You should look for a callback function configuration option within Waypoint.Infinite that you can call .stars() on the new elements.
Assuming you are using waypoint's Infinite Scroll, you can use the onAfterPageLoad callback
onAfterPageLoad
Default: $.noop.
Parameters: $items.
This is a callback that will fire at the end of the request cycle, after new items have been appended to the container. It is passed one parameter, which is a jQuery object of all the items that were appended during the page load.
Note that despite using the jquery convention of $name indicates a jquery object and stating is a jquery object, in this case, trial and error shows that $items are the DOM elements, not a jquery object.
No example provided in the docs, so it will probably look something like:
<script>
var infinite = new Waypoint.Infinite({
element: $('.infinite-container')[0],
onAfterPageLoad: function(items) {
$(items).find(".stars").stars();
}
});
</script>

Conditionally add keywords to string in JavaScript

The problem is fairly complex. Let's say the input of my function is a string. The input can contain the following keywords:
"{% if some_condition %}" (Start)
"{% endif %}" (End)
"{% else if some_other_condition %}" (Optional)
"{% else %}" (Optional)
Along with these keywords, the input may also contain any other string. The conditions are basically just names of variables, so within a string before parsing, they are just strings (any valid string is acceptable as a variable name).
Here's what the function is meant to do:
Take the input and look for start and end tag pairs.
When found, look inside the contents between the start tag and end tag.
If the content contains "{% else if <any_condition> %}" OR "{% else %}", then do nothing.
Otherwise, add the "{% else %}<div class='empty'>Empty</div>" string right at the end of the content.
So for clarity, here is an example input:
{% if username %}
<p>Hello {{ username }}</p>
{% endif %}
{% if score > 10 %}
<p>You are doing great.</p>
{% else if score > 50 %}
<p>You are freaking amazing!</p>
{% endif %}
<p>This is your <strong>awesome</strong> profile page.</p>
{% if not email %}
<p>Please consider setting your email address</p>
{% endif %}
{% if balance > 0 %}
<p>You are ready to go!</p>
{% else %}
<p>No balance</p>
{% endif %}
And the output would be the following:
{% if username %}
<p>Hello {{ username }}</p>
{% else %}<div class="empty">Empty</div>{% endif %}
{% if score > 10 %}
<p>You are doing great.</p>
{% else if score > 50 %}
<p>You are freaking amazing!</p>
{% endif %}
<p>This is your <strong>awesome</strong> profile page.</p>
{% if not email %}
<p>Please consider setting your email address</p>
{% else %}<div class="empty">Empty</div>{% endif %}
{% if balance > 0 %}
<p>You are ready to go!</p>
{% else %}
<p>No balance</p>
{% endif %}
In short, if the contents inside the if block doesn't have a conditional, then the extra string is appended right before the closing tag. Hope that makes sense. I know it's a lot to ask for, but could someone just get me started with this in the right direction? I'm not very good with regex using JavaScript, and it's basically kicking my ass. Thanks for any help!
I would do like the following. Flag "g" is global, "m" is multiline and "s" is "dotall" mode which means that "." can grab "\n". I grab all the inputs, replace them, see if there's "else if" or "else" therefore replace "{% endif %}" with "{% else %}" plus what you wanted plus "{% endif %}".
input.replace(
/{% if.*?%}.*?{% endif %}/gms,
match =>
(!/(else if|else)/.test(match))
? match.replace(
/{% endif %}/,
'{% else %}<div class="empty">Empty</div>{% endif %}'
)
: match
);
/*{% if username %}
<p>Hello {{ username }}</p>
{% else %}<div class="empty">Empty</div>{% endif %}
{% if score > 10 %}
<p>You are doing great.</p>
{% else if score > 50 %}
<p>You are freaking amazing!</p>
{% endif %}
<p>This is your <strong>awesome</strong> profile page.</p>
{% if not email %}
<p>Please consider setting your email address</p>
{% else %}<div class="empty">Empty</div>{% endif %}
{% if balance > 0 %}
<p>You are ready to go!</p>
{% else %}
<p>No balance</p>
{% endif %}*/

JavaScript: unable to disable/enable an input button

I'm building a web app using the Django framework. I'm attempting to use some JavaScript to disable a button that users can press to submit a text-based review.
The JavaScript in the listing.js file looks as follows:
document.addEventListener('DOMContentLoaded', function () {
hide_submit_review_button();
});
// Hide the 'Submit Review'button until the user begins typing a review
// Prevent the user from typing more than 100 characters
function hide_submit_review_button() {
var submit_review_button = document.getElementById('submit-review-button');
if (submit_review_button !== null) {
document.getElementById('submit-review-button').disabled = true;
document.getElementById('review-contents').onkeyup = () => {
if ((document.getElementById('review-contents').value.length > 0 &&
document.getElementById('review-contents').value.length <= 100 )) {
document.getElementById('submit-review-button').disabled = false;
} else {
document.getElementById('submit-review-button').disabled = true;
}
};
}
}
In my listing.html file, I identify the review-contents and submit-review-button. Here's the code:
{% extends "layout.html" %}
{% load static %}
{% block body %}
{% if user.is_authenticated %}
<form action="{% url 'review' listing.id %}" method="POST">
{% csrf_token %}
<input type="text" class="form-control" name="review" id="review-contents" placeholder="Write a review...">
<input class="btn btn-primary mt-1" type="submit" id="submit-review-button" value="Submit Review">
</form>
{% endif %}
{% endblock %}
{% block script %}
{% if user.is_authenticated %}
{{ block.super }}
<script src="{% static 'listing.js' %}"></script>
{% endif %}
{% endblock %}
An example of a page where the button appears is: http://127.0.0.1:8000/listings/7
And, here is what the urls.py file looks like (if it matters):
urlpatterns = [
path('', views.index, name='index'),
path('listings/<int:listing_id>', views.listing, name='listing'),
]
Can anyone see why this button doesn't disable?
Thanks!
The problem resulted from not having the following in the header tag in the layout.html file:
{% block script %}
{% endblock %}

NoReverseMatch at /blog/ in django 2.0

am getting this error trying to set a blog category page
NoReverseMatch at /blog/
Reverse for 'category_detail' with arguments '('',)' not found. 1 pattern(s) tried: ['blog\/category\-detail\/(?P[-a-zA-Z0-9_]+)$']
Here is my url.py
from django.urls import path,include
from .import views
urlpatterns = [
path('blog/',views.post_list,name="post_list"),
path('blog/post-detail/<slug:slug>',views.post_detail,name="post_detail"),
path('blog/category-detail/<slug:slug>',views.category_detail,name="category_detail"),
]
views.py
from django.shortcuts import render,get_object_or_404
from.models import Post,Category
# Create your views here.
def post_list(request):
object_list=Post.objects.all()
context={
'object_list': object_list,
}
return render(request,"blog.html",context)
def post_detail(request,slug=None):
post=get_object_or_404(Post,slug=slug)
context={
'post':post,
}
return render(request,"post_detail.html",context)
def category_detail(request,slug=None):
category=get_object_or_404(Category,slug=slug)
post=Post.objects.filter(category=category,status='Published')
context={
'category':category,
'post':post,
}
return render(request,"category_detail.html",context)
blog.html
{% for obj in object_list %}
{% if obj.status == 'Published' %}
<article>
<div class="embed-responsive embed-responsive-16by9">
<img src="images/blog1.jpg" alt="" />
</div>
<div class="post-content">
<h2>{{obj.title}}</h2>
<div>
{{obj.created}} Author {{obj.user}} <h4>{{obj.Category}}</h4>
<hr/>
<p>{{obj.body}}</p>
<a class="mtr-btn button-navy ripple" href= "{% url 'post_detail' obj.slug %}">Continue reading →</a><br>
</div>
</article>
{% endif %}
{% endfor %}
category_detail.html
{% extends "base.html" %}
{% load static %}
{% block seo_title %}{{category.seo_title}}{% endblock %}
{% block seo_description %}{{category.seo_description}}{% endblock %}
{% block Content %}
<h2>{{category.title}}</h2>
<p>{{category.description}}</p>
{% for item in post %}
{{item.title}}
{{item.body|truncatechars:50}}
{% endfor %}
{% endblock Content %}
NOTE THE OTHER VIEWS.PY ARE WORKING FINE JUST THE category_detail function
As the error said, the argument is missing here. Maybe you need to change {% url 'category_detail' slug=post.Category.slug %} with {% url 'category_detail' slug=obj.category.slug %} because I do not see any post variable reference in the blog.html template.
update
You have not shared your Model codes, but I am assuming your Post Model has Foreign Key to Category Model and it looks like Category=models.ForeignKey(Category). So you need to update the view like this:
def category_detail(request,slug=None):
category=get_object_or_404(Category,slug=slug)
post=Post.objects.filter(Category=category,status='Published')

Categories