Делая отладку производительности небольшого проекта, но с достаточно большой базой, столкнулся с неприятным спецэффектом.
Django при выборках с условиями по внешним ключам, связанным с проверкой на NULL, генерирует запросы, содержащие JOIN по каждому такому ключу. К примеру, для модели
class ForumPlugin(models.Model):
name = models.CharField(
null=False,
blank=False,
max_length=50,
unique=True,
verbose_name=_('name')
)
class Thread(MPTTModel):
parent = TreeForeignKey(
'self',
null=True,
blank=True,
related_name='children',
verbose_name=_('parent thread')
)
plugin = models.ForeignKey(
ForumPlugin,
null=True,
blank=True,
related_name='threads',
verbose_name=_('plugin')
)
При выполнении выборки
Thread.objects.filter(plugin__isnull=True, parent__isnull=True)
Djando формирует такой запрос:
SELECT `forum_thread`.`id`, `forum_thread`.`parent_id`, `forum_thread`.`plugin_id`, `forum_thread`.`lft`, `forum_thread`.`rght`, `forum_thread`.`tree_id`, `forum_thread`.`level` FROM `forum_thread` LEFT OUTER JOIN `forum_thread` T2 ON (`forum_thread`.`parent_id` = T2.`id`) LEFT OUTER JOIN `forum_forumplugin` ON (`forum_thread`.`plugin_id` = `forum_forumplugin`.`id`) WHERE (T2.`id` IS NULL AND `forum_forumplugin`.`id` IS NULL AND ) ORDER BY `forum_thread`.`id
Естественно время исполнения такого запроса увеличивается на несколько порядков, что при больших таблицах может быть критично. Так на моем проекте на таблице порядка 20-30к записей такая выборка вместо положенной 1мс выполняется от 100мс до 300мс, что увеличивает время генерации страницы вдвое.
К сожалению бага разработчикам ORM известна уже четыре года и имеет долгую и печальную историю.
В данный момент присутствует во всех стабильных версиях, в том числе в 1.4.3. Предполагается, что в 1.5 она наконец-то будет исправлена.
В качестве обходного пути советуют использовать двойное отрицание:
Thread.objects.exclude(plugin__isnull=False, parent__isnull=False)
но мне не удалось на практике таким способом избавится от проблемы. Обращение напрямую к полю parent_id также не помогает.
Будьте внимательны при проектировании моделей и старайтесь учитывать эту особенность Django, и избегать выборок по внешним ключам с использованием NULL условий.
Автор: kozzztik