Набор запросов Django — фильтровать / исключать по сумме по столбцу

#django #django-models #django-queryset

#django #django-модели #django-набор запросов

Вопрос:

Для небольшого приложения для кэширования у меня следующая проблема / вопрос:

Часть модели:

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

Кеш должен быть, например, ограничен 200 МБ — и сохранять самые новые файлы.

Как я могу создать набор запросов, например:

 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  ...
             ...         ...        ...     ...
  

Комментарии:

1. Насколько важно, чтобы точный совокупный размер файла составлял 200 МБ (или 1 ТБ)? Разве не было бы достаточно приблизительного определения?

2. Да — полностью. Скажем, /- 20%…

Ответ №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> 200 МБ и запустить запрос на удаление в наборе запросов для всех объектов с меньшей или равной датой создания для этого объекта:

 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')
  

Комментарии:

1. 🙂 Да, это то, что я бы тоже взломал, если нет другого решения… Но это крайне неэффективно. Подумайте, что предел не 200 МБ, а 1 ТБ — и файлы размером ~ 1 КБ…

2. Вы правы, это может быть проблемой… Я подумаю о лучшем решении

Ответ №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 и срез приведут к набору запросов всех записей для удаления.