【问题标题】:Filtering a model in Django based on a condition upon the latest child record根据最新子记录的条件过滤Django中的模型
【发布时间】:2012-01-17 09:35:52
【问题描述】:

我有两个这样的模型:

class Store(models.Model):
    name = models.CharField(max_length=255)

class Order(models.Model):
    store = models.ForeignKey(Store)
    date = models.DateTimeField(auto_now_add=True)
    success = models.BooleanField()

我想过滤掉最近下单成功的Store模型中的所有记录,即success == True

虽然它看起来很简单,但我在弄清楚如何使用 ORM 查询系统完成此任务时遇到了问题。

有什么帮助吗?谢谢。

【问题讨论】:

  • 或者您需要一个查询?是否适合您进行 2 个查询:获取每个 sotore 的最后一个订单日期并检查是否存在此数据的 success 订单并存储是一个解决方案?循环对您来说是一个有效的解决方案吗?

标签: python django django-queryset


【解决方案1】:

我的方法是:做 2 个列表,第一个使用 (id_store, last_success_date) 元组,第二个使用 (id_store, last_date) 元组:

l_succ = stores.objects.filter( 
                       order__success = True 
                  ).annotate(
                       last_success=Max('order__date')
                  ).value_list (
                       'id', 'last_success'
                  )
#l_succ = [ (1, '1/1/2011'), (2, '31/12/2010'), ... ] <-l_succ result

l_last = stores.objects.annotate(
                       last_date=Max('order__date')
                  ).value_list (
                       'id', 'last_date'
                  )
#l_last = [ (1, '1/1/2011'), (2, '3/1/2011'), ... ]   <-l_last result

然后获取最后数据和最后成功日期相等的商店的商店 id,然后您就可以查询:

store_success_ids =  [ k[0] for k in l_succ if k in l_last ]
#store_success_ids = [1, 5, ... ]          <-store_success_ids result
#Cast l_last to dictionary to do lookups if you have a lot of stores.

result = Store.objects.filter( pk__in = store_success_ids)        

这似乎是一个优雅的解决方案,一个复杂的查询只需四行代码(但有一个简单的请求)。免责声明,未经测试。

【讨论】:

  • HI DaniHP,这为我解决了。我使用了类似的解决方案。看起来不可能执行此单个查询。真可惜。 (有时 ORM 真的让我觉得 DB 很笨。)
【解决方案2】:

这只是一个想法,所以如果我完全错了,请不要打我:) 我以为你可以这样做:

# first annotate latest order to Store which returns new queryset, filter new qs
# by success = True
stores = Store.objects.annotate(Max('order__date')).filter(order__success=True)

说实话,这是未经测试的。

【讨论】:

【解决方案3】:

您可以在一个查询中完成。

使用 QuerySets 的 .extra() 功能编写如下所示的 SQL:

Store.objects.extra(where=['id in (select store_id from order where success = true)']) 

【讨论】:

  • 这不会检查成功订单是否是最新的 - 此查询将返回至少有一个成功订单的商店。这里也不需要额外的:Store.objects.filter(order__success=True) 会做同样的事情(或者类似的东西,没有测试过)。
  • 如果 Order 不包含在 Store 模型中,你能做 order_-success=True 吗?你对最新的观点是正确的。您可以通过适当地修改查询中的 extra() 来检查最新订单是否成功。
  • M,是的,我可以,这两个模型是连接的:docs.djangoproject.com/en/1.3/topics/db/queries/…。或者我不明白一些东西,它在这里不适用?修改extra怎么样,你的解决方案是什么?我也很想知道答案:)
【解决方案4】:

这种方法对您有用吗?循环遍历所有商店并获取每个商店的最新成功(如果存在)。

我最初的想法是不正确的:

latest_successes = []
store_objects = Store.objects.all()
for store_obj in store_objects:
    try:
        latest_success = Order.objects.filter(store=store_obj).filter(success=True).order_by('-date')[0]
    except IndexError:
        pass
    else:
        latest_successes.append(latest_success)

根据@demalexx 的可靠评论进行编辑

latest_successes = []
store_objects = Store.objects.all()
for store_obj in store_objects:
    try:
        is_latest_success = store_obj.order_set.order_by('-date')[0].success
    except IndexError:
        pass
    else:
        latest_successes.append(store_obj)

【讨论】:

  • 这不起作用:您选择最新的成功订单,但可能有更多最近的不成功订单。需要修改:is_latest_success = store_obj.order_set.order_by('-date')[0].success。这是非常无效的:对每个 Store 对象进行新查询。但这取决于 OP 任务,在某些情况下它可以很好地工作。
  • 这是一个很好的观点,即最近的订单可能不成功。我没有想到这一点。我不同意所提议的方法基于“每个 Store 对象的新查询”是无效的,但我同意它可能不是最有效的方法(或者至少我希望有人发布更有效的东西)。
  • 我所说的无效是什么意思,假设你有一百万个商店——你会收到一百万个 SQL 请求,这不是解决问题的最有效方法 :) 而且 OP 也没有告诉任何时间限制,如果他需要实时,比如在视图中准备这个列表,这是不合适的解决方案。
  • @demalexx 我肯定和你在一起。我只是对低效和低效持迂腐的态度。但是如果运行时间接近宇宙年龄,无效将是绝对正确的=)。顺便说一句,我的修改现在对你来说是否正确。
  • @demalexx 顺便说一句,您能否指出解释您使用 order_set 的文档。我假设基于外键关系我们可以访问每个商店的订单集,但我想确保我理解正确的语法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-22
  • 1970-01-01
  • 2021-04-10
  • 2011-07-26
  • 2013-08-25
相关资源
最近更新 更多