Reputation: 2781
I have a Car
model which has multiple associations with other models through foreignkeys and manytomany fields, in this case a Car
have one Owner
and has Many Obligations
.
class Car(TimeStampedModel, models.Model):
owner = models.ForeignKey(
to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE
)
class Obligation(TimeStampedModel, models.Model):
"""Representation of the obligations that a car should have up to date."""
car = models.ForeignKey(to=Car, on_delete=models.CASCADE)
expiration_date = models.DateField(
default=date.today,
)
I expect to obtain those cars with expired obligations and from these cars to have their owner and those expired obligations.
For this task I create a custom Car Manager that retrieve those cars with expired obligations:
class CarManager(models.Manager):
"""Define a manager for Car model."""
def with_obligations_expired(self) -> "QuerySet[Car]":
"""Returns a Car given where there is at least one Obligation that is expired."""
return (
self.get_queryset()
.filter(obligation__expiration_date__lte=date.today())
.distinct()
)
I run the custom manager method with_obligations_expired()
to obtain the cars with obligation expired. Through the result I can access the owners and the obligations of that vehicle.
cars_with_expired_obligations = Car.objects.with_obligations_expired()
owners = [car.owner for car in cars_with_expired_obligations]
obligations = # ...something similar to owners
With the above code I can obtain the owners and the obligations for that vehicle with expired obligations but separately.
What I expect is a query to return a set of owner and obligations for those cars with expired obligations. This set is necessary, because an email should be sent to those vehicle owners with expired obligations, with those expired obligations as a reminder.
Upvotes: 1
Views: 36
Reputation: 32244
If you get all Obligation
s in a queryset and use select_related
to also get the related Car
and Owner
. You can then use itertools.groupby
to iterate over a single query while grouping by the Owner
expired_obligations = Obligation.objects.filter(
expiration_date__lte=date.today()
).select_related('car__owner').order_by('car__owner')
for owner, obligations in itertools.groupby(expired_obligations, lambda obligation: obligation.car.owner):
print(owner, list(obligations))
Upvotes: 1