【问题标题】:Django queryset - filter/exclude by sum over columnDjango 查询集 - 按列的总和过滤/排除
【发布时间】:2014-06-22 13:05:57
【问题描述】:

对于小型缓存应用程序,我有以下问题/疑问:

部分模型:

class CachedResource(models.Model):
    ...
    filesize = models.PositiveIntegerField()
    created = models.DateTimeField(auto_now_add=True, editable=False)
    ...

缓存应该是例如限制为 200MB - 并保留最新的文件。

如何创建查询集,例如:

CachedResource.objects.order_by('-created').exclude(" summary of filesize < x ")

任何意见表示赞赏!

例子:

created             filesize        keep/delete?

2014-06-22 15:00          50        keep    (sum: 50)
2014-06-22 14:50         100        keep    (sum: 150)
2014-06-22 14:40          30        keep    (sum: 180)
2014-06-22 14:30          20        keep    (sum: 200)
2014-06-22 14:20          50        delete  (sum: 250 > 200)
2014-06-22 14:10          10        delete  ...
2014-06-22 14:00         200        delete  ...
2014-06-22 13:50          10        delete  ...
2014-06-22 13:40           2        delete  ...
             ...         ...        ...     ...

【问题讨论】:

  • 确切的累积文件大小为 200MB(或 1TB)有多重要?一个近似值还不够吗?
  • 是的 - 完全。说 +/- 20% ...

标签: django django-models django-queryset


【解决方案1】:

以下查询集中的每个对象都有一个“filesize_sum”属性,其中包含自该对象创建以来创建的所有缓存资源的文件大小摘要。

qs = CachedResource.objects.order_by('-created').extra(select={
  'filesize_sum': """
   SELECT
     SUM(filesize)
   FROM
     CachedResource_table_name as cr
   WHERE
     cr.created >= CachedResource_table_name.created
     """})

然后你可以做一个循环来做你想做的事。例如,您可以创建一个循环,在 filesize_sum > 200MB 的第一个对象上中断,并针对该对象的创建日期更小或相等的所有对象在查询集上运行删除查询:

for obj in qs:
   if obj.filesize_sum > 200:
       qs.filter(created__lte=obj.created).delete()
       break

请记住,您可能还想在插入新的缓存资源之前采取一些措施,以使新资源的文件大小不超过您的限制。例如,您可以运行上述过程:

limit = configured_limit - filesize_of_cache_resource_to_insert

【讨论】:

    【解决方案2】:

    可能有更好的方法来做到这一点:

    cachedResources = CachedResource.objects.order_by('-created')
    list_of_items = []
    size_of_files = 0
    for item in cachedResources:
        if size_of_files < 200:
            list_of_items.append(item.id)
        else
            break
    cached_resources_by_size = CachedResource.objects.filter(id__in=list_of_items).order_by('-created')
    

    【讨论】:

    • :) 是的,如果没有其他解决方案,我也会一起破解这个东西......但它的效率非常低。认为没有 200MB 作为限制,而是 1TB - 以及 ~1kb 大小的文件......
    • 你说得对,这可能是个问题……我会考虑更好的解决方案
    【解决方案3】:
    totals = CachedResource.objects.values('id').aggregate(sum=Sum('filesize'), count=Count('id'))
    num_to_keep = totals['count'] * min(MAX_FILESIZE / totals['sum'], 1)
    while num_to_keep < totals['count']:
        new_sum = CachedResource.objects.filter(id__in=CachedResource.objects.order_by('-created')[:num_to_keep]).aggregate(sum=Sum('filesize'))
        # if <not acceptable approximation>:
        #    adjust approximation
        #    continue
    
        CachedResource.objects.order_by('-created')[num_to_keep:].delete()
        break
    

    第 1 行的聚合可以让您获得总文件大小和单个查询中的条目数。根据这些结果,很容易计算出要保留的大致条目数量。你可以做一些额外的检查来断言这个近似值在一定的范围内(正如你所说的 +/- 20%)。然后一个简单的order_by 和一个切片将产生一个包含所有要删除的条目的查询集。

    【讨论】:

      猜你喜欢
      • 2021-11-26
      • 2016-07-01
      • 2018-09-26
      • 1970-01-01
      • 2013-04-20
      • 1970-01-01
      • 2019-07-12
      • 2013-04-26
      • 2012-05-18
      相关资源
      最近更新 更多