Search code examples
pythondjangoimporterror

django circular import in models


I understand I have a circular import here, but one of them is being used to define some foreign key fields and the other is only there for a property in the other model.

models:

receipts/models.py

from django.db import models
from employees.models import Employee

class Receipt(models.Model):
    Supervisor = models.ForeignKey(Employee, on_delete=models.CASCADE, related_name='receipt_supervisor')
    Employee = models.ForeignKey(Employee, on_delete=models.CASCADE, related_name='receipt_employee')
    amount = models.DecimalField(max_digits=6, decimal_places=2)

employees/models.py

from django.db import models
from receipts.models import Receipt

class Employee(models.Model):
    number = models.CharField(max_length=50, unique=True)
    first_name = models.CharField(max_length=255)
    last_name = models.CharField(max_length=255)

    @property
    def receipt_sum(self):
        receipts = Receipt.objects.filter(Employee=self.pk)
        sum = 0
        for receipt in receipts:
            sum += receipt.amount
        return sum

I get this error:

cannot import name 'Employee' from partially initialized module 'employees.models' (most likely due to a circular import)

Is there a way I can get around this so that I can do the receipt query in the Employee property? Or do I need to do this calculation elsewhere?

Thanks!


Solution

  • Is there a way I can get around this so that I can do the receipt query in the Employee property?

    Yes, you can import the module in the method:

    from django.db import models
    
    class Employee(models.Model):
        number = models.CharField(max_length=50, unique=True)
        first_name = models.CharField(max_length=255)
        last_name = models.CharField(max_length=255)
    
        @property
        def receipt_sum(self):
            from receipts.models import Receipt
            receipts = Receipt.objects.filter(Employee=self.pk)
            sum = 0
            for receipt in receipts:
                sum += receipt.amount
            return sum

    but it is actually not necessary to import Receipt at all, you can make use of the receipt_employee relation to access the related Receipt object, and you can use .aggregate(…) [Django-doc] to sum up the elements at the database side, this is likely more efficient:

    from django.db import models
    from django.db.models import Sum
    
    class Employee(models.Model):
        number = models.CharField(max_length=50, unique=True)
        first_name = models.CharField(max_length=255)
        last_name = models.CharField(max_length=255)
    
        @property
        def receipt_sum(self):
            return self.receipt_employee.aggregate(
                total=Sum('amount')
            )['total'] or 0