Как выполнить эффективную проверку обратного внешнего ключа в модели многостолового наследования Django? - PullRequest
1 голос
/ 10 апреля 2019

У меня есть файловые объекты разных типов, которые наследуются от 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?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...