【问题标题】:How do I use Django's "extra" method to filter by a calculated property?如何使用 Django 的“额外”方法按计算属性进行过滤?
【发布时间】:2013-01-21 16:29:00
【问题描述】:

我的应用中有一个搜索功能,它接收“城市”和“持续时间”输入(两个列表)并返回按包“评级”排序的前 30 个匹配“包”结果。

如果所有参数都是列,那么实现起来会很容易,但是“duration”和“rating”是计算属性。这意味着我不能使用标准的 Django 查询来过滤包。 seems that Django 的“额外”方法是我需要在这里使用的,但是我的 SQL 不是很好,这似乎是一个非常复杂的查询。

我应该在这里使用额外的方法吗?如果是这样,那句话会是什么样子?

下面复制了适用的代码。

#models.py
class City(models.Model):
    ...
    city = models.CharField(max_length = 100)

class Package(models.Model):
    ....
    city = models.ManyToManyField(City, through = 'PackageCity')

    @property
    def duration(self):
        duration = len(Itinerary.objects.filter(package = self))
        return duration

    @property
    def rating(self):
        #do something to get the rating
        return unicode(rating)


class PackageCity(models.Model):
    package = models.ForeignKey(Package)
    city = models.ForeignKey(City)


class Itinerary(models.Model):
    # An Itinerary object is a day in a package, so len(Itinerary) works for the duration
    ...
    package = models.ForeignKey(Package)




#functions.py
def get_packages(city, duration):
    cities = City.objects.filter(city = city) # works fine
    duration_list = range(int(duration_array[0], 10), int(duration_array[1], 10) + 1) # works fine

    #What I want to do, but can't because duration & rating are calculated properties
    packages = Package.objects.filter(city__in = cities, duration__in = duration_array).order_by('rating')[:30]

【问题讨论】:

  • 您应该使用 Signals 将评分和持续时间保存在某处,这样会更容易
  • @sacabuche 信号与过滤查询集有什么关系?
  • @patrickn 什么都没有,但最好保存这些数据而不是每次都计算恕我直言

标签: django django-queryset


【解决方案1】:

首先,不要在查询集上使用 len(),使用 count()。 https://docs.djangoproject.com/en/dev/ref/models/querysets/#when-querysets-are-evaluated

其次,假设您正在使用您的 rating 属性计算平均评分,您可以使用 annotate: https://docs.djangoproject.com/en/dev/ref/models/querysets/#annotate

然后您可以执行以下操作:

queryset = Package.objects.annotate({'duration': Count('related-name-for-itinerary', distinct=True), 'rating': Avg('packagereview__rating')})

其中“PackageReview”是我刚刚制作的一个假模型,它有一个用于打包的外键,并且有一个“评级”字段。

然后您可以按照此处所述过滤带注释的查询集: https://docs.djangoproject.com/en/dev/topics/db/aggregation/#filtering-on-annotations (注意 annotate -> filter 和 filter -> annotate 之间的子句顺序差异。

属性是在运行时计算的,所以你真的不能将它们用于过滤或类似的事情。

【讨论】:

  • 啊,这看起来很有希望,谢谢。我会解决这个问题并尝试让它工作。
  • 搞定了。非常感谢,这比我想象的要干净得多。我最终使用的查询是: packages = Package.objects.filter(city__in = city).annotate(avg_rating = Avg('rating__rating'), total_duration = Count('itinerary', distinct = True)).filter(total_duration__in = duration_array).order_by('avg_rating')[:30]
猜你喜欢
  • 2011-05-19
  • 2015-02-26
  • 2014-09-02
  • 2021-09-04
  • 2014-09-15
  • 2021-06-28
  • 1970-01-01
  • 2015-02-20
  • 1970-01-01
相关资源
最近更新 更多