Django'da tehlikeli template etiketi: length.

01/2014

Bugün yeni projeyle uğraşırken template içinde bir noktanın baya yavaş render edildiğini hissettim. (Bu arada Markafoni'den ayrıldım onu bilhare anlatırım.) Template içerisinde bir QuerySet length template filtresiyle çağırılıyordu:

{% if BlaBla.all|length == 5 %}
    ..
{% endif %}

length template filtresini açıp baktığımda verilen değeri len fonksiyonuna geçirdiğini gördüm:

@register.filter(is_safe=False)
def length(value):
    """Returns the length of the value - useful for lists."""
    try:
        return len(value)
    except (ValueError, TypeError):
        return 0

İşte tam da arıza burada başlıyor. Len fonksiyonu ile bir QuerySet'in uzunluğunu almaya çalıştığınızda QuerySet'in tamamını veritabanından alıp, bir listeye dönüştürüp ardından listenin uzunluğunu alıyor. Bu da devasa tablolarda hem memory sıkıntısı, hem de veritabanı sunucularını gereksiz meşgul etmek demek. Sqldebugshell ile len metodunun nasıl bir sorgu çalıştırdığına baktığımda manzara şu şekilde:

In [6]: len(BlaBla.objects.all())

SELECT `c..`.`id`,
       `c..`.`i..`,
       `c..`.`p..`,
       `c..`.`c..`,
       `c..`.`s..`,
       `c..`.`i..`,
       `c..`.`i..`,
       `c..`.`u..`,
       `c..`.`u..`,
       `c..`.`s..`,
       `c..`.`c..`,
       `channel_customstream`.`t..`
FROM `channel_customstream`

Görüldüğü üzere o modele ait her şeyi toplayarak alan bir sorgu oluşturdu. Halbuki count metodu QuerySet'lerin direkt olarak veritabanında Count çalıştırması sağlanabiliyor:

In [5]: CustomStream.objects.count()

SELECT COUNT(*)
FROM `channel_customstream`

Diyeceğim o ki, length etiketini count sorgusu yaptığını sanarak kullanmayınız.