django查询,外键上的不同和另一个属性的顺序

时间:2014-03-31 03:42:05

标签: django postgresql

我被困在django的查询中。 我有两个模型,

Class Course(models.Model):
    provider = ForeignKey(Provider)
    rating = FloatField()

Class Provider(models.Model):
    ...

我想查询课程列表,但同一个提供者不能提供两门课程。也就是说,只选择每个提供者最相关的课程。 我这样做了,

Course.objects.filter(xxxx).distinct('provider')

这可行,但如果我想将order_by添加到此查询,例如

Course.objects.filter(xxxx).distinct('provider').order_by('rating')

发生错误。在django文档中, order_by()中的字段必须以distinct()中的字段开头,顺序相同。

然而,我仍然找不到任何其他解决方法来做同样的事情。

任何想法? 非常感谢你。

2 个答案:

答案 0 :(得分:0)

您需要从反向'中查看此问题。透视和使用" annotage"。以下是您在视图中获得所需输出的方式:

#views.py
from xxx.models import Course, Provider
from django.db.models import Min

from django.shortcuts import render

def home(request):
    t = Provider.objects.annotate(best_rating = Min('course__rating')).filter('xxx').order_by('best_rating')

    return render(request, 'home.html', {
                                        't':t,
    })

所以这里发生了什么:

首先" annotate"是Django的做法,通常被称为" group by"声明。不幸的是,它们根据被调用的对象提供不同的结果。所以在我们的案例中,我们必须看看每个提供商的最佳课程"而不是"评价最高的课程及其提供者"。

在我们的.annotate(best_rating = Min('course__rating'))语句中,内部子句返回最佳路线(假设1.0等级是最好的,如果是相反的方式则使用Max())。返回值被分配给名为best_rating的虚拟模型属性,您可以像任何其他模型成员一样在模板中调用该属性。它只是不会存储在数据库中。

字符串'course__rating'是一个特殊字符串。它告诉Django ORM我们想要找到哪个字段的最小值。此字符串的格式为"<MODEL>__<FIELD>"。虽然提供者不知道他与课程的关系,但Django会为我们进行反向查找。所以这个字符串基本上说:&#34;给我所有与提供者有关系的课程的评级字段&#34;

annotate像许多其他querySet方法一样返回QuerySet,因此您可以根据需要过滤和排序新集。

当然,您也可以在注释之前进行过滤,但请注意,当您执行聚合(如查找min()或max()值)时,这可能会影响最终结果。

基本模板示例:

     #home.html
     {% for e in t %}
     {{ e.name }} {{ e.best_rating }}<br>
     {% endfor %}

查看Django Documentation for further information about annotations and reverse lookups

更新01.04.2014: 如果您希望保持您的查询由课程列表驱动,您可以使用F()语句

使用以下语句
from xxx.models import Course, Provider
from django.db.models import Min, F
from django.shortcuts import render

def home(request):
    t = Course.objects.annotate(best_rating = Min('provider__course__rating')).filter(rating = F('best_rating'))

    return render(request, 'home.html', {
                                        't':t,
    })

此查询将按提供商和课程评分进行注释。但仅此一项将使所有提供者返回一个额外的列,其中课程的评分最低。所以我们需要过滤这个新值的每一行。为此,我们需要一个F()表达式,它匹配每一行的值。

答案 1 :(得分:0)

对您的问题实际上有一个更简单的解决方案。简单地做Django告诉你的事情:

Course.objects.filter(xxxx).distinct('provider').order_by('provider', 'rating')

我不确切知道幕后发生了什么,但这对我有用。只需将distinct字段添加为 first order参数即可。之后,您可以添加自定义订购。除了相关/外键排序。例如。这不起作用:

Course.objects.filter(xxxx).distinct('provider').order_by('provider', 'some_model__created')

第二部分不是你要求的,但这是一个典型的错误 - 所以记住这一点。此外,在模型中使用“class Meta”排序时,这将不起作用。

相关问题