how to display many to many field properly in django - django

This code saves the data and displays the data as i wanted.but while displaying courses it displays like this , ]> .It displays with
QuerySet[].i only want the courses name to be displayed.How can i do that
models.py
class Teacher(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=50)
phone = models.CharField(max_length=15)
email = models.EmailField()
image = models.ImageField(upload_to='Teachers',blank=True)
courses = models.ManyToManyField(Course)
views.py
def viewteacher(request):
teachers = Teacher.objects.all().order_by('-joined_date')
year1 = datetime.datetime.today().year
return render(request,'students/view_teacher.html',{'teachers':teachers,'year_list':range(2016,year1+1)})
template
{% for teacher in teachers %}
<tr>
<td>{{teacher.name}}</div></td>
<td>{{teacher.courses.all}}</td>
<td>{{teacher.address}}</td>
<td>{{teacher.phone}}</td>
<td>Profile</td>
<td>Edit</td>
<td>Delete</td>
</tr>
{% endfor %}
</tbody>

A QuerySet is rendered like <QuerySet [ ... ]>, so you can iterate over the queryset, and render it properly, like:
{% for teacher in teachers %}
<tr>
<td>{{teacher.name}}</div></td>
<td>{% for course in teacher.courses %} {{course.name}} {% endfor %}</td>
<td>{{teacher.address}}</td>
<td>{{teacher.phone}}</td>
<td>Profile</td>
<td>Edit</td>
<td>Delete</td>
</tr>
{% endfor %}
Obtain the list of courses for every individual teacher is however not very efficient. You can use .prefetch_related to speed up the process. It will then perform a limited number of queries to fetch all the courses, and link these properly to the correct teacher(s):
def viewteacher(request):
teachers = Teacher.objects.prefetch_related('courses').order_by('-joined_date')
year1 = datetime.datetime.today().year
return render(
request,
'students/view_teacher.html',
{'teachers':teachers,'year_list':range(2016,year1+1)}
)

Related

Django, Unable to view certain database information

