get request.session from a model method in django - django

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 %}

Related

Django template with forloop. Exception Type: IndexErrorm, Exception Value: pop from empty list

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.

django: Exception has occurred: IndexError pop from empty list

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.

Django: Proper Way to Acess data from a One to Many Relationship?

I am having issues with accessing data through a django one to many relationship. After 3 painstaking days I figured out a way to display the data from the relationship by overriding the get_context_data method. I was wondering if that was the proper way to do it. This works but I could imagine that there is a better way to do this that I missed in the documentation.
Here is the code for that:
class QuestionDetailView(DetailView):
model = Question
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['answers'] = Answer.objects.filter(firm=context['object'])
return context
Here is the code for the models:
class Question(models.Model):
text = models.CharField(max_length=120, unique=True)
class Answer(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
Here is the code in my template:
{% extends "base.html" %}
{% block body %}
<div class="">
<h3>{{ object.text }}</h3>
<p>Answers:</p>
<ul>
{% for answer in answers %}
<li> {{ answer }}</li>
{%empty%}
<li>No answers</li>
{% endfor %}
</ul>
</div>
{% endblock %}
Add a related_name to your question field.
class Answer(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name="answers")
Or, just use the default that Django gives: answer_set
Then in your template, you can do:
{% for answer in object.answers.all %}
<li> {{ answer }}</li>
{% empty %}
<li>No answers</li>
{% endfor %}
No need to override get_context_data unless you want to do something more specific with the queryset.

Combining model data to form url pattern

I am generating a generic list right now, with the following code:
views.py
class ServiceReportIndex(LoginRequiredMixin, ListView):
model = TblServiceRecords
context_object_name = 'all_servicereports'
login_url = 'login'
template_name = 'servicereport/servicereport_index.html'
def get_context_data(self, **kwargs):
context = super(ServiceReportIndex, self).get_context_data(**kwargs)
context['companies'] = TblCompanies.objects.all()
return context
In my template, I want to generate a URL using both of the models. The TblServiceRecords model contains a column that references the company_id, which is the primary key of the appropriate company in the TblCompanies model. I want to use the company_name from the Companies model in my list view. How would I go about doing that? I'm sure it's simple but I can't seem to get my url tags done correctly.
<div class="col-sm-4">
<p>Company Name</p>
{% for servicereport in all_servicereports %}
<p>{% for servicereport.company_id in companies.company_id %} {{ companies.company_name }} {% endfor %}</p>
{% endfor %}
</div>
Also, how can I be sure my views.py is set up correctly for multiple model functionality? I ask because if I put
{% for company_name in companies %}
{{companies.company_name}}
{% endfor %}
In my template, nothing comes up, but there are no errors either.
Probably you cannot see companies bacause of this:
{{companies.company_name}}
companies is queryset and it does not have company_name property.
Try this:
{% for company_name in companies %}
{{company_name.company_name}}
{% endfor %}

Django query to get User's favorite posts?

I'm a little confused by the Django lingo. So I have 3 models: Post, UserProfile(User), Favorite. Favorite keeps track of which Posts a User has favorited.
Post--->Favorite<---User/UserProfile
Favorite model:
class Favorite(models.Model):
user = models.ForeignKey(User, unique=False)
post = models.ForeignKey(Post, unique=False)
def __unicode__(self):
return self.user.username
UserProfile model:
class UserProfile(models.Model) :
user = models.ForeignKey(User, unique=True)
def get_favorites(self):
if self.user:
return self.user.favorite_set.all()
In my post_list view I pass all Posts to my template, and in the template I have a for loop that displays all Posts.
{% for post in post_list %}
<hr/>
<div id=”post_{{ post.id }}”>
{% include 'posts/_post.html' %}
</div>
{% endfor %}
Now in that for loop I would like to put a logic that will display "Favorited!" if the logged-in User has favorited the Post. I think the conventional SQL is something like this:
SELECT favorite.post FROM favorite WHERE favorite.user = user.id
So that in the template loop I can do
{% if post in the.above.SQL.query%}Favorited!{% endif %}
Now I just can't translate that to Django lingo for some reason. Your help is much appreciated!
The thing to recognise is that your Favorite model is actually the through table of a many-to-many relationship between Post and User. Django can actually manage that automatically if you declare a ManyToManyField somewhere. Personally, I would do that on UserProfile - so the relationship actually becomes one between Post and UserProfile:
class UserProfile(models.Model):
favorites = models.ManyToManyField(Post, related_name='favorited_by')
Now you don't need your get_favorites method, as it is available via userprofile.favorites.all(). You could just use this as-is in the template:
{% if post in userprofile.favorites.all %}Favorited!{% endif %}
but this will end up being extremely inefficient, as you'll be doing the same identical query for each post in your list of posts. So, use the {% with %} tag to get the favorites once before the loop:
{% with userprofile.favorites.all as favorite_posts %}
{% for post in post_list %}
{% if post in favorite_posts %}Favorited{% endif %}
...
{% endfor %}
{% endwith %}
While Daniel makes good point, i'll just post the query you wanted :)
Post.objects.filter(favorite__user=user)
Since its many to many relationships,
fav_post = user.favourite.all() you can pass this fav_post to context. Then in the template, you will need to iterate that context key