flask

Якось зайшла мова в одній комюніті про легкий фреймворк на Python для невеличких сайтів.
Ось невеличка презентація. Буде трошки вільного часу, треба буде покурити мануал і потестувати.

Перша оптимізація Django

Закінчив загальну свою першу оптимізацію проекту на Django. Цікаво і круто. Підгадаю, на стартовій сторінці в мене виконувалось більше 300 запитів, від чого я мало не впав і не розчарувався в "Object-relational mapping". Оптимізація з кешуванням зараз дає результат в 30 дуже легеньких запитів і в час неактуальності кешу ще біля десятка, але також легеньких.
Основне зло, з яким довелось боротись:
  •  django-tagging. Дуже корисна і зручна бібліотека міток, але має в post-init два запити і любе звертання до моделі, в якій використовується поле міток множить кількість запитів на 3. Там де мітки не використовуються, лікується через .defer('tags');
  • В мене кілька пов'язаних моделей і використовується в URL два slug з різних моделей. Звісно по документації описав генерацію URL у get_absolute_url і в результаті отримав у вибірках кількість запитів * 2. Так як в мене кешуються ці моделі, то у шаблонах через template tags по ID я вибирав потрібні значення з інших моделей з кешу, в результаті позбувся цього зла;
Додаткове зло - моменти говнокоду, який довелось розібрати по крупинці і сумістити все з кешуванням. На продакшені швидше за все буде використовуватись memcached.

Висновок: Django дуже рулить. Все надзвичайно слухняне + хороша реалізація фреймворку кешування = хороший результат.

оптимізація і django-tagging

Взявся за оптимізацію проджекту, запустив монітор запитів і офігів, на стартовій більше 300 запитів. А виною всього виявився django-tagging, який запускає post-init і на кожен об'єкт отримуємо + 2 додаткових запити. Вирішується досить просто. У запит додаємо defer('tags')

django-tagging і get_by_model з логікою OR

Хороша ліба django-tagging, яка дозволяє тегувати все що завгодно. Вибірку по тегах можна зробити методом get_by_model.
TaggedItem.objects.get_by_model(Post, [u'духовність',u'культура',u'мистецтво'])
і ми отримаємо об'єкти з наступними тегами але з логікою "і". Але мені потрібно такий самий метод, але з логікою "або", тобто вибрати об'єкти де зустрічається хоча би один з перелічених тегів. Мій варіант методу. Трошки говнокод, але працює. Колись може дойдуть руки навести есетичний марафєт.
def get_by_model_or(self, queryset_or_model, tags):
    tags = get_tag_list(tags)
    tag_count = len(tags)
    if tag_count == 0:
        queryset, model = get_queryset_and_model(queryset_or_model)
        return model._default_manager.none()

    queryset, model = get_queryset_and_model(queryset_or_model)
    content_type = ContentType.objects.get_for_model(model)
    opts = self.model._meta # return tagging.taggeditem
    tagged_item_table = qn(opts.db_table) # `tagging_taggeditem`

    where = []
    where.append('%s.content_type_id = %%s' % tagged_item_table,)

    or_string = "("
    or_tuple = ()
    params = [content_type.pk]
    for i in range(len(tags)):
        if i == 0:
            or_string += "%s.tag_id = %%s "
        else:
            or_string += "OR %s.tag_id = %%s "
        or_tuple += (tagged_item_table,)
        params.append(tags[i].pk)
    or_string += ")"

    where.append(or_string % or_tuple)
    where.append('%s.%s = %s.object_id' % (qn(model._meta.db_table),
                                           qn(model._meta.pk.column),
                                           tagged_item_table))

    return queryset.extra(
        tables=[opts.db_table],
        where=where,
        params = params
    )

Django + TinyMCE + Django-tinymce-filebrowser

Як бісить, коли оупенсорсним програмістам впадло писати хоча би мінімальну документацію. Інколи доводиться курити годинами мануали. Цей раз не виключення. Потрібно прикрутити до HTML-едітора файловий браузер (заванаження малюночків на сервер і вставка їх в документ). Одне з легеньких рішень Django-tinymce-filebrowser. Але ні слова не вказано про ініціалізацію цього аппа. Тож:
{% block head_extra %}
    <script src="{{ settings.TINYMCE_JS_URL }}" type="text/javascript"></script>

    <script type="text/javascript">
        tinyMCE.init({
            mode: "textareas",
            theme: "advanced",
            language : 'uk',
            theme_advanced_toolbar_location : "top",
            theme_advanced_toolbar_align : "left",
            theme_advanced_statusbar_location : "bottom",
            theme_advanced_resizing : "True",
            file_browser_callback : "mce_filebrowser"
        });

        function mce_filebrowser(field_name, url, type, win) {
            var cmsURL = "/tinymce-fb/image";    
            if (cmsURL.indexOf("?") < 0) {
                cmsURL = cmsURL + "?type=" + type;
            }
            else {
                cmsURL = cmsURL + "&type=" + type;
            }

            tinyMCE.activeEditor.windowManager.open({
                file : cmsURL,
                title : 'My File Browser',
                width : 420,  
                height : 400,
                resizable : "yes",
                inline : "yes",  
                close_previous : "no"
            }, {
                window : win,
                input : field_name
            });
        }
    
</script>
{% endblock %}

шаблонний фільтр для числівників

Інколи потрібно разом з цифрою вказати слово у вірній множині. Наприклад: 1 коментар, 9 коментарів, 2 коментарі. Django в комплекті має багато шаблонних фільтрів, але для нашої солов'їної треба написати власний.


# -*- coding: utf-8 -*-

from django import template
register = template.Library()

@register.filter
def uapluralize(value, arg="дурак,дураки,дураків"):
    args = arg.split(",")
    number = abs(int(value))
    a = number % 10
    b = number % 100

    if (a == 1) and (b != 11):
        return args[0]
    elif (a >= 2) and (a <= 4) and ((b < 10) or (b >= 20)):
        return args[1]
    else:
        return args[2]
І в шаблоні:
{% load uapluralize %}
{{ m.comments_count|uapluralize:"коментар,коментарі,коментарів" }}