The following code is giving me an output I don't expect. Help to interpret my code would be appreciated (I'm very new to Django).
I've created this model:
class Question(models.Model):
author = models.ForeignKey('auth.User')
question = models.TextField()
created_date = models.DateTimeField(
default=timezone.now)
def __str__(self):
return self.question
With a corresponding view:
def home(request):
questions = Question.objects.filter(created_date__lte=timezone.now()).order_by('created_date')
return render(request, 'core/home.html', {'questions':questions})
That is called by an HTML template:
<div id = "centreBlock" >
{{ questions }}
{{questions.created_date}}
</div>
<div id = "rightBlock">
<h2> Other questions</h2>
{% for quest in questions %}
<h3>{{ quest.created_date }}</h3>
<p>{{ quest.text|linebreaks }}</p>
{% endfor %}
</div>
The second line of Django code {{ questions.created_date}} in the template doesn't give anything (all the other code works as I expect). Why is this? I was expecting to see a list of the Created Dates.
Because questions is a QuerySet and has no attribute created_date.
If you want to display all dates from a QuerySet then you'd have to get it explicitly or loop over and show each questions created_date
{% for question in questions %}
{{ question.created_date }}
{% endfor %}
The template variable questions is a queryset of questions. You can't access individual fields from the queryset, you have to loop through it.
{% for question in questions %}
{{ question.created_date }}
{% endfor %}
Related
I've searched a lot and couldn't find anything to reverse the list in descending order so that I can get the last two comments of each post in the template.
Here is my model:
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
commented_by = models.ForeignKey(User, on_delete=models.CASCADE)
comment = models.CharField(max_length=128)
views.py:
posts = Post.objects.all().order_by("date_posted").reverse()
return render(request, "cs50gram/explore.html", {"posts": posts})
template.html:
{% for post in posts %}
{% for comment in post.comments.all|slice:":2" reversed %}
<div style="color: gray">
<b> {{ comment.commented_by }} </b>
{{ comment.comment }}
</div>
{% endfor %}
{% endfor %}
The problem here is that it slices first and then reverse..
What have I tried?
How to reverse a for loop in a Django template and then slice the result
Using the solution presented in the answer, it didn't work for some reason. This is exactly how I tried it in my code.
{% for post in posts %}
{% for comment in post.comments.all.reverse|slice:":2"%}
<div style="color: gray">
<b> {{ comment.commented_by }} </b>
{{ comment.comment }}
</div>
{% endfor %}
{% endfor %}
The output is that it only slice it without reversing.
Any help in reversing the list in descending order and getting the last n (2) comments of each post is appreciated.
You can use order_by('-id') to get the reversed list. And with [:2] you will get the first 2 objects.
posts = Post.objects.all().order_by("-id")[:2]
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.
I have the follow model:
class UserProfile(...):
...
photo1 = models.URLField()
photo2 = models.URLField()
photo3 = models.URLField()
photo4 = models.URLField()
photo5 = models.URLField()
And in the Create/Update template, I have to write five copies of the following div for file1 to file5:
<div>
Photo 1:
{% if userProfileForm.instance.file1 %}
<a href="{{ userProfileForm.instance.file1 }}" target=_blank>View</a>
{% endif %}
<input type=file name=file1>
{% if userProfileForm.instance.file1 %}
Delete
{% endif %}
</div>
Is there a way to iterate field file<i>?
{% for i in '12345' %}
<div>
Photo {{ forloop.counter }}:
...
</div>
{% endfor %}
in django you have _meta API. So I think this solves your problem if you use get_fields method (and maybe you will filter desired fields out).
hope it helps.
update with example
let me show how you should solve your problem:
desired_fields = []
for field in UserProfile._meta.get_fields()
if "photo" in field.name:
desired_fields.append(field.name)
context.update(fields=desired_fields) # pass it in the context
at this point you have your desired fields which should be used with for loop in the template. And one more thing, you would need to add some template tag to get real field from string representation:
# custom template tag
def from_string_to_field(instance, field_str):
return getattr(instance, field_str, None)
and in the template code will look like this
{% for field in fields %}
{{userProfileForm.instance|from_string_to_field:"field"}}
{% endfor %}
I am trying to follow this previous question here:
Django: getting the list of related records for a list of objects
but can't seem to get it to work.
I get a list of owners but do not get a list of pet names. The html code doesn't seem to execute the 2nd FOR loop. Any ideas?
models.py
class Teacher(models.Model):
teacher = models.CharField(max_length=300, verbose_name="teacher")
def __unicode__(self):
return self.teacher
class Owner(models.Model):
relevantteacher = models.ForeignKey(Teacher, verbose_name="teacher")
owner = models.CharField(max_length=300, verbose_name="owner")
def __unicode__(self):
return self.owner
class PetName(models.Model):
relevantowner = models.ForeignKey(Owner, verbose_name="owner")
pet_name = models.CharField(max_length=50, verbose_name="pet Name")
def __unicode__(self):
return self.pet_name
views.py
def ownersandteachers(request):
owners = Owner.objects.all()
context = {'owners': owners}
return render(request, 'ownersandpets.html', context)
template
{% for i in owners %}
{{ i.owner }} has pets:<br />
{% for v in owners.petname_set.all %} //this doesn't seem to be executing
- {{ v.pet_name }}<br />
{% endfor %}
{% endfor %}
You are doing
{% for v in owners.petname_set.all %}
where you should be doing
{% for v in i.petname_set.all %}
owners is the queryset, but you need to get the petname_set for the individual object in the queryset which is i in this case.
Also, I would recommend a condition check if i.petname_set exists. If not, do not show the has pets: text.
{% for i in owners %}
{{ i.owner }}
{% if i.petname_set.count %} has pets:<br />
{% for v in i.petname_set.all %} //this doesn't seem to be executing
- {{ v.pet_name }}<br />
{% endfor %}
{% endif %}
{% endfor %}
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 %}