Django сложные отношения с джойнами
У меня есть следующие модели:
class Position(BaseModel):
name = models.CharField()
class Metric(BaseModel):
name = models.CharField()
class PositionKPI(BaseModel):
position = models.ForeignKey(Position)
metric = models.ForeignKey(Metric)
expectation = models.FloatField()
class Employee(BaseModel):
position = models.ForeignKey(Position)
class EmployeeKPI(BaseModel):
employee = models.ForeignKey(Employee)
metric = models.ForeignKey(Metric)
value = models.FloatField()
def kpi(self):
return PositionKPI.objects.filter(position=self.employee.position, metric=self.metric).first()
Я считаю, что метод kpi
можно переписать в виде отношения.
На языке SQL это выглядело бы примерно так:
select
positionkpi.*
from employeekpi
join employee on employee.id = employeekpi.employee_id
join positionkpi on positionkpi.id = employee.position_id
and positionkpi.metric_id = employeekpi.metric_id
Посоветуйте, пожалуйста, как это сделать
Вы можете получить такие записи с помощью F
-объекта [Django-doc]:
from django.db.models import F
PositionKPI.objects.filter(employee__position__metric_id=F('metric_id'))
Вы можете попробовать что-то подобное, но в этом случае kpi
свойство возвращает значение expectation
поля, а не PositionKPI
экземпляра
class EmployeeKPIQuerySet(models.QuerySet):
def with_kpi(self):
position_kpi_subquery = PositionKPI.objects.filter(
position=OuterRef('employee__position'),
metric=OuterRef('metric')
).values('expectation')[:1]
return self.annotate(
_kpi=Subquery(position_kpi_subquery, output_field=FloatField())
)
class EmployeeKPI(BaseModel):
employee = models.ForeignKey(Employee)
metric = models.ForeignKey(Metric)
value = models.FloatField()
objects = models.Manager.from_queryset(EmployeeKPIQuerySet)()
def kpi(self):
if hasattr(self, '_kpi'):
return self._kpi
return PositionKPI.objects.filter(position=self.employee.position, metric=self.metric).first().expectation
PositionKPI.objects.with_kpi() # instead of PositionKPI.objects.all()