按 Django ModelAdmin 更改列表中聚合的外部字段排序

Sort by aggregated foreign field in Django ModelAdmin changelist

在我当前的项目中,我试图在 Django 中设置一个简单的测试应用程序。对于管理,我使用生成的 Django 管理,但我很难在模型的变更列表视图中包含一个具有最佳测试结果的可排序计算字段。

我的模型如下(简化):

class Candidate(models.Model):
   name = models.CharField(max_length=255, null=False)
   email = models.EmailField(unique=True, null=False)

class Test(models.Model):
   candidate = models.ForeignKey(Candidate, on_delete=models.CASCADE, null=False)
   result = models.PositiveIntegerField(null=True)

class Question(models.Model):
   text = models.TextField(null=False)
   correct_answer = models.CharField(max_length=1, choices=OPTIONS, null=False)

class Answer(models.Model):
   test = models.ForeignKey(Test, on_delete=models.CASCADE)
   question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name='answers')
   answer = models.CharField(max_length=1, choices=Question.OPTIONS, null=True)

一个候选人可能有多个测试,我想在更改列表视图中显示他最好结果的字段并能够按它排序。结果是使用相同测试 FK 的所有答案中正确答案的百分比 (Answer.question.correct_answer == Answer.answer)。

发现我无法使用由函数定义的 自定义计算字段,因为 Django 无法对其进行排序,因为排序需要修改 queryset 直接转换为 SQL。所以我添加了计算百分比的 Test.result 字段(这使方案非规范化 :-/ )并尝试在 queryset 中为每个候选人添加带有 SELECT MAX(Test.result) FROM Test WHERE Test.candidate = {candidate} 的注释字段,但找不到方法。

问题是,由于 candidate:test 的 1:M 映射,查询需要反向外键映射,我还没有找到实现它的方法。据我所知:

class CandidateAdmin(admin.ModelAdmin):
   list_display = ['name', 'email','best_result']
   search_fields = ['name', 'email']

   def get_queryset(self, request):
      queryset = super().get_queryset(request)
      queryset = queryset.annotate(
         _best_result = models.Max('tests_result')
      )
      return queryset

   def best_result(self, obj):
      return obj._best_result

但是 Django 不理解我在 tests_result 的反向外键搜索中使用 MAX 的尝试。你能建议吗?或者,如果我错过了如何添加自定义排序的方法,所以我不需要在数据库中保留计算的测试结果,同时仍然按它排序,我将不胜感激任何提示。

最后,我用查询创建了一个数据库视图,用 managed = False in Meta 将它添加到我的模型中,然后改用它。就像一个魅力。