I have the following problem. I need to get the information of the question based on the questionitem.question_id
I have the following files.
# models.py
class Question(models.Model):
qtype_id = models.ForeignKey(QuestionType, on_delete=models.CASCADE )
q_discription = models.CharField(max_length=150)
def __iter__(self):
return self.pk
class QuestionItem(models.Model):
exam_id = models.ForeignKey(Exam, on_delete=models.CASCADE)
question_id = models.ForeignKey(Question, on_delete=models.CASCADE)
question_pontuation = models.FloatField(default=0.0)
def __iter__(self):
return self.pk
# views.py
def ExamDetail(request, exam_id):
exam = get_object_or_404(Exam, pk=exam_id)
questionitems = QuestionItem.objects.filter(exam_id= exam_id).values_list('id', flat=True)
questions = Question.objects.filter(pk__in=questionitems)
context = {
'exam': exam,
'questions': questions,
'questionitem': questionitems,
}
return render(request, 'evaluation/exam_detail.html' , context)
And now on exam_detail.html i have something like this:
{% for questionitem in exam.questionitem_set.all %}
Question {{questionitem.id}}</a>: {{question.q_discription}}
{% endfor%}
but nothing shows, and i need to show the description of the question in questionitem.question_id, and i cant change the models.py.
Probably, you have problem in the way, you are accessing queryset in loop. Change code to :-
{% for questionitem in exam.questionitem_set.all %}
Question {{questionitem.id}}</a>: {{questionitem.question_id.q_discription}}
{% endfor%}
q_discription is the part of Question class, which is accessible using question_id on questionitem instance, that you receive in the loop.
Related
Accessing primary keys in Django class based view
Let's start from the beginning. I have 2 models, Recipe, and Ingredient. They look like this.
In models.py
class Recipe(models.Model):
name=models.CharField(max_length=20, help_text='Enter the name of this recipe')
description=models.TextField(max_length=75, help_text='Describe your recipe')
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('recipe-detail', kwargs={'pk': self.pk`})
class Ingredient(models.Model):
recipe=models.ForeignKey(Recipe, on_delete=models.CASCADE)
ingredient=models.CharField(max_length=100)
class Meta:
ordering = ['ingredient']
def __str__(self):
return self.ingredient
What I want to be able to do is have a detail view, where I can access the Recipe attributes, like the name and description, as well as, be able to loop through the ingredients. This is what I have working so far:
In views.py
def recipe_detail_view(request, pk):
recipe = get_object_or_404(Recipe, pk=pk)
context = {
'recipe': recipe,
'ingredients': Ingredient.objects.filter(recipe=pk)
}
return render(request, 'recipes/recipe_detail.html', context=context)
In urls.py
# ...
path('recipes/<str:pk>', views.recipe_detail_view, name='recipe-detail')
# ...
In template
<h1 class="title is-1">{{ recipe.name }}</h1>
<p>{{ recipe.description }}</p>
<h3 class="title">Ingredients</h3>
{% for ingredient in ingredients %}
<h4 class="">{{ ingredient.ingredient.title }}</h3>
{% endfor %}
I am wondering how I could turn this into a class based view however. More specifically, I am wondering how I can access and pass in the primary key to the filter like so:
class RecipeDetailView(generic.DetailView):
model = Recipe
template_name = 'recipes/recipe_detail.html'
context_object_name='recipe'
extra_context = {
'ingredients': Ingredient.objects.filter(recipe=pk),
}
Can anyone help?
You can use get_context_data and get_object to get the data you want to your template.
class RecipeDetailView(generic.DetailView):
model = Recipe
template_name = 'recipes/recipe_detail.html'
context_object_name='recipe'
def get_context_data(self, **kwargs)
ctx = super().get_context_data(**kwargs)
ctx['ingredients'] = Ingredient.objects.filter(recipe=self.get_object().pk)
return ctx
In Django, how can I sort the results of a method on my model?
class Flashcard(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
deck = models.ForeignKey(Deck, on_delete=models.CASCADE)
question = models.TextField()
answer = models.TextField()
difficulty = models.FloatField(default=2.5)
objects = FlashcardManager()
def __str__(self):
return self.question
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE,related_name='profile')
bio = models.TextField(max_length=500,null=True, default='',blank=True)
def __str__(self):
return f'{self.user.username} Profile'
def avg_diff_user(self):
avg_diff = Flashcard.objects.filter(owner = self.user).aggregate(Avg('difficulty'))['difficulty__avg']
return avg_diff
So with avg_diff_user, I get each user's average difficulty rating. Which I can then use in my leaderboard template as follows:
<ol>
{% for user in leaderboard_list %}
<li>{{user.username}}: {{user.profile.avg_diff_user|floatformat:2}}</li>
{% endfor %}
</ol>
The results show, but it's not sorted - how can I sort by avg_diff_user? I've read many similar questions on SO, but to no avail. I've tried a different method on my model:
def avg_diff_sorted(self):
avg_diff_sorted = Flashcard.objects.all().annotate(get_avg_diff_user=Avg(Flashcard('difficulty'))['difficulty__avg'].order_by(get_avg_diff_user))
return avg_diff_sorted
Which I don't think is right and didn't return any results in my template. I also tried the following, as suggested in https://stackoverflow.com/a/930894/13290801, which didn't work for me:
def avg_diff_sorted(self):
avg_diff_sorted = sorted(Flashcard.objects.all(), key = lambda p: p.avg_diff)
return avg_diff_sorted
My views:
class LeaderboardView(ListView):
model = User
template_name = 'accounts/leaderboard.html'
context_object_name = 'leaderboard_list'
def get_queryset(self):
return self.model.objects.all()
something like:
leaderboard_list = User.objects.all().annotate(avg_score=Avg('flashcard__difficulty').order_by('-avg_score')
will sort you the users by their average score.
I don't use ListView that often by if you just used a standard view like:
def LeaderboardView(request):
leaderboard_list = ...
context = {'leaderboard_list':leaderboard_list}
return render(request, 'accounts/leaderboard.html', context)
In your html you could do the same:
{% for user in leaderboard_list %}
...
{% endfor %}
I can't figure out how to create a page with multiple Questions and their Answers. The main thing is that I want User to take a Quiz which contain's multiple Questions and Questions have multiple Answers.
The first thing I want is to render at least one Question with it's answers, and if it works, then figure out how to render multiple questions on one page (whole Quiz), but it doesn't render anything except base.html.
But when I try to print question_form in a view, it returns:
Exception Type: TypeError at /language-tests/question
Exception Value:'Answer' object is not iterable
Do you have any ideas, what's wrong?
question.html
{% extends 'base.html' %}
{% block content %}
{{ question_form }}
{% endblock %}
FORM
class QuestionForm(forms.Form):
def __init__(self, question, *args, **kwargs):
super(QuestionForm, self).__init__(*args, **kwargs)
choice_list = [x for x in question.get_answers_list()]
self.fields["answers"] = forms.ChoiceField(choices=choice_list,
widget=forms.RadioSelect)
VIEW - simple view just for see if the question form can be rendered
def question(request):
question_form = forms.QuestionForm(question=models.Question.objects.get(pk=1))
return render(request,'question.html',context={'question_form':question_form})
MODELS
class LanguageQuiz(models.Model):
name = models.CharField(max_length=40)
language = models.OneToOneField(sfl_models.Language)
max_questions = models.IntegerField()
def __str__(self):
return '{} test'.format(self.name)
def __unicode__(self):
return self.__str__()
class Question(models.Model):
language_quiz = models.ForeignKey(LanguageQuiz,related_name='questions')
text = models.TextField()
def get_answers_list(self):
return self.answers.all()
class Answer(models.Model):
question = models.ForeignKey(Question,related_name='answers')
text = models.TextField()
correct = models.BooleanField()
A Django form field choices argument should be an iterable of 2-tuples, see https://docs.djangoproject.com/en/1.9/ref/models/fields/#choices
You probably want to say something like
choice_list = [(x.id, x.text) for x in question.get_answers_list()]
Your exception 'Answer' object is not iterable is because Django is trying to iterate over this 2-tuple, but finding instead the Answer object.
Hi i want to display a count of answers to my question model
my model:
class Question(models.Model):
text = models.TextField()
title = models.CharField(max_length=200)
date = models.DateTimeField(default=datetime.datetime.now)
author = models.ForeignKey(CustomUser)
tags = models.ManyToManyField(Tags)
def __str__(self):
return self.title
class Answer(models.Model):
text = models.TextField()
date = models.DateTimeField(default=datetime.datetime.now)
likes = models.IntegerField(default=0)
author = models.ForeignKey(CustomUser)
question = models.ForeignKey(Question)
my view:
def all_questions(request):
questions = Question.objects.all()
answers = Answer.objects.filter(question_id=questions).count()
return render(request, 'all_questions.html', {
'questions':questions, 'answers':answers })
Right now view displays count of all answers. How can i filter it by the Question model?
You can use .annotate() to get the count of answers associated with each question.
from django.db.models import Count
questions = Question.objects.annotate(number_of_answers=Count('answer')) # annotate the queryset
By doing this, each question object will have an extra attribute number_of_answers having the value of number of answers associated to each question.
questions[0].number_of_answers # access the number of answers associated with a question using 'number_of_answers' attribute
Final Code:
from django.db.models import Count
def all_questions(request):
questions = Question.objects.annotate(number_of_answers=Count('answer'))
return render(request, 'all_questions.html', {
'questions':questions})
In your template, then you can do something like:
{% for question in questions %}
{{question.number_of_answers}} # displays the number of answers associated with this question
See the docs
You can annotate the Query, like:
from django.db.models import Count
questions = Question.objects.annotate(num_answer=Count('answer'))
but, refactor the code to this.
Remove the count of answers:
def all_questions(request):
questions = Question.objects.all()
return render(request, 'all_questions.html', {'questions':questions })
Now, in all_question.html. Just use :
{% for question in questions %}
Title: {{question.title}}
Count Answers: {{question.answer_set.all|length}}
{% for answer in question.answer_set.all %}
{{answer.text}}
{% endfor %}
{% endfor %}
It is more efficienty.
I have a reddit-like website where users post links and those links can be commented on. I want to display the number of comments under each link before being taken to the comment page for that link. What is the most query efficient way to do this in django that doesn't slow down the rendering of my site? I assume I'll have to go through a for loop of each link on the page to count the number of posts for each one and populate a list of some sort with the number returned from the .count() of each queryset? Here's what I have:
class Post(models.Model):
newlinktag = models.ForeignKey('NewLink', null=False)
childtag = models.ForeignKey('Post', blank=True, null=True)
postcontent = models.CharField(max_length=1024)
def __unicode__(self):
return self.postcontent
class NewLink(models.Model):
posttag = models.ForeignKey('PageInfo') #the page each link belongs to
linkcomment = models.CharField(max_length=512)
postlinkdate = models.DateTimeField(auto_now_add=True) #submission datestamp.
url = models.URLField(max_length = 1024)
linkowner = models.ForeignKey(User, null=True, blank=True)
def __unicode__(self):
return self.url
Jape gave you a good answer, but it is always more efficient to preform counting in the database rather than in python loops.
views.py
from django.db.models import Count
def view(request):
# Calculate the counts at the same time we fetch the NewLink(s)
links = NewLink.objects.annotate(post_count=Count('post_set'))
return render(request, 'template.html', {'links': links})
html
{% for link in links %}
{{ link.post_count }}
{% endfor %}
In your model, I would create a cached_property and then when you run the for loop in your template, call the property to get the count.
For example,
models.py:
class NewLink(models.Model):
posttag = models.ForeignKey('PageInfo') #the page each link belongs to
linkcomment = models.CharField(max_length=512)
postlinkdate = models.DateTimeField(auto_now_add=True) #submission datestamp.
url = models.URLField(max_length = 1024)
linkowner = models.ForeignKey(User, null=True, blank=True)
def __unicode__(self):
return self.url
# Might also want to flush this after a post_save method in your signals
#cached_property
def linkcomment_count(self):
return self.linkcomment.count()
views.py:
def view(request):
# Could do a 'select_related' relationship to save hits on the database
comments = NewLink.objects.all()
return render(request, 'template.html', {'comments': comments})
html:
{% for link in comments %}
{{ link.linkcomment_count }}
{% endfor %}
Did I understand your question correctly?