Django template table formatting - django

I am building a student test results template but am having a problem. Currently I have created 2 tests (Maths and Spelling) in my Tests model. The problem is, if I enter the data (scores) for the second test (Spelling) for a student first, the score get incorrectly placed in the template - the score gets placed in the Maths column and not the Spelling column. It gets corrected/moved into the spelling column as soon as I enter the same student’s name in the Maths test. It is a minor issue, but the result still is being put in the wrong place and I don’t know why.
I must be doing something wrong. I am missing some sort of filtering I guess. Hopefully the code below will help explain it further. Thanks.
Models.py
class Student(models.Model):
first = models.CharField(max_length=100)
last = models.CharField(max_length=100)
gender = models.CharField(max_length=1)
teacher = models.ForeignKey(Teacher)
def __unicode__(self):
return "%s %s" % (self.first, self.last)
class Test(models.Model):
name = models.CharField(max_length=100)
Out_of = models.IntegerField(null=True, blank=True)
def __unicode__(self):
return self.name
class Meta:
ordering = ['name']
class Display(models.Model):
name = models.ForeignKey(Student, related_name='art')
test = models.ForeignKey(Test)
one = models.IntegerField(null=True, blank=True)
two = models.IntegerField(null=True, blank=True)
three = models.IntegerField(null=True, blank=True)
# views.py
def test(request):
return list_detail.object_list(
request,
queryset = Student.objects.all(),
template_name = 'display.html',
template_object_name = 'results',
# display.html
<table>
<tr>
<th>First</th>
<th>Name</th>
<th>Maths</th>
<th>Spelling</th>
</tr>
{% for item in results_list %}
<tr>
<td> {{ item.first }} </td>
<td> {{ item.last }} </td>
{% for x in item.art.all %}
<td> {{ x.one }} </td>
{% endfor %}
{% endfor %}
</tr>
</table>
# admin.py
class StudentInline(admin.TabularInline):
model = Display
class TestAdmin(admin.ModelAdmin):
inlines = [StudentInline]

The problem is you need to somehow order the item.art.all() in same order that of your table columns (ie. math numbers and then spelling). Also need to handle cases when only one of the test result is added.
But there isn't straight forward way of achieving this, you can't have foreign key (or relationship) member for ordering in Meta class.
So can have a method in your Display model to return list in preferred order and call it from template.
Sample solution below:
class Student(models.Model):
first = models.CharField(max_length=100)
last = models.CharField(max_length=100)
gender = models.CharField(max_length=1)
teacher = models.ForeignKey(Teacher)
def __unicode__(self):
return "%s %s" % (self.first, self.last)
#this would return in order as math, spelling
def get_result_list_in_order(self):
rlist = []
for tname in ['math', 'spelling']:
rtest = self.art.filter(test__name__iexact=tname)
if rtest:
rlist.append(rtest[0]) #add the first entry from filtered queryset
# or do
#rlist.extend(rtest)
else
rlist.append(None)
return rlist
So what you can in template is
{% for x in item.get_result_list_in_order %}
<td> {{x.one}} </td>
{% endfor %}

Related

Get the first three human readable elements in a queryset

I'm trying to render elements in a Django view. Every clinic object has many specialities, but for estetic reasons I only want the first three of them to be displayed in the template. I've tried:
def clinics_index(request):
clinics = Clinic.objects.all()
for clinic in clinics:
speciality = clinic.get_speciality_display
context = {
'clinics' : clinics,
'speciality' : speciality,
}
return render(request, 'guide/clinic/clinic_directory.html', context)
This now renders the human-readable name of the speciality field (which is a multiple choice field in the model). However, I can't use substraction to only get 3 elements like here:
speciality = clinic.get_speciality_display[:3]
As I get the following error:
TypeError at /guide/clinics/
'method' object is not subscriptable
How can I render it?
Edit:
This is the Clinic model:
class Clinic(models.Model):
name = models.CharField(max_length=75, blank=True, null=True)
speciality = MultiSelectField(choices=Speciality.choices, max_length=100, blank=True, null=True)
city = models.CharField(max_length=20, choices=Cities.choices, blank=True, null=True)
ward = models.CharField(max_length=20, choices=Wards.choices, blank=True, null=True)
full_address = models.CharField(max_length=100, blank=True, null=True)
maps_link = models.CharField(max_length=75, blank=True, null=True)
train_access = models.CharField(max_length=50, blank=True, null=True)
bus_access = models.CharField(max_length=50, blank=True, null=True)
parking = models.CharField(_('Parking availability'), max_length=75, blank=True, null=True)
phone_number = models.CharField(max_length=20, blank=True, null=True)
english_support = models.BooleanField(default=False, blank=True, null=True)
holiday_availability = models.BooleanField(_('Availability on weekends/holidays'), default=False, blank=True, null=True)
slug = models.SlugField(blank=True, null=True)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('guide:clinic_detail', kwargs={"slug" : self.slug})
And the template snippet:
<tbody>
{% for clinic in clinics %}
<tr>
<td>{{clinic.name}}</td>
<td>{{clinic.city}}</td>
<td>{{clinic.ward}}</td>
<td>{{speciality}}</td>
<td>More...</td>
</tr>
{% endfor %}
</tbody>
EDIT:
This code is rendering the first 3 human readable elements as I wanted:
clinics = Clinic.objects.all()
for clinic in clinics:
speciality = ','.join(clinic.get_speciality_display().split(',')[:3])
However, I am struggling to render it correctly with its correspondant instance. This code:
fff = [{'name': i.name, 'speciality': ','.join(i.speciality[:3])} for i in Clinic.objects.all()]
Is rendering the non-human readable names. How could connect both (and also display city and ward fields for each instance)?
I assume that in a loop you want to collect all the data. To do this, you need to save them to a list. But that's overkill, just pass clinics to a dictionary and iterate over all the values in the template. Also, for links, I used clinic.slug instead of clinic.get_absolute_url, since the model already returns the generated url through the get_absolute_url method.
views.py
def clinics_index(request):
clinics = Clinic.objects.all()[:3]
return render(request, 'guide/clinic/clinic_directory.html', {'context': clinics})
templates
{% for clinic in context %}
<p>{{ clinic }}</p>
<tr>
<td>{{ clinic.name }}</td>
<td>{{ clinic.city }}</td>
<td>{{ clinic.ward }}</td>
<td>{{ clinic.speciality }}</td>
<td>More...</td>
</tr>
{% endfor %}
</tbody>
Update 05.11.2022
To get the clinic.get_speciality_display() value, you need to call the method using parentheses. When I print out the value type, I get a string. Therefore, in order to take the first three elements, I turn the string into a list, select the desired number and again turn it into a string.
So you can select the first three records:
clinics = Clinic.objects.all()
for clinic in clinics:
speciality = ','.join(clinic.get_speciality_display().split(',')[:3])
all code:
views.py
def clinics_index(request):
#fff = [{'name': i.name, 'speciality': i.speciality[:3]} for i in Clinic.objects.all()]#if you need to display 'speciality' as a list
fff = [{'name': i.name, 'speciality': ','.join(i.speciality[:3])} for i in Clinic.objects.all()]
return render(request, 'guide/clinic/clinic_directory.html', {'context': fff})
templates
<tbody>
{% for a in context %}
<tr>
<p><td>{{ a.name }}</td></p>
<p><td>{{ a.speciality }}</td></p>
</tr>
{% endfor %}
</tbody>
If that's not what you need. Show what the data looks like and what you want to see.

Compare user attributes in template using IF statement

I have a list of users and want to display their tasks only if the selected user belongs to the same department. My Models have a department field that I want to compare.
This is my template code.
{% extends 'view_users.html' %}
{% block view_user_tasks %}
Back
<p> todo lists for {{ user }}</p>
{% for todo in view_user_tasks %}
<a id="{{todo.id}}" class="todo_remove"></a>
{% endfor %}
{% endblock view_user_tasks %}
What i want to do is evaluate this condition:
if request.user.Department == user.Department:
show user tasks
This are my respective views.
class ViewUsers(ListView):
model = CustomUser
template_name = 'view_users.html'
class ViewUserTasks(ListView):
model = Todo
template_name = 'view_user_tasks.html'
context_object_name = 'view_user_tasks'
My models.py
class Todo(models.Model):
title = models.CharField(max_length=30)
body = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=True, blank=True)
checked = models.BooleanField(default=False)
owner = models.ManyToManyField(CustomUser)
id = HashidAutoField(primary_key=True)
def __str__(self):
return "%s: %s" % (self.title, self.body)
class CustomUser(AbstractUser):
Department = models.CharField(max_length=30, blank=True)
How can I be able to accomplish this?
Do your filtering logic in the view. You can override the default get_queryset method and return only the Todos that you want.
class ViewUserTasks(ListView):
template_name = 'view_user_tasks.html'
context_object_name = 'view_user_tasks'
def get_queryset(self):
return Todo.objects.filter(user__Department=self.request.user.Department)
And then just loop through the returned data like you are already doing.
If I clearly understand your question, you can compare it like this:
{% if todo.user.id == user.id %}

