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.
Related
I'm making a django app to do with tests/exams. Two of the models in this app are the "Question" and "Answer" model which have a one-to-many relationship. On the page, each question will be listed with all of its possible answers below it which the user can choose from.
In my views.py file, after getting the list of question objects, the best way I thought of for storing the answers was in a dictionary like so:
answers = {}
for question in questions:
answers[question.id] = Answer.objects.filter(question=question.id)
Then, for the template, I thought I would be able to do something like:
{% for question in questions %}
<h2>{{ question.title }}</h2>
<ul>
{% for answer in answers.{{ question.id }} %}
<li>{{ answer.title }}</li>
{% endfor %}
</ul>
{% endfor %}
The error arises in this line - {% for answer in answers.{{ question.id }} %}.
I've tried rewriting this line as per this post which suggested things like:
{% for answer in answers[question.id] %}
and
{% for answer in answers.get(question.id) %}
but none of these worked.
Please let me know if there is a syntax in Jinja that allows this or else if there is a better way of me passing the answers variable in my views.py file altogether.
You can use the _set.all() convention to get all the related items:
{% for answer in question.answer_set.all %}
{{ answer.id }}
{% endfor %}
This works, but if you want to have a more readable version you can define a related_name in your model:
class Question(models.Model):
prompt = (models.CharField)
class Answer(models.Model):
question = models.ForeignKey(Question, related_name="answers")
correct = models.BooleanField()
prompt = models.CharField()
And then you can use that related name instead of the _set:
{% for answer in question.answers.all %}
{{ answer.id }}
{% endfor %}
which makes it a bit more readable.
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 %}
This question is related to this one.
My Django Model:
from mptt.models import MPTTModel, TreeForeignKey
class myModel(MPTTModel):
myIntA = models.IntegerField(default=0)
myParent = TreeForeignKey('self', null=True, blank=True, related_name='children')
My View:
myModelList = myModel.objects.all()
for i in range(len(myModelList)):
myModelList[i].myIntB = i
return render(
request,
'myApp/myTemplate.html',
Context(
{
"myModels": myModelList,
}
)
)
Is the above legal? You can see that I added a variable myIntB to each myModel object.
However when I try to print myIntB in the template below, nothing shows up.
How can I access myIntB from the template? It is not a field I have defined for this model, nor do I want it to be. I just want myModel to be augmented with this extra variable during rendering of this particular template. And I need to use {% recursetree %} in the template file to display the tree structure inherent in a collection of myModels.
My Template:
{% load mptt_tags %}
<ul>
{% recursetree nodes %}
<li>
{{node.id}} {{node.myIntA} {{node.myIntB}}
{% if not node.is_leaf_node %}
<ul>
{{ children }}
</ul>
{% endif %}
</li>
{% endrecursetree %}
</ul>
I have two basic models, Story and Category:
class Category(models.Model):
title = models.CharField(max_length=50)
slug = models.SlugField()
...
class Story(models.Model):
headline = models.CharField(max_length=50)
slug = models.SlugField()
categories = models.ManyToManyField(Category)
...
And my view for story detail:
from django.views.generic import date_based, list_detail
from solo.apps.news.models import Story
def story_detail(request, slug, year, month, day):
"""
Displays story detail. If user is superuser, view will display
unpublished story detail for previewing purposes.
"""
stories = None
if request.user.is_superuser:
stories = Story.objects.all()
else:
stories = Story.objects.live()
return date_based.object_detail(
request,
year=year,
month=month,
day=day,
date_field='pub_date',
slug=slug,
queryset=stories,
template_object_name = 'story',
)
On the view for a given story object -- I'm using a generic detail view -- I'd like to display a list of stories related to the current story via the categories applied to the current story.
Here's how I'm doing this currently in the story detail template:
{% for category in story.category.all %}
<ul id="related_stories">
{% for story in category.story_set.all|slice:"5" %}
<li>{{ story.headline }}</li>
{% endfor %}
</ul>
{% endfor %}
This provides me what I need except I'd like to avoid displaying the linked headline for the story I'm viewing currently.
I believe this is done via the "exclude" filter, but I'm not sure if this belongs on the Category or Story model as a method, or how to construct it.
Any help would be appreciated!
Do this:
class Story(models.Model):
...
#property
def related_story_set(self):
category_id_list = self.category.values_list("id", flat=True)
return Story.objects.filter(category__id__in=category_id_list).exclude(id=self.id)
Then you can do this in the template:
<ul id="related_stories">
{% for related_story in story.related_story_set.all|slice:"5" %}
<li>{{ related_story.headline }}</li>
{% endfor %}
</ul>
You could just check in the template if the currently iterated story is the original story:
{% for category in story.category.all %}
<ul id="related_stories">
{% for substory in category.story_set.all|slice:"5" %}
{% if substory != story %}
<li>{{ story.headline }}</li>
{% endif %}
{% endfor %}
</ul>
{% endfor %}
You asked to put it in a model method:
class Story(models.Model):
...
def get_categories_with_stories(self):
categories = self.category.all()
for category in categories:
category.stories = category.story_set.exclude(id=self.id)[:5]
return categories
This doesn't solve your expensive query issue, but that wasn't a part of the question.
{% for category in story.get_categories_with_stories %}
<ul id="related_stories">
{% for substory in category.stories %}
<li>{{ story.headline }}</li>
{% endfor %}
</ul>
{% 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 %}