Get the first three human readable elements in a queryset - django

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.

Related

Django can't display data from two models

I am trying to display data from two different models. The two models have a one-to-many relationship but I am not sure why it is not displaying the data from the MembersPresent model. Here are my models
and view
class fly_minute(models.Model):
mode = (
('Email', 'Email'),
('Zoom', 'Zoom'),
('Alternative', 'Alternative'),
)
mode_of_meeting = models.CharField(max_length=100, choices=mode, blank=False, )
date = models.DateField()
Time = models.TimeField()
minute_prepared_by = models.CharField(max_length=25)
location = models.CharField(max_length=25)
authorize_by = models.CharField(max_length=25)
item = models.TextField()
owner = models.CharField(max_length=25)
def __str__(self):
return self.mode_of_meeting
class MembersPresent(models.Model):
flyminute = models.ForeignKey(fly_minute, on_delete=models.CASCADE)
name = models.CharField(max_length=25)
status = models.CharField(max_length=25)
email = models.EmailField(max_length=25)
phone = models.IntegerField()
def __str__(self):
return self.name
#login_required(login_url='login_page')
def MinutesReport(request, minute_id):
report = fly_minute.objects.filter(id=minute_id)
return render(request, 'minute_report.html', locals())
{%for rpt in report%}
<tbody>
<tr class="table-active">
<td>{{rpt.flyminute.name}}</td>
<td>{{rpt.flyminute.status}}</td>
<td>{{rpt.flyminute.email}}</td>
<td>{{rpt.flyminute.phone}}</td>
</tbody>
{%endfor%}
You need to use 'prefetch_related()'
Returns a QuerySet that will automatically retrieve, in a single batch, related objects for each of the specified lookups.
This has a similar purpose to select_related, in that both are designed to stop the deluge of database queries that is caused by accessing related objects, but the strategy is quite different.
models.py
class MembersPresent(models.Model):
flyminute = models.ForeignKey(
fly_minute,
related_name='memberspresent_set', # this will be the default name, incase of need of change provide a new one
on_delete=models.CASCADE
)
...
views.py
#login_required(login_url='login_page')
def MinutesReport(request, minute_id):
report = fly_minute.objects.filter(id=minute_id).prefetch_related('memberpresent_set')
return render(request, 'minute_report.html', locals())
For further control, you can use Prefetch along with prefetch_related()
Note:
locals() returns a dict of all the locally defined variables in the current scope and this may result in exposing sensitive info. Instead of that create a custom dict and return it.
...
context = {
'report': report,
...
}
return render(request, 'minute_report.html', context)
You are referencing and querying in the opposite way. Your fly_minute model doesn't contain MembersPresent model as the Foreign Key. You are filtering the fly_minute model and trying to access MembersPresent model. So, you should change your fly_minute to in this way:
class fly_minute(models.Model):
mode = (
('Email', 'Email'),
('Zoom', 'Zoom'),
('Alternative', 'Alternative'),
)
mode_of_meeting = models.CharField(max_length=100, choices=mode, blank=False, )
date = models.DateField()
Time = models.TimeField()
minute_prepared_by = models.CharField(max_length=25)
location = models.CharField(max_length=25)
authorize_by = models.CharField(max_length=25)
item = models.TextField()
owner = models.CharField(max_length=25)
member = models.ForeignKey(MembersPresent, on_delete=models.CASCADE)#add this line and remove fly_minute in MembersPresent model
def __str__(self):
return self.mode_of_meeting
Then in template file change it to in this way:
{%for rpt in report%}
<tbody>
<tr class="table-active">
<td>{{rpt.member.name}}</td>
<td>{{rpt.member.status}}</td>
<td>{{rpt.member.email}}</td>
<td>{{rpt.member.phone}}</td>
</tbody>
{%endfor%}
I guess it works
Thanks, I ended up using fetch-related and had to make adjustments to my template.
<tr>
<th>Name</th>
<th>Status</th>
<th>Email</th>
<th>Phone</th>
</tr>
</thead>
{%for rpt in report%}
{%for rpts in rpt.memberspresent_set.all%}
<tbody>
<tr class="table-active">
<td>{{rpts.name}}</td>
<td>{{rpts.status}}</td>
<td>{{rpts.email}}</td>
<td>{{rpts.phone}}</td>
</tbody>
{%endfor%}
{%endfor%}

Django display multiple uploaded files

