django templates loop two querysets - python

I am trying to get details of two querysets and display on my datatable. I can display details of first queryset but not of the second which is also dependent on the first queryset.
My views.py function;
def truck_list(request):
q_data = quar_contacts.objects.all().filter(.....)
hot_details = []
for d in q_data:
h_details = truck_contacts.objects.filter(p_contacts=d.id)
hot_details.append(h_details)
my_list_data = zip(q_data, hot_details)
data = {'quar_data': my_list_data}
return render(request, 'syst/truck_list.html', data)
I can print out details of the zip list as a tuple i.e data = tuple(my_list_data) and see the data.
Then my truck_list template on the datatable body section :
<tbody>
{% for q_data, hot_details in quar_data %}
<tr>
<td>{{ q_data.last_name }}</td>
<td>{{ q_data.first_name }}</td>
<td>{{ q_data.age }}</td>
<td>{{ q_data.sex }}</td>
<td>{{ q_data.phone_number }}</td>
<td>{{ hot_details.hotel_name}}</td>
<td>{{ hot_details.date_check_in }}</td>
<td>{{ hot_details.created_by }}</td>
</tr>
{% endfor %}
</tbody>
How can i get the hot_details (hotel_name and date_checked_in) displayed on my datatable. How can I loop for each contact what hotel details are there.
Is there an easier way of achieving this?
My Models:
class quar_contacts(models.Model):
first_name = models.CharField(max_length=50)
middle_name = models.CharField(max_length=50, blank=True)
last_name = models.CharField(max_length=50)
sex = models.CharField(max_length=50)
dob = models.DateField(default=date.today)
passport_number = models.CharField(max_length=50, blank=True)
created_by = models.ForeignKey(User, on_delete=models.DO_NOTHING, related_name='quar_updated_by')
class truck_contacts(models.Model):
patient_contacts = models.ForeignKey(quar_contacts, on_delete=models.DO_NOTHING, related_name='truck_contact')
vehicle_registration = models.CharField(max_length=50, blank=True)
company_name = models.CharField(max_length=50, blank=True)
hotel = models.CharField(max_length=50, blank=True)
hotel_town = models.CharField(max_length=50, blank=True)
date_check_in = models.DateField(default=date.today)
date_check_out = models.DateField(default=date.today)
Also, how do you get the 'created_by' value which is a foreign key of the Users table?

You can use annotate to add additional fields to each object from quar_contacts queryset. I don't know your db schema, but if "quar contact" has a foreign key to "truck contact" it can be something like that:
from django.db.models import F
q_data = quar_contacts.objects.all().filter(.....).annotate(
hotel_name=F("your_relation_name__hotel_name"),
date_check_in=F("your_relation_name__date_check_in"),
)
Then you can access hotel_name and date_check_in just like other fields from this objects.
Please let me know if it's helpful. If you have different relation between those models I'll update my answer.

Using "annotate" I have managed to to add the fields the other way. getting 'truck_contacts' queryset then look for the parent field values 'quar_contacts'. Thanks to #rafaljusiak idea above.
all_data = truck_contacts.objects.all().annotate(
first_name=F("patient_contacts__first_name"),
last_name=F("patient_contacts__last_name"),
sex=F("patient_contacts__sex"),
age=F("patient_contacts__dob"),
passport_number=F("patient_contacts__passport_number"),
phone_number=F("patient_contacts__phone_number"),
created_by=F("patient_contacts__created_by"),
)
But am still struggling how to display 'created by' being a foreign key of Users table

Related

How can I get Total Deposit of Customers

