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
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.
I have three models Order, OrderEntries and Product
class Order(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
order_entries = models.ManyToManyField(OrderEntries, blank=True)
...
class OrderEntries(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.IntegerField()
class Product(models.Model):
title = models.CharField(max_length=1000)
slug = models.SlugField(blank=True, null=True, unique=True)
Now, I have to get history of purchased products. As I can get all previous orders placed by a user like this
queryset = Order.objects.filter(user=self.request.user, client=self.request.client)
But how I can get list of products from all those orders?
If you specify a related_name on the ForeignKey field as follows:
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='products')
You could then access the products in your template, for example:
{% for order in orders %}
{% for order_entry in order.order_entries %}
{% for product in order_entry.products.all %}
{{ product }}
Please note: it isn't necessary to set related_name. If you don't set it, you can use BLA_set where BLA is the name of the field e.g.:
{% for product in order_entry.product_set.all %}
maybe this help :
product=[]
for e in queryset:
for ord in e['order_entries']:
for pr in ord['product'] :
product.append(pr)
I'm trying to make account view in my django-shop. I want to display information about the order and the ordered goods. I have a ProductInOrder model with foreign key to Order. Now I want to filter the ordered goods by order. But something is going wrong.
class Order(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
ref_code = models.CharField(max_length=15)
items = models.ForeignKey(Cart, null=True ,on_delete=models.CASCADE, verbose_name='Cart')
total = models.DecimalField(max_digits=9, decimal_places=2, default=0.00)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100, blank=True)
phone = models.CharField(max_length=20)
buying_type = models.CharField(max_length=40, choices=BUYING_TYPE_CHOICES,
default='Доставка')
address = models.CharField(max_length=250, blank=True)
date_created = models.DateTimeField(default=timezone.now)
date_delivery = models.DateTimeField(default=one_day_hence)
comments = models.TextField(blank=True)
status = models.CharField(max_length=100, choices=ORDER_STATUS_CHOICES,
default='Принят в обработку')
class ProductInOrder(models.Model):
order = models.ForeignKey(Order, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.IntegerField(default=1)
item_cost = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
all_items_cost = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
And views.py
def account_view(request):
order = Order.objects.filter(user=request.user).order_by('-id')
products_in_order = ProductInOrder.objects.filter(order__in=order)
categories = Category.objects.all()
instance = get_object_or_404(Profile, user=request.user)
if request.method == 'POST':
image_profile = ProfileImage(request.POST, request.FILES, instance=instance)
if image_profile.is_valid():
avatar = image_profile.save(commit=False)
avatar.user = request.user
avatar.save()
messages.success(request,
f'Ваш аватар был успешно обновлен!')
return redirect('ecomapp:account')
else:
image_profile = ProfileImage()
context = {
'image_profile': image_profile,
'order': order,
'products_in_order': products_in_order,
'categories': categories,
'instance': instance,
}
return render(request, 'ecomapp/account.html', context)
This line products_in_order = ProductInOrder.objects.filter(order__in=order) doesn't work.
Any help please.
Unless you explicityly mention order_by in ProductInOrder queryset, it will order by its default setup, which is mentioned in ProductInOrder model's meta class(if its not mentioned, then default ordering is pk). So using following line should resolve your issue:
ProductInOrder.objects.filter(order__in=order).order_by('-order')
But an improved answer is like this:
products_in_order = ProductInOrder.objects.filter(order__user=request.user).order_by('-order')
In this way, you can remove line order = Order.objects.filter(user=request.user).order_by('-id') from your code. Whats happening here is that, django allows nested filtering, so you can filter by order__user which will allow you order by user from Order model. You don't need to make a filter for Order separately.
Update:
I am not sure, probably you are looking for this:(in template)
{% for o in order %}
{% for po in o.productinorder_set.all %}
{{ po.product }}
{{ po.item_cost }}
{% endfor %}
{% endfor %}
Here I am using reverse relation between Order and ProductInOrder here.
I have master to child tables, need a solution to render it with below condition.
Master Table Fields:-
Person product categ price image field1 field2
Child Table (User customised):-
User Product categ customprice customfield1 customfield2
Query:-
totalrecords = Master.objects.filter(Person=person1).filter(categ=catogory1)
enabledrecords = Child.objects.filter(user=user).filter(categ=categ1)
product in child is foreign key from Master.
In template I will extract the master fields(in for loop) but if price and customfields exists in child then I need to take child object using product relation, otherwise populate these from master table.
Here the confusion comes,
{% for obj in totalrecords %}
if obj.id in enabledrecords (using product forign key) then
Get en_obj from enabledrecords__product
{{obj.id}} {{en_obj.id}} {%if en_obj.customprice%} {{en_obj.customprice}}
{%else%}{%obj.price%}{%endif%} -->do same for other customfields
if obj.id not in enabledrecords
{{ obj.id }} <p> Product not customized click here to customise </p>
Please advice.
EDIT:
Products(Master Table):-
vendor = models.ForeignKey(Shop,verbose_name='Shop Name')
name = models.CharField('Product Name', max_length=100 )
pfimage = models.ImageField('Image', upload_to='pd/%Y',)
pdctg = models.ForeignKey(PdCtg, null=True, blank=True, verbose_name='Product Category')
mrp =models.DecimalField('MRP (optional)',max_digits=6, decimal_places=2, null=True, blank=True)
ourprice =models.DecimalField('Our Price (optional)',max_digits=6, decimal_places=2, null=True, blank=True)
offer =models.CharField('Offers (optional)',max_length=10, null=True, blank=True)
Child Table:
vendor =models.ForeignKey(Shop,verbose_name='Shop Name')
pdctg = models.ForeignKey(PdCtg,null=True, blank=True,verbose_name='Product Category')
products =models.ForeignKey(Products, verbose_name='Product Name')
pdid =models.CharField('Item ID',max_length=100, null=True, blank=True)
mrp =models.DecimalField('MRP (optional)',max_digits=6, decimal_places=2, null=True, blank=True)
ourprice =models.DecimalField('Our Price (optional)',max_digits=6, decimal_places=2, null=True, blank=True)
offer =models.CharField('Offers (optional)',max_length=10, null=True, blank=True)
Child table may or may not have all rows of master for a given vendor and pdctg
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.