I am trying to display multiple uploaded file URLs but I'm not sure how to do it. I am a form that the users can use to upload multiple files that are working perfectly but now I want to be able to display these files. For instance, a company can have more than one file.
class InternationalCompany(models.Model):
International_company_Name = models.CharField(max_length=50)
International_company_Id = models.CharField(max_length=50)
options = (
('Yes', 'Yes'),
('No', 'No'),
)
Does_it_have_a_Financial_Dealers_License = models.CharField(max_length=20, choices=options, null=True)
Register_Office = models.TextField(max_length=500, null=True)
Beneficial_owner = models.CharField(max_length=100, null=True)
Beneficial_owner_id = models.FileField(upload_to='passport/pdf/',null=True)
expire_date = models.DateField(null=True)
BO_Phone_number = models.IntegerField(null=True)
BO_Email_Address = models.EmailField(null=True)
BO_Residential_Address = models.TextField(max_length=200, null=True)
def __str__(self):
return self.International_company_Name
class SupportDocuments(models.Model):
Supporting_Documents = models.FileField(upload_to='support_doc/pdf/', null=True)
internationalcompany = models.ForeignKey(InternationalCompany, on_delete=models.CASCADE)
def __str__(self):
return self.Supporting_Documents.url
I try something like this but it only displayed one url of the file instead of the multiple urls for the files that are associate with that company.
{%for company in doc%}
<tr>
<td></td>
<td>{{company.internationalcompany.Beneficial_owner}}</td>
<td>{{company.Supporting_Documents.url}}</td>
</tr>
{%endfor%}
I have also try out this but it doesn't display anything.
{%for company in doc.Supporting_Documents.all%}
{{company.url}}
{%endfor%}
def supporting_doc(request, company_id):
doc = SupportDocuments.objects.filter(id=company_id)
return render(request, 'supporting_doc.html', locals())
First of all, it might be better to use context rather than locals() as discussed here
Second, the way you get the doc queryset is wrong. id=company_id The id of SupportingDocuments is different from company_id. What you really need to do is internationalcompany.id=company_id.
so views.py:
def supporting_doc(request, company_id):
documents = SupportDocuments.objects.filter(internationalcompany.id=company_id)
context = { "documents": documents}
return render(request, 'supporting_doc.html', context)
in template:
{% for doc in documents %}
{{ doc.internationalcompany.Beneficial_owner}}
{{ doc.Supporting_Documents }}
{% endfor %}
On a side note, it may be better to use all lowercases in fields. This way, it is easy to distinguish fields and models.

Problem with set value in view and template