Django printing many to many children not in same order as input

I have a Tourguide model with a many to many relationship with Places. Both models are defined as :
class Tourguide(models.Model):
id = models.AutoField(db_column='ID', primary_key=True, unique=True)
title = models.CharField(db_column='Title', max_length=255, blank=True)
places = models.ManyToManyField(Place, db_column='placesdj')
places_app = models.CharField(max_length=255, db_column='places')
created_on = models.DateTimeField(db_column='Created_On', default = now)
class Meta:
managed = False
db_table = 'tourguide'
def __str__(self):
return self.title
class Place(models.Model):
place_id = models.AutoField(db_column='ID', primary_key=True)
place_name = models.CharField(db_column='Place_Name', max_length=255)
address_line_1 = models.CharField(db_column='Address_Line_1', max_length=255)
address_line_2 = models.CharField(db_column='Address_Line_2', max_length=255)
area = models.CharField(db_column='Area', max_length=255)
class Meta:
managed = False
db_table = 'Place'
def __str__(self):
return self.place_name
THE PROBLEM
When I try to print the places in a tourguide using :
{% for place in tour.places.all %}
<tbody>
<td>{{ forloop.counter }}</td>
<td>id: {{place.place_id}}, {{ place.place_name }} </td>
<td> {{ place.description }} </td>
</tbody>
{% endfor %}
The order of the places is all random and not the same as the order I inputed it as. I want to print the places in the same order that I placed them in. My view for listing the tourguides and places within them is as so.
def tour_list(request, template_name='tourguides/tour_list.html'):
tourguide_list = Tourguide.objects.all()
paginator = Paginator(tourguide_list,6)
page = request.GET.get('page')
tourguides = paginator.get_page(page)
data = {}
data['tourguides'] = tourguides
return render(request, template_name, data)
Update
I have an array of place id's in the tourguide table, is there a way i can use that?
Relational databases do not sort rows by default, and since they are internally stored in all kinds of weird data structures, there is no "input order" either.
As a workaround, you can use the automatically generated place_id field as a sort key as it is pretty much guaranteed to go up as new entries are created. You can add a default sorting to your Place class, for example:
class Place(models.Model):
...
class Meta:
...
ordering = ('place_id',)
That would guarantee that any queries that will return a Place queryset will be ordered by place_id by default (i.e. unless you have an explicit .order_by() clause).

