Пероксид на Джанге.

Жаба на метле

03.03.12

logger

Correctly logs to stdout under runserver. Under mod_wsgi logs into apache error log and file named after python import path + function name. Call logger = get_logger() or logger = get_logger(request)
import inspect
import logging
import os
import os.path
import sys

from django.conf import settings
from django.http import HttpRequest

def obtain_request():
    ancestors = inspect.getouterframes(inspect.currentframe())
    for frame_record in ancestors:
        frame = frame_record[0]
        if 'request' in frame.f_locals:
            req = frame.f_locals['request']
            if isinstance(req, HttpRequest):
                return req

def get_logger(request=None):
    if request is None:
        request = obtain_request()
    stack = inspect.stack()
    logger_name = '%s.%s' % (stack[1][0].f_globals['__name__'], stack[1][3])
    logger = logging.getLogger(logger_name)
    logger.setLevel(logging.DEBUG)
    def addLogger(x):
        x.setLevel(logging.DEBUG)
        logger.addHandler(x)
    server = request.META.get('wsgi.file_wrapper', None) if request else None
    if server is None or server.__module__ == 'django.core.servers.basehttp':
        addLogger(logging.StreamHandler(sys.stdout))
    else:
        addLogger(logging.StreamHandler(request.META['wsgi.errors']))
        log_file = os.path.join(
            os.path.abspath(settings.PROJECT_ROOT),
            'logs', '%s.log' % logger_name)
        log_dir = os.path.dirname(log_file)
        if not os.path.exists(log_dir):
            os.mkdirs(log_dir)
        addLogger(logging.FileHandler(log_file))
    return logger

25.10.11

Uploadify на Mac OS X

Говорят, что у Mac OS X есть какие-то проблемы с Flash. Или наоборот, что у Flash есть какие-то проблемы с Mac OS X. В общем, есть между ними какие-то трения. Я лично столкнулся с чем-то таким, используя Uploadify. Этот плагин jQuery вносит метод uploadify, который принимает объект опций, среди которых есть scriptData. scriptData - объект, свойства которого будут добавлены к POSTу, когда flash-часть uploadify начнет загружать файл на сервер. В теории и документации вы увидите все, что передали в scriptData, в request.POST (если, конечно, используете Django).

На самом деле все не так. На Mac OS X вы увидите только первое свойство объекта scriptData. Так, если scriptData: {'size': '160x160', 'crop': true}, то вы request.POST будет только size. Обойти это можно так. Вместо {'scriptData': {'foo': 'bar', 'bar': 'foo'} }надо передать {'auto': false, 'onSelect': function(){ $x.uploadifySettings({'foo': 'bar', 'bar': 'foo'}); $x.uploadifyUpload();}}. Здесь $x - тот же объекта, метод котрого uploadify() вы вызываете. Так работает.

Но это не вся правда. в request.POST будет элемент 'foo' и элемент 'amp;bar'. Ну это просто:


if 'amp;crop' in request.POST:
        request.POST['crop'] = request.POST['amp;crop']

Почему так - не знаю. Но надеюсь, что моя небольшая находка сэкономит кому-то (мне) немного времени.

18.10.11

Важность правильных названий переменных

Я никак не мог запомнить кто из аргументов функции, передаваемой reduce, аккумулятор, а кто очередной элемент обрабатываемой последовательности. Теперь-то я понимаю, что это не только потому, что я немного пустоголовый, но и потому, что в официальной документации Python в примере для функции reduce эти аргументы называются x и y.

Но сегодня я прочел заметку про underscore.js некоего кого-то и буду помнить, что аккумулятор - первый аргумент. Просто в примере использования reduce автор заметки назвал аргументы передаваемой в reduce функции sum и n. Вот так просто.

16.10.11

node.js-like next

Есть у нас профили. Отдельный профиль доступен по пути, который соответствует '^(\w+)/$'. Хорошо. Прошло полгода, появилась новая сущность (НС). Попросили, чтобы отдельная НС была доступна по пути, который соответствует '^(\w+)/$'. Для простоты и красоты.

Можно в первой вьюшке генерировать 404, если по слагу не находится профиль, ловить 404 тут же и вызывать вторую вьюшку, передавая туда слаг.


