I need query objects passed 2 of multiple conditions. Example:
We have a model:
class A(models.Model):
id = models.PositiveSmallIntegerField(primary_key=True)
cost = models.IntegerField(null=True, blank=True)
price = models.IntegerField(null=True, blank=True)
quality = models.IntegerField(null=True, blank=True)
code = models.CharField(max_length=255, null=True, blank=True)
name = models.CharField(max_length=255, null=True, blank=True)
address = models.CharField(max_length=255, null=True, blank=True)
Conditions:
cost < 5
price < 7
quality > 0
...
code = 1234
name contains 'apple'
Result can be:
- 'C' item with cost = 6, price = 6, quality = 2, code = 321, name = 'asd asdsd'
- 'D' with value: cost=4, price=6, quality=2, code=322, name='xyz'
How to query item passed as less 2 conditions?
We can first annotate with the number of conditions that are satified, and then filter on that number:
from django.db.models import Q, IntegerField
from django.db.models.functions import Cast
A.objects.annotate(
nvalid=Cast(Q(cost__lt=5), output_field=IntegerField()) +
Cast(Q(price__lt=7), output_field=IntegerField()) +
Cast(Q(quality__gt=0), output_field=IntegerField()) +
Cast(Q(code='1234'), output_field=IntegerField()) +
Cast(Q(name__contains='apple'), output_field=IntegerField())
).filter(nvalid__gte=2)
Related
I already calculate, the total of one consummation, now i just want to sum all the consumations
class Consommation(models.Model):
food = models.ManyToManyField(Food)
consomme_le = models.DateTimeField(default=timezone.now, editable=False)
vipcustomer = models.ForeignKey(VipCustomer, models.CASCADE, null=True,
blank=True, verbose_name='Client prestigieux',
related_name='vip_consommations')
to calculate one consummation:
def total(self):
return self.food.aggregate(total=Sum('price'))['total']
Food class :
class Food(models.Model):
nom = models.CharField(max_length=100, verbose_name='Mon menu')
price = models.PositiveIntegerField(verbose_name='Prix')
category = models.ForeignKey(FoodCategory, models.CASCADE,
verbose_name="Categorie")
vipcustomer class:
class VipCustomer(models.Model):
first_name = models.CharField(max_length=150, verbose_name='Prénom')
last_name = models.CharField(max_length=100, verbose_name='Nom')
matricule = models.PositiveIntegerField(verbose_name='Matricule',
default=0)
adresse = models.CharField(max_length=200, verbose_name='Adresse',
blank=True)
telephone = PhoneField()
company = models.CharField(max_length=100, verbose_name='La société')
service = models.CharField(max_length=100, verbose_name='Service',
null=True, blank=True)
numero_badge = models.IntegerField(verbose_name='Numero du badge',
null=True, blank=True)
My goal is to calculate the total of all the consummations.
For a given VipCustomers, you can query with:
my_vip_customer.vip_consommations.aggregate(
total=Sum('food__price')
)['total']
We thus aggregate over the set of related Consommations, and we then aggregate over all the related Foods of these Consommations, and their corresponding price.
If there are no related Consommations, or no related Foods of these Consommations, then the sum will return None, instead of 0. We can add or 0 to convert a None to an 0 here:
my_vip_customer.vip_consommations.aggregate(
total=Sum('food__price')
)['total'] or 0
or for all Customers, we can annotate this with:
VipCustomer.objects.annotate(
total=Sum('vip_consommations__food__price')
)
Here the VipCustomers that arise from this, will have an extra attribute .total that contains the sum of the prices of the related Foods of the related Consommations.
My table named Value has a one to many relationship with the table Country and the table Output_outcome_impact. I have a query that is working fine and gets what I want but then I need to do an average of the value field, but this average needs to be done for each unique id_output_outcome_impact and not the whole query.
class Country(models.Model):
country_name = models.CharField(max_length=255, primary_key=True)
CONTINENTCHOICE = (
('Africa', 'Africa'),
('America', 'America'),
('Asia', 'Asia'),
('Europe', 'Europe'),
('Oceania', 'Oceania')
)
region = models.CharField(max_length=255)
continent = models.CharField(max_length=255, choices=CONTINENTCHOICE)
GDP_per_capita = models.IntegerField(null=True)
unemployment_rate = models.FloatField(null=True)
female_unemployment_rate = models.FloatField(null=True)
litteracy_rate = models.FloatField(null=True)
def __str__(self):
return self.country_name
class OutputOutcomeImpact(models.Model):
output_outcome_impact_name = models.CharField(max_length=255, primary_key=True)
TYPECHOICE = (
('Output', 'Output'),
('Outcome', 'Outcome'),
('Impact', 'Impact'),
)
type = models.CharField(max_length=255, choices=TYPECHOICE)
description = models.TextField()
TARGETGROUP = (
('Standard', 'Standard'),
('Investors', 'Investors'),
('Local authorities and NGOs', 'Local authorities and NGOs'),
)
target_group = models.CharField(max_length=255,choices=TARGETGROUP)
question = models.TextField(null=True, blank=True)
parent_name = models.ForeignKey('self', on_delete=models.PROTECT, null=True, blank=True)
indicator = models.ForeignKey(Indicator, on_delete=models.PROTECT)
def __str__(self):
return self.output_outcome_impact_name
class Activity(models.Model):
activity_name = models.CharField(max_length=255, primary_key=True)
description = models.TextField()
product_service = models.TextField()
output_outcome = models.TextField()
outcome_impact = models.TextField()
output_outcome_impacts = models.ManyToManyField('OutputOutcomeImpact')
countries = models.ManyToManyField('Country')
sectors = models.ManyToManyField('Sector')
def __str__(self):
return self.activity_name
class Value(models.Model):
value_name = models.CharField(max_length=255, primary_key=True)
country = models.ForeignKey(Country, on_delete=models.PROTECT)
id_output_outcome_impact = models.ForeignKey(OutputOutcomeImpact, on_delete=models.PROTECT)
value_has_source = models.ManyToManyField('Source')
value = models.FloatField()
function_name = models.CharField(max_length=255, default = "multiply")
def __str__(self):
return self.value_name
region_values = Value.objects.filter(id_output_outcome_impact__output_outcome_impact_name__in = output_pks, country_id__region = region).exclude(country_id__country_name = country).values()
So the result of the query is available below, and what I would like to achieve is to set the value field to an average of every object that has the same id_output_outcome_impact_id, here Dioxins and furans emissions reduction appears twice so I would like to get the 2 values set as their average.
<QuerySet [{'value_name': 'Waste_to_dioxins', 'country_id': 'Malawi', 'id_output_outcome_impact_id': 'Dioxins and furans emissions reduction', 'value': 0.0003, 'function_name': 'multiply'}, {'value_name': 'Waste_to_dioxins_south_africa', 'country_id': 'South Africa', 'id_output_outcome_impact_id': 'Dioxins and furans emissions reduction', 'value': 150.0, 'function_name': 'multiply'}, {'value_name': 'Households getting electricity per kWh', 'country_id': 'Malawi', 'id_output_outcome_impact_id': 'Households that get electricity', 'value': 0.0012, 'function_name': 'multiply'}, {'value_name': 'Dioxin to disease', 'country_id': 'Malawi', 'id_output_outcome_impact_id': 'Reduction of air pollution related diseases', 'value': 0.31, 'function_name': 'multiply'}]>
I am wondering if django models allow such modification (I went through the doc and saw the annotate function with the average but couldn't make it work for my specific case), that would be nice. Thanks.
region_values = Value.objects.filter(id_output_outcome_impact__output_outcome_impact_name__in = output_pks, country_id__region = region).exclude(country_id__country_name = country).values('id_output_outcome_impact__output_outcome_impact_name').annotate(Avg('value'))
I'm trying to find the total amount of material costs in columns given I have to select the amount with django choices.
I have tried adding using self.amount1+ self.amount2 to no avail.
PO_STEEL_COST_CHOICES = (
('10000' ,'10000'),
('20000','20000'),
('30000','30000'),
('40000','40000'),
)
PO_ELECTRICAL_MATERIAL_CHOICES = (
('10000' ,'10000'),
('20000','20000'),
('30000','30000'),
('40000','40000'),
)
PO_SUBCONTRACTORS_CHOICES = (
('10000' ,'10000'),
('20000','20000'),
('30000','30000'),
('40000','40000'),
)
class ProcurementTeam(models.Model):
project_name = models.OneToOneField(Project, on_delete=models.DO_NOTHING)
po_steel = models.FileField(upload_to='files/ProcurementTeam/posteel/%Y/%m/%d/', blank=True, null=True)
po_steel_cost = models.CharField(max_length=120,choices=PO_STEEL_COST_CHOICES, default='None', blank=True)
po_electrical_materials = models.FileField(upload_to='files/ProcurementTeam/poelectrical/%Y/%m/%d/', blank=True, null=True)
po_electrical_materials_cost =models.CharField(max_length=120, choices=PO_ELECTRICAL_MATERIAL_CHOICES, default='None', blank=True)
po_subcontractors = models.FileField(upload_to='files/ProcurementTeam/posubcontractor/%Y/%m/%d/', blank=True, null=True)
po_subcontractors_cost = models.CharField(max_length=120, choices=PO_SUBCONTRACTORS_CHOICES, default='None', blank=True)
posted_by = models.ForeignKey(CustomUser, on_delete=models.DO_NOTHING)
is_approved = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_active = models.BooleanField(default=True)
def __str__(self):
return str(self.project_name)
def total_material_cost(self):
"""Function to return total procurement PO cost"""
total_procurpo = self.po_steel_cost + self.po_electrical_materials_cost + self.po_subcontractors_cost
return total_procurpo
I expect the output to be 40,000 given that I have chosen 10000, 20000 and 10000 but the current output is 100002000010000. Why is this happening?
since the options are given as a string within quotes('') thus they are getting appended instead of getting added
total_procurpo = int(self.po_steel_cost) + int(self.po_electrical_materials_cost) +int(self.po_subcontractors_cost)
You can also try with sum and map:
total_procurpo = sum(
map(
float,
(
self.po_steel_cost,
self.po_electrical_materials_cost,
self.po_subcontractors_cost,
),
)
)
My models:
class Ward(models.Model):
id = models.AutoField(primary_key=True, unique=True)
clinic = models.ForeignKey(Clinic, on_delete=models.CASCADE)
name = models.CharField(max_length=500, default='', blank=True)
description = models.CharField(max_length=2000, default='', blank=True)
bedcapacity = models.IntegerField(default=1)
class Bed(models.Model):
id = models.AutoField(primary_key=True, unique=True)
name = models.CharField(max_length=200, default='',
blank=True, unique=True)
clinic = models.ForeignKey(Clinic, on_delete=models.CASCADE)
ward = models.ForeignKey(Ward, on_delete=models.CASCADE)
occupied = models.BooleanField(default=False)
I'm writing to convert the following pseudocode to django:
from django.db.models import F, Q, When
clinic = Clinic.objects.get(pk=10)
wards = Ward.objects.filter(clinic=clinic)
ward_set = []
for ward in wards:
occupied = len(Bed.objects.filter(clinic = clinic, ward = ward, occupied = True))
total = len(Bed.objects.filter(clinic = clinic, ward = ward))
ward['occupied'] = occupied # The next two lines are pseudocode
ward['total']=total
ward_set.append(ward)
return render(request, 'file.html',
{
'wards': ward_set
})
I believe I should be using annotate, but I'm finding it difficult to understand annotate from the docs.
What about this ?
from django.db.models import Q, Count
ward_set = Ward.objects.filter(clinic=10).annotate(
occupied=Count('bed', filter=Q(bed__occupied=True)),
total=Count('bed')
)
You could see some examples for conditional aggregation here
model.py
class Fatture(models.Model):
numero = models.CharField(max_length=30, null=True, blank=True)
data = models.DateField()
iva = models.PositiveIntegerField()
commissione = models.DecimalField(max_digits=6, decimal_places=2, null=True, blank=True)
class Ddts(models.Model):
fattura = models.ForeignKey('Fatture')
class DdtsArticoli(models.Model):
ddt = models.ForeignKey('Ddts')
articolo = models.ForeignKey('Articoli')
quantita = models.DecimalField(max_digits=6, decimal_places=2)
prezzo = models.DecimalField(max_digits=6, decimal_places=2, null=True, blank=True)
colli = models.PositiveIntegerField()
I have to make a query that calculate this total:
aggregate table DdtsArticoli SUM(quantita * prezzo)
aggregate table DdtsArticoli ((quantita * prezzo) / (1 + (iva of table
Fatture)/100 ))
resut of 1) - result of 2)
See the Django docs on aggregation for detail but 1. should be something like:
from django.db.models import Sum
DdtsArticoli.objects.all().aggregate(
your_key=Sum(F('quantita') * F('prezzo'), output_field=FloatField()))
Number 2. is no aggragation, you can simply calculate it:
(obj.quantita * obj.prezzo) / (1 + (obj.ddt.fattura.iva/100.0))
Where obj is your object aka. your database row.
Number 3. should be trivial then.