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