def profile_detail(request, slug):
    try:
        profile = get_object_or_404(Profile, slug=slug)
    except Profile.DoesNotExist:
        return another_detail(request, slug)
    else:
        ... 

В node.js фреймворке Express можно передать контроль следующему обработчику пути. Вот бы Джанге что-то такое.

30.10.09

load_object - пара к render_object

Я написал шаблонный load_object, который удобно использовать в паре с render_object. Тег парный, его назначение - загрузить в контекст под заданным именем нужный экземпляр нужной модели.Первые два аргумента как в contenttypes, третий - первичный ключ.
from django.template import Library, Node, TemplateSyntaxError
from django.contrib.contenttypes.models import ContentType


register = Library()


class LoadObjectNode(Node):

    def __init__(self, app_label, model, pk, name, nodelist):
        self.app_label = app_label
        self.model = model
        self.pk = pk
        self.name = name
        self.nodelist = nodelist

    def __repr__(self):
        return "<LoadObjectNode>"

    def render(self, context):
        app_label = self.app_label.resolve(context)
        model = self.model.resolve(context)
        pk = self.pk.resolve(context)
        try:
            content_type = ContentType.objects.get(
                app_label=app_label, model=model)
        except ContentType.DoesNotExist:
            return ''
        else:
            klass = content_type.model_class()
            try:
                val = klass._default_manager.get(pk=pk)
            except (klass.DoesNotExist, klass.MultipleObjectsReturned):
                return ''
            else:
                context.push()
                context[self.name] = val
                output = self.nodelist.render(context)
                context.pop()
                return output


def do_load_object(parser, token):
    """
    Used get an object by content type app_name and model
    attributes and object primary key and inject it into context.
    
    Usage:
    {% load_object app_label model pk as name %}
    {{ varname }}
    {% endload_object %}
    
    Exceptionally usful in conjunction with render_object template tag
    http://www.djangosnippets.org/snippets/1745/
    """
    bits = list(token.split_contents())
    if len(bits) != 6 or bits[-2] != 'as':
        raise TemplateSyntaxError(
            "%r expected format is 'load_object app_label model pk as name'" %
            bits[0])
    app_label = parser.compile_filter(bits[1])
    model = parser.compile_filter(bits[2])
    pk = parser.compile_filter(bits[3])
    name = bits[5]
    nodelist = parser.parse(('endload_object', ))
    parser.delete_first_token()
    return LoadObjectNode(app_label, model, pk, name, nodelist)

do_load_object = register.tag('load_object', do_load_object)
Для ясности: как честный тщеславный человек я хотел выложить шаблонный тег на djangosnippets.org. Но я забыл пароль к своему аккаунту и восстановления пароля не нашел. На призывы о помощи ubernostrum не откликнулся (или они до него не дошли). Другой аккаунт там заводить не хочу, так что публикую тег тут.

29.10.09

render_object

Есть шикарный шаблонный тег render_object. Он очень разумный. Если в шаблоне есть экземпляр модели, то этот тег рендерит экземпляр, используя шаблон по соглашению:
  • "render/[position]/[app_label]_[model_name].html"
  • "render/[position]/default.html"
  • "render/[app_label]_[model_name].html"
  • "render/default.html"
app_label и model_name понятно, как в contenttypes. position позволяет различать ситации, в которых рендерится экземпляр. Например, position может быть search_results или details, соответственно в первом случае можно рендерить кратко как-то, а во втором случае развёрнуто. jeffschenck - хороший человек - придумал и написал этот тег.

28.10.09

Орднунг defaulttags.py

В документации про шаблонные теги делают так:
def do_xxx():
#или так:
def do_xxx():
...
register.tag('xxx', do_xxx)0
А в defaulttags.py систематически делают так:
def do_xxx():
...
do_xxx = register.tag('xxx', do_xxx)
Понятно, что вариант с простым вызовом register.tag работает, потому как нам в общем-то надо, чтобы тег был зарегистрирован в register. Ясно, что вариант с присваиванием более полно делает то, что делает декоратор. Интересно, кроме тяги к чистоте рук и помыслов, вариант с присваиванием еще чем-то вызван или нет? Ломается ли оно где-то из-за того, что я просто присваиваю, или это просто четкий строевой шаг такой?