I want to create a list where the user can give titles to his list in which he selects in which category he should be, this is how the model looks like
class UserListAnime(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
anime = models.ManyToManyField(Anime, through='ListAnime')
class Anime(models.Model):
title = models.CharField(max_length=200)
slug = extension_fields.AutoSlugField(populate_from='title', blank=True)
class ListAnime(models.Model):
LIST_CHOICES = (
(WATCHING, 'Oglądam'),
(PLANNING, 'Planuję'),
(COMPLETED, 'Ukończone'),
(DROPPED, 'Porzucone'),
(ONHOLD, 'Wstrzymane'),
(MISSING, 'Pomijam'),
)
user = models.ForeignKey(UserListAnime, on_delete=models.CASCADE, null=True, blank=True)
anime = models.ForeignKey(Anime, on_delete=models.CASCADE, null=True, blank=True)
type = models.CharField(max_length=30, choices=LIST_CHOICES, null=False, blank=False)
In the view I only have to take a list of the user and I have displayed it but I want it to be filtered through the type in ListAnime
def ListAnimeView(request, pk, username):
list_anime = UserListAnime.objects.filter(user__pk=pk, user__username=username,
listanime__type='ogladam',
anime__listanime__type='ogladam').all()
context = locals()
and html looks like
{% for list in list_anime.anime.all %}
{{ list }}
{% endfor %}
My question is how to extract all records when type = LIST_CHOICES and show this in html
EDIT: SOLVED just need change in view from UserListAnime,objects.. to ListAnime.objects
and in html should be
{% for list in list_anime %}
{{ list.anime }}
{% endfor %}
I don't know exactly what are you trying to achieve. Do you want to get a list of ListAnime entities given a user, and get only which are of type 'ogladam'?
def listAnimeView(request, pk):
list_anime = ListAnime.objects.filter(user__user_id=pk, type='ogladam')
return render(request, 'template-name', context=['list_anime': list_anime])'

Django loop over list from view in html template

I'm asking how I can pick up a for loop in my django class based view to my HTML template.
User can check one or multiple checkboxe(s) and I would like to display informations for each checked checkboxes.
In my view I have :
checkbox_list = self.request.GET.getlist('PUBSDChoice') #Get object id checked by user
context_data['checkbox_list'] = checkbox_list
for checkbox in checkbox_list:
pubsd = Publication.objects.get(id=checkbox)
get_request = Download.objects.filter(pub__publication__id=pubsd.id).count()
context_data['pubsd'] = pubsd
context_data['get_request'] = get_request
Now, in my template I have :
<table>
<tbody>
<tr>
<th>{% trans 'Publication ID' %}</th>
<th>{% trans 'Requests' %}</th>
</tr>
{% for item in checkbox_list %} # I'm not sure for this loop
<tr>
<td>{{ pubsd.pub_id }}</td> #Do I have to add item.??
<td>{{ get_request }}</td>
</tr>
{% endfor %}
</tbody>
</table>
Thank you very much
EDIT :
This is my real code with all querysets inside my view :
checkbox_list = self.request.GET.getlist('PUBSDChoice')
context_data['checkbox_list'] = checkbox_list
for checkbox in checkbox_list:
pubsd = Publication.objects.get(id=checkbox) # display publication id
# Count number of requests in download table for checked publication
get_request = Download.objects.filter(pub__publication__id=pubsd.id).count()
#Get sum of publication usage (= downloads) for checked publication
get_download = Download.objects.filter(pub__publication__id=pubsd.id).aggregate(get_sum_usage=Sum('usage'))
# Get country which maximum download the checked publication
get_country = Country.objects.filter(download__pub__publication__id=pubsd.id).annotate(
ndown=Count('download')).order_by('-ndown').first()
# Get customer which maximum download the checked publication
get_customer = Download.objects.filter(pub__publication__id=pubsd.id).values('email').order_by(
'email').annotate(count_email=Count('email')).first()
# Get the best downloaded format for the checked publication
get_format = Download.objects.filter(pub__publication__id=pubsd.id).values('pub__format').annotate(
format_count=Count('pub__format')).order_by('-format_count').first()
context_data['pubsd'] = pubsd
context_data['get_request'] = get_request
context_data['get_download'] = get_download
context_data['get_country'] = get_country
context_data['get_customer'] = get_customer['email']
context_data['get_format'] = get_format['pub__format']
And this is exactly my model :
class Publication(models.Model):
pub_id = models.CharField(max_length=10, verbose_name=_('publication ID'), default='')
title = models.CharField(max_length=512, verbose_name=_('publication title'), null=False, unique=True)
category = models.ForeignKey(Category, verbose_name=_('category'), null=False, related_name='publication')
nb_document = models.IntegerField(verbose_name=_('number of documents'), default=0)
nb_download = models.IntegerField(verbose_name=_('number of downloads'), default=0)
new_publication = models.BooleanField(verbose_name=_('new publication'), default=True)
class Document(models.Model):
language = models.CharField(max_length=2, verbose_name=_('language'), choices=LANGUAGE_CHOICES, null=False)
format = models.CharField(max_length=10, verbose_name=_('format'), choices=FORMAT_CHOICES, null=False)
title = models.CharField(max_length=512, verbose_name=_('document title'), null=False)
publication = models.ForeignKey(Publication, verbose_name=_('publication title'), null=False,
related_name='documents')
nb_download = models.IntegerField(verbose_name=_('number of downloads'), default=0)
class Download(models.Model):
email = models.CharField(max_length=150, verbose_name=_('e-mail'), null=False)
country = models.ForeignKey(Country, verbose_name=_('country'), related_name='download')
pub = models.ForeignKey(Document, verbose_name=_('Publication ID'), null=False)
usage = models.IntegerField(verbose_name=_('usage'), default=0)
class Customer(models.Model):
email = models.CharField(max_length=150, verbose_name=_('e-mail'), null=False)
country = models.ForeignKey(Country, verbose_name=_('country'))
institution = models.CharField(max_length=255, verbose_name=_('institution'), null=True)
As I said previously, for one checked publication it works fine, but when I want to check multiple publications, it does't work.
If someone could help me to convert this, it will be super ! Else I will try to use the good answer previously done to do that :)
Here you each time overwrite the previous value of the key. As a result in the final context_data contains only values extracted in the last iteration.
checkbox_list = self.request.GET.getlist('PUBSDChoice')
context_data['checkbox_list'] = checkbox_list
context_data['pubsd'] = Publication.objects.filter(
id__in=checkbox_list
).annotate(
ndown=Count('publication__download') # or something related
)
We thus filter the queryset such that it only contains Publications with the id in the list, and we annotate with the number of downloads (It is possible that the expression is a bit different, since you did not include the models.py).
The above will not be sorted according to the checkbox_list. If that is a requirement, you can post-sort it.
In the template, you can then render this with:
{% for item in pubsd %}
<tr>
<td>{{ item.pub_id }}</td>
<td>{{ item.ndown }}</td>
</tr>
{% endfor %}
We thus iterate over the pubsd queryset, and for each item (this is a Publication object), we render item.pub_id, and item.ndown.

Only show object once and count it

