You're viewing all posts tagged with snippet

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 # Декоратор для 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

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

“Красивые” цены # Nice price formatting # nice_price

Классика жанра - отформатировать вывод цены: “1000” вывести как “1 000”. Наверное, после ещё пары-тройки лет программирования на питоне у меня получится написать это покороче :))

Пока же - вот:

def nice_price(price, delimiter=','):
    """
    Красиво отформатировать цену - через каждые 3 знака
    ставится разделитель, копейки не учитываются.
    
    Примеры
    -------
        >>> print nice_price(1000)
        1,000
        >>> print nice_price(10500)
        10,500
        >>> print nice_price(123000, ' ')
        123 000

    """
    try:
        price = int(price)
    except TypeError:
        price = 0
    
    nice = ''
    while price >= 1000:
        nice = delimiter + '%.3d%s' % (price % 1000, nice)
        price = price / 1000
    if nice:
        nice = '%s%s' % (price, nice)
    else:
        nice = '%s' % price
    return nice

Кусок списка с центрированием # Get center slice from list # get_center_slice

Иногда нужно сделать хитрую “вырезку” из списка: определенной длинны, плюс так, чтобы выбранный элемент оказался бы в центре “вырезки”. Чтобы это работало всегда и безотказно, нужно учесть случаи, когда элемент близко к началу или к концу списка. А так же, требуемая длина вырезки может быть больше длины списка.

Типичный пример использования - красивый такой paginator, у которого текущая страница всегда в центре списка страниц.

Когда я встретил такую задачу, то использовал простое “лобовое” решение. Все отлично сработало :) Над усовершенствованием можно будет подумать в следующий раз, если такая задача попадется снова.

Вот оно:

def get_center_slice(full_list, slice_size, selected):
    """
    Обрезать список по указанной длине, чтобы выбранный
    элемент оказался в центре списка.
    """
    list_len = len(full_list)
    slice_next = slice_size/2
    slice_prev = slice_size - slice_next
    
    start_index = selected - slice_prev + 1
    end_offset = 0
    if start_index  list_len:
        start_index = max(0, start_index - end_index + list_len)
        end_index = list_len
    
    return full_list[start_index:end_index]