I am working on a Django project with 2 Models; Customer and Deposit and I want to display a list of Customers with their names, deposited dated, account number, and Total Deposited within the year so how do I do it the right way.
See what I have tried but Couldn't get the Customers' name in my Django Templates.
Models:
class Customer(models.Model):
surname = models.CharField(max_length=10, null=True)
othernames = models.CharField(max_length=20, null=True)
account_number = models.CharField(max_length=10, null=True)
address = models.CharField(max_length=50, null=True)
phone = models.CharField(max_length=11, null=True)
date = models.DateTimeField(auto_now_add=True, null=True)
#Get the url path of the view
def get_absolute_url(self):
return reverse('customer_create', args=[self.id])
#Making Sure Django Display the name of our Models as it is without Pluralizing
class Meta:
verbose_name_plural = 'Customer'
#
def __str__(self):
return f'{self.surname} {self.othernames} - {self.account_number}'
class Deposit(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, null=True)
acct = models.CharField(max_length=6, null=True)
staff = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
deposit_amount = models.PositiveIntegerField(null=True)
date = models.DateTimeField(auto_now_add=True)
def get_absolute_url(self):
return reverse('create_account', args=[self.id])
def __str__(self):
return f'{self.customer} Deposited {self.deposit_amount} by {self.staff.username}'
here is my view code:
def customer_list(request):
#Get Current Date
current_date = datetime.now().date()
#Get Current Month Name from Calendar
current_month_name = calendar.month_name[date.today().month]
group_deposits = Deposit.objects.filter(date__year=current_date.year).order_by('acct')
grouped_customer_deposit = group_deposits.values('acct').annotate(total `=Sum('deposit_amount')).order_by()`
context = { 'customers':grouped_customer_deposit,}
Here is how I tried to display the result in Django Template:
{% for deposit in customers %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ deposit.acct }}</td>
<td>{{ deposit.customer.surname }}</td>
<td>{{ deposit.total }}</td>
<td>{{ customer.deposit.date }}</td>
<th scope="row"><a class="btn btn-info btn-sm" href=" ">Deposit</a></th>
</tr>
{% endfor %}
Someone should graciously help with the most efficient way of getting the Total Deposit for each customer with their names, account number, and date deposited. Thank in anticipation for your kind answers.
As you are using the values method:
grouped_customer_deposit = group_deposits.values('acct').annotate(total `=Sum('deposit_amount')).order_by()`
Django will return a queryset that returns dictionaries rather than model instances. See Django Queryset API reference for more details.
This means that you cannot reference your model relationships as you are trying to do here:
<td>{{ deposit.customer.surname }}</td>
as that information will not be in the dictionary passed to the template (you can do a print of grouped_customer_deposit in your view, or display it in your template -- {{ customers }} -- to see what I mean).
Ergo, if you remove your call to values() then your template will receive model instances which should solve your problem, and make your template work as-is.
grouped_customer_deposit = group_deposits.annotate(total `=Sum('deposit_amount')).order_by()`
(Although your syntax for your annotate is not correct, so I'm assuming that was a mistake when you pasted your code here.)

How can i group by student id

Please i would like anyone to help me out, tryna grouping this student result from the lecturer session so that lecturer can view each student based on the course student submitted i have successfully get a list but the list are not grouped by the student ids
here is the view for lecturer request
class LecturerListOfStudentResult(View):
def get(self, request, *args, **kwargs):
lecturer = Lecturers.objects.get(admin=request.user.id)
result = LecturerCourse.objects.filter(lecturer=lecturer)
context = {
'question':result,
}
return render(request, 'superadmin/lecturer/list-result-
student.html', context)
Here is the list of all submitted student result view
class ListAllSubmittedAnswerByCourseRegister(View):
def get(self, request, *args, **kwargs):
lecturer = Lecturers.objects.get(admin=request.user.id)
result = StudentSubmittedAnswer.objects.filter(lecturer=lecturer,
student=int(kwargs['id']))
context = {'question':result}
return render(request, 'superadmin/lecturer/list-all-result-
student.html', context)
here is the detailview for the list which i only get the count record
class LecturerListOfStudentResultDetail(View):
def get(self, request, *args, **kwargs):
student = Students.objects.get(admin=int(kwargs['id']))
result = StudentSubmittedAnswer.objects.filter(student=student)
skipped = StudentSubmittedAnswer.objects.filter(student=student,
right_answer='Not Submitted').count()
attempted = StudentSubmittedAnswer.objects.filter
(student=student).exclude(right_answer='Not Submitted').count()
rightAns=0
percentage=0
for row in result:
if row.question.right_opt == row.right_answer:
rightAns+=1
if len(result) > 0:
percentage=(rightAns*100)/result.count()
return render(request, 'superadmin/lecturer/view-result-
detail.html', {'result': result,'total_skipped':
skipped,'attempted': attempted, 'rightAns': rightAns,
'percentage':percentage})
Here is my url.py
path('lecturer/course/registration',
LecturerCourseRegistration.as_view(), name="lecturer-course-
registration"),
path('list/lecturer/course/registration',
ListLecturerCourseRegistration.as_view(), name="list-lecturer-
course-registration"),
path('list/submitted/course/answer/student/<int:id>/',
ListAllSubmittedAnswerByCourseRegister.as_view(), name="list-all-
submitted-course-question-by-student"),
here is the list of student html
{% for r in question %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ r.course.name }}</td>
<td>{{ r.created_at }}</td>
<td><a href="{% url 'emisapp:list-all-submitted-course-
question-by-student' r.lecturer.admin.id %}">View</a></td>
{% endfor %}
Here is the html view for looping all the exam submitted for each student
{% for row in result %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ row.course.name }}</td>
<td>{{ row.question.question }}</td>
<td>{{ row.question.right_opt }}</td>
{% if row.question.right_opt == row.right_answer %}
<td class="bg-success text-white">{{ row.right_answer }}</td>
{% else %}
<td class="bg-danger text-white">{{ row.right_answer }}</td>
{% endif %}
</tr>
{% endfor %}
Models.py
class StudentSubmittedAnswer(models.Model):
id = models.AutoField(primary_key=True)
course = models.ForeignKey(Courses)
lecturer = models.ForeignKey(Lecturers)
question = models.ForeignKey(QuizQuestion)
student = models.ForeignKey(Students)
right_answer = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=True, null=True)
Here is my added Models.py
class StudentSubmittedAnswer(models.Model):
id = models.AutoField(primary_key=True)
lecturercourse = models.ForeignKey(LecturerCourse, null=True,
related_name='studentsubmittedanswer', on_delete=models.CASCADE)
lecturer = models.ForeignKey(Lecturers, null=True,
related_name='studentsubmittedanswer', on_delete=models.CASCADE)
question = models.ForeignKey(QuizQuestion,
related_name='studentsubmittedanswer', on_delete=models.CASCADE)
student = models.ForeignKey(Students,
related_name='studentsubmittedanswer', null=True,
on_delete=models.CASCADE)
right_answer = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=True, null=True)
class Faculty(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
slug = models.SlugField(null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now_add=True)
objects = models.Manager()
class Department(models.Model):
id = models.AutoField(primary_key=True)
faculty = models.ForeignKey(Faculty, default=None)
name = models.CharField(max_length=255)
slug = models.SlugField(null=True)
short_desc = models.TextField(null=True, blank=True)
class Courses(models.Model):
id = models.AutoField(primary_key=True)
faculty = models.ManyToManyField(Faculty, related_name='courses')
department = models.ManyToManyField(Department)
name = models.CharField(max_length=255)
slug = models.SlugField()
short_desc = models.TextField(blank=True, null=True)
long_desc = models.TextField(blank=True, null=True)
class StudentFaculty(models.Model):
id = models.AutoField(primary_key=True)
student = models.ForeignKey(Students, null=True)
studentfaculty = models.ForeignKey('Faculty')
slug = models.SlugField(null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now_add=True)
class StudentDepartment(models.Model):
id = models.AutoField(primary_key=True)
student = models.ForeignKey(Students)
studentfaculty = models.ForeignKey(StudentFaculty)
studentdepartment = models.ForeignKey(Department)
slug = models.SlugField(null=True)
class StudentCourses(models.Model):
id = models.AutoField(primary_key=True)
student = models.ForeignKey(Students)
studentfaculty = models.ForeignKey(StudentFaculty)
studentdepartment = models.ForeignKey('StudentDepartment')
studentcourse = models.ForeignKey(Courses)
class QuizQuestion(models.Model):
id = models.AutoField(primary_key=True)
lecturer = models.ForeignKey(Lecturers)
faculty = models.ForeignKey(LecturerFaculty, null=True)
lecturerdepartment = models.ForeignKey(LecturerDepartment, null=True)
lecturercourse = models.ForeignKey(LecturerCourse)
session = models.ForeignKey(SessionYearModel)
semester = models.ForeignKey(Semester, null=True)
mark = models.IntegerField(null=True)
examtype = models.ForeignKey('ExamType', related_name='quizzes')
question = models.TextField()
opt_1 = models.CharField(max_length=255)
opt_2 = models.CharField(max_length=255)
opt_3 = models.CharField(max_length=255)
opt_4 = models.CharField(max_length=255)
class LecturerFaculty(models.Model):
id = models.AutoField(primary_key=True)
lecturer = models.ForeignKey('Lecturers',
related_name='lecturerfaculty', null=True, on_delete=models.CASCADE)
faculty = models.ForeignKey('Faculty')
slug = models.SlugField(null=True)
class LecturerDepartment(models.Model):
id = models.AutoField(primary_key=True)
lecturer = models.ForeignKey('Lecturers')
lecturerfaculty = models.ForeignKey('LecturerFaculty')
lecturerdepartment = models.ForeignKey('Department')
class LecturerCourse(models.Model):
id = models.AutoField(primary_key=True)
lecturer = models.ForeignKey('Lecturers')
faculty = models.ForeignKey('Faculty')
lecturerdepartment = models.ForeignKey('LecturerDepartment')
lecturercourse = models.ForeignKey('Courses')
slug = models.SlugField(null=True)
class Lecturers(models.Model):
id = models.AutoField(primary_key=True)
admin = models.OneToOneField(CustomUser, on_delete=models.CASCADE)
profile_pic = models.ImageField(default='default.jpg')
middle_name = models.CharField(max_length=255)
You can use values and annotate to group by student Ids.
class ListAllSubmittedAnswerByCourseRegister(View):
def get(self, request, *args, **kwargs):
lecturer = Lecturers.objects.get(admin=request.user.id)
=> result = StudentSubmittedAnswer.objects.values('student','lecturer').annotate(answers_count=Count('answers')).filter(lecturer=lecturer,
student=int(kwargs['id'])).order_by()
context = {'question':result}
return render(request, 'superadmin/lecturer/list-all-result-
student.html', context)
I assumed the name of the fields in the model student answers lecturer
Refrence
Django documentation: values(), annotate(), and Count
Django documentation: Aggregation, and in particular the section entitled Interaction with default ordering or order_by()
https://docs.djangoproject.com/en/4.0/topics/db/aggregation/#order-of-annotate-and-values-clauses

Dictionary items not showing up in template in django

I have gathered data from two tables that is Orders and Purchases (joined) and saved that in dictionary, when I'm printing the qty on the console its giving me the value but when I'm trying to print the values on template it not showing up however columns from the orders table like first name last name status address is shown on the template but qty and product from the purchases table is not showing up on the template but can be easily printed on console
can somebody help???
Models.py
Dispatcher = models.ForeignKey(Dispatchers , null=True, on_delete=models.SET_NULL )
Firstname = models.CharField(max_length=200 , null=True)
Lastname = models.CharField(max_length=200 , null=True)
Email = models.CharField(max_length=200 , null=True)
Address = models.CharField(max_length=200 , null=True)
Country = models.CharField(max_length=200 , null=True)
State = models.CharField(max_length=200 , null=True)
status = models.CharField(max_length=200 , null=True)
def __str__(self):
return self.Lastname
class purchases(models.Model):
orders_id = models.ForeignKey(Orders , null=True, on_delete=models.SET_NULL )
product = models.CharField(max_length=200 , null=True)
qty = models.CharField(max_length=200 , null=True)
price = models.CharField(max_length=200 , null=True)
def __str__(self):
return self.product
views.py file
dispatcherOrders = {}
orders = request.user.dispatchers.orders_set.all()
for order in orders:
dispatcherOrders[order] = purchases.objects.get(orders_id = order.id)
print(dispatcherOrders[order].qty)
return render(request , 'dispatcherOrders.html',{'dispatcherOrders' : dispatcherOrders})
dispatcherOrders.html
{%for Orders in dispatcherOrders%}
<tr>
<th scope="row">1</th>
<td>{{Orders.Firstname}}</td>
<td>{{Orders.Lastname}}</td>
<td>{{Orders.Address}}</td>
<td>{{Orders.product}}</td>
<td>{{Orders.qty}}</td>
<td>{{Orders.status}}</td>
</tr>
{% endfor %}
In your code, dispatcherOrders dictionary keys are orders and dictionary values are purchases:
dispatcherOrders[order] = purchases.objects.get(orders_id = order.id)
yet you are iterating only on dictionary keys, the orders:
{%for Orders in dispatcherOrders%}
To get the info from purchases, iterate the dictionary items too.
So your template code should be similar to
{%for Orders, Purchase in dispatcherOrders.items %}
<tr>
<th scope="row">1</th>
<td>{{Orders.Firstname}}</td>
<td>{{Orders.Lastname}}</td>
<td>{{Orders.Address}}</td>
<td>{{Purchase.product}}</td>
<td>{{Purchase.qty}}</td>
<td>{{Purchase.status}}</td>
</tr>
{% endfor %}
Here's how .items work: https://stackoverflow.com/a/6285769/3694363

Traversing Django models downstream in template language

I have the following model setup:
class Model1(models.Model):
val1 = models.CharField(max_length=25, blank=True)
val2 = models.CharField(max_length=25, blank=True)
user = models.ForeignKey('users.User', on_delete=models.PROTECT, related_name='model1')
class Model2(models.Model):
val3 = models.BinaryField()
model1_link = models.ForeignKey(Case, on_delete=models.CASCADE, related_name='model2')
class Model3(models.Model):
id = models.BigAutoField(primary_key=True)
model2_link = models.ForeignKey(Model2, on_delete=models.CASCADE, related_name='model3')
class Model4(models.Model):
id = models.BigAutoField(primary_key=True)
model3_link = models.ForeignKey(Model3, on_delete=models.CASCADE, related_name='model4', null=True, default=None)
pred = models.CharField(max_length=50)
In my HTML template, I have a section where I iterate over entries from Model1 (e.g. val1), and would like to be able for each value, to include field 'pred' from Model4. Models 1-4 are daisy-chained through their FK`s at the moment. Yes, I know I could just include FK in Model4 linking it to Model1, but from logical point of view, I do not prefer this option at the moment.
Anyway, expression like this does not get the job done on my end:
...
{% for entry in model1_entries %}
...
{% if user.is_superuser and entry.model2.model3.model4.count > 0 %}
something here
{% endif %}
...
{% endfor %}
I figure the problem has something to do with the fact that call model1.model2 returns a set of all model2's related to model1, but I dunno how I can pick one in this expression and run with it through the rest of models before reaching #4.
Any ideas?
I'd add a method to your Model1 something like:
class Model1(models.Model):
val1 = models.CharField(max_length=25, blank=True)
val2 = models.CharField(max_length=25, blank=True)
user = models.ForeignKey('users.User', on_delete=models.PROTECT, related_name='model1')
def get_model4_count(self):
count = 0
for m2 in Model2.objects.filter(model1_link=self):
for m3 in Model3.objects.filter(model2_link=m2):
count += Model4.objects.filter(model3_link=m3).count()
return count
your template says count, so this uses, count, but you can update to whatever you want.
At least that would allow you to write a test/debug that your method is returning the correct data, and allow you to easily use it in your template.
...
{% for entry in model1_entries %}
...
{% if user.is_superuser and entry.get_model4_count > 0 %}
something here
{% endif %}
...
{% endfor %}

Django - join two query sets by id?

Im trying to join two tables together before being sent to a view, using _set in a view causes 100s of queries which is highly inefficient.
example structure sites.models.py
class SiteData(models.Model):
location = models.CharField(max_length=50)
site_type = models.ForeignKey(SiteTypes, verbose_name="Site Type", \
on_delete=models.PROTECT)
bgp_as = models.CharField(max_length=6, verbose_name="BGP AS Number")
opening_date = models.DateField(verbose_name="Site opening date")
last_hw_refresh_date = models.DateField(verbose_name="Date of latest hardware refresh", \
blank=True, null=True)
is_live = models.BooleanField(default=False, verbose_name="Is this a live site?")
example structure config.models.py
class SiteSubnets(models.Model):
site_data = models.ForeignKey(SiteData, verbose_name="Location", \
on_delete=models.PROTECT, blank=True, null=True)
subnet = models.GenericIPAddressField(protocol='IPv4', \
verbose_name="Subnet", blank=True, null=True)
subnet_type = models.ForeignKey(SubnetTypes, verbose_name="Subnet Type")
vlan_id = models.IntegerField(verbose_name="Vlan ID", blank=True, null=True)
peer_desc = models.IntegerField(verbose_name="Peer description", blank=True, null=True)
site_ip = models.BooleanField(default=False, verbose_name="Is this a site supernet IP?")
class Meta:
verbose_name = "Site Subnets"
verbose_name_plural = "Site Subnets"
Queries:
site_subnets = SiteSubnets.objects.only('subnet').filter(site_ip=True)
site_data = SiteData.objects.only('location','is_live','bgp_as','postcode','opening_date','live_link_type')
Desired Outcome example:
Location | Subnet | BGP AS
---------------------------------
London | 10.10.10.0 | 65001
Manchester | 10.10.20.0 | 65002
...
I cant do a select_related without having the SitesSubnet table as the main table, as when I do it on site data, I get
django.core.exceptions.FieldError: Invalid field name(s) given in select_related: 'site_subnets'. Choices are: site_type
If I use the SiteSubnet as the main table, if a Site does not have a SiteSubnet.site_up I wont get the Site info. displayed
does anyone know a way around this that will display all the data and not run n+1 queries?
EDIT:
prefetch also fails with the below error:
AttributeError: Cannot find 'site_subnets_set' on SiteData object, 'site_subnets_set' is an invalid parameter to prefetch_related()
I would be sending the data to a template to be accessed in a loop i.e
<table>
<tr>
<td>Location</td>
<td>Subnet</td>
<td>BGP AS</td>
<tr>
{%for site in sitedata %}
<tr>
<td>{{ site.location }}</td>
<td>{{ site.subnet }}</td>
<td>{{ site.bg_as }}</td>
<tr>
{% endfor %}
Thanks
You can use prefetch_related to prefetch sitesubnets for the sitedata queryset.
SiteData.objects.prefetch_related('sitesubnets_set')

Categories

Resources