I've been working on a project and I've been stuck for over a week and haven't been able to find the solution to this problem.
I'm creating a school management app. I've successfully allowed the teachers to create a schedule, allowed the students to view it and select it as part of their class. The problem I'm running into is that the teacher is not able to see the students information after they've chosen a class.
For models I have:
class Student(models.Model):
user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
englishName = models.CharField(max_length=200)
studentName = models.CharField(max_length=200)
studentId = models.CharField(max_length=200)
birthday = models.DateField()
gender = models.CharField(max_length=6)
gradeLevel = models.CharField(max_length=8)
since = models.DateField()
duration = models.CharField(max_length=3)
contactNumber = models.CharField(max_length=13, default=00000000)
email = models.EmailField(max_length=200, default='address#email.com')
def __str__(self):
return self.studentName
def index(request):
data=Student
context = {'form': data}
return render(request, 'student-information.html', context)
class Schedule(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
date = models.DateField(null = True, blank = True)
time = models.TimeField(null = True, blank = True)
duration = models.TextField(null = True, blank = True)
capacity = models.PositiveSmallIntegerField(default=1)
student = models.ForeignKey(Student, null = True, on_delete=models.CASCADE)
def __datetime__(self):
return self.date + '' + self.student
def index(request):
data = Schedule
context = {'form': data}
return render(request, 'set-schedule.html', context)
For my views file I have:
#login_required
#allowed_users(allowed_roles=['teacher'])
def dailySchedule(request):
today = datetime.now().date()
schedules = Schedule.objects.filter(date=today)
students = []
for schedule in schedules:
students.append(schedule.student)
context = {'today': datetime.now().date(), 'schedules': schedules, 'students': students}
return render(request, 'eslbeeph/daily-schedule.html', context)
and for the html file I have:
{% extends 'main.html' %}
{% block content %}
<div class="container-fluid">
<div class="daily-schedule-title">
<h3 class="daily-schedule-header">Schedule</h3>
</div>
<!--Information to be obtained by from the database.-->
<div class="daily-schedule-table">
<table class="daily-schedule">
<tr>
<th>Date</th>
<th colspan="6">{{ today|date:"F d, Y" }}</th>
</tr>
<tr>
<th>Start Time</th>
<th>Account</th>
<th>Duration</th>
<th>Student ID</th>
<th>Student Name</th>
<th>Class Status</th>
<th>Student Profile</th>
</tr>
{% for schedule in schedules %}
<tr>
<td>{{ schedule.time|date:"H:i" }}</td>
{% for student in students %}
<td>{{ schedule.student.user.username }}</td>
<td>{{ schedule.duration }}</td>
<td>{{ schedule.student.studentId }}</td>
<td>{{ schedule.student.englishName }}</td>
{% endfor %}
<td>{ status }</td>
<td></td>
</tr>
{% endfor %}
</table>
</div>
</div>
{% endblock content %}
Now, There's a lot more code to the project but these are the areas I think are causing the problem. I've tried a number of different things to get this to work, but nothing's worked.
If anyone can see where I'm going wrong or what I need to do let me know. I appreciate any help anyone can give me.
What I've been trying to do is get the a table to show up with the schedule time, the user name of the student who selected the schedule, the number of minutes the teacher stated the class would be, the students id number, and the students english name. The class status and the button for the students profile I plan to handle later.
Right now the time and the class length show up fine. But no matter what I do I cannot get the students user name, english name, and ID to populate at all.
Any tips, examples, or guesses as to why this is happening would be appreciated.
EDIT
After doing a bit more work I found that the Student model is not connecting to the Schedule model. So, the problem is different from what I originally suspected. It's not that the student information isn't being shown. It's that the schedule a student selects isn't recognizing the student has selected it.

Django: Accessing full User information via ManyToMany field

everyone- I'm new to Django and working on my first big project and I'm having trouble with accessing default Django User model information via a ManyToMany relationship. I've spent a great deal of time searching and can't crack it.
models.py
class Event(models.Model):
event_name = models.CharField(max_length=200, null=True, unique=True)
#etc...
class School(models.Model):
user = models.ManyToManyField(User)
event = models.ForeignKey(Event, null=True, on_delete=models.PROTECT)
#etc...
My url contains the id of the Event, so...
views.py
def schools(request, pk):
event = Event.objects.get(id=pk)
school = School.objects.filter(event=event)
return render(request, 'accounts/schools.html', {'event':event, 'school':school})
template
{% for school in school %}
<tr>
<td>{{school.name}}</td>
<td>{{school.user.all}}</td>
{% endfor %}
On my template, I'm able to use {{school.user.all}} to get me a Queryset displayed with the username of each User, but I want the first_name and last_name and can't see to figure out how to get that..
Thank you for any suggestions. I greatly appreciate your time!
You should include both schools and users to your context.
You can do this with a dictionary. Add each school as a key, and users of each school as its values. Then you can pass this dictionary to your template.
View function:
def schools(request):
school_dict = dict()
schools = School.objects.all()
for school in schools:
school_dict[school] = school.user.all()
return render(request, 'accounts/schools.html', {'schools': school_dict})
And in your template:
{% for school, users in schools.items %}
<h3>{{ school.title }}</h3>
<table>
{% for user in users %}
<tr>
<td>{{ user.first_name }}</td>
<td>{{ user.last_name }}</td>
</tr>
{% endfor %}
</table>
{% endfor %}
I was able to add this to my school model to get what I wanted:
def director(self):
test = ",".join([str(p) for p in self.user.all()])
user = User.objects.get(username=test)
return user.first_name + " " + user.last_name
HOWEVER: if there is more than one user associated with "School" it displays blank

Django initial loading of page taking too long

Hi I am a beginner at Django and I am working on a project that lists 100 companies in each page along with there contacts and also the amount of items sold. Here is an example:
As you can see the initial loading time of the page is very high. But when I refresh the page it refreshes very fast because I am using caching.
Here are some of my other files:
models.py
from __future__ import unicode_literals
from django.db import models
class Company(models.Model):
name = models.CharField(max_length=150)
bic = models.CharField(max_length=150, blank=True)
def get_order_count(self):
orders = self.orders.count()
return orders
def get_order_sum(self):
orders = Order.objects.filter(company=self)
total_sum = sum([x.total for x in orders])
return total_sum
class Meta:
ordering = ['-id']
class Contact(models.Model):
company = models.ForeignKey(
Company, related_name="contacts", on_delete=models.PROTECT)
first_name = models.CharField(max_length=150)
last_name = models.CharField(max_length=150, blank=True)
email = models.EmailField()
def get_order_count(self):
orders = self.orders.count()
return orders
class Order(models.Model):
order_number = models.CharField(max_length=150)
company = models.ForeignKey(Company, related_name="orders", on_delete=models.CASCADE)
contact = models.ForeignKey(Contact, related_name="orders", on_delete=models.SET_NULL, blank=True, null=True)
total = models.DecimalField(max_digits=18, decimal_places=9)
order_date = models.DateTimeField(null=True, blank=True)
added_date = models.DateTimeField(auto_now_add=True)
modified_date = models.DateTimeField(auto_now=True)
def __str__(self):
return "%s" % self.order_number
views.py
from django.shortcuts import render
# Create your views here.
from django.views.generic import ListView
from mailer.models import Company, Contact, Order
class IndexView(ListView):
template_name = "mailer/index.html"
model = Company
paginate_by = 100
The html
<div class="container">
<table class="table table-borderless">
{% if is_paginated %}
<tr><td>
{% if page_obj.has_previous %}
«
{% endif %}
</td>
<td></td>
<td></td>
<td>
{% if page_obj.has_next %}
»
{% endif %}
</td>
</tr>
{% endif %}
<tr>
<th>Name</th>
<th>Order Count</th>
<th>Order Sum</th>
<th>Select</th>
</tr>
{% for company in company_list %}
<tr>
<td>{{ company.name }}</td>
<td>{{ company.get_order_count }}</td>
<td>{{ company.get_order_sum|floatformat:2 }}</td>
<td><input type="checkbox" name="select{{company.pk}}" id=""></td>
</tr>
{% for contact in company.contacts.all %}
<tr>
<td> </td>
<td>{{ contact.first_name }} {{ contact.last_name }}</td>
<td>Orders: {{ contact.get_order_count }}</td>
<td></td>
</tr>
{% endfor %}
{% endfor %}
</table>
</div>
Is there any way in which I can reduce the initial load time. Please show me an efficient way to solve this problem.
Each {{company.get_order_count}} will hit the DB. Admittedly with a very simple query, but even so, it will slow things down.
You want to annotate the objects with this count. Use
from django.db.models import Count
class IndexView(ListView):
template_name = "mailer/index.html"
model = Company
paginate_by = 100
def get_queryset(self):
return super().get_queryset().annotate( num_orders=Count('orders') )
and replace {{ company.get_order_count }} with {{ company.num_orders }}. This will turn N+1 DB queries into one DB query.
That's the easy one. There's a similar problem with get_order_sum which can almost certainly be solved with another annotation involving the django.db.Sum. Sorry but its late and my stomach is growling and I don't have any confidence that I would get that one right straight off the top of my head.
The cheat sheet on annotation is here. You might also need to look at aggregation.
Oh, and install Django_debug-toolbar in your developer environment. Every time in future it gets slow, you can just click there to see what SQL was executed and how long it took.

Getting a specific field (e.g. name) instead of pk in object_list for rendering for a foreignkey

I have an app for example:
class example(models.Model, TemplateHelperClass):
reviewer = CharField(max_length=255, verbose_name="Title")
book = ForeignKey(books, on_delete=models.PROTECT)
review = TextField()
...
class books(models.Model, TemplateHelperClass):
author = CharField(max_length=255, verbose_name="Author")
book_title = CharField(max_length=255, verbose_name="Title")
publisher = CharField(max_length=255, verbose_name="Author")
def __str__(self):
return self.book_title
...
class templateHelperClass():
paginate_by = 15
def get_fields(self):
return [(field, field.value_to_string(self)) for field in self._meta.fields]
*views.py*
class bookReviewList(ListView):
model = example
template_name = "bookreviews.html"
...
*bookreviews.html*
{% extends "base.html" %}
{% block content %}
<table class="table">
<thead>
<tr>
{% for fld, val in object_list.0.get_fields %}
<th>{{ fld.verbose_name |title }}</th>
{% endfor %}
<th></th>
</tr>
</thead>
<tbody>
{% for object in object_list %}
<tr>
{% for fl1, val1 in object.get_fields %}
<td>{{val1}}</td>
{% endfor %}
<td><a class="btn btn-dark" href='{{ object.get_absolute_url }}'>Update</a></td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
The output html displays the pk for the book, and not the book title (the __str__ method) in this case. I'd like to display the __str__ method. Two questions, both related:
(1) Is there a more elegant way to represent the form with my attributes in table format. (I'm using bootstrap for some of the formatting)
(2) If not, how do I get the foreignkey field to display the __str__ return and not the pk?
The TemplateHelperClass is a MixIn for multiple models and the equivalent template for bookreview.html is likewise used for multiple views so I don't want to have to use specific field names in either if I can avoid it on the principle of DRY.
The easy solution is to use custom logic instead of field.value_to_string(...) method
class templateHelperClass():
paginate_by = 15
def get_fields(self):
return [(field, str(getattr(self, field.name))) for field in self._meta.fields]
OR,
use the custom logic only for FK field, as
class templateHelperClass:
paginate_by = 15
def get_fields(self):
return [
(field, str(getattr(self, field.name)))
if isinstance(field, models.ForeignKey)
else (field, field.value_to_string(self))
for field in self._meta.fields
]

Django: Two questions on extending ListView with get_context_data

I tried something similar to this. I have three models:
class PartBase(models.Model):
name = models.CharField('Name', max_length=120)
price = models.DecimalField("Price per part", decimal_places=2, max_digits=6)
class Sett(models.Model):
name = models.CharField('Name', max_length=120)
class PartRelation(models.Model):
part = models.ForeignKey(PartBase, on_delete=models.CASCADE)
qty = models.PositiveIntegerField("Quantity")
sett = models.ForeignKey(Sett, related_name='setts', on_delete=models.SET_NULL, null=True)
def get_position_price(self):
return self.qty * self.part.price
now I want to add the price of all the items in a Sett in a row in my HTML.
{% extends 'base.html' %}
{% block title %}
Add Set
{% endblock title %}
{% block content %}
<table class="table">
<tr>
<th>Set Name</th>
<th>Total price</th>
</tr>
{% for set in setts %}
<tr>
<td>{{ set.name }}</td>
<td>{{ set.test }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
I wanted to override the get_context_data method somehow like this:
class SetListView(ListView):
model = Sett
context_object_name = "setts"
def get_context_data(self,**kwargs):
context = super(SetListView, self).get_context_data(**kwargs)
context['test'] = "price"
return context
But I only get an empty field in the template (which I assumed would have the word "price".
I can access the price in the shell via
for s in Sett.objects.all():
pr = PartRelation.objects.filter(sett=s)
price = 0
for p in pr:
price += p.get_position_price()
But how would I put the code from the shell in the get_context_data() function, so that for every row I get the corresponding total price?
This did the trick:
class SetListView(ListView):
model = Sett
context_object_name = "setts"
def get_context_data(self, **kwargs):
context = super(SetListView, self).get_context_data(**kwargs)
for s in context["setts"]:
pr = PartRelation.objects.filter(sett=s)
s.total_price = 0
for p in pr:
s.total_price += p.get_position_price()
return context
I would gladly get feedback if that is a good approach (and why not/how to do better).