Как удалить все, кроме самого последнего объекта в каждой группе в Django
Я хочу сгруппировать объекты моей модели по трем полям, и удалить все объекты, кроме самых молодых для каждой группы .
Моя модель:
class DataFile(models.Model):
filename = models.CharField(unique=True, max_length=256)
timestamp = models.DateTimeField()
tile_label = models.CharField(max_length=256, blank=True)
region = models.CharField(max_length=256, blank=True)
resolution = models.CharField(max_length=256, blank=True)
Этот запрос дает мне список значений, которые я хочу сохранить:
DataFile.objects.values('resolution', 'region', 'tile_label').annotate(maxfield=Max('timestamp'))
Результатом этого является
{'resolution': '30', 'region': 'SRC', 'tile_label': '10r_20d', 'maxfield': datetime.datetime(2021, 9, 11, 7, 13, tzinfo=<UTC>)}
{'resolution': '30', 'region': 'NRC', 'tile_label': '10r_20d', 'maxfield': datetime.datetime(2021, 8, 16, 2, 8, tzinfo=<UTC>)}
{'resolution': '30', 'region': 'NRC', 'tile_label': '100r_200d', 'maxfield': datetime.datetime(2021, 11, 15, 23, 5, tzinfo=<UTC>)}
{'resolution': '300', 'region': 'SRC', 'tile_label': '10r_20d', 'maxfield': datetime.datetime(2021, 11, 1, 13, 46, tzinfo=<UTC>)}
{'resolution': '300', 'region': 'NRC', 'tile_label': '10r_20d', 'maxfield': datetime.datetime(2021, 11, 5, 12, 20, tzinfo=<UTC>)}
{'resolution': '300', 'region': 'NRC', 'tile_label': '100r_200d', 'maxfield': datetime.datetime(2021, 11, 18, 5, 54, tzinfo=<UTC>)}
{'resolution': '30', 'region': 'SRC', 'tile_label': '100r_200d', 'maxfield': datetime.datetime(2021, 11, 5, 21, 8, tzinfo=<UTC>)}
{'resolution': '300', 'region': 'SRC', 'tile_label': '100r_200d', 'maxfield': datetime.datetime(2021, 11, 18, 8, 29, tzinfo=<UTC>)}
Теперь мне нужно отфильтровать все объекты DataFile
, кроме объекта с наибольшей датой в каждой группе resolution
, region
, tile_label
, а затем удалить их (т.е. оставить объект с наибольшей датой).
Как мне это сделать? Мне нужно сделать GROUP BY с values()
, чтобы получить максимум из каждой группы, но это означает, что я больше не оперирую всем набором запросов.
Я думаю, что есть решение, связанное с подзапросами, но я так и не смог понять, как они работают.
Мне нужно решение на основе базы данных, поэтому зацикливание - не вариант. Я бы также предпочел избегать запросов __in
по соображениям производительности (но в конечном итоге сделаю это, если не будет другого решения).
Этот запрос дает мне список значений, которые я хочу сохранить:
DataFile.objects.values('resolution', 'region', 'tile_label').annotate(maxfield=Max('timestamp'))
Этот запрос дает вам максимальную временную метку, но не говорит, к какой строке она относится. Вы можете получить это вторым запросом, но это кажется мне сложным.
Я попытался подумать, каким свойством должен обладать файл DataFile, чтобы претендовать на удаление. Ответ я нашел в том, что должен существовать более новый DataFile с таким же resolution
/region
/tile_label
. Когда я перевожу это в Django, то получаю что-то вроде этого:
newer = DataFile.objects.filter(
resolution=OuterRef('resolution'),
region=OuterRef('region'),
tile_label=OuterRef('tile_label'),
timestamp__gt=OuterRef('timestamp'),
)
DataFile.objects.filter(Exists(newer)).delete()
Exists
создает подзапрос, а OuterRef
может использоваться для ссылки на поля из внешнего запроса.
В SQL это будет выглядеть примерно так (не проверено):
DELETE from datafiles WHERE EXISTS (
SELECT id from datafiles as datafiles2 WHERE (
datafiles2.resolution = datafiles.resolution
AND datafiles2.region = datafiles.region
AND datafiles2.tile_label = datafiles.tile_label
AND datafiles2.timestamp >= datafiles.timestamp
)
)