Here I need to get the student_id in the values. I have defined s_id but if values() is called it does not show.
FeeSection.objects.get(id=1).section.sectionstudent_set.annotate(s_id=F('student__id')).get(student__id=1).qouta.feeqouta_set.all().values()
This query returns:
<QuerySet [{'id': 1, 'qouta_id': 2, 'type_id': 1, 'amount': Decimal('200.00')}, {'id': 2, 'qouta_id': 2, 'type_id': 2, 'amount': Decimal('10.00')}]>
what I need is: 's_id':1
<QuerySet [{'id': 1,'s_id':1, 'qouta_id': 2, 'type_id': 1, 'amount': Decimal('200.00')}, {'id': 2, 's_id':1,'qouta_id': 2, 'type_id': 2, 'amount': Decimal('10.00')}]>
Models:
class SectionStudent(models.Model):
student = models.ForeignKey('users.Student', on_delete=models.CASCADE)
section = models.ForeignKey(Section, on_delete=models.CASCADE, )
roll = models.IntegerField()
qouta = models.ForeignKey(Qouta, on_delete=models.CASCADE)
class FeeQouta(models.Model):
qouta = models.ForeignKey(to='section.qouta', on_delete=models.CASCADE)
type = models.ForeignKey(FeeType, on_delete=models.CASCADE)
amount = models.DecimalField(max_digits=10, decimal_places=2)
class FeeSection(models.Model):
section = models.ForeignKey('section.Section', on_delete=models.CASCADE)
month = models.IntegerField(choices=MONTH_CHOICES, default=datetime.datetime.now().month)
year = models.IntegerField(choices=YEAR_CHOICES, default=datetime.datetime.now().year)
class Section(models.Model):
section = models.CharField(max_length=1, verbose_name='section')
branch = models.ForeignKey(Branch, on_delete=models.CASCADE)
shift = models.ForeignKey(Shift, on_delete=models.DO_NOTHING)
cls = models.ForeignKey(Class, on_delete=models.CASCADE, verbose_name='Class')
version = models.ForeignKey(Version, on_delete=models.DO_NOTHING)
year = models.IntegerField(choices=YEAR_CHOICES, default=datetime.datetime.now().year)
class Student(models.Model):
sid = models.CharField(verbose_name='Student ID', max_length=10, unique=True)
class Qouta(models.Model):
name = models.CharField(max_length=20)
version = models.ForeignKey(Version, on_delete=models.PROTECT)
cls = models.ForeignKey(Class, on_delete=models.PROTECT, verbose_name='Class')
get() returns a model instance, so anything following after it is basically a new SQL query, no matter how much are you chaining it together. Your code does this right now:
fee_section = FeeSection.objects.get(id=1)
# this starts new SQL query from zero
section_student = fee_section.section.sectionstudent_set.annotate(s_id=F('student__id')).get(student__id=1)
# this starts new SQL query from zero, `annotate()` from above get lost
fee_quotas = fee_section.qouta.feeqouta_set.all().values()
If you want to keep chaining things, you have to replace your get() with filter(), however the resulting SQL query seems a bit wild...
Related
I am having trouble with django ORM.
I want to get the data which I can get using PostgreSQL and raw sql query in views. But is there any solution to achieve this using Django ORM.
Here is my models
class Invoice(models.Model):
class PaymentMode(models.TextChoices):
Cash = 0, _('CASH')
Credit = 1, _('CREDIT')
class PaymentStatus(models.TextChoices):
Paid = 0, _('PAID')
Pending = 1, _('PENDING')
total_amount = models.IntegerField()
payment_mode = models.CharField(choices=PaymentMode.choices,
max_length=20,
default=PaymentMode.Credit)
payment_status = models.CharField(choices=PaymentStatus.choices,
default=PaymentStatus.Pending,
max_length=15)
print_date = models.DateField(default=now)
invoice_date = models.DateField(default=now)
created_by = models.ForeignKey(User, on_delete=models.DO_NOTHING)
customer = models.ForeignKey(Customer, on_delete=models.DO_NOTHING)
class Payment(models.Model):
class PaymentMethod(models.TextChoices):
Cash = 0, _('CASH')
Cheque = 1, _('CHEQUE')
OnlineTransfer = 2, _('WEB_TRANSFER')
Other = 3, _('OTHER')
invoice = models.ForeignKey(Invoice,
on_delete=models.DO_NOTHING,
default=0)
amount = models.IntegerField()
date = models.DateField(default=now)
recieved_by = models.CharField(max_length=150)
payment_method = models.CharField(choices=PaymentMethod.choices,
default=PaymentMethod.Cash,
max_length=20)
and here is my PostgreSQL query
SELECT
A.id ,
A.total_amount ,
A.payment_mode,
A.payment_status,
A.print_date,
A.invoice_date,
A.created_by_id,
A.customer_id,
coalesce(SUM(P.amount), 0) AS "paid",
(A.total_amount - coalesce(SUM(P.amount), 0)) As "remaining"
FROM
public."Invoicing_invoice" as A
LEFT JOIN public."Invoicing_payment" as P
ON P.invoice_id = A.id
GROUP BY A.id
You can annotate with:
from django.db.models import F, Sum, Value
from django.db.models.functions import Coalesce
Invoice.objects.annotate(
paid=Coalesce(Sum(payment__amount), Value(0)),
remaining=F('total_amount') - Coalesce(Sum(payment__amount), Value(0))
)
The Invoices that arise from this queryset will have two extra attribute .paid and .remaining with the paid and remaining amount of the invoice.
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)
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'))
class Meet(models.Model):
type = models.ForeignKey(MeetType)
owner = models.ForeignKey(User)
name = models.CharField(max_length=200)
description = models.TextField(blank=True, null=True)
limit_per_day = models.IntegerField(blank=False, default=30)
start_at = models.DateTimeField(blank=False, auto_now=False, auto_now_add=True)
finish_at = models.DateTimeField(blank=False, auto_now=False, auto_now_add=False)
class MeetList(models.Model):
meet = models.ForeignKey(Meet)
user = models.ForeignKey(User)
owner = User.objects.create_user(username='owner', email='owner#gmail.com', password='pass2')
meet = Meet.objects.create(name='test_meet', owner=owner, type=meettype, start_at=datetime.datetime(2016, 3, 11),finish_at=datetime.datetime(2016, 3, 14))
meet2 = Meet.objects.create(name='test_meet2', owner=owner, type=meettype, start_at=datetime.datetime(2016, 3, 11),finish_at=datetime.datetime(2016, 3, 14))
MeetList.objects.create(meet=meet, user=owner)
MeetList.objects.create(meet=meet2, user=owner)
MeetList.objects.all() and get
MeetList.objects.all()[0].meet.name
u'test_meet'
MeetList.objects.all()[1].meet.name
u'test_meet2'
Then i want to filter by user, but i get next
MeetList.objects.filter(user=owner)[0].meet.name
u'test_meet2'
MeetList.objects.filter(user=owner)[1].meet.name
u'test_meet2'
Why is MeetList.objects.filter(user=owner)[0].meet.name the same as MeetList.objects.filter(user=owner)[1].meet.name?
class Office(models.Model):
person = models.ForeignKey(Person, verbose_name="Person")
department = models.ForeignKey(Department, verbose_name="Department")
office_desc = models.CharField('Office', max_length=100,unique=True)
office_acronym = models.CharField('Office Acronym', max_length=20,blank=True,help_text="Add acronym if any, not required")
location = models.CharField('Location',max_length=100,blank=True)
trunkline = models.CharField('Trunk Line',max_length=30,blank=True)
directline = models.CharField('Direct Line',max_length=30,blank=True)
localnumber = models.CharField('Local Number',max_length=30,blank=True)
telefax = models.CharField('Telefax',max_length=30,blank=True)
active = models.BooleanField('Active',default=True)
class Department(models.Model):
department_desc = models.CharField('Department', max_length=100,unique=True)
department_acronym = models.CharField('Department Acronym', max_length=20,blank=True,help_text="Add acronym if any, not required")
active = models.BooleanField('Active',default=True)
class Person(models.Model):
GENDER = (
('M','Male'),
('F','Female'),
)
first_name = models.CharField("First Name", max_length=100)
last_name = models.CharField("Last Name",max_length=100)
middle_name = models.CharField("Middle Name", max_length=100, blank=True)
salutation = models.ForeignKey(Salutation, verbose_name="Salutation", null=True, blank=True) #
suffix_name = models.ManyToManyField(SuffixName, verbose_name="Suffix Name",null=True, blank=True) #
job_title = models.ManyToManyField(JobTitle, verbose_name="Job Title",null=True, blank=True) #
gender = models.CharField(max_length=1, choices=GENDER,default='Male')
birthdate = models.DateField('Birthdate',null=True,blank=True)
image = models.ImageField('Image',upload_to='persons',blank=True)
email = models.EmailField('Email',blank=True)
street = models.CharField('Street',max_length=100, blank=True)
brgy = models.CharField('Barangay',max_length=100, blank=True)
town_city = models.CharField('Town/City',max_length=100, blank=True)
zip_code = models.IntegerField('ZIP Code',null=True, blank=True)
department = models.ManyToManyField(Department, verbose_name="Department",null=True, blank=True) #
office = models.ManyToManyField(Office, verbose_name="Office", null=True, blank=True) #
sql_query
select pd.department_desc, pp.last_name, o.office_desc from person_person as pp
INNER JOIN person_person_department as ppd on pp.id = ppd.person_id
INNER JOIN person_department as pd on pd.id = ppd.id
INNER JOIN person_office as o on o.department_id = pd.id
where pd.department_desc = 'Executive'
views code:
per = Person.objects
qry_name = per.values_list('salutation__salutation_desc','first_name','middle_name','last_name', 'office__office_desc', 'office__location','office__localnumber','office__trunkline','office__directline','office__telefax').filter(department__department_desc='Executive')
Result: query result includes person with different department
Expected result: only person with Executive department
If I query directly from the database I get the correct result but when I translate the query into django code it's returning different queryset. Query returns ok if certain person has a single office but if a person has multiple office thats where the inconsistency starts .Query returns list with different description other than what was specified. Am I doing the translation of the query to python right?
A closer representation of the sql query would be:
from django.db.models import F
qry_name = Person.objects.filter(
department__department_desc='Executive',
office__department=F('department')
).values_list(<..FIELDS..>)
Let know if this works.