Django | Queryset ManyToMany Relation through Instance - django

I would like to display the different dates for each course. A course can also have several dates. Unfortunately I can't find a solution.
Models:
class Course(models.Model):
course_number = models.CharField(max_length=24, blank=True)
course_location = models.ForeignKey(Course_location, on_delete=models.CASCADE)
course_dates = models.ManyToManyField('Course_dates', through="Course_Course_dates")
def __str__(self):
return self.course_number
class Course_Course_dates(models.Model):
course = models.ForeignKey(Course, on_delete=models.CASCADE)
course_dates = models.ForeignKey(Course_dates, on_delete=models.CASCADE)
def __str__(self):
return self.id
class Course_dates(models.Model):
date = models.DateField()
start = models.TimeField()
end = models.TimeField()
trainer = models.ManyToManyField('Trainer', through="Course_dates_trainer")
def __str__(self):
return self.date.strftime("%d.%m.%Y")
View:
def course_list(request):
courses = Course.objects.all()
course_list = []
for course in courses:
course_location = course.course_location.description
dates = course.course_dates.all()
course_list.append({'course': course, 'course_location': course_location, 'dates': dates,})
context = { 'course_list': course_list, }
return render(request, 'kursverwaltung_tenant/course.html', context)
Thanks for help!!!

Your courses queryset will already have access to your course dates through the foreign key so you don't need to run a second dates queryset in the view to access them.
When you loop through the courses, you'll then need to run a second loop through the dates of those courses within your template. Here's an example of that:
Display foreign key value in django template

Related

Syntax to reverse-query a cached queryset

I have the following 3 models related by Foreign Key as following:
class Seller(models.Model):
name = models.CharField(max_length=20)
def __str__(self):
return self.name
class Genre(models.Model):
seller= models.ForeignKey(Seller, related_name="genre", on_delete=models.CASCADE)
name = models.CharField(max_length=20)
def __str__(self):
return self.name
class Book(models.Model):
genre= models.ForeignKey(Genre, related_name="book", on_delete=models.CASCADE)
name = models.CharField(max_length=20)
def __str__(self):
return self.name
And I want to retrieve the whole 3 tables in one database query, by querying the Seller objects, as following:
sellers = Seller.objects.select_related('genre', 'book').all().values('name')
seller_df = pd.DataFrame(list(sellers))
What is the syntax to filter for books carried by a particular seller, without hitting the database again (by utilizing either the Seller queryset or the pandas seller_df)
seller1 = seller_df ['name'].iloc[0]
seller1_books = Book.objects.filter(...)
seller_last = seller_df ['name'].iloc[-1]
seller_last_books = Book.objects.filter(...)
I dont know so mach about caching but I know something that you like:
We use select_related when the object is single like onetoone or fk.
.
for many to many or reverse fk like your example use prefetch_related

How to send a 2 Django view context objects to single html template in Django?

Please help me, I'm working on a project where I need to display a page about tutors and need to add their ratings also.so I created 2 models and 2 views for both tutors but I don't know how to display the context objects of both views in a single template.
class tutors(models.Model):
category = models.ForeignKey(Category, related_name='tutors', on_delete=models.CASCADE)
name = models.CharField(max_length=100, db_index=True)
Tagline = models.CharField(max_length=100, db_index=True)
slug = models.SlugField(max_length=100, db_index=True)
description_tutor = models.TextField(blank=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
images=models.ImageField(upload_to="media")
qualification=models.TextField(null=True)
class Meta:
ordering = ('name', )
index_together = (('id', 'slug'),)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('onlinetutors:tutor_detail', args=[self.id, self.slug])
class ratings(models.Model):
username= models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,)
rating=models.FloatField()
tutorname=models.ForeignKey(tutors,related_name='ratingss',on_delete=models.CASCADE)
created_date=models.DateTimeField(auto_created=True)
def tutor_detail(request, id, slug):
tutor = get_object_or_404(tutors, id=id, slug=slug)
context = {
'tutor': tutor,
}
return render(request, 'detail.html', context)
def rating(request,tutorname_id):
display_rating = ratings.objects.filter(tutorname_id=tutorname_id).aggregate(find_average=Avg('rating'))
display = display_rating["find_average"]
return render(request, 'detail.html',{'display':display})
when you return render(request.., you can set your dictionary to return multiple querysets. Filter your model objects as you did for ratings, then call them to your context dict.
display_rating = ratings.objects.filter(..
display_tutors = tutors.objects.filter(..
return render(request, '.html', {'rating': display_rating, 'tutors': display_tutors}
Also, make sure to capitalize your models since they are classes if you haven't already in your actual project.
Added ratings query set into the existing tutors views function then sending both contexts to a single template. It is working now.
def Tutor_Detail(request, id, slug,tutorname_id=None):
tutor = get_object_or_404(tutors, id=id, slug=slug)
display_rating = ratings.objects.filter(tutorname_id = id).aggregate(find_average=Avg('rating'))
display_rating = display_rating['find_average']
context = {
'tutor': tutor,
'display':display_rating,
}
return render(request, 'detail.html', context)

Count number of post in a category

I want to list all the categories in my news table plus add the number of post in each categories.
Here is my model :
class Blog(models.Model):
titre_article = models.CharField(max_length=255)
auteur_article = models.ForeignKey(User, on_delete=models.CASCADE)
date_article = models.DateTimeField(auto_now_add=True)
modif_article = models.DateTimeField(auto_now=True)
categorie_article = models.ForeignKey('BlogCat',
on_delete=models.CASCADE,
default='1')
contenu = RichTextField(null=False)
def __str__(self):
return self.titre_article
def get_absolute_url(self):
return f"/blog/{self.id}"
class BlogCat(models.Model):
nom_categorie = models.CharField(max_length=255)
def __str__(self):
return self.nom_categorie
def get_absolute_url(self):
return f"/blog/cat/{self.nom_categorie}"
From there I can't imagine what the code in the view.py should be.
Can someone help ?
Thx a lot :)
Jeff
You can annotate your BlogCat model with the number of Blogs:
from django.db.models import Count
BlogCat.objects.annotate(nblog=Count('blog'))
The BlogCat objects that arise from this queryset will have an extra attribute .nblog that contains the number of related Blog objects.
You have to use cout()
obj = YourObject.objects.all().count()

Many DB queries for string representation of model object

I have this models:
class Country(models.Model):
name = models.CharField(max_length=250)
def __str__(self):
return str(self.name)
class City(models.Model):
name = models.CharField(max_length=250)
country = models.ForeignKey(Country, default=None, blank=True)
def __str__(self):
return str(self.name)
class Airport(models.Model):
name = models.CharField(max_length=250)
city = models.ForeignKey(City, default=None, blank=True)
def __str__(self):
return "{0} - {1} - {2}".format(self.city, self.city.country, self.name)
class Tour(models.Model):
title = models.CharField(max_length=200)
tour_from = models.ForeignKey(Airport)
tour_to = models.ForeignKey(Airport)
def __str__(self):
return str(self.title)
For string representation of Airport Django sends many requests to DB:
302.06 ms (591 queries including 586 similar and 586 duplicates )
Queries screenshot:
At tour/create page I have a ModelForm for creating a tour and Django sends these queries for displaying form.
forms.py:
class TourCreateForm(forms.ModelForm):
class Meta:
model = Tour
fields = ['title', 'tour_from', 'tour_to']
views.py:
class DashboardTourCreate(CreateView):
model = Tour
template_name = "dashboard/tour/create.html"
form_class = TourCreateForm
def get_context_data(self, **kwargs):
context = super(DashboardTourCreate, self).get_context_data(**kwargs)
context['page_name'] = ['tour', 'tour-index']
context['page_title'] = "Create Tour"
return context
How I can reduce queries count?
Root Cause
def __str__(self):
return "{0} - {1} - {2}".format(self.city, self.city.country, self.name)
When the tour_to and tour_from fields are rendered as <option> in the <select> widget the Airport.__str__ method is called. Because Airport.__str__ has self.city.county and both of these are ForeignKey's, the Django ORM issues a query to grab the airports city and the citys country.
And it does this for every single Airport that is an <option> which means the problem will get progressively worse the more Airport's that are added.
Solution
Leverage select_related[1]. select_related will tell the Django ORM to pull in the related fields ('city', 'county') whenever it grabs an Airport.
class TourCreateForm(forms.ModelForm):
class Meta:
model = Tour
fields = ['title', 'tour_from', 'tour_to']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['tour_from'].queryset = Airport.objects.select_related(
'city__country',
)
self.fields['tour_to'].queryset = Airport.objects.select_related(
'city__country',
)
[1] https://docs.djangoproject.com/en/2.1/ref/models/querysets/#select-related
As f-string is a string literal expressions evaluated at run time link, this might be faster that other string format but i am not fully sure. I am expecting following modification may reduce the over all time.
class Airport(models.Model):
name = models.CharField(max_length=250)
city = models.ForeignKey(City, default=None, blank=True)
def __str__(self):
return f"{self.city} - {self.city.country} - {self.name}"
I fix this issue by adding Queryset to forms.py:
class TourCreateForm(BaseForm):
airports = Airport.objects.select_related('city', 'city__country').all()
tour_from = forms.ModelChoiceField(queryset=airports)
tour_to = forms.ModelChoiceField(queryset=airports)
But I think this is not correct!

querysets in models with manytomany fields (Django)

My question is associated with making querysets on models that are interconnected across many to many fields.
context - an app that a student enters and has to rate his or her teachers. the questions the app shows the student have the following logic: each student must rate some teachers. Each teacher has different categories of questions associated with them ("intelligence", "respect", "empathy",etc.) and each of these categories has some questions associated with it.
The models are:
class Items(models.Model):
item = models.TextField()
def __str__(self):
return self.item
class Categories(models.Model):
category = models.CharField(max_length=50,null=True)
items_associated = models.ManyToManyField(Items)
def __str__(self):
return self.category
class Professors(models.Model):
professor = models.CharField(max_length=50,null=True)
categories_assigned = models.ManyToManyField(Categories)
def __str__(self):
return self.professor
class Students(models.Model):
student_logged = models.CharField(max_length=50,null=True)
professors_to_evaluate = models.ManyToManyField(Professors)
def __str__(self):
return self.student_logged
when a student enters the web has some associated teachers (model Students) these teachers in turn have some categories assigned (model Professors), these categories in turn have some questions associated (model Categories). I want to store in a dictionary these questions that are in the model Items. How can I do it?
I've tried to filter and __in but I can't get it.
Many thanks and thank you for the wisdom
I highly suggest you use the related_name attribute. I've added _x to the related names to make the query more obvious.
class Items(models.Model):
item = models.TextField()
def __str__(self):
return self.item
class Categories(models.Model):
category = models.CharField(max_length=50,null=True)
items_associated = models.ManyToManyField(Items, related_name='category_x')
def __str__(self):
return self.category
class Professors(models.Model):
professor = models.CharField(max_length=50,null=True)
categories_assigned = models.ManyToManyField(Categories, related_name='professor_x')
def __str__(self):
return self.professor
class Students(models.Model):
student_logged = models.CharField(max_length=50,null=True)
professors_to_evaluate = models.ManyToManyField(Professors, related_name='student_x')
def __str__(self):
return self.student_logged
items_for_student = Items.objects.filter(category_x__professor_x__student_x=student)
But also the naming conventions you are using for the fields are a bit quirky. I've used best practices below so you can see what that would look like.
Don't have a field with the same name as the model
Models should be singular (with rare exceptions)
ManyToMany or ForeignKey relations should share the name of the model to make querying self document.
With those rules here is what the best practice looks like.
class Item(models.Model):
name = models.TextField()
def __str__(self):
return self.name
class Category(models.Model):
name = models.CharField(max_length=50,null=True)
items = models.ManyToManyField(Item, related_name='categories')
def __str__(self):
return self.name
class Professor(models.Model):
name = models.CharField(max_length=50,null=True)
categories = models.ManyToManyField(Category, related_name='professors')
def __str__(self):
return self.name
class Student(models.Model):
name = models.CharField(max_length=50,null=True)
professors = models.ManyToManyField(Professor, related_names='students')
def __str__(self):
return self.name
And with that structure the query would look like:
items = Item.objects.filter(categories__professors__students=student)
Also note that the above query will be very expensive to run on a database as it would evaluate to 3 joins.