You're viewing all posts tagged with django

Nicedit wysiwyg

Обычно, wysiwyg - это какая-то боль. Зачем-то нужно качать 1 мегабайтовую библиотеку и получить 100 кнопок, а потом методично отключать 90% ненужного функционала. И все равно будет тормозить и глючить. Не люблю wysiwyg. :)

Nicedit мне понравился, по первому впечатлению. Весит 35 кб, кнопочек всего 16 и все компактно лежат в gif-ке размером 3 кб. Можно конфигурировать. Синтаксис для подключения - симпатичный.

Вот так например, можно подключить его на все textarea в Django:

<script type="text/javascript" src="{{ MEDIA_URL }}js/nicEdit.js"></script>
<script type="text/javascript">
bkLib.onDomLoaded(function() {
    nicEditors.allTextAreas({iconsPath : '{{ MEDIA_URL }}js/nicEditorIcons.gif'});
});
</script>

Nicedit wysiwyg: http://nicedit.com

Django # Грабли с отловом exceptions в декорированных views # Debug output for decorated views fails

Нашел грабли :) Надо будет сделать баг-репорт, но пока - просто workaround.

Грабли - если view задекорирован и в нем возникает ошибка, то джанга не всегда правильно обрабатывает traceback - дебажный вывод не показывает, точнее слетает в самом дебажном выводе :)

Пример:

from decorators import render_to

@render_to('main.html')
def main_view(request):
    # тут что-то пошло не так
    raise Exception('AAA!!!')
    return context

Так вот, чтобы дебажный вывод работал правильно (как обычно), render_to должен быть объявлен:

  • либо в этом же модуле view
  • либо в другом package’е

Если render_to импортится из того же package, что и view, то дебажный вывод сходит с ума и не может правильно определить, в каком файле произошла ошибка.

То есть, вот так - сломается: есть views.py, где лежат views, и рядом decorators.py, в котором объявлен render_to.

Работает - так (main_view должен быть не в myproject.utils :)

from myproject.utils.decorators import render_to

# ...

Django # Декоратор render_html # Decorator

Ещё один декоратор по мотивам… :)

Вот “декоратор”:

def render_html(template_name):
    def wrapper(func):
        @wraps(func)
        def inner(request, *args, **kwargs):
            result = func(request, *args, **kwargs)
            if result.has_key('_redirect'):
                return HttpResponseRedirect(result['_redirect'])
            else:
                return render_to_response(template_name, RequestContext(request, result))
        return inner
    return wrapper

Вот пример его использования:

def page_edit_handler(request, slug):
    page = get_object_or_404(slug=slug)
    if request.method == 'POST':
        form = PageForm(request.POST, instance=page)
        if form.is_valid():
            form.save()
            return {'_redirect': page.get_absolute_url()}
    else:
        form = PageForm(instance=page)
    return {'form': form}
    
page_edit_view = render_html('page_edit.html')(page_edit_handler)

Комментарий к примеру: page_edit_handler выдает обычный dict, который обычно попадает в контекст, и выводится как html. Но иногда нужно сделать редирект, тогда возвращается специальный dict, с ключом ‘_redirect’.

Ключ ‘_redirect’ начинается с подчеркивания - такие ключи не работают в контексте, т.е. {{ _redirect }} выдал бы ошибку. И это - на руку нам, потому что можно использовать этот ключ для наших служебных целей, и никому это не должно помешать.

Решение на первый взгляд - хитрое. Но на самом деле - вполне понятное, и если такой подход использовать в проекте, то вьюхи получаются более компактные, что хорошо. В этом и смысл шаманств :)

И комментарий второй - я специально не использовал в примере синтаксис декоратора! Если делать так, как в примере, то у нас остается “неиспорченная” функция page_edit_handler, которая может нам где-нибудь ещё пригодиться :)

Django # Приложение для определения макросов # Custom macro defenitions

Выложил аппликейшн django_autotext: http://github.com/rudyryk/django_autotext

Оно делает следующее: дает возможность определить произвольный “макрос”, который можно использовать в тексте - вместо макроса будет подставлен текст, сгенерированный функцией.

Как оно задумано и как работает?

Например - хочется чтобы можно было в тексте использовать имя текущего пользователя. Текст хранится в базе данных и редактируется через админку. Определим макрос user_full_name, и будем его использовать так:

Привет, дорогой { user_full_name }!

Определяется макрос следующим образом:

from django_autotext import formatter

def user_full_name(context):
    return context['user'].get_full_name()
formatter.add_macro('user_full_name', user_full_name)

В шаблоне - нужно включить текст внутрь тега-обработчика:

{% load autotext %}
{% autotext %}{{ content }}{% endautotext %}

Собственно, все :) Как установить, примеры, readme - прилагается к проекту, как положено :)

Django # Декоратор для confirm # View decorator for confirmation dialog # confirm_required

Inspired by http://vorushin.ru/blog/26-decorators-python :)

Например, если у меня есть view для удаления чего-нибудь - скажем, файла:

def remove_file(request, id):
    #...

И мне нужно показать страницу с подтверждением удаления - простую “да / нет”. Я написал декоратор, который может работать вот так:

def confirm_context(request, id):
    context = {}
    # ...
    return RequestContext(request, context)

@confirm_required('confirm_remove.html', confirm_context)
def remove_file(request, id):
    #...

Оригинальный view при этом не модифицируется, что радует.

Теперь при вызове view проверяется POST-запрос на наличие в нем ключа “__confirm__”, либо другого - это определяется необязательным параметром декоратора “key”. Если ключа нет - показывается страница подтверждения. Если есть - вызывается оригинальный view.

В шаблон подтверждения передается контекст, созданный функцией “confirm_context”. Это обычная функция, которая принимает те же параметры, что и изначальный view.

Собственно, код декоратора:

def confirm_required(template_name, context_creator, key='__confirm__'):
    def decorator(func):
        def inner(request, *args, **kwargs):
            if request.POST.has_key(key):
                return func(request, *args, **kwargs)
            else:
                context = context_creator and \
                    context_creator(request, *args, **kwargs) \
                    or RequestContext(request)
                return render_to_response(template_name, context)
        return wraps(func)(inner)
    return decorator

ПС. Надо будет подправить верстку блога, а то сниппеты не влезают нормально по ширине.