I've come across a strange bug in my Django application that has baffled me for the last few days.
Basically one of my dynamic templates stops rendering on the staging server. Everything works fine locally and when push to my staging server all seems fine however, after clicking around a few pages and then navigating back to one of the pages with this template it has suddenly disappeared. If i restart the supervisor processes the template reappears but then disappears again after navigating to other pages.
The weird thing is it's not consistent when it disappears. Sometimes it takes a few times navigating to other pages to disappear.
The template itself has dynamically generated content however even the container divs are not appearing so this leads me to believe that this is not a context error.
Another point about it is that if i stop my supervisor processes and run ./manage.py runserver instead, the problem doesn't seem to happen.
The things i've tried:
I've used django-template-repl to load the url and view the context
the template receives, all the data i expect to be rendered appears to be there.
Any help would be much appreciated, any tips on where to look next.
the view thats rendered:
def list(request):
projects = Project.objects.active()
order = request.GET.get('order', 'popular')
projects = projects.ordered_by(order)
search_q = request.GET.get('search_q')
if search_q:
project_query_terms = ['title', 'short_description', 'who_description']
username_query_terms = [
'user__userprofile__first_name',
'user__userprofile__last_name']
project_query = create_query(search_q, project_query_terms)
username_query = create_query(search_q, username_query_terms) & Q(
name_is_visible__exact=True)
projects = projects.filter(project_query | username_query)
if request.GET.get('sector'):
projects = projects.filter(sector__id=request.GET['sector'])
if request.GET.get('location'):
projects = projects.filter(
location=request.GET['location'],
location_public=True)
form = ProjectFilterForm
form = (form(request.GET, projects=projects)
if request.GET else form(projects=projects))
paginator = Paginator(projects, 9)
page = request.GET.get('page')
try:
projects = paginator.page(page)
except PageNotAnInteger:
projects = paginator.page(1)
except EmptyPage:
projects = paginator.page(paginator.num_pages)
amounts_by_project = Need.objects.amounts_by_project()
amounts_raised_by_project = MoneyDonation.objects.amounts_raised_by_project()
context = {
'body_id': 'project-list',
'projects': projects,
'form': form,
'order': order,
'amounts_by_project': amounts_by_project,
'amounts_raised_by_project': amounts_raised_by_project,
}
template = "_projects-content" if request.is_ajax() else "projects"
template = "projects/" + template + ".html"
return render(request, template, context)
the projects-projects.html template
{% extends 'projects/__base.html' %}
{% load image_tags staticfiles projects %}
{% block content %}
{% include 'projects/_projects-content.html' %}
{% endblock %}
{% block scripts %}
<script>
FCC.drawDonuts('.col-project');
jQuery(function updateWithFilters($){
function queryWithParams() {
var getParams = '';
$('#project-filter-form select').each(function(i) {
getParams += (i === 0) ? '?' : '&';
getParams += this.name + '=' + $(this).children('option:selected')[0].value;
});
$.get({% url 'projects' %} + getParams, function(res){
$("#projects-content").html(res);
updateWithFilters($);
FCC.drawDonuts('.col-project');
});
}
$('#project-filter-form select').change(function(e){
queryWithParams();
});
$('#reset-filters').click(function(e) {
e.preventDefault();
$('#project-filter-form select option:selected').each(function(){
$(this).prop('selected', false);
queryWithParams();
});
});
});
</script>
{% endblock %}
the _projects-content.html
<div id='projects-content'>
{% include 'projects/_projects-filters.html' %}
{% include 'projects/_projects-projects.html' %}
</div>
the _projects-projects.html
<section class="section hp-projects">
<div class="container">
<div class="row">
<div class="col-xs-12 sub-header">
<sub-header>
<h2>Browse all projects</h2>
</sub-header>
</div>
{% if not projects %}
<p>No projects match your search.</p>
{% endif %}
{% for project in projects %}
<div class="col-xs-12 col-sm-6 col-md-4">
{% include '_project-thumbnail.html' %}
</div><!--col-->
{% endfor %}
</div><!--row-->
<div class="row">
<div class="col-xs-12">
{% with projects as collection %}
{% include 'pagination.html' %}
{% endwith %}
</div>
</div>
</div><!--container-->
</section>
_project-thumbnail.html
{% load total staticfiles projects %}
<div class="project-container">
<header>
<a href="{% url 'project_detail' project.id %}" class="project">
<h5 class="project-title">{{ project.title }}</h5>
</a>
</header>
<figure>
<div class="img-wrap">
<a href="{% url 'project_detail' project.id %}" class="project">
<img class="img-responsive" src="{{ MEDIA_URL }}{{ project.profile_image }}" alt="project image" />
</a>
</div>
<figcaption>{{ project.short_description }}</figcaption>
</figure>
<div class="row three-donuts">
<div class="col-xs-6">
{% include "projects/_project-donate-thumbnail.html" %}
</div>
<div class="col-xs-6">
{% include "projects/_project-volunteer-thumbnail.html" %}
</div>
</div>
</div><!--box-container-->
_project-donate-thumbnail.html is the template that is not rendering after navigating to another page
here's the content of the file but as i said nothing inside this renders once you navigate away from this page after a fresh server restart.
however the _project-volunteer-thumbnail stays completely fine. It is just the donate-thumnail that disappears.
_project-donate-thumbnail.html
<div id="project-{{ project.id }}-donate" class="col-project this-project-donate">
<span class="percentage">
{% if project.donate_aim %}
{{ project.donate_percent }}%
{% endif %}
</span>
</div>
<div class="equation">
£{{ project.donate_status }}
<span class="desired-amount">/ £{{ project.donate_aim }}</span>
</div>
<div class="button-link-wrapper">
{% if project.donate_aim %}
donate
{% else %}
<span class="button graph-button donate deactivated">donate</span>
{% endif %}
</div>
again any tips on where to look next would be helpful
Related
I'm receiving a list of messages, each message has a tag. It may be "info", "warning", "danger" and "spreadsheet". I need to display the first three first and then spreadsheet's but in collapsible if there are more than 3 of them.
So for all messages BUT spreadsheet the template is:
<div class="row banner">
<ul class="banner-message">
{% for message in messages %}
<li{% if message.tags %} class="banner-message--{{ message.tags }}" {% else %} class="banner-message--info" {% endif %}>
<div class="message-content">
<p>{{ message|safe }}</p>
<span class="close">×</span></div>
</li>
{% endfor %}
</ul>
</div>
For tags with "spreadsheet" tag the template is:
<div class="row banner">
<ul class="banner-message">
{% for message in messages %}
<li class="{% if forloop.counter >= 3 %}collapsible_content{% endif %} banner-message--info">
<div class='message-content'>
<p>{{ message|safe }}</p>
<span class="close">×</span>
</div>
</li>
{% endfor %}
{% if messages|length > 3 %}
<button id="show_more" class="btn btn-secondary collapsible_button">Show all</button>
{% endif %}
</ul>
</div>
Where button is shown for spreadsheet messages if there are more then 3 of them and shows all of them on click.
Problem is that I receive these messages in one array and I have no guarantee that they won't be all mixed up in different order.
I need to sort them somehow with message.tags to two separate arrays? Or use some smart if but I cannot figure out how to achieve that in the template. Could you please help?
EDIT:
After comment below I am trying to sort the messages in the view.py
m = messages.get_messages(request)
if len(m) > 0:
context['msgs'] = m.filter(tags__in=['info', 'warning', 'danger']).order_by('tags')
context['spreadsheet'] = m.filter(tags='spreadsheet')
m is FallbackStorage object and it doesn't have filter method. m.objects and FallbackStorage.objects do not work either (for the same reason: has no attribute 'objects'). I've tried to do all above with messages straight away (without the get_messages method) but the result is the same. How to filter this correctly?
get_messages is a utility function and not a class method. You can read more about how to use it in the docs here.
In your view you can do:
from django.contrib.messages import get_messages
...
message_list = get_messages(request)
messages = []
spreadsheet_messages = []
for message in message_list:
tags = message.tags
if ("info" in tags) or ("warning" in tags) or ("danger" in tags):
messages.append(message)
if "spreadsheet" in tags:
spreadsheet_messages.append(message)
context["messages"] = messages
context["spreadsheet"] = spreadsheet_messages
...
you can divide messages in two different querysets. On your views.py:
context['messages'] = messages.filter(tags__in = ['info','warning','danger']).order_by('tags')
context['spreadsheet'] = messages.filter(tags = 'spreadsheet')
On your template:
<div class="row banner">
<ul class="banner-message">
{% for message in messages %}
<li{% if message.tags %} class="banner-message--{{ message.tags }}" {% else %} class="banner-message--info" {% endif %}>
<div class="message-content">
<p>{{ message|safe }}</p>
<span class="close">×</span></div>
</li>
{% endfor %}
</ul>
</div>
<div class="row banner">
<ul class="banner-message">
{% for message in spreadsheet %}
<li class="{% if forloop.counter >= 3 %}collapsible_content{% endif %} banner-message--info">
<div class='message-content'>
<p>{{ message|safe }}</p>
<span class="close">×</span>
</div>
</li>
{% endfor %}
{% if messages|length > 3 %}
<button id="show_more" class="btn btn-secondary collapsible_button">Show all</button>
{% endif %}
</ul>
</div>
EDIT:
According to you comment. I suggest to try something like this:
msgs = []
spreadsheet = []
m = messages.get_messages(request)
if len(m) > 0:
for mensaje in m:
#TRY THE THREE OPTIONS BECAUSE I DON'T KNOW THE MESSAGES' STRUCTURE#
if 'spreadsheet' in message['tags']:
#if 'spreadsheet' in message.tags:
#if 'spreadshhet' in message.tags():
spreadsheet.append(mensaje)
else:
msgs.append(mensaje)
context['msgs'] = msgs
context['spreadsheet'] = spreadsheet
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>
I'm trying to implement infinite scroll with django and jquery(Waypoint).
I have a ListView with a pagination of 5, but when waypoint loads second page, AJAX requests are no longer performed so I added the AJAX function on the onAfterPageLoad which helps bring back AJAX function to the newly loaded page.
That's fine, but then it introduces a bug to my code making the page loaded initially (First Page) no longer perform AJAX functions very well. It makes AJAX on the first page run 3 times if I just loaded a third page and makes AJAX on the second page run twice and so on depending on the number of pages loaded already.
I don't know if there are any cool ways to achieve this because I tried using just jquery without waypoint, but still get same errors as I get when using just waypoint making it an error. This is kinda tricky so far.
{% extends "base.html" %}
{% block content %}
{% load static %}
<div class="container" style="max-width:700px">
<div class="px-3 pt-md-5 pb-md-4 mx-auto text-center">
<h1 class="display-4">All Groups</h1>
<p class="lead">List of groups</p>
</div>
<div class="">
<div class="row infinite-container">
{% for group in groups %}
<div class="col-md-12 infinite-item">
<!-- <img class="img-fluid" src="https://picsum.photos/700"> -->
<a class="text-dark" href="{{ group.get_absolute_url }}">
<div class="card mb-4 box-shadow">
<div class="card-body">
<h2 style="font-size:18px;font-weight:bold;min-height:42px;">
{{group.name|truncatechars:50}}</h2>
<div class="d-flex justify-content-between align-items-center">
<small class="text-muted">{{group.owner}}</small>
<small class="text-muted">{{group.date_created}}</small>
</div>
<p><a class='follow-btn' data-href='{{ group.get_group_follow_api_url }}'
data-likes='{{ page.likes.count }}'
href='{{ group.get_group_follow_url }}'>{{ group.follows.count }}
{% if request.user.profile in group.follows.all %}
Unfollow
{% else %}
Follow
{% endif %}
</a></p>
</div>
</div>
</a>
</div>
{% endfor %}
</div>
<!-- Comment for loading spinner -->
{% comment %}
<div class="d-flex d-none position-fixed" style="top:35vh;left:46vw">
<button class="btn btn-primary loading">
<span class="spinner-border spinner-border-sm"></span>
Loading...
</button>
</div>
{% endcomment %}
<!-- End of comment for loading spinner -->
<div class="row">
<div class="col-12">
{% if page_obj.has_next %}
<a class="infinite-more-link" href="?page={{ page_obj.next_page_number }}"></a>
{% endif %}
</span>
</div>
</div>
</div>
</div>
<script src="{% static "js/jquery.waypoints.min.js" %}"></script>
<script src="/static/js/infinite.min.js"></script>
<script>
var infinite = new Waypoint.Infinite({
element: $('.infinite-container')[0],
offset: 'bottom-in-view',
onBeforePageLoad: function () {
$('.loading').show();
},
onAfterPageLoad: function () {
$('.loading').hide();
$(document).ready(function(){
function updateText(btn, newCount, verb){
btn.text(newCount + " " + verb)
}
$(".follow-btn").click(function(e){
e.preventDefault()
var this_ = $(this)
var badge = this_.find('.unlike-update')
var followUrl = this_.attr("data-href")
var followCount = parseInt(this_.attr("data-follows")) | 0
var addFollow = followCount + 1
var removeFollow = followCount - 1
if (followUrl){
$.ajax({
url: followUrl,
method: "GET",
data: {},
success: function(data){
console.log(data)
var newFollows;
if (data.followed){
// updateText(this_, addLike, "Unlike")
// updateText(this_, data.likescount, badge)
updateText(this_, data.followscount, "Unfollow")
} else {
// updateText(this_, removeLike, "Like")
updateText(this_, data.followscount, "Follow")
// remove one like
}
}, error: function(error){
console.log(error)
console.log("error")
}
})
}
})
})
}})
</script>
{% include 'group/snippets/group_follow.html' %}
{% endblock %}
class GroupListView(LoginRequiredMixin, ListView):
model = Group
paginate_by = 5
context_object_name = 'groups'
template_name = 'group/group_list.html'
ordering = ['-date_created']
I am using creating a Django application where I want to list down different products in the Django template by JS AJAX way. I just wonder how I can render it through js -
So I have two function one is for create the template for each product and here if you notice I have if else condition-
function createTemplate(product) {
return `<div class="col-12 col-lg-3 col-md-6 col-sm-6 mb-45 hot sale">
<div class="single-product">
<div class="single-product__image">
<a class="image-wrap" href="{{product.get_absolute_url}">
<img src="{% if product.image %}
{{ product.image.url }}
{% else %}
{% static 'img/no_image.png' %}
{% endif %}" class="img-fluid" alt="">
</a>
<div class="single-product__floating-icons">
<span class="wishlist">
{% if product.id in wishlist_item_check %}
<a href="javascript:void(0)" id="wishlistalready_${product.id}" data-tippy="Already Exists"
data-tippy-inertia="true" data-tippy-animation="shift-away" data-tippy-delay="50"
data-tippy-arrow="true" data-tippy-theme="sharpborder" data-tippy-placement="left"><i
class="ion-android-favorite" style="color:red"></i></a>
{% endif %}
</span>
</div>
</div>
</div>
</div>`;
}
And another function to render all the products in a div with specific id (for example her id="products>"
function renderProducts(products) {
const template =
products.length === 0
? `
<p>No matching results found.</p>
`
: products.map((product) => createTemplate(product)).join('\n');
$('#products').html(template);
}
Lastly I am rendering all the products in a div
<div class="row" id="products">
Do let me know if we any better and easy way of doing the same.
I'm trying to make an e-commerce site with Django and I'm stuck at my home page not being able to display which products are already in my cart. I am pretty sure I haven't got the JavaScript right but I'm an absolute beginner to web-dev so could also be the Python.
This is my home :
{% extends 'base.html' %}
{% block content %}
{% for pett in pets.all %}
<div class="row pt-3" onfocus="func(pett)">
<div class="col-8" onclick="window.location='{% url 'detail' pett.id %}';" style="cursor: pointer">
<h2>{{ pett.name }}</h2>
<h4>{{ pett.breed }}</h4>
</div>
<div class="col-4">
<a id="link{{pett.id}}" href= "javascript:{document.getElementById('add{{pett.id}}').submit()}"><button id="button{{pett.id}}" class='btn btn-primary btn-xs'><h1>Add to cart +</h1></button></a>
</div>
</div>
<form id="add{{pett.id}}" method="POST" action="{% url 'add_to_cart' pett.id %}">
{% csrf_token %}
<input type="hidden"/>
</form>
{% endfor %}
<script type="text/javascript">
function func(pett){
if(cart.objects.filter(pet=pett).count()>0) {
document.getElementById('button{{pett.id}}').innerHTML = "Added ✔︎";
document.getElementById('link{{pett.id}}').href = "";}
}
</script>
{% endblock %}
This is the call to my home method :
from django.shortcuts import render
def home(request):
pets = Pet.objects
if request.user :
cart = Cart.objects.get(buyer=request.user)
else:
cart=NULL
return render(request,'pets/home.html',{'pets':pets,'cart':cart})
This is my cart model :
from django.db import models
class Cart(models.Model):
buyer = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
pet = models.ManyToManyField('pets.Pet')
Everything else is working perfectly.