Django ORM. Joining subquery on condition - django

I have a table TickerStatement, which contains financial statements about companies
class Statements(models.TextChoices):
"""
Supported statements
"""
capital_lease_obligations = 'capital_lease_obligations'
net_income = 'net_income'
price = 'price'
total_assets = 'total_assets'
short_term_debt = 'short_term_debt'
total_long_term_debt = 'total_long_term_debt'
total_revenue = 'total_revenue'
total_shareholder_equity = 'total_shareholder_equity'
class TickerStatement(TimeStampMixin):
"""
Model that represents ticker financial statements
"""
name = models.CharField(choices=Statements.choices, max_length=50)
fiscal_date_ending = models.DateField()
value = models.DecimalField(max_digits=MAX_DIGITS, decimal_places=DECIMAL_PLACES)
ticker = models.ForeignKey(Ticker, on_delete=models.CASCADE, null=False,
related_name='ticker_statements')
And now I'm trying to calculate a multiplier. The formula looks like:
(short_term_debt + total_long_term_debt) / total_shareholder_equity
I wrote a raw SQL query
SELECT "fin_tickerstatement"."fiscal_date_ending",
t2.equity AS "equity",
value AS "debt",
short_term_debt AS "short_term_debt",
(value + short_term_debt) / t2.equity AS "result"
FROM "fin_tickerstatement"
JOIN
(SELECT "fin_tickerstatement"."fiscal_date_ending",
fin_tickerstatement.value AS "equity"
FROM "fin_tickerstatement"
WHERE ("fin_tickerstatement"."ticker_id" = 12
AND "fin_tickerstatement"."fiscal_date_ending" >= date'2015-09-03'
AND "fin_tickerstatement"."name" = 'total_shareholder_equity')
GROUP BY "fin_tickerstatement"."fiscal_date_ending",
fin_tickerstatement.value
ORDER BY "fin_tickerstatement"."fiscal_date_ending" DESC) t2
ON fin_tickerstatement.fiscal_date_ending = t2.fiscal_date_ending
JOIN
(SELECT "fin_tickerstatement"."fiscal_date_ending",
fin_tickerstatement.value AS "short_term_debt"
FROM "fin_tickerstatement"
WHERE ("fin_tickerstatement"."ticker_id" = 12
AND "fin_tickerstatement"."fiscal_date_ending" >= date'2015-09-03'
AND "fin_tickerstatement"."name" = 'short_term_debt')
GROUP BY "fin_tickerstatement"."fiscal_date_ending",
fin_tickerstatement.value
ORDER BY "fin_tickerstatement"."fiscal_date_ending" DESC) t3
ON fin_tickerstatement.fiscal_date_ending = t3.fiscal_date_ending
WHERE ("fin_tickerstatement"."ticker_id" = 12
AND "fin_tickerstatement"."fiscal_date_ending" >= date'2015-09-03'
AND "fin_tickerstatement"."name" = 'total_long_term_debt')
GROUP BY "fin_tickerstatement"."fiscal_date_ending",
equity,
debt,
short_term_debt
ORDER BY "fin_tickerstatement"."fiscal_date_ending" DESC;
and have no idea how to translate it into Django ORM. Maybe you have some ideas or know some Django plugins that can help me.

The only way to solve this problem is to install django-query-builder.

Related

django PostgreSQL Partitioning reference error