after searching for a long time without getting an answer i'm gonna try here.
I'm working on django. My project is a mailling system, each time a recipient open a mail i know what mail get opened who opened it and when it get opened.
Here is the table where i display the stats. It shows me every recipient and the datetime, but my problem is that i want to see every recipient once, and then to show how many times it get opened.
<tbody>
{% for stat_mail in stat_mail %}
{% ifchanged stat_mail.recipient %}
<tr>
<td>{{ stat_mail.recipient }}</td>
<td>{{ stat_mail.datetime }}</td>
<td>{{ stat_mail.recipient_set.all|length }}</td>
</tr>
{% endifchanged %}
{% endfor %}
</tbody>
For example :
Test 1 opened the mail at 5PM and at 8PM, so the table should display
Test 1 / 5PM, 8PM / 2
The name of the recipient, the date of the oppening, and the number of oppening.
Sorry for the bad english but i'm french :)
Hope someone can help me, if u have other question or need more code just ask.
Thanks a lot !
EDIT : My models.py
class Recipient(models.Model):
name = models.CharField(max_length=255, verbose_name= ('Nom'), null=True, blank=True)
first_name = models.CharField(max_length=255, verbose_name= ('Prénom'), null=True, blank=True)
mail = models.EmailField(verbose_name= ('Adresse du destinataire'))
def __unicode__(self):
return u'%s %s' % (self.name, self.first_name)
class Tag(models.Model):
name = models.CharField(max_length=255, blank=False)
recipients = models.ManyToManyField(Recipient, verbose_name='Destinataires', null=False, blank=False, related_name="tags")
def __unicode__(self):
return self.name
class Mail(models.Model):
subject = models.CharField(max_length=255, verbose_name= ('Sujet'), blank=False, null=False)
content = HTMLField(verbose_name= ('Contenu'), blank=False, null=False, default=' ')
tags = models.ManyToManyField(Tag, verbose_name= ('Destinataires'), null=True, blank=True, related_name='mails')
recipients = models.ManyToManyField(Recipient, verbose_name='Destinataires', null=True, blank=True, related_name='mails')
date_create = models.DateTimeField(verbose_name= ('Date de création'), default=datetime.now, blank=False, editable = False)
writer = models.ForeignKey(Intervenant, verbose_name= ('Personne écrivant la compagne'), null=True, blank=True)
holding = models.ForeignKey(Holding, verbose_name= ('Organisation'),related_name= ('Questionnaire_mail'), null=False, blank=False, default=1, editable = False)
sended = models.BooleanField(verbose_name = ('Compagne envoyée ?'), default=False, editable=False)
opened = models.IntegerField(verbose_name=("Nombre totale d'ouverture"), default=0, editable=False)
email = models.EmailField(verbose_name='Destinataire de Test', blank=True, null=True)
date_create = models.DateTimeField(verbose_name= ('Date de création'), default=datetime.now, blank=False, editable = False)
date_sent = models.DateTimeField(verbose_name= ('Date de création'), blank=True, null=True, editable = False)
def __unicode__(self):
return self.subject
class Mail_Stats(models.Model):
mail = models.ForeignKey(Mail, verbose_name= ('Compagne'), related_name='mails_stats')
recipient = models.ForeignKey(Recipient, verbose_name= ('Destinataires'), null=True, blank=True, related_name='mails_stats')
datetime = models.DateTimeField(verbose_name= ('Date et Heure'), auto_now_add=True)
You should prepare your data in the view and then pass it to the template. Something like this (untested):
from django.shortcuts import render
from .models import Mail_Stats
def stats_view(request):
recipient_list = [] # this will be put in the template context
current_recipient = None
cnt_read = 0
read_datetime_list = []
for stat in Mail_Stats.objects.all().order_by('mail', 'recipient'):
if stat.recipient != current_recipient:
if cnt_read > 0:
recipient_list.append({
'recipient': current_recipient,
'read_datetime_list': read_datetime_list,
'cnt_read': cnt_read
})
current_recipient = stat.recipient
cnt_read = 0
read_datetime_list = []
cnt_read += 1
read_datetime_list.append(stat.datetime)
# add last recipient to the list, if not None
if current_recipient is not None:
recipient_list.append({
'recipient': current_recipient,
'read_datetime_list': read_datetime_list,
'cnt_read': cnt_read
})
render(request, 'mail_stats.html', { 'recipient_list': recipient_list })
Then in your template you could simply do something like this:
<tbody>
{% for r in recipient_list %}
<tr>
<td>{{ r.recipient }}</td>
<td>
<ul>
{% for dt in r.read_datetime_list %}
<li>{{ dt }}</li>
{% endfor %}
</ul>
</td>
<td>{{ r.cnt_read }}</td>
</tr>
{% endfor %}
</tbody>
The important thing is: do not struggle doing complex stuff in templates, keep them only for presentation purposes. Move all logic in the view (or models, or utility modules), there you can leverage all the power of Python.