You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django中将kpi方法改写为模型关联关系

实现EmployeeKPI与PositionKPI的关联关系

步骤1:给PositionKPI添加唯一约束

首先确保每个Position和Metric的组合唯一,让每个EmployeeKPI能对应唯一的PositionKPI:

class PositionKPI(BaseModel):
    position = models.ForeignKey(Position, on_delete=models.CASCADE)
    metric = models.ForeignKey(Metric, on_delete=models.CASCADE)
    expectation = models.FloatField()

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['position', 'metric'], 
                name='unique_position_metric'
            )
        ]

步骤2:给EmployeeKPI添加关联字段

在EmployeeKPI中新增ForeignKey字段关联PositionKPI,禁用数据库层面的外键约束(因为关联依赖两个字段匹配),同时限制后台可选范围:

class EmployeeKPI(BaseModel):
    employee = models.ForeignKey(Employee, on_delete=models.CASCADE)
    metric = models.ForeignKey(Metric, on_delete=models.CASCADE)
    value = models.FloatField()

    kpi = models.ForeignKey(
        PositionKPI,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        db_constraint=False,
        limit_choices_to=lambda self: {
            'position': self.employee.position,
            'metric': self.metric
        } if self.employee and self.metric else {}
    )

步骤3:用信号自动填充关联字段

通过Django信号在保存EmployeeKPI时自动匹配对应PositionKPI,无需手动设置:

from django.db.models.signals import pre_save
from django.dispatch import receiver

@receiver(pre_save, sender=EmployeeKPI)
def auto_set_kpi(sender, instance, **kwargs):
    if instance.employee and instance.metric:
        instance.kpi = PositionKPI.objects.filter(
            position=instance.employee.position,
            metric=instance.metric
        ).first()

替代方案:用属性替代数据库字段

如果不想新增数据库字段,可改用property实现,同时支持ORM预加载避免N+1查询:

class EmployeeKPI(BaseModel):
    employee = models.ForeignKey(Employee, on_delete=models.CASCADE)
    metric = models.ForeignKey(Metric, on_delete=models.CASCADE)
    value = models.FloatField()

    @property
    def kpi(self):
        if not hasattr(self, '_kpi_cache'):
            self._kpi_cache = PositionKPI.objects.filter(
                position=self.employee.position,
                metric=self.metric
            ).first()
        return self._kpi_cache

# 预加载示例(避免重复查询)
from django.db.models import Prefetch

employee_kpis = EmployeeKPI.objects.all().prefetch_related(
    Prefetch(
        'employee__position__positionkpi_set',
        queryset=PositionKPI.objects.all(),
        to_attr='_position_kpis'
    )
)

# 手动映射缓存
for ekpi in employee_kpis:
    ekpi._kpi_cache = next(
        (pkpi for pkpi in ekpi.employee.position._position_kpis if pkpi.metric == ekpi.metric),
        None
    )

以上方案均可实现你需要的关联逻辑,对应给出的SQL查询效果。

内容的提问来源于stack exchange,提问作者Alex Tonkonozhenko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 18:32:41