Why can not I display the data in a template for some models?

I'm new in django and i'm trying to display data in templates. Some models it can be displayed well. But, not for some models, and i don't know how it can be hapen.
This is my models:
class DosenPublikasi(models.Model):
userid = models.CharField(db_column='UserID', max_length=50, blank=True) # Field name made lowercase.
publikasiid = models.IntegerField(db_column='PublikasiID', blank=True, null=True) # Field name made lowercase.
class Meta:
managed = False
db_table = 'dosen_publikasi'
class Publikasi(models.Model):
id = models.IntegerField(primary_key=True) # AutoField?
judul = models.CharField(db_column='Judul', max_length=255, blank=True) # Field name made lowercase.
class Meta:
managed = False
db_table = 'publikasi'
And i try to display DosenPublikasi models. So this is my views:
def lihat_riwayat_publikasi(request):
username_session = request.session['username']
hak_akses_session = request.session['hak_akses']
dosenpublikasi = DosenPublikasi.objects.select_related().all()
data = {
'object_list':dosenpublikasi,
'username':username_session,
'hak_akses':hak_akses_session,
}
return render(request, 'lihat_riwayat_publikasi.html', data)
And this is my html templates:
{% for dosenpublikasi in object_list %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ dosenpublikasi.userid }}</td>
{% endfor %}
My question:
why when i try to display DosenPublikasi models it can't be displayed, but when i try to display anoter models like Publikasi it displayed properly ?
How is the solution ?
You will need one to do something like this
username_session = request.session['username']
hak_akses_session = request.session['hak_akses']
dosenpublikasi = DosenPublikasi.objects.all()
data = {
'object_list':dosenpublikasi,
'username':username_session,
'hak_akses':hak_akses_session,
}
return render(request, 'lihat_riwayat_publikasi.html', data)
Then on your html you will haven something like this
{% for items in object_list %}
{{ items.abdul }}
{% endfor %}
The other thing, you don have to have the id field on your models django will do the trick for you, happy coding #rang

How do I get one to many/foreign key to work in django?

Here is the code I've written:
models.py:
class Name_overall(models.Model):
rank = models.IntegerField()
name = models.CharField(max_length=50)
frequency = models.IntegerField()
def __unicode__(self):
return self.name
class Name_state(models.Model):
gender = models.CharField(max_length=1, choices=GENDER_CHOICES)
name_overall = models.ForeignKey(Name_overall, db_column='name')
frequency = models.IntegerField()
rank = models.IntegerField()
state = models.CharField(max_length=2, choices=STATE_CHOICES)
def __unicode__(self):
return self.state
views.py:
def single_name(request, baby_name):
baby_list = get_list_or_404(Name_overall, name=baby_name)
return render_to_response('names/single_name.html', {'baby_list': baby_list})
single_name.html:
{{ baby_list.name_state_set.all }}
Nothing shows up in the single_name.html template. If I change it to {{ baby_list }}, there is an object there, but I am not able to access the Name_state class. I thought I should be able to have access to Name_state because of the foreign key. What am I missing?
The baby_list context variable is a QuerySet. You need to iterate it and access the ForeignKey in the loop.
{% for item in baby_list %}
{{item.name_state_set.all}}
#iterate the name_state_set
{% for obj in item.name_state_set.all %}
{{obj}}
{% endfor %}
{% endfor %}