Thursday, October 8th, 2009 03:13 pm

Читаю книжку про Django. Встретил там один интересный пример. Вопрос к профессионалам: есть ли в таком способе решения _данной конкретной задачи_ глубокий смысл, которого я не вижу, или это чисто понты для демонстрации возможностей питона? Подробности под катом, не-программистам будет неинтересно.

Итак, задача:

Есть несколько функций, у которых есть общий кусок – проверка аутентифицированности пользователя и редирект на страницу аутентификации, если он не:

def my_view1(request):
    if not request.user.is_authenticated():
        return HttpResponseRedirect('/accounts/login/')
    # ...
    return render_to_response('template1.html')

def my_view2(request):
    if not request.user.is_authenticated():
        return HttpResponseRedirect('/accounts/login/')
    # ...
    return render_to_response('template2.html')

def my_view3(request):
    if not request.user.is_authenticated():
        return HttpResponseRedirect('/accounts/login/')
    # ...
    return render_to_response('template3.html')

и, соответственно, ссылки на эти функции в URLconf:

urlpatterns = patterns('',
    (r'^view1/$', my_view1),
    (r'^view2/$', my_view2),
    (r'^view3/$', my_view3),
)

(Для тех, кто не в курсе Django: вторым параметром в кортежах указывается ссылка на функцию-обработчик запроса.)

Это некрасиво, надо вынести этот общий кусок кода в единое место.

Как бы это сделал я: написал бы wrapper, который получал бы ссылку на одну из различающихся функций, проверял бы аутентифицированность пользователя, и либо выдавал редирект, либо вызывал нужную функцию по ссылке. Примерно так:

def requires_login(request, view):
    if not request.user.is_authenticated():
        return HttpResponseRedirect('/accounts/login/')
    return view(request);

И соотетствующий URLconf:

urlpatterns = patterns('',
    (r'^view1/$', requires_login, {'view': my_view1} ),
    (r'^view2/$', requires_login, {'view': my_view2} ),
    (r'^view3/$', requires_login, {'view': my_view3} ),
)

А в книжке приводится другой вариант решения: динамическое создание функций, каждая из которых проверяет аутентифицированность пользователя и либо выдаёт редирект, либо вызывает функцию по переданной ссылке:

def requires_login(view):
    def new_view(request, *args, **kwargs):
        if not request.user.is_authenticated():
            return HttpResponseRedirect('/accounts/login/')
        return view(request, *args, **kwargs)
    return new_view;
urlpatterns = patterns('',
    (r'^view1/$', requires_login(my_view1)),
    (r'^view2/$', requires_login(my_view2)),
    (r'^view3/$', requires_login(my_view3)),
)

То есть, при чтении URLconf происходит три вызова requires_login(), каждый из которых возвращает ссылку на динамически сгенерированную функцию.

С моей точки зрения у такого решения [повторяю: решения данной конкретной задачи, а не вообще] есть только минусы. На каждую функцию my_view*() создаётся по одной дополнительной функции вместо единой общей, создаются они динамически, и на это тратятся лишние ресурсы. А на скорость работы это практически не влияет.

Я чего-то не заметил?

Оригинал этой записи в личном блоге.
Любые материалы из этого блога запрещается использовать на сайте livejournal.ru в любой форме и любом объёме.

Tuesday, December 1st, 2009 11:45 pm (UTC)
Хм, а какая из двух идей тут здравая? Централизованного белого списка сайтов? Или программной защиты компьютера от пользователя с полным физическим доступом?
Wednesday, December 2nd, 2009 12:54 am (UTC)
Уникальное по своей корявости решение. Право, "родительский контроль" в Винде и то разумнее устроен.

P.S.
Для версии 1.4 функция сброса пароля при повторной инсталляции удалена. Реализована функция напоминания пароля на электронную почту.
Wednesday, December 2nd, 2009 07:08 am (UTC)
централизованного белого списка с возможностью локального изменения его в обе стороны
Wednesday, December 2nd, 2009 07:11 am (UTC)
"Люди имеющие детей"? 8/ И они запрещают мне ковыряться в носу?
Wednesday, December 2nd, 2009 11:30 am (UTC)
"а, нычего страшного, это дэтский!"
Wednesday, December 2nd, 2009 03:21 pm (UTC)
(задумчиво) запускае небольшой юниксовый серверочек, который на все DNS-запросы отвечает собственным IP-адресом и принимает почту для всех доменов, подключаем к нему машинку и.. отправляем себе пароль :)