I have implemented django generic relations to create relations between models. My models are,
class CessPoint(BaseModel):
....
title = models.CharField(max_length=100)
class BilledVehicle(BaseModel):
....
source = models.ForeignKey(CessPoint, on_delete=models.CASCADE)
class Bill(BaseModel):
.....
content_type = models.ForeignKey(
ContentType, on_delete=models.CASCADE, null=True)
object_id = models.UUIDField(null=True)
billed_item = GenericForeignKey()
class BillPayment(BaseModel):
.....
bill = models.ForeignKey(Bill, on_delete=models.CASCADE)
I would like to get the payments for BilledVehicle
. This is how I would like to go about it.
BillPayment.objects.filter(bill__content_type=ContentType.objects.get_for_model(BilledVehicle)).values('bill__billed_item__source__title').annotate(total_paid=Sum('amount_paid')).order_by('-total_paid')
I am getting the error:
Field 'billed_item' does not generate an automatic reverse relation and therefore cannot be used for reverse querying. If it is a GenericForeignKey, consider adding a GenericRelation.
According to this answer, How to traverse a GenericForeignKey in Django?, defining a GenericRelation might solve my problem. But then again, I did not define a GenericRelation because adding one will cascade all relations as per the default behavior
Unlike ForeignKey, GenericForeignKey does not accept an on_delete argument to customize this behavior; if desired, you can avoid the cascade-deletion simply by not using GenericRelation, and alternate behavior can be provided via the pre_delete signal.
If you do not want to create a GenericRelation object in your BilledVehicle
module, django will have no way of linking it directly to your Bill
using values.
You can work around this by using a dictionary to store unique CessPoint
titles and their total
(do the annotation yourself) from the BillPayment
queryset amount paid as follows.
qs = BillPayment.objects.filter(
bill__content_type=ContentType.objects.get_for_model(BilledVehicle))
ds = {}
for billPayment in qs:
title = billPayment.billed_item.source.title
ds[title] = ds.get(title, 0) + billPayment.amount_paid