The problem
I'm writing a program where I register treatments performed on patients in various medical places. Each treatment is performed on certain body part and each medical place can define their own way of naming those body parts.
My models look like this:
class Treatment(Model):
medical_place = ForeignKey(to=MedicalPlace, on_delete=PROTECT, related_name='treatments')
body_part = CharField(max_length=64, choices=BodyPart.choices(), db_index=True)
... (some other fields) ...
class LocalProcedure(ProcedureBase):
medical_place = ForeignKey(to=MedicalPlace, on_delete=PROTECT, related_name='procedures')
identifier = CharField(verbose_name=_('Procedure number'), max_length=64, blank=False, null=False)
description = CharField(verbose_name=_('Procedure description'), max_length=256, blank=True, null=False)
body_part = CharField(max_length=64, choices=BodyPart.choices(), blank=False, db_index=True)
class Meta:
unique_together = (('body_part', 'medical_place', 'identifier'),)
I need to retrieve list of "LocalProcedure" objects with annotated:
- count of related treatments
- count of related treatments performed up to X days ago (Treatments model has created datetime field I didn't show here)
Solutions I have tried so far
I can retrieve annotated list of LocalProcedures while using subqueries:
related_treatments = Treatment.objects.filter(
body_part=OuterRef('body_part'),
medical_places=OuterRef('medical_place'),
).values(
f'body_part',
)
LocalProcedure.objects.annotate(
treatments_count = Subquery(related_treatments.annotate(count=Count('pk')).values('count'),
treatments_count = Subquery(related_treatments.filter(created__gt=now()).annotate(count=Count('pk')).values('count'),
)
but this solution does indeed perform subqueries, whereas joining those two tables (when writing raw query) is much much faster.
Any help appreciated.
Partially working solution (in the end marked as accepted)
The answer posted here by @ruddra has been really helpful, but does not meet my need completely. I know that I have relation between LocalProcedure and Treatment object via medical_place
field but this means that the query would have another, completely unnecessary join clause...
As for people looking for a way to introduce relations to their models without ForeignKey - there is something called ForeignObject
and can be used to create relations between two objects using any fields.