How to display related One to Many Instances in ListView? - django

I want to display all belonging Instances of model Report referenced to model Module referenced to model Course. Therefore I implemented three Models related via Foreignkey.
class Course(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField(max_length=200, blank=True, null=True)
class Module(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField(max_length=200, blank=True, null=True)
course_id = models.ForeignKey(
Course,
null=True,
on_delete=models.SET_NULL,
related_name="modules",
default=uuid.uuid4,
)
class Report(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField(max_length=200, blank=True, null=True)
module_id = models.ForeignKey(
Module,
null=True,
on_delete=models.SET_NULL,
related_name="reports",
default=uuid.uuid4,
)
I want to display model Module referenced to model Course in CourseDetailView(DetailView):
Here is what I implemented:
class CourseDetailView(DetailView):
model = Course
context_object_name = "course"
template_name = "course/course_detail.html"
fields = ["title", "description"]
def get_context_data(self, **kwargs):
context = super(CourseDetailView, self).get_context_data(**kwargs)
context["courses"] = Course.objects.filter(pk=self.kwargs.get("pk"))
return context
and I get the instance belonging to itsself.
If I change the context to:
context["modules"] = Module.objects.all()
and iterate over modules in course_detail.html:
{{ course.description|safe }}
{% for module in modules %}
<div>
<h2>{{ modules.title }}</h2>
</div>
{% endfor %}
I'll get all instances of Module but I want only the ones related to the specific Course.
I know I have to filter context["modules"] but I don't know how to do it.
Due to the fact that I want to display the modules in CourseDetailView I am not able to get module.pk via super(CourseDetailView, self).get_context_data(**kwargs).
Many thanks in advance

As far as I understood you need smth like Module.objects.filter(course_id=id), where id is the id of specific Cource. This queryset is a common case. As you said in comments for your case needed: Module.objects.filter(course_id=self.object)

Related

Django render many to many in template

I have this models:
class roles(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=255, blank=False)
company = models.ForeignKey(Company, blank=True, null=True, on_delete=models.SET_NULL)
def __str__(self):
return self.name
class freelancers(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
company = models.ForeignKey(Company, blank=True, null=True, on_delete=models.SET_NULL)
user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)
role = models.ManyToManyField(roles)
I try to get the name that is related to the user at the roles table.
In my view.py It looks like this:
def company_details(request,id):
obj = Company.objects.get(id=id)
pro = Projects.objects.filter(company=id)
free = freelancers.objects.filter(company=id)
#free = freelancers.objects.all()
return render(request, 'company/company_details.html',
{
'obj':obj,
'pro':pro,
'free':free,
}
)
And in the HTML:
{% for f in free %}
{{ f.user }} // <br/>
{% endfor %}
{{ f.role.all }}
{% endfor %}
I have been trying different ways to get the name to show up.
Like: {{ f.role.name }}.
So any tips to make this work?
I think you will have to iterate through the f.role.all

not triggering the post_delete_signal when it's coming from a CASCADE delete

Hello I have two django models linked by a foreign key and it looks like this
class Author(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=255, null=False, db_index=True)
number_of_books=models.IntegerField()
#other fields
class Book(models.Model):
author = models.ForeignKey('Author', on_delete=models.CASCADE, related_name='books')
name = models.CharField(max_length=255, null=False, db_index=True)
#other fields
The Book model has a post_delete_signal that triggers some computations for te author instance
def book_post_delete(sender, instance, **kwargs):
# Re save max exposure
author = instance.author
author.number_of_books = author.books.count()
author.save()
trigger_computation(author)
My problem is that if I delete an author for example, then his books are also deleted because of the on_delete=models.CASCADE, the book_post_delete function is then called and the trigger_computation(author) is called, which takes a lot of time for me and is also useless because the author is being deleted. Is there a way to get around not triggering the computation when the instance that is being deleted is an author?

Django - How to get image field of ForeignKey query elements?

I have the following code at views.py to sort TvShows by there latest released episode (release_date) within 90 days:
def tv_show_by_set_just_released(request):
latest_episodes = TvShows.objects.filter(episode_show_relation__release_date__gte=now() - datetime.timedelta(days=90))
...
For each element found, I now also want to display a cover. But the cover is located at a different table and I don't really know how to pull it out probably in this query context. In the end I want to display the very first TvShowSeasons.cover for each element of my query from above. Is it somehow possible to marry these two elements, latest_episodes and TvShowSeasons.cover to display them properly at a template?
Please also see models.py
class TvShows(models.Model):
objects = RandomManager()
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.TextField(verbose_name=_("Title"), blank=False, null=True, editable=False, max_length=255)
genre_relation = models.ManyToManyField(through='GenreTvShow', to='Genre')
date_added = models.DateTimeField(auto_now_add=True, blank=True, verbose_name=_("Date Added"))
class TvShowSeasons(models.Model):
objects = RandomManager()
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
show = models.ForeignKey(TvShows, on_delete=models.CASCADE, related_name='season_show_relation')
season_number = models.IntegerField(verbose_name=_("Season Number"), blank=True, null=True, editable=False)
cover = models.ImageField(verbose_name=_("Cover"), blank=True, null=True, upload_to=get_file_path_images)
cover_tn = models.ImageField(verbose_name=_("Cover Thumbnail"), blank=True, null=True, upload_to=get_file_path_images)
total_tracks = models.IntegerField(verbose_name=_("Total Tracks #"), blank=True, null=True)
rating = models.CharField(verbose_name=_("Rating"), blank=True, null=True, editable=False, max_length=255)
copyright = models.TextField(verbose_name=_("Copyright"), blank=True, null=True, editable=False, max_length=255)
date_added = models.DateTimeField(auto_now_add=True, blank=True, verbose_name=_("Date Added"))
Thanks in advance
That is what related_name argument in ForeignKey is meant to be. Assuming you return latest_episodes as context, here is how you can access cover for each episode in templates:
{% for episode in latest_episodes %}
<p>Episode name: {{ episode.title }}</p>
<img src="{{ episode.season_show_relation.first.cover.url }}">
{% endfor %}

How to make a form for this model that will able user to select choice for each question asked - Django

Im trying to make a form for a quiz that will able user to select choice for each question when presented, so that when i user submits the form it is saves into the user response. Also how would I list the choices taken from the choice model for each given question?
what im trying to achieve:
display form for each question for a given quiz
display all on same page as one form
once all questions answered submit into UserResponse model alongside
the user that did the quiz
My models seem to work correctly as im able to populate them with data and the relations between them seem to be okay. I am new to django so i have been trying to read the documentation and ive done mutliple tutorials but can seem to find a solution.
Model for quiz:
class Quiz(models.Model):
title = models.CharField(max_length=255, unique=True)
description = models.CharField(max_length=255, blank=True, unique=False)
created = models.DateTimeField(auto_now_add=True, editable=False)
slug = models.SlugField()
active = models.BooleanField('Is active?', default=True, db_index=True)
class Question(models.Model):
type = models.IntegerField(choices=TYPES, default=1, verbose_name='Question Type')
question_text = models.CharField(max_length=255, unique=True)
description = models.TextField(max_length=500, unique=False)
quiz = models.ForeignKey(Quiz, on_delete=models.DO_NOTHING)
class Choice(models.Model):
choice_text = models.CharField(max_length=255)
question = models.ForeignKey(Question, on_delete=models.CASCADE, verbose_name='Question')
class QuizTakers(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE)
timestamp = models.DateTimeField(auto_now_add=True)
class UserResponse(models.Model):
quiztaker = models.ForeignKey(QuizTakers, on_delete=models.CASCADE)
question = models.ForeignKey(Question, on_delete=models.CASCADE)
response_option = models.ForeignKey(Choice,on_delete=models.CASCADE,null=True,blank=True)
Try this:
You can use ManyToManyField field to connect all the choices into a Question and all the questions into a Quiz.
set the null option of ManyToManyField to False, so that If no choices are selected for a queston still the Question can be created. And so with the Quiz.
class Choice(models.Model):
choice_text = models.CharField(max_length=255)
class Question(models.Model):
type = models.IntegerField(choices=TYPES, default=1, verbose_name='Question Type')
question_text = models.CharField(max_length=255, unique=True)
description = models.TextField(max_length=500, unique=False)
choices = models.ManyToManyField(Choice, null=True)
class Quiz(models.Model):
title = models.CharField(max_length=255, unique=True)
description = models.CharField(max_length=255, blank=True, unique=False)
created = models.DateTimeField(auto_now_add=True, editable=False)
slug = models.SlugField()
active = models.BooleanField('Is active?', default=True, db_index=True)
questions = models.ManyToManyField(Question, null=True)
You can display the Quiz by looping it and it's questions and also looping the choices of the questions.
views.py
def show_quiz(request):
quizes = Quiz.objects.all()
quiz = quizes.first() # Select the first quiz
context = { 'quiz': quiz }
t = loader.get_template('quiz.html')
return HttpResponse(request, t.render(context, request))
Display Quiz title, description & created date.
quiz.html
<h1>{{ quiz.title }}</h1>
<h3>{{ quiz.description }}</h3>
<p>Date : {{ quiz.created }}</p>
To display questions in a Quiz you can do the following:
{% for question in quiz.questions.all %}
<h4>Q{{ forloop.counter }}. : {{ question.question_text }}</h4>
<p>{{ question.description }}</p>
{% endfor %}
To display choices of a Question you can do the following:
{% for choice in question.choices.all %}
{{ forloop.counter }} {{ choice.choice_text }}
{% endfor %}

Django queryset annotate calculated values from another model

I have 4 models in a hierarchy: analysis, books, category and publisher. In my app you analyze books. The books belong to categories where you can view the average analysis rating of all the books in the category. This average rating (that I call avg-catg-rating) is not stored in the dB, it’s calculated in the view.
Here’s my question: how do I get the average category rating for each category that the publisher has onto a single publisher view? In other words, how do I annotate the avg-catg-rating onto each category so I can display all of them on the publisher’s page?
So do I iterate using the category somehow like this question? If yes, please give a tip on how because I'm not really sure how to go about it. I also tried groupby as suggested by this chap, but I could only get the count of book instances, I couldn't accurately annotate the avg-catg-rating.
To clarify:
Models.py:
class Analysis:
title = models.CharField
content_rating_1 = models.IntegerField(blank=True,
null=True, default="0")
content_rating_1_comment = models.TextField(max_length=300, blank=True,
null=True)
source_rating_1 = models.IntegerField(blank=True,
null=True, default="0")
source_rating_1_comment = models.TextField(max_length=300, blank=True,
null=True)
book = models.ForeignKey
class Book:
publication_date = models.DateField(default=datetime.date.today)
slug = models.SlugField(allow_unicode=True, unique=False, max_length=160)
category = models.ForeignKey
class Category:
title = models.CharField(max_length=150, unique=False, blank=False)
sub_title = models.CharField(max_length=100, blank=True, null=True)
publisher = models.ForeignKey
class Publisher:
title = models.CharField(max_length=150, unique=False, blank=False)
publisher/views.py:
class ViewPublisher:
def get_context_data(self, **kwargs):
category_qs = Category.objects.filter(publisher__pk=self.kwargs['pk'])
avg-catg-rating = Books.objects.annotate(long complex queryset that uses values from the analysis model)
context = super(ViewPublisher, self).get_context_data(**kwargs)
context['categories'] = category_qs
context['category_stats'] = Books.objects.filter(category__pk__in=category_qs)\
.annotate(.....and now what???)
I let go of my obsession with doing everything in the view.
The solution is to create a method in your models that you can call in the template. In my case, I created a method in the Category/models.py like so:
class Category:
title = models.CharField(max_length=150, unique=False, blank=False)
sub_title = models.CharField(max_length=100, blank=True, null=True)
publisher = models.ForeignKey
def sum_catg_avg_rating(self):
scar = self.books.annotate(avg-catg-rating=(annotate the AVG calculations from the
analysis))
return scar
Then included the category into the context of the publisher:
class ViewPublisher:
def get_context_data(self, **kwargs):
category_qs = Category.objects.filter(publisher__pk=self.kwargs['pk'])
context = super(ViewPublisher, self).get_context_data(**kwargs)
context['categories'] = category_qs
return context
Now, I can just call it in the template:
{% for catg in categories %}
<h5>{{ catg.title }}</h5>
<p> RATING:<i>{{ catg.sum_catg_avg_rating.avg-catg-rating }}</i></p>