I have been building a website where each essay may have a video object. For some reason, this error in Django continues to persist and I cannot understand where it comes from and how to fix it.
The error related to views.py
Exception has occurred: IndexError
pop from empty list
File "C:\foo\project\bar\views.py", line 65, in index_dev
return render(
IndexError: pop from empty list
views.py:
def index_dev(request):
essay = EssayCls.objects.all().order_by('-date')[:3]
video_obj = VideoObject.objects.all()
user_agent = get_user_agent(request)
context = {
'text_content': essay,
'show_text_content': True,
'video_obj': video_obj
}
if user_agent.is_pc:
return render(
request,
'dev/index_page.html',
context
)
models.py:
...
from embed_video.fields import EmbedVideoField
...
class VideoObject(models.Model):
video_item_url = EmbedVideoField() # same like models.URLField()
added_date = models.DateTimeField(auto_now_add=True, null=True)
title_video = models.CharField(max_length=200)
class Meta:
ordering = ['-added_date']
class EssayCls(models.Model):
title = models.CharField(max_length=200)
organizer_email = models.EmailField(null=True)
date = models.DateField(null=True)
slug = models.SlugField(unique=True, db_index=True)
description = models.TextField(validators = [MinLengthValidator(10)])
details = models.TextField()
image = models.ImageField(upload_to='images')
video = models.ForeignKey(VideoObject, blank=True, null=True, on_delete=models.SET_NULL, related_name="video_obj")
# video = models.ManyToManyField(VideoObject, blank=True, related_name="video_obj") # Many-to-many relationship
language = models.ForeignKey(ProgLang, null=True, on_delete=models.SET_NULL)
guest = models.ManyToManyField(SendMeMessage, blank=True)
tags = models.ManyToManyField(Tag)
template.html:
{% for essay_item in text_content %}
<div class="contact-details">
<h3>{{ title}}</h3>
<p>{{ essay_item.description|convert_markdown|safe }}</p>
<br /><br />
<p>Does this video work? check below:</p>
{% comment %} {% for vid in video_obj %}
<p>{% video vid.video_item_url %}</p>
{% endfor %} {% endcomment %}
{% if essay_item.video %}
{% video %}
{{ essay_item.video.video_item_url }}
{% endvideo%}
{% endif %}
</div>
{% endfor %}
I'm able to display all videos from DB everywhere (i.e. all videos from DB in every essay) with:
{% for vid in video_obj %}
<p>{% video vid.video_item_url %}</p>
{% endfor %}
But, even with {% if video.video_item_url %} I'm unable to display a particular video/s that is/are linked with one-to-many relationship to my EssayCls class. You can see my attempt in template.html (at the end):
{% if essay_item.video %}
{% video %}
{{ essay_item.video.video_item_url }}
{% endvideo%}
{% endif %}
I thought that the error is related to the fact that not all essays have a video i.e. that the key for an essay that doesn't have a video is none/empty. So I did this Boolean comparison hoping it will fix it, but it doesn't. The error is the same with and without this comparison.
UPDATE:
At this point, I can access a particular essay(dev/article-content_pc_tablet.html) with connected videos by:
class MyEssaysView(View):
def get(self, request, slug):
user_agent = get_user_agent(request)
selected_essay = EssayCls.objects.get(slug=slug)
user_feedback = UserFeedback()
context = {
'essay_found': True,
'essay_all': selected_essay,
'post_tags': selected_essay.tags.all(),
'form': user_feedback,
}
if request.method == 'GET':
if user_agent.is_pc:
return render(
request,
'dev/article-content_pc_tablet.html',
context
)
article-content_pc_tablet.html:
<p>{% video essay_all.video.video_item_url %}</p>
As I said, this part works.
The difference between those two views and pages (the one with an error and another one that works) is the fact that in my class for a single essay MyEssaysView in views.py I have selected_essay = EssayCls.objects.get(slug=slug). On the other hand in index_dev function, I have essay = EssayCls.objects.all().order_by('-date')[:3]. It is so because on my index_dev page I have several essays displayed from model EssayCls. I don't know how can I display adequate esay list on my "index page" with adequate video that is related to the particular essay from that list. All I understand from that error is the fact that the process tries to find those videos but has the wrong logic it hit an empty list.
Try to do something like this?
{% if essay_item.video %}
{% if essay_item.video.video_item_url %}
{% video %}
{{ essay_item.video.video_item_url }}
{% endvideo%}
{% else %}
<p>No video</p>
{% endif %}
{% else %}
<p>No video</p>
{% endif %}
I found the solution for this bug. Inspired by this and my own solution for a similar issue I had in another template. There are a few bugs in the template's for loop.
First of all, I should choose text_content from context instead of video_obj. With that, I am able to display a particular video for a particular essay. Next, having a relation one-to-many I can simply loop that in the template using the proper syntax:
{% for vid in text_content %}
<p>{% video vid.video.video_item_url %}</p>
{% endfor %}
But, in the meantime, I realized that it's a good idea to have the possibility to have several videos that can be added to several essays (*I added this part to the class EssayCls in my question: # video = models.ManyToManyField(VideoObject, blank=True, related_name="video_obj") # Many-to-many relationship). Having that in mind I needed to set up my main page in such a way that for each essay's div I needed to be able to display all videos from many-to-many relations.
To do that the proper syntax should include the chained part .all because the essay can have a query set of multiple videos that have to be handled. So I need to loop over text_content from EssayCls (which has a video column which is in many-to-many relation with VideoObject), after that I need to chain the iterator with the video column, then with all (because there may be more than one video) then with a link to a particular video from VideoObject class i.e. video_item_url be:
{% for vid in text_content %}
<p>{% video vid.video.all.video_item_url %}</p>
{% endfor %}
Of course in this case the column video = models.ForeignKey(VideoObject, blank=True, null=True, on_delete=models.SET_NULL, related_name="video_obj") in EssayCls class should be hashed or deleted.
Related
I have a web page with essays. Each essay (on its own sub-page) can have multiple videos from another model (linked with many-to-many relations).
When I had - in the past - one video in a subpage with a particular essay (with a different column related to the video, in the same model: 'one-to-many model', now hashed in my models.py) then I used to be able to invoke the video in my essay page with:
<p>{% video essay_videos.video.video_item_url %}</p>
EDIT: *The code above just prove that library embed-video works, so all settings are ok. Now, this code is no longer in use - hashed in models.py and in my template. All code below is related to my problem and IS NOT related to <p>{% video essay_videos.video.video_item_url %}</p>.
Right now "behind" essay_videos there is a different column in models.py with many-to-many relation that connects the "video" model with the "essay" model.*
But now when I try to invoke a list of videos, with this new code:
<ul>
{% for vid in essay_videos %}
<li>{{ vid.video_item_url }}</li>
{% endfor %}
</ul>
I'm getting a list of URLs - that's ok, but, when I try to use the embed-video tag (like before):
<ul>
{% for vid in essay_videos %}
<li>{% video %}{{ vid.video_item_url }}{% endvideo %}</li>
{% endfor %}
</ul>
or
<ul>
{% for vid in essay_videos %}
{% video %}
<li>{{ vid.video_item_url }}</li>
{% endvideo %}
{% endfor %}
</ul>
or
<ul>
{% video %}
{% for vid in essay_videos %}
<li>{{ vid.video_item_url }}</li>
{% endfor %}
{% endvideo %}
</ul>
...In all cases, I've got Exception Type: IndexErrorm, Exception Value: pop from empty list
My models.py:
...
from embed_video.fields import EmbedVideoField
...
class VideoObject(models.Model):
video_item_url = EmbedVideoField() # same like models.URLField()
added_date = models.DateTimeField(auto_now_add=True, null=True)
title_video = models.CharField(max_length=200)
class Meta:
ordering = ['-added_date']
class EssayCls(models.Model):
title = models.CharField(max_length=200)
organizer_email = models.EmailField(null=True)
date = models.DateField(null=True)
slug = models.SlugField(unique=True, db_index=True)
description = models.TextField(validators = [MinLengthValidator(10)])
details = models.TextField()
image = models.ImageField(upload_to='images')
# video = models.ForeignKey(VideoObject, blank=True, null=True, on_delete=models.SET_NULL, related_name="video_obj") # One-to-many relationship
video = models.ManyToManyField(VideoObject, blank=True, related_name="video_obj") # Many-to-many relationship
language = models.ForeignKey(ProgLang, null=True, on_delete=models.SET_NULL)
guest = models.ManyToManyField(SendMeMessage, blank=True)
tags = models.ManyToManyField(Tag)
My views.py:
class MyEssaysView(View):
def get(self, request, slug):
user_agent = get_user_agent(request)
selected_essay = EssayCls.objects.get(slug=slug)
user_feedback = UserFeedback() # handling form submission 3.18.20
context = {
'essay_found': True,
'essay_all': selected_essay,
'post_tags': selected_essay.tags.all(),
'comments': selected_essay.comments.all().order_by("-id"),
'essay_videos': selected_essay.video.all()
}
if request.method == 'GET':
if user_agent.is_pc:
return render(
request,
'dev/article-content_pc_tablet.html',
context
)
How to solve it? The problem is with DTL syntax, or somewhere else (views/models)?
EDIT:
{% video %} tag is related to embed-video library.
The traceback of the exception:
The problem was with the syntax in for loop.
A proper way of defining a list of videos from the context provided by a class-based view that uses two models that have a relation many-to-many with the embed-video library is:
<ul>
{% for vid in essay_videos %}
<li>{% video vid.video_item_url %} </li>
{% endfor %}
</ul>
The rest of the code was fine.
I have 2 Django models Review and Item that I am working with. I want to see if the user has already reviewed the item. If yes he sees the review score. if no he sees the button to review the item
I have the below Review model
class Review (models.Model):
review_from = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='review_from')
review_for = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='review_for')
item = models.ForeignKey(OrderItem, related_name='items')
Defining the variables in the view context (pseudocode)
admin = User.objects.get(username="admin")
admins_reviews = Review.objects.filter(review_from__username = "admin")
Below is my template
{% for item in buyers_items %}
{% for review in buyers_review%}
{% if review.item.id == item.id %}
<button class="text-success">Your rating<br/><b>{{review.ratings}}/10</b></button>
{% else %}
<a href="{% url ... %}">
<button>Leave Review</button>
</a>
{% endif %}
{% endfor %}
{% endfor %}
If I do this I get a below error
How can I overcome this problem.
View
from django import template
register = template.Library()
class OrderHistory(LoginRequiredMixin, ListView):
model = Order
template_name = 'order/order_list.html'
def get_context_data(self, **kwargs):
context = super(OrderHistory, self).get_context_data()
context['order_details'] = Order.objects.filter(emailAddress=self.request.user.email)
context['order_items'] = OrderItem.objects.filter(order__emailAddress=self.request.user.email)
context['buyers_review'] = Review.objects.filter(review_from=self.request.user)
print(context['buyers_review'])
return context
Custom Tag
#register.filter()
def review_bought_items(order_items, buyers_review):
return buyers_review.filter(item__in=order_items).exists()
Based on what I see in your templates, you could do it simpler with a tag filter or in your view side. Let's go with a custom tag:
#register.filter
def review_bought_items(buyers_items,buyers_review):
return buyers_review.filter(item__in=buyers_items).exists()
Now in the templates you could do
<!-- load the tag -->
{% load file %}
{% if buyers_items|review_bought_items:buyers_review %}
<button class="text-success">Your rating<br/><b>{{review.ratings}}/10</b></button>
{% else %}
Leave Review
{% endif %}
The issue is that you are iterating over all buyers_reviews. In this particular case, you have 2 buyer reviews, one for the current item and one for a different one.
First iteration will evaluate to False the first condition and it will display all the Leave Review button and the 2nd iteration will evaluate it to True and display the "Your rating" block.
If you don't want to move all the logic on the backend, maybe make us of a template tag in order to filter the reviews based on item.id
I have two models with one having a foreign key to the other as such:
Models:
class WhoAmI(models.Model):
name = models.CharField(max_length=200)
company = models.CharField(max_length=200)
def __str__(self):
return self.name
class SolarClient(models.Model):
name = models.CharField(max_length=200)
client_owner = models.ForeignKey(WhoAmI, on_delete=models.CASCADE, related_name='solarclients')
addr = models.CharField(max_length=200)
city = models.CharField(max_length=200)
state = models.CharField(max_length=200)
email = models.EmailField()
I am trying to simply display an html table showing each client a salesperson has, with the salesperson listed first with a table of clients below their name.
The only way I could figure out how to do this was to create a dictionary using the code shown below.
class Homeowners(DetailView):
def get(self, request, **kwargs):
salespersons = WhoAmI.objects.all()
homeowners = SolarClient.objects.all().order_by("client_owner") #the name 'objects' is the Manager
rangers = {}
for e in salespersons:
k = salespersons.get(id = e.id)
v = k.solarclients.all()
rangers[k] = v
return render(request, 'homeowners.html', {'homeowners': homeowners, 'salespersons': salespersons, 'rangers': rangers })
I then iterate over the dictionary using:
{% for key, values in rangers.items %}
... display salesperson
{% if values %}
{% for v in values %}
.... display clients
{% endfor %}
{% else %}
... display "No Clients"
{% endif %}
{% endfor %}
Is there a more efficient way to do this? It seems silly to put the data into a dictionary to display it, but after many, many hours of trying different methods, this is the only way I could display the data.
thanks for any suggestions.
views.py
class Homeowners(DetailView):
def get(self, request, **kwargs):
salespersons = WhoAmI.objects.all()
return render(request, 'homeowners.html', {'salespersons': salespersons })
html:
{% for sales in salespersons %}
{% for client in sales.solarclients.all %}
------ Print Client
{% empty %}
---- Client not exist
{% endfor %}
{% endfor %}
There is a nice handy template filter called regroup built in Django which does exactly what you're looking for.
# views.py
homeowners = SolarClient.objects.all().order_by("client_owner").select_related('client_owner')
return render(request, 'homeowners.html', {'homeowners': homeowners})
# homeowners.html
{% regroup homeowners by client_owner as home_owners_list %}
<ul>
{% for client_owner in home_owners_list %}
<b>{{ client_owner.grouper }}</b>
<ul>
{% for client in client_owner.list %}
<li>{{ client.name }}</li>
{% endfor %}
</ul>
{% endfor %}
</ul>
The select_related method is just for performance improvement and omitting it wouldn't affect functionality.
I have a lot of duplicated queries (in django debug toolbar) when i load my menu tabs, im sure i can optimize this but don't find the good way.
Models :
class Categorie(models.Model):
name = models.CharField(max_length=30)
visible = models.BooleanField(default = False)
def __str__(self):
return self.nom
def getscateg(self):
return self.souscategorie_set.all().filter(visible = True)
class SousCategorie(models.Model):
name = models.CharField(max_length=30)
visible = models.BooleanField(default = False)
categorie = models.ForeignKey('Categorie')
def __str__(self):
return self.name
def gettheme(self):
return self.theme_set.all().filter(visible = True)
class Theme(models.Model):
name = models.CharField(max_length=100)
visible = models.BooleanField(default = False)
souscategorie = models.ForeignKey('SousCategorie')
def __str__(self):
return self.name
Views :
def page(request):
categs = Categorie.objects.filter(visible = True)
return render(request, 'page.html', locals())
Templates :
{% for categ in categs %}
<li>
{{categ.name}}
<ul>
{% for scateg in categ.getscateg %}
<li>
{{scateg.name}}
<ul>
{% for theme in scateg.gettheme %}
<li>{{ theme.name }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
</li>
{% endfor %}
I have look at prefetch_related but only work if i want load Categorie from SousCategorie and SousCategorie from Theme, so if i understand i need the contrary of this...
Solved !
If it can help :
from .models import Categorie, SousCategorie, Theme, SousTheme
from django.db.models import Prefetch
pf_souscategorie = Prefetch('souscategorie_set', SousCategorie.objects.filter(visible=True))
pf_theme = Prefetch('souscategorie_set__theme_set', Theme.objects.filter(visible=True))
pf_soustheme = Prefetch('souscategorie_set__theme_set__soustheme_set', SousTheme.objects.filter(visible=True))
categs = Categorie.objects.filter(visible=True).prefetch_related(pf_souscategorie, pf_theme, pf_soustheme)
and call like this in the template :
{% with currscat=categ.souscategorie_set.all %}
{% with currth=souscateg.theme_set.all %}
{% with currsth=theme.soustheme_set.all %}
Bye
In Django every time you evaluate a new queryset a query is executed, so you need to reduce the number of queryset being used. Here is what is happening:
You create a queryset Categorie.objects.filter(visible=True) and it is passed into the view layer, there the first query is executed at the this tag {% for categ in categs %}
Inside the loop, for every category you're calling a method categ.getscateg which returns a new queryset return self.souscategorie_set.all().filter(visible = True), this queryset will be executed at the second loop in your template {% for scateg in categ.getscateg %}
The same thing happens with {% for theme in scateg.gettheme %}
Using prefetch_related was the right move, try something like (didn't test it):
Categorie.objects.filter(visible=True, souscategorie_set__visible=True, souscategorie_set__theme_set__visible=True).prefetch_related('souscategorie_set__theme_set')
prefetch_related works by running a first query to load the categories that satisfy your current filter, then it executed a second query to load all subcategories and so on.
In other cases you can use select_related, but that only works when a single query can be used, as an example, it would work if you needed the category and subcategory of a theme, like:
Theme.objects.filter(pk=1).select_related('souscategorie__categorie')
The difference here is that Theme is the one that has the ForeignKey, so it has only one subcategory, and it can be loaded with a single join, that means you can use select_related only when your queryset is from the Model that points and I think that it also works with OneToOneField.
I have reduce my querysets by 2 by using "with"
It's a good point but i always got a lot of duplicate (like 44 duplicate for 51 queries), for example i hit my database when i do that :
{% for categ in categs %}
{% with currscat=categ.getscateg %}
<li class = "{% if currscat %} has_menu {% endif %}"> {{categ.name}} # This line hit database
{% if currscat %} # This line hit database
<ul class = "menu-1"> ... </ul>
{% endif %}
</li>
{% endwith %}
{% endfor %}
I try to use prefetch_related like this :
categs = Categorie.objects.filter(visible=True).prefetch_related('souscategorie_set')
but thats just add query to database, don't reduce...
Some advice?
Thanks
Hay, is it possible to a get a request.session value from a model method in django?
MEGA UPDATE
Here is my model
class GameDiscussion(models.Model):
game = models.ForeignKey(Game)
message = models.TextField()
reply_to = models.ForeignKey('self', related_name='replies', null=True, blank=True)
created_on = models.DateTimeField(blank=True, auto_now_add=True)
userUpVotes = models.ManyToManyField(User, blank=True, related_name='threadUpVotes')
userDownVotes = models.ManyToManyField(User, blank=True, related_name='threadDownVotes')
votes = models.IntegerField()
def html(self):
DiscussionTemplate = loader.get_template("inclusions/discussionTemplate")
return DiscussionTemplate.render(Context({
'discussion': self,
'replies': [reply.html() for reply in self.replies.all().order_by('-votes')]
}))
def _find_users_who_have_voted(self):
user_list = []
for user in self.userDownVotes.all():
user_list.append(user.id)
for user in self.userUpVotes.all():
user_list.append(user.id)
return user_list
users_voted = property(_find_users_who_have_voted)
and my view is called like this
<ul>
{% for discussion in discussions %}
{{ discussion.html }}
{% endfor %}
</ul>
and the template
<li>
<small>
({{ discussion.votes }} votes)
{% if user_id not in discussion.users_voted %}
user not in list!
{% endif %}
</small>
<strong>{{ discussion.message }}</strong>
{% if replies %}
<ul>
{% for reply in replies %}
{{ reply }}
{% endfor %}
</ul>
{% endif %}
the value 'user_voted' returns a list of user ids who has voted on this discussion.
I want to see if the request.session['user'].id value is inside this list
Why don't you use Django's render_to_string inside a view, which would have request happily available to it, and avoid model method altogether?
UPDATE: after skimming your mega update, you should look into Django's inclusion tags and use the data from the model to fill a template, not use a model to render the template. Keep your model and your template separate - Django is an MVT / MCV framework for good reason :)
you can access the current user, and so their session using threadlocals middleware
http://code.arcs.org.au/gitorious/django/django-andsome/blobs/ee8447e3dad2da9383ff701ec640b44cd50d2b0a/middleware/threadlocals.py
but keep in mind:
http://code.djangoproject.com/wiki/CookBookThreadlocalsAndUser
There might be better solutions to your problem. Maybe you'd like to elaborate why you need request.session on model level?
UPDATE:
since you explicitly call some method - probably from a view - why don't you just put the request.user as parameter to your html method?
models.py:
def html(self, user):
your code...
views.py:
yourmodel.html(request.user)
UPDATE to your MEGA UPDATE:
This is exactly what {% include %} is for:
in your first template do this:
{% for discussion in discussions %}
{% include "discussion.html" }}
{% endfor %}
and the second template has request.user.id in its namespace:
{% if request.session.user.id not in discussion.users_voted %}
user not in list!
{% endif %}