Using jinja variable as dictionary key - django

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.

Related

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.

Django: one-to-many relationship and reverse lookup

i have a Problem which i am unsure what the most django/pythonic way to solve it would be.
I have the following models:
class Order(models.Model):
ord_someinformation = models.CharField(max_length=10)
class Articles(models.Model):
myFK = models.ForeignKey(Order)
detail_article= models.CharField(max_length=10)
so every Order can have multiple Order_Details think about it like a shopping basket where i have an Order and within it i have multiple articles.
I want to iterate over the orders and the articles within the template. I thought about something like.
myOrder = Order.objects.("" i have no idea what to put here "")
within the template i thought about something like this:
{% for order in myOrder %}
{{ order.ord_someinformation }}
{% for articles in order.articles %}
{{ detail_article }}
{% endif %}
{% endif %}
is this possible?
If so how ?
I don't know why you think you need to put anything there. You just want to send all the orders to the template, then iterate through them their articles there.
myOrder = Order.objects.all()
...
{% for order in myOrder %}
{{ order.ord_someinformation }}
{% for article in order.articles_set.all %}
{{ article.detail_article }}
{% endif %}
{% endif %}

a list inside a list - django views

{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li>{{ question.question_text }}</li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
and I have
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
template = loader.get_template('polls/index.html')
context = RequestContext(request, {
'latest_question_list': latest_question_list,
})
return HttpResponse(template.render(context))
But how do I do an inner list?
Since its a list of lists how do I define the lists in such a way that they can be access correctly? The question model certainly doesn't contain any list of comments. I can do a query but I really have no idea where to begin here.
Thanks for any suggestion.
edit:
have a list of questions from the database. The code above lets me view them just fine. But every list has multiple comments. How do I list all the comments per question? I need another for loop in the html but I am not sure how to give each question its own data in the view.
This isn't at all a question about lists.
Presumably you have a separate Comment model, and that model has a ForeignKey to the Question model; if you don't have that structure, then you should.
Then it's simply a matter of following the reverse relationship for each question:
{% for question in latest_question_list %}
...
{% for comment in question.comment_set.all %}
{{ comment.text }}
{% endfor %}
{% endfor %}
Note that this exact structure is well described in the tutorial, with the example of Poll and Choice.

django ModelChoiceField: how to iter through the instances in the template?

I am trying to get access to the instances of a ModelChoiceField to render them the way I want in a template by displaying several fields of the instances.
class MyForm(forms.Form):
mychoices = forms.ModelChoiceField(
queryset=ObjectA.objects.all(), widget=forms.RadioSelect(), empty_label=None
)
This does not work:
{% for choice in form.mychoices %}
{{ choice.pk}}
{% endfor %}
I also tried to use the queryset but it does not render anything
{% for choice in form.mychoices.queryset %}
{{ choice.pk}}
{% endfor %}
Any idea?
Thanks
{% for choice in form.mychoices.field.queryset %}
{{ choice.pk }}
{% endfor %}
Notice the extra .field. It's a bit strange the first time you come across it, but it gives you what you want. You can also access the choices attribute of that object, instead of accessing queryset directly, but you'll need to access the first element of the choice to get the PK of the instance, like this:
{% for choice in form.mychoices.field.choices %}
{{ choice.0 }}
{% endfor %}

How to limit a collection of objects related on a foreign key in Django Templates?

Given is a model called "comment" with a foreign key relationship to a model called "task".
{% for task in tasks %}
{% for comment in task.comment_set.all %}
{{ comment }}
{% endfor %}
...
What is the best way to limit this to 5 comments like:
Entry.objects.all()[:5]
{% for task in tasks %}
{% for comment in task.comment_set.all|slice:"5" %}
{{ comment }}
{% endfor %}
{% endfor %}
You don't. You should not do "real work" in a template, this breaks the MVC pattern.
Do the real work in the view, and pass the data to the template (using the context dictionary).
def handle_comments(request):
tasks = Task.objects.all()
comments = {}
for task in tasks:
comments[task] = task.comment_set.all()[:5]
return render_to_response('commenting.html', {'comments': comments})
You can then iterate over the comments in your template:
{% for task, task_comments in comments.items %}{{ task }}{% endfor %}