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.
Related
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.
Okay I thought I can google my way out of this but I am stuck.
Desired results are
Account_name Total
local sales 1802.50
int sales 0.00
from my models
class Account(models.Model):
account_number = models.IntegerField(unique=True, null=True, blank=True, default=None)
account_name = models.CharField(max_length=200, null=True, blank=True, unique=True)
class Sales(models.Model):
account_name = models.ForeignKey(Account, on_delete=models.CASCADE, related_name='incomes')
amount = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
def sales_total(self):
sales_total = Income.objects.values('account_name').order_by('account_name').annotate(sales_total=Sum('sales__total'))
return sales_total
So in my template when I do
{% for account in accounts %}
<tr>
<td>{{ account.sales_total }}</td>
</tr>
{% endfor %}
I get
Account_name Total
local sales <QuerySet [{'account_name': 3, 'sales_total': Decimal('1802.5')}]>
int sales <QuerySet [{'account_name': 3, 'sales_total': Decimal('1802.5')}]>
So I managed to to this after so much research on the net
in the account model I defined a function to get the sales totals
def sales_total(self):
return sum([item.sales_total() for item in self.incomes.all()])
The in the template got the values as {{ account.sales_total }}
Change the sales_total func:
def sales_total(self):
sales_total = Income.objects.values('account_name').order_by('account_name').annotate(sales_total=Sum('sales__total'))
return sales_total[0].sales_total
More info: https://docs.djangoproject.com/en/4.0/topics/db/aggregation/#cheat-sheet
I have a simple models:
class Pv(models.Model):
accounts =(
('Sub CF','Sub CF'),
('Special','Special'),
('Directors','Directors'),
('Operations','Operations'),
('LSGDP','LSGDP'),
('DWAP','DWAP'),
('Capacity(USD)','Capacity(USD)')
)
acc =(
('Yes','Yes'),
('No', 'No')
)
source =(
('GOG','GOG'),
('Others', 'Others')
)
pv =(
('General','General'),
('Honorarium','Honorarium')
)
center=(
('Cost Center 1','Cost Center 1'),
('Cost Center 2','Cost Center 2'),
('Cost Center 3','Cost Center 3'),
('Cost Center 4','Cost Center 4'),
('Cost Center 5','Cost Center 5')
)
stat =(
('Completed','Completed'),
('Returned','Returned'),
('Cancelled','Cancelled')
)
IA_System_Code = models.AutoField(primary_key = True)
IA_code = models.CharField(max_length = 150)
Date_recieved = models.DateField()
Pv_reference = models.CharField(unique = True, max_length = 120)
Source_of_Funding = models.CharField(max_length=50, choices = source)
Cost_center = models.CharField(max_length=50, choices = center)
Payee = models.CharField(max_length=500)
Description = models.CharField(max_length = 500)
Account_code = models.CharField(max_length=350)
Gross_amount = models.DecimalField(max_digits=19, decimal_places=2)
Withholding_tax = models.DecimalField(max_digits=19, decimal_places=2)
Net_amount = models.DecimalField(max_digits=19, decimal_places=2)
Status = models.CharField(max_length = 60, choices = stat )
Remarks =models.CharField(max_length = 500, blank = True)
Acc_Impress = models.CharField(max_length = 350,choices=acc)
Date_returned =models.DateField(null=True,blank = True)
Type_of_accounts= models.CharField(max_length = 100, choices = accounts)
Type_of_pv = models.CharField(max_length = 20, choices = pv)
returned_to_chest = models.DecimalField(max_digits=19, decimal_places=2)
created = models.DateTimeField(null=True)
created_by = models.ForeignKey('auth.User', blank=True, null=True,\
default=None,on_delete=models.CASCADE,related_name='create')
modified = models.DateTimeField(null=True)
modified_by = models.ForeignKey('auth.User', blank=True, null=True,\
default=None ,on_delete=models.CASCADE,related_name='modified')
class Meta():
ordering = ["IA_System_Code"]
def __str__(self):
return self.Description
def save(self, *args, **kwargs):
user = get_current_user()
if user and not user.pk:
user = None
if not self.pk:
self.created_by = user
self.created = datetime.now()
else:
self.modified_by = user
self.modified = datetime.now()
super(Pv, self).save(*args, **kwargs)
I'm struggling to figure out, what I think should be a simple query, with Django's ORM.
Let's say I have a total of 6 PV created by 3 unique Users Now, what I really want is to query all pv and count the pvs created by each User, but only want one of each unique User and add a (what I think should be an annotated) property called total to each User. i dont also want to display users in the system who have not made any pv entry.
So if the User has 2 PV created , his total will be 2. please can anybody help me out .
How do i display it on my template as a table.
example :
user total pv recorded for the yr
John 10
peter 20
pack 5
please can anyone help me out . looks like i dont know what am doing. thank u
You can write this query.
views.py
from django.db.models import Count, Q # import Q here
import datetime
today = datetime.datetime.now()
qry = User.objects.filter(Q(create__isnull=False) & Q(create__created__year=today.year)).\
annotate(counter=Count('create')).\
prefetch_related('create') # this will only count the PV s from current yaer
send qry in template
template
{% for i in qry %}
{% if i.counter != 0 %} # checking if PV exist for user
# edited line
<p>{{ i.first_name }} {{ i.counter }}</p> # first name number of PVs
{% endif %}
{% endfor %}
I hope this helps.
Edit
You can print anything you want. {{ j.description }} is for example. I have edited the code. Current code will print fisrt_name and counte (john 5). you can replace first_name with any like first_name last_name count, or you have defined name in model.
Edited
i edited your query so it's now counting only those users who have only registered a pv. now i need reset the counter to zero when ever a new year start
#sandeep your query was not displaying anything on the template so i first queried out all the users
VIew.py
from django.db.models import Count, Q
import datetime
qry = User.objects.all()
i then filtered the query with your code
qryr = qry.filter(Q(create__isnull=False)\ &
Q(create__created__year=today.year))\
.annotate(counter=Count('create'))\
.prefetch_related('create').order_by('-counter')
and here is my template
template.html
<table class="table table-bordered table-hover">
<thead>
<tr>
<th scope="col">Users</th>
<th scope="col">Pv's Recorded</th>
</tr>
</thead>
<tbody>
{% for i in qryr %}
<tr>
<td>{{i.first_name}} {{i.last_name}}</td>
<td> {{ i.counter }}</td>
</tr>
{% endfor %}
</tbody>
</table>
thank you very much
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.
Please, I am a new user of django and I don't understand what is wrong in my model.
I have activated the admin, and created a new model:
class Strumenti(models.Model):
cod_stru = models.AutoField(db_column='cod_stru', primary_key=True)
sht_name = models.CharField(db_column='sht_name', max_length=15, blank=True, null=True)
descr = models.CharField(db_column='descr', max_length=45, blank=True, null=True)
isin = models.CharField(db_column='isin', max_length=16)
tipo = models.CharField(db_column='tipo', max_length=1)
mktcode = models.ForeignKey(Mercati, on_delete=models.CASCADE, default=1)
ccy1 = models.ForeignKey(Valute, related_name='ccyone', on_delete=models.CASCADE)
ccy2 = models.ForeignKey(Valute, related_name='ccytwo', on_delete=models.CASCADE)
beta = models.DecimalField(db_column='beta', max_digits=2, decimal_places=2, blank=True, null=True)
system = models.NullBooleanField(db_column='system', default=False, null=True)
template = models.NullBooleanField(db_column='template', default=False, null=True)
tenor = models.CharField(db_column='tenor', max_length=4, blank=True, null=True)
divy = models.DecimalField(db_column='divy', max_digits=5, decimal_places=5, blank=True, null=True)
fxpair = models.CharField(db_column='fxpair', max_length=6, blank=True, null=True)
cod_emitt = models.ForeignKey(Emittenti, on_delete=models.CASCADE, null=True)
idx_code = models.ForeignKey(Indexdef, on_delete=models.CASCADE, null=True)
vol_type = models.IntegerField(db_column='vol_type', blank=True, null=True)
def __unicode__(self):
return u'%s - %s - %s - %s' % (self.cod_stru, self.sht_name, self.descr, self.tipo)
when I try to ADD a new record I get this error:
**'ascii' codec can't encode character u'\xe3' in position 14: ordinal not in range(128)**
I am using Django 1.9, python 2.7, Mysql (utf8_unicode_ci)
hereunder the error during template rendering
In template /usr/local/lib/python2.7/dist-packages/django/contrib/admin/templates/admin/change_form.html, error at line 33
ascii
23 {% endblock %}
24 {% endif %}
25
26 {% block content %}<div id="content-main">
27 {% block object-tools %}
28 {% if change %}{% if not is_popup %}
29 <ul class="object-tools">
30 {% block object-tools-items %}
31 <li>
32 {% url opts|admin_urlname:'history' original.pk|admin_urlquote as history_url %}
33 {% trans "History" %}
someone can help me?
Ok, I found the problem.
I stored in the 'Valute' table the description "São Tomé and Príncipe".
chars like ã é í produce the error, even if I don't understand why my .encode(uft-8) didn't work...
Any why I have just changed the string to "Sao Tome and Principe" and now it seems to be fine.