Django: want to loop through _set for pk values only - django

I'm stuck trying to figure out how to filter my template values with the detail view PK. I have a detail view for my employee. I wish to display my employee's subjects, where I then wish to filter the subjects with the evaluations that have been made for the subject.
I've gotten so far that I can show my subject names, and show all the evaluations for each subject. However, I don't want to show ALL of them I only want to show the ones that exist for the current employee (detailView PK). As you can see in my template, I'm using _set to make the relation, but I have no clue on how to filter the PK into that equation.
Example, what I want:
Subject 1:
Evaluationname - employee Johnny
Evaluationname - employee Johnny
Example, what I currently have:
Subject 1:
Evaluationname - employee Johnny
Evaluationname - employee Chris
I don't want Chris's evaluation, I only wish to filter the primary key, so in this case Johnny's evaluations.
Template
{% for subject in subject_list %}
{{ subject.subejctname }}
{% for evaluation in subject.evaluation_set.all %}
<div>
<p>{{ evaluering.ma }} | {{ evaluering.ma.firstname }} | {{ evaluering.ma.lastname }}</p>
</div>
{% empty %}
<p>No evaluations founds.</p>
{% endfor %}
{% endfor %}
View
class EmployeeDetailView(DetailView):
template_name = 'evalsys/employee/alle_employees_eval.html'
model = Employee
# Uses employee PK to make a detail view
def view_employee_with_pk(self, pk=None):
if pk:
employee = Employee.objects.get(pk=pk)
else:
employee = self.employee
args = {'employee': employee, }
return render(self, 'evalsys/employee/alle_employees_eval.html', args)
def get_context_data(self, **kwargs):
context = super(EmployeeDetailViewDetailView, self).get_context_data(**kwargs)
context['subject_list'] = Subject.objects.all()
return context
Subject Model
class Subject(models.Model):
id = models.AutoField(primary_key=True)
subjectname = models.CharField(max_length=255, help_text="Indtast navnet på faget.")
slug = models.SlugField(max_length=200, unique=True)
Evaluation model
class Evaluation(models.Model):
id = models.AutoField(primary_key=True)
employee_num = models.ForeignKey('Employee', on_delete=models.CASCADE, null=True)
subjectname = models.ForeignKey('Subject', on_delete=models.CASCADE, null=True)
Employee model
class Employee(models.Model):
id = models.AutoField(primary_key=True)
slug = models.SlugField(max_length=200)
employee_num = models.IntegerField(help_text="Indtast medarbejderens MA-nummer. (F.eks 123456)")
firstname = models.CharField(max_length=30, help_text="Indtast medarbejderens fornavn.")
lastname = models.CharField(max_length=30, help_text="Indtast medarbejderens efternavn.")
subjectname = models.ForeignKey('Subject', on_delete=models.CASCADE, null=True)

Reverse relationships (subject.evaluation_set) can be prefetched, this is a technique for reducing the number of database queries made when you access the reverse relationship for many objects in a queryset. When using the following queryset, when you access subject.evaluation_set.all it will not perform an additional DB access as the result has already been cached on each instance
Subject.objects.all().prefetch_related('evaluation_set')
This cached result can be modified by using Prefetch objects. Using these you can limit the contents of subject.evaluation_set.all to only contain the result that you want
Subject.objects.all().prefetch_related(
Prefetch(
'evaluation_set',
queryset=Evaluation.objects.filter(employee=self.employee)
)
)

Your model structure is confusing. Are you able to detail the relationship between employee, subject and evaluation?? You have mentioned you wish to display an employee's subjects, but via your model structure, an employee can have only one subject, as employee is related to the subject by a foreign key.
Below I have suggested some changes to your model names and your model structure so it might make more sense to retrieve your evaluations in the template. Feel free to ask questions about your model design as that is crucial to design your views, templates, etc.
Also please refer here for model naming conventions
Django Foreign Key Reference
Django Model Coding Style (PEP8)
Subject Model
class Subject(models.Model):
id = models.AutoField(primary_key=True)
subject_name = models.CharField(max_length=255, help_text="Indtast navnet på
faget.")
slug = models.SlugField(max_length=200, unique=True)
Employee Model
class Employee(models.Model):
id = models.AutoField(primary_key=True)
slug = models.SlugField(max_length=200)
employee_num = models.IntegerField(help_text="Indtast medarbejderens MA-nummer. (F.eks 123456)")
first_name = models.CharField(max_length=30, help_text="Indtast medarbejderens fornavn.")
last_name = models.CharField(max_length=30, help_text="Indtast medarbejderens efternavn.")
subjects = models.ManyToManyField(Subject, related_name='employee', through='Evaluation')
Evaluation Model
class Evaluation(models.Model):
name = models.CharField(blank=True,max_length=50)
employee = models.ForeignKey('Employee', on_delete=models.CASCADE)
subject = models.ForeignKey('Subject', on_delete=models.CASCADE)
So the assumption, is an employee can have different subjects and the mapping is defined via a through model using many-to-many.
Your DetaiView can then just be
class EmployeeDetailView(DetailView):
template_name = 'evalsys/employee/alle_employees_eval.html'
model = Employee
def get_context_data(self, **kwargs):
context = super(EmployeeDetailViewDetailView,
self).get_context_data(**kwargs)
context['evaluations'] = Evaluation.objects.filter(employee=self.object)
return context
Template
{% for evaluation in evaluations %}
{{ evaluation.subject.subject_name }}
<p>{{ evaluation.name }} | {{ evaluation.employee.first_name }} |
{{evaluation.employee.last_name }}</p>
{% empty %}
<p>No evaluations founds.</p>
{% endfor %}

Related

How can I display something from data base after multiple criteria is satisfied in django

Here i have a Model Recommenders:
class Recommenders(models.Model):
objects = None
Subject = models.ForeignKey(SendApproval, on_delete=models.CASCADE, null=True)
Recommender = models.CharField(max_length=20, null=True)
Status = models.CharField(null=True, max_length=8, default="Pending")
Time = models.DateTimeField(auto_now_add=True)
And another model Approvers:
class Approvers(models.Model):
objects = None
Subject = models.ForeignKey(SendApproval, on_delete=models.CASCADE, null=True)
Approver = models.CharField(max_length=20, null=True)
Status = models.CharField(null=True, max_length=8, default="Pending")
Time = models.DateTimeField(auto_now_add=True)
And my SendApproval model as:
class SendApproval(models.Model):
Subject = models.CharField(max_length=256)
Date = models.DateField(null=True)
Attachment = models.FileField(upload_to=get_file_path)
SentBy = models.CharField(null=True, max_length=100)
Status = models.CharField(null= True, max_length=8, default="Pending")
Now my problem is that I have to display the Subject and Attachment from SendApproval table only when all the recommender's Status in Recommenders table related to that subject is "Approved"
Don't know how can I know that...Thanks in advance...
Actually not have any Idea about the solution but the best answer will be appreciated...By the way, I am new to StackOverflow...So please let me know if there is some ambiguity in my question.
Offer two ways.
1.Here the Recommenders model is filtered by the Status='Approved' field. By the Subject field, which is associated with the primary model, I get access to the SendApproval fields.
Replace bboard with the name of the folder where your templates are placed.
I have this: templates/bboard which are in the application folder.
views.py
def info(request):
recomm = Recommenders.objects.filter(Status='Approved')
return render(request, 'bboard/templ.html', {'context': recomm})
urls.py
urlpatterns = [
path('info/', info, name='info'),
]
templates
{% for a in context %}
<p>{{ 'Subject' }} : {{ a.Subject.Subject }} {{ 'Attachment' }} : Link
{{ 'id SendApproval' }} : {{ a.Subject.id }}</p>
{% endfor %}
2.You can also filter by primary model by fields from a secondary model that is linked to the primary. Here, I passed in the filter the name of the model in lower case and the search field.
In the primary model, by default, a property is created to refer to the secondary model, this is the name of the model in lower case and the _set prefix. In the outer loop, all received rows of the primary model are sorted out, and in the inner loop, through recommenders_set.all(), we get the Status. As you can see it is Approved.
sa = SendApproval.objects.filter(recommenders__Status='Approved')
print(sa)
for i in sa:
for k in i.recommenders_set.all():
print('status', k.Status)

Get Data from all objects of a queryset in django

I have three models Order, OrderEntries and Product
class Order(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
order_entries = models.ManyToManyField(OrderEntries, blank=True)
...
class OrderEntries(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.IntegerField()
class Product(models.Model):
title = models.CharField(max_length=1000)
slug = models.SlugField(blank=True, null=True, unique=True)
Now, I have to get history of purchased products. As I can get all previous orders placed by a user like this
queryset = Order.objects.filter(user=self.request.user, client=self.request.client)
But how I can get list of products from all those orders?
If you specify a related_name on the ForeignKey field as follows:
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='products')
You could then access the products in your template, for example:
{% for order in orders %}
{% for order_entry in order.order_entries %}
{% for product in order_entry.products.all %}
{{ product }}
Please note: it isn't necessary to set related_name. If you don't set it, you can use BLA_set where BLA is the name of the field e.g.:
{% for product in order_entry.product_set.all %}
maybe this help :
product=[]
for e in queryset:
for ord in e['order_entries']:
for pr in ord['product'] :
product.append(pr)

Optimise django query when fetching information from multiple models

I am new to Django and using Django 3.0.6.
With the following code, I have been able to achieve the desired results and display detailed book information onto the template. However, on average, ORM makes 8 to 9 database queries to get detailed information about the book. I am looking for expert help to optimize my database queries so that I could fetch book-related information with fewer queries.
I tried using select_related() and prefetch_related() but without any luck, maybe I did it improperly. Is there a scope of using Q object or union(), just my thought? How can I achieve the same results with minimum queries to the database?
Please help me with detailed code, if possible.
models.py
class Publisher(models.Model):
publisher_name = models.CharField(max_length=50)
class Author(models.Model):
author_name = models.CharField(max_length=50)
class Booktype(models.Model):
book_type = models.CharField(max_length=20) # Hard Cover, Soft Cover, Kindle Edition, Digital PDF etc.
class Book(models.Model):
book_title = models.TextField()
slug = models.SlugField(max_length=50, unique=False)
published_date = models.DateField(auto_now=False, auto_now_add=False)
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
book_type = models.ManyToManyField(Booktype, through='BookPrice', through_fields=('book', 'book_type'))
# I created this separate model due to havy content and to keep Book model light
class BookDetail(models.Model):
a = models.TextField(null=True, blank=True)
b = models.TextField(null=True, blank=True)
c = models.TextField(null=True, blank=True)
book = models.OneToOneField(Book, on_delete=models.CASCADE)
class BookPrice(models.Model):
book_type = models.ForeignKey(Booktype, on_delete=models.CASCADE)
book = models.ForeignKey(Book, on_delete=models.CASCADE)
price = models.DecimalField(max_digits=7, decimal_places=2)
view.py
def get_book_details(request, book_id, slug):
book = Book.objects.get(id=book_id, slug=slug)
context = {'book': book}
return render(request, 'products/book_detail.html', context)
book_detail.html Template
# 1st databse query
{{ book.book_title }}
{{ book.id }}
{{ book.published_date }}
# 2nd databse query
{{ book.publisher.publisher_name }}
# 3rd databse query
{{ book.author.author_name }}
# 4th databse query
{{ book.bookdetail.a }}
{{ book.bookdetail.b }}
{{ book.bookdetail.c }}
# 5th to 9th databse query depending upon avaialble Book Types
{% for x in book.bookprice_set.all %}
{{ x.book_type }} {{ x.price|floatformat }}
{% endfor %}
You can use .select_related(…) [Django-doc] to fetch the publisher, author and bookdetail. We can use prefetch_related
def get_book_details(request, book_id, slug):
book = Book.objects.select_related(
'publisher', 'author', 'bookdetail'
).prefetch_related(
'bookprice_set', 'bookprice_set__book_type'
).get(id=book_id, slug=slug)
context = {'book': book}
return render(request, 'products/book_detail.html', context)

Django Retrieve Data From Two Tables By Join

Hi I have two tables which are Employee(eid,eName,contact_num,e-mail,adress,salary) and Nurse(nurse_id,e_id) and the "e_id" column of Nurse model has foreign key on 'eid' of Employee model.I know how to filter by specific id, however all of them as list so that, I want to return all nurses from Employee table.You can find my models below. I am new to Django that is why any help or hint is appreciated. Thanks in advance.
class Employee(models.Model):
eid = models.IntegerField(primary_key=True)
ename = models.CharField(db_column='eName', max_length=25) # Field name made lowercase.
contact_num = models.DecimalField(max_digits=12, decimal_places=0)
e_mail = models.CharField(max_length=30)
adress = models.CharField(max_length=250, blank=True, null=True)
salary = models.IntegerField()
class Nurse(models.Model):
nurse_id = models.IntegerField(primary_key=True)
e = models.ForeignKey(Employee, models.DO_NOTHING)
from an instance of employee class you can get the nurses as follows
employee_1 = Employee.objects.first()
nurses = employee_1.nurse_set.all()
can call in templates of list view of employee by
{% for obj in object_list %}
{{ obj.ename }}
{% for nurse in obj.nurse_set.all %}
{{ nurse.eid }}
{% endfor %}{% endfor %}

Django: transfering two primary keys to new detail view

I'm trying to create a detail view where I use employee and subject name to show the evaluations created in the specific subject.
I currently have this detail where, where I'm looping through the subject names and displaying all the evaluations for the subject for the employee. The view is using employee as the primary key.
So when I press on the subject name from the loop below, I wish to go to a new view where I show the evaluations for the subject by the specific employee.
Employee template
{% for subject in subject_list %}
<h5>{{ subject.subjectname }}</h5>
{% for evaluation in subject.evaluation_set.all %}
{{ evaluation.instructor }}
{{ evaluation.ma }}
{% endfor %}
{% endfor %}
I can't figure out what the best way to do this is. I believe using the employee primary key for the new view is correct, but how do I "transfer" the subject id from the loop to the next view?
Alternatively I can use the subject id for the view but then I don't understand how to "transfer" the employee over.
Employee view with subjects
class EmployeeEvalDetailView(DetailView):
template_name = 'evalsys/evalueringer/se_alle_evalueringer.html'
model = Employee
def get_context_data(self, **kwargs):
context = super(EmployeeEvalDetailView, self).get_context_data(**kwargs)
context['fagene'] = Subject.objects.all().prefetch_related(Prefetch('evaluation_set', queryset=Evaluation.objects.filter(ma=self.object)))
return context
Employee model
class Medarbejder(models.Model):
id = models.AutoField(primary_key=True)
slug = models.SlugField(max_length=200)
ma = models.IntegerField(help_text="Indtast medarbejderens MA-nummer. (F.eks 123456)")
firstname = models.CharField(max_length=30, help_text="Indtast medarbejderens fornavn.")
lastname = models.CharField(max_length=30, help_text="Indtast medarbejderens efternavn.")
subject = models.ManyToManyField('Subject', related_name='medarbejder', through='Evaluering')
Evaluering model
class Evaluering(models.Model):
id = models.AutoField(primary_key=True)
oprettet = models.DateTimeField(auto_now_add=True)
opdateret = models.DateField(auto_now=True)
ma = models.ForeignKey('Employee', on_delete=models.CASCADE, null=True)
subjectname = models.ForeignKey('Subject', on_delete=models.CASCADE, null=True)
instructor = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
Subject model
class Subject(models.Model):
id = models.AutoField(primary_key=True)
subjectname = models.CharField(max_length=255, help_text="Indtast navnet på faget.")
slug = models.SlugField(max_length=200, unique=True)
New detail view with multiple PK's
class FagEvalDetailView(DetailView):
model = Employee
template_name = 'evalsys/evalueringer/eksporter/detail_fag_eval.html'
def get_context_data(self, **kwargs):
context = super(FagEvalDetailView, self).get_context_data()
context['pk_alt'] = Subject.objects.get(id=self.kwargs.get('pk_alt', ''))
return context