We have two models. I'm implementing partitioning on the PaymentModel. It gives an error when I want to add to the BuyerModel table linked to this table;
insert or update on table "betik_app_payment_buyermodel" violates
foreign key constraint
"betik_app_payment_bu_payment_id_d7022880_fk_betik_app" DETAIL: Key
(payment_id)=(2) is not present in table
"betik_app_payment_paymentmodel".
models.py
#architect.install('partition', type='range', subtype='date', constraint='month', column='dt')
class PaymentModel(models.Model):
class Meta:
app_label = 'betik_app_payment'
indexes = [
models.Index(fields=['user_email'])
]
payment_status = models.PositiveIntegerField(default=1)
price = MoneyField(max_digits=20, decimal_places=2)
dt = models.DateTimeField()
user_email = models.EmailField(null=True, blank=True)
token = models.CharField(max_length=255, null=True, blank=True)
class BuyerModel(models.Model):
class Meta:
app_label = 'betik_app_payment'
indexes = [
models.Index(fields=['name', 'surname'])
]
payment = models.OneToOneField(to='betik_app_payment.PaymentModel', on_delete=models.CASCADE,related_name='buyer')
name = models.CharField(max_length=100)
surname = models.CharField(max_length=100)
email = models.EmailField(null=True, blank=True)
ip = models.GenericIPAddressField(null=True, blank=True)
main.py
from datetime import datetime
from djmoney.money import Money
buyer_name="Name"
buyer_surname="Surname"
buyer_email="developer#betik.com.tr"
buyer_ip="10.0.0.1"
price= Money("100.00","TRY")
payment_instance = PaymentModel.objects.create(
price=price,
dt=datetime.now(),
user_email=buyer_email
)
# raise error at here
buyer_instance = BuyerModel.objects.create(
payment=payment_instance,
name=buyer_name,
surname=buyer_surname,
email=buyer_email,
ip=buyer_ip
)
using library:
money
partition
I'm looking at the tables in the database with the pgadmin tool and the partition has been applied successfully.
data added in both tables. But BuyerModel table is empty
PaymentModel table has two triggers. These triggers are created automatically by the architect library. Maybe there is an error here?;
after_insert_betik_app_payment_paymentmodel_trigger.
-- FUNCTION: public.betik_app_payment_paymentmodel_delete_master()
-- DROP FUNCTION IF EXISTS public.betik_app_payment_paymentmodel_delete_master();
CREATE OR REPLACE FUNCTION public.betik_app_payment_paymentmodel_delete_master()
RETURNS trigger
LANGUAGE 'plpgsql'
COST 100
VOLATILE NOT LEAKPROOF
AS $BODY$
BEGIN
DELETE FROM ONLY "betik_app_payment_paymentmodel" WHERE id = NEW.id;
RETURN NEW;
END;
$BODY$;
ALTER FUNCTION public.betik_app_payment_paymentmodel_delete_master()
OWNER TO postgres_user;
before_insert_betik_app_payment_paymentmodel_trigger.
-- FUNCTION: public.betik_app_payment_paymentmodel_insert_child()
-- DROP FUNCTION IF EXISTS public.betik_app_payment_paymentmodel_insert_child();
CREATE OR REPLACE FUNCTION public.betik_app_payment_paymentmodel_insert_child()
RETURNS trigger
LANGUAGE 'plpgsql'
COST 100
VOLATILE NOT LEAKPROOF SECURITY DEFINER
AS $BODY$
DECLARE
match "betik_app_payment_paymentmodel"."dt"%TYPE;
tablename VARCHAR;
checks TEXT;
BEGIN
IF NEW."dt" IS NULL THEN
tablename := 'betik_app_payment_paymentmodel_null';
checks := '"dt" IS NULL';
ELSE
match := DATE_TRUNC('month', NEW."dt");
tablename := 'betik_app_payment_paymentmodel_' || TO_CHAR(NEW."dt", '"y"YYYY"m"MM');
checks := '"dt" >= ''' || match || ''' AND "dt" < ''' || (match + INTERVAL '1 month') || '''';
END IF;
BEGIN
EXECUTE 'CREATE TABLE IF NOT EXISTS ' || tablename || ' (
CHECK (' || checks || '),
LIKE "betik_app_payment_paymentmodel" INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES
) INHERITS ("betik_app_payment_paymentmodel");';
EXCEPTION WHEN duplicate_table THEN
-- pass
END;
EXECUTE 'INSERT INTO ' || tablename || ' VALUES (($1).*);' USING NEW;
RETURN NEW;
END;
$BODY$;
ALTER FUNCTION public.betik_app_payment_paymentmodel_insert_child()
OWNER TO postgres_user;

Django queryset from raw SQL

I want an equivalent of this sql query in Django
SELECT Gender, ServCode
FROM [openimisproductTestDb_16_08_22].[dbo].[tblInsuree]
JOIN [openimisproductTestDb_16_08_22].[dbo].[tblServices] ON [openimisproductTestDb_16_08_22].[dbo].[tblInsuree].AuditUserID = [openimisproductTestDb_16_08_22].[dbo].[tblServices].AuditUserID
WHERE Gender = 'F'
AND ServCode = 'F4'
What I have tried:
def assisted_birth_with_cs_query(user, **kwargs):
date_from = kwargs.get("date_from")
date_to = kwargs.get("date_to")
hflocation = kwargs.get("hflocation")
format = "%Y-%m-%d"
date_from_object = datetime.datetime.strptime(date_from, format)
date_from_str = date_from_object.strftime("%d/%m/%Y")
date_to_object = datetime.datetime.strptime(date_to, format)
date_to_str = date_to_object.strftime("%d/%m/%Y")
dictBase = {
"dateFrom": date_from_str,
"dateTo": date_to_str,
}
dictGeo = {}
if hflocation and hflocation!="0" :
hflocationObj = HealthFacility.objects.filter(
code=hflocation,
validity_to__isnull=True
).first()
dictBase["fosa"] = hflocationObj.name
claimItem = Insuree.objects.filter(
validity_from__gte = date_from,
validity_to__lte = date_to,
**dictGeo,
gender = 'F'
).count()
data = Service.objects.filter(code = 'F4').count() | Insuree.objects.filter(gender = 'F').count()
dictGeo['health_facility'] = hflocationObj.id
dictBase["post"]= str(data)
return dictBase
I tried like that but the one just adds when I want the women included in the insured table and the F4 code contained in the service table. both tables have the auditUserID column in common
It would be great if you could add the models to better see the relations between Insuree and Service. Assuming it's a 1-M, I'd go with this query:
Service.objects.filter(code='F4', insuree__gender='F').count()

Django Select across three models with filter criteria

I have three models:
class Vehicle(models.Model):
Vehicle_ID = models.AutoField('ID', primary_key= True)
Vehicle_VIN = models.CharField('FIN', max_length=30)
Vehicle_DELETED = models.BooleanField('Gelöscht',default=False)
class Recall(models.Model):
Recall_ID = models.AutoField('ID', primary_key= True)
Recall_CODE = models.CharField('Rückruf-Code',max_length=500, unique= True)
class Vehicle_Recall(models.Model):
Vehicle_Recall_ID = models.AutoField('ID', primary_key=True)
Vehicle = models.ForeignKey(Vehicle, on_delete=models.CASCADE)
Recall = models.ForeignKey(Recall, on_delete=models.CASCADE)
I want to make a Select Statement like this:
SELECT * FROM Vehicle INNER JOIN(Recall INNER JOIN Vehicle_Recall ON Recall.ID = Vehicle_Recall.Recall) ON Vehicle.ID = Vehicle_Recall.Vehicle WHERE Recall.ID=1 AND Vehicle.DELETED)=FALSE;
Is there a way to make such query in django?
You canuse the Django's ORM as follows (it is an example):
vehicles = Vehicle.objects.filter(Vehicle_DELETED=False).filter(
Q(vehicle_recall__Recall__Recall_ID=1)
)
which generates the following SQL query:
SQL SELECT "vehicle"."Vehicle_ID", "vehicle"."Vehicle_VIN", "vehicle"."Vehicle_DELETED" FROM "vehicle" INNER JOIN "vehicle_recall" ON ("vehicle"."Vehicle_ID" = "vehicle_recall"."Vehicle_id") WHERE ("vehicle"."Vehicle_DELETED" = False AND "vehicle_recall"."Recall_id" = 1)
You can use the raw query as follows:
vehicles = Vehicle.objects.raw(f"""
SELECT * FROM Vehicle
INNER JOIN(Recall INNER JOIN Vehicle_Recall ON Recall.ID = Vehicle_Recall.Recall)
ON Vehicle.ID = Vehicle_Recall.Vehicle
WHERE Recall.ID=1 AND Vehicle.DELETED=FALSE;
""")
and remember to adjust name of tables.
I propose the first solution, but the appropriate code depends on needs. I prepared only some example to present its simplicity.

Building up subqueries of derived django fields

I have a few transformations I need to perform on my table before I aggregate.
I need to multiply transaction_type (which is either 1 or -1) by amount to yield a signed_amount. Then I need to sum all signed_amounts by primary_category (which is a foreign key to secondary category which is a foreign key of my table).
DEBIT = -1
CREDIT = 1
TRANSACTION_TYPE_CHOICES = (
(DEBIT, 'debit'),
(CREDIT, 'credit'),
)
class Transaction(models.Model):
amount = models.DecimalField(max_digits=7, decimal_places=2)
transaction_type = models.IntegerField(choices=TRANSACTION_TYPE_CHOICES)
secondary_category = models.ForeignKey(Secondary_Category)
class Primary_Category(models.Model):
name = models.CharField("Category Name", max_length=30)
category = models.ForeignKey(Primary_Category_Bucket)
class Secondary_Category(models.Model):
name = models.CharField("Category Name", max_length=30)
primary_category = models.ForeignKey(Primary_Category)
I'm stuck on the first bit though.
from django.db.models import Sum, Count, F
original_transactions = Transaction.objects.all()
original_transactions.signed_amount = F('transaction_type') * F('amount')
for transaction in original_transactions:
print transaction.signed_amount
When I try to sanity check that signed_amount is being calculated, I get an error that 'Transaction' object has no attribute 'signed_amount'. I don't want to save signed_amount to the database. I just want to generate it as derived field so I can calculate my totals.
How do I calculate this derived field and subsequently aggregate by primary_category.name?
User python decorator property on a method for class Transaction:
class Transaction(models.Model):
amount = models.DecimalField(max_digits=7, decimal_places=2)
transaction_type = models.IntegerField(choices=TRANSACTION_TYPE_CHOICES)
secondary_category = models.ForeignKey(Secondary_Category)
#property
def signed_amount(self):
return self.amount * self.transaction_type
Then for each Transaction object you can do transaction.signed_amount.
I'm not sure if the aggregation part could be done using queries, but if you don't have that many PrimaryCategory, then python would be good enough to achieve it.
Or you can do this.
all_transactions = Transaction.objects.all().order_by('secondary_category__primary_category_id')
sum = 0
if all_transactions:
primary_category_id = all_transactions[0].secondary_category.primary_category_id
for transaction in all_transactions:
if primary_category_id == transaction.secondary_category.primary_category_id:
sum += (transaction.amount * transaction_type)
else:
sum = (transaction.amount * transaction_type)
print sum

What is the difference between these two Django Q-based queries:

Assuming a simple model like this:
class Item(models.Model):
name = models.CharField(max_length=10)
class Relation(models.Model):
item = models.ForeignKey(Item)
weight = models.IntegerField()
And a couple of Q objects like this:
some = Q(relation__x__gt=3)
others = Q(relation__x=7)
What is the semantic difference between:
first = Item.objects.filter(some, ~others)
and
second = Item.objects.filter(some).exclude(others)
NOTE: Querying through the relation appears to be different than querying a single simple object. The SQL generated for the two above queries is different.
Here's the generated SQL for first:
SELECT "item_item"."id", "item_item"."name"
FROM "item_item"
INNER JOIN "item_relation"
ON ("item_item"."id" = "item_relation"."item_id")
WHERE ("item_relation"."weight" > 3
AND NOT ("item_item"."id" IN
(SELECT U1."item_id"
FROM "item_relation" U1
WHERE (U1."weight" = 7 AND U1."item_id" IS NOT NULL))))
And the SQL for second:
SELECT "item_item"."id", "item_item"."name"
FROM "item_item"
INNER JOIN "item_relation"
ON ("item_item"."id" = "item_relation"."item_id")
INNER JOIN "item_relation" T3
ON ("item_item"."id" = T3."item_id")
WHERE ("item_relation"."weight" > 3 AND NOT (T3."weight" = 7 ))