У меня есть файловые объекты разных типов, которые наследуются от BaseFile, и добавляются пользовательские атрибуты, методы и, возможно, поля. BaseFile также хранит идентификатор типа файла, так что соответствующая модель подкласса может быть получена из любого объекта BaseFile:
class BaseFile(models.Model):
name = models.CharField(max_length=80, db_index=True)
size= models.PositiveIntegerField()
time_created = models.DateTimeField(default=datetime.now)
file_type = models.ForeignKey(ContentType, on_delete=models.PROTECT)
class FileType1(BaseFile):
storage_path = '/path/for/filetype1/'
def custom_method(self):
<some custom behaviour>
class FileType2(BaseFile):
storage_path = '/path/for/filetype2/'
extra_field = models.CharField(max_length=12)
У меня также есть различные типы событий, которые связаны с файлами:
class FileEvent(models.Model):
file = models.ForeignKey(BaseFile, on_delete=models.PROTECT)
time = models.DateTimeField(default=datetime.now)
Я хочу иметь возможность эффективно получать все файлы определенного типа, которые не были задействованы в определенном событии, например:
unprocessed_files_type1 = FileType1.objects.filter(fileevent__isnull=True)
Однако, глядя на SQL, выполненный для этого запроса:
ВЫБРАТЬ "app_basefile". "Id", "app_basefile". "Name", "app_basefile". "Size", "app_basefile". "Time_created", "app_basefile". "File_type_id", "app_filetype1". "Basefile_ptr_. «
ОТ "app_filetype1"
ВНУТРЕННИЙ РЕЙТИНГ "app_basefile"
ON ("app_filetype1". "Basefile_ptr_id" = "app_basefile". "Id")
ВЛЕВО НАРУЖНОЕ СОЕДИНЕНИЕ "app_fileevent" ВКЛ ( "app_basefile". "Id" = "app_fileevent". "File_id" )
ГДЕ "app_fileevent". "Id" НУЛЬ
Похоже, что это может быть не очень эффективно, потому что он присоединяется к BaseFile.id вместо FileType1.basefile_ptr_id, поэтому он будет проверять ВСЕ идентификаторы BaseFile, чтобы увидеть, присутствуют ли они в FileEvent.file_id, когда мне нужно только проверить Идентификаторы BaseFile, соответствующие FileType1 или FileType1.basefile_ptr_ids.
Это может привести к значительной разнице в производительности, если существует очень большое количество BaseFiles, но FileType1 является лишь небольшим подмножеством этого, потому что он будет выполнять большое количество ненужных поисков.
Есть ли способ заставить Django присоединиться к "app_filetype1". "Basefile_ptr_id" или иным образом повысить эффективность этой функции?
Спасибо за помощь
UPDATE:
Использование аннотаций и подзапросов Exists, кажется, делает то, что мне нужно, однако полученный SQL все еще выглядит странно:
unprocessed_files_type1 = FileType1.objects.annotate(file_event=Exists(FileEvent.objects.filter(file=OuterRef('pk')))).filter(file_event=False)
ВЫБЕРИТЕ "app_basefile". "Id", "app_basefile". "Name", "app_basefile". "Size", "app_basefile". "Time_created", "app_basefile". "File_type_id", "app_filetype1". "Basefile_ptr_. »,
EXISTS (
ВЫБЕРИТЕ U0. "Id", U0. "File_id", U0. "Time"
ОТ "app_fileevent" U0
ГДЕ U0. "File_id" = ("app_filetype1". "Basefile_ptr_id"))
AS "file_event"
ОТ "app_filetype1"
ВНУТРЕННЕЕ СОЕДИНЕНИЕ "app_basefile" ON ("app_filetype1". "Basefile_ptr_id" = "app_basefile". "Id")
ГДЕ СУЩЕСТВУЕТ (
ВЫБЕРИТЕ U0. "Id", U0. "File_id", U0. "Time"
ОТ "app_fileevent" U0
ГДЕ U0. "File_id" = ("app_filetype1". "Basefile_ptr_id")) = 0
Кажется, он выполняет подзапрос WHERE EXISTS дважды, а не просто использует аннотированную метку file_event ... Может быть, это просто ошибка драйвера Django / SQLite?