Комментарии 78
большинство таких задач решается через CBV.
обычно создаю базовый класс от TemplateView с методами:
* reverse
* redirect
* json_response
* get_object_or_404
* оберктой над django.contrib.messages вида self.messages.success('Hello, man!')
* get_model
и др.
для login_required так же делается класс-потомок от предыдущего с переопределенным методом dispatch.
все это избавляет от кучи импортов в каждом views.py и добавляет удобства в работе.
обычно создаю базовый класс от TemplateView с методами:
* reverse
* redirect
* json_response
* get_object_or_404
* оберктой над django.contrib.messages вида self.messages.success('Hello, man!')
* get_model
и др.
для login_required так же делается класс-потомок от предыдущего с переопределенным методом dispatch.
все это избавляет от кучи импортов в каждом views.py и добавляет удобства в работе.
Кажется, Вы перемудрили :-)
get_object_or_404 — это как get_object() в DetailView. Что такое get_model не очень из контекста ясно, но наверняка в List/Detail не пригодится. А вместо json_response, я считаю, лучше пользовать микшин.
get_object_or_404 — это как get_object() в DetailView. Что такое get_model не очень из контекста ясно, но наверняка в List/Detail не пригодится. А вместо json_response, я считаю, лучше пользовать микшин.
from django.db.models import get_model
entries = get_model('auth', 'User').objects.get(...)
а теперь сделайте DetailView с login_required :)
можно через urls.py, но это зачастую размазывает логику по urls.py и views.py
можно через urls.py, но это зачастую размазывает логику по urls.py и views.py
Ох, сколько раз мне это приходилось делать…
Ох уж эти новомодные горячие клавиши.
class LoginRequiredMixin(object):
@ method_decorator(login_required) # пробел после собачки надо убрать.
def dispatch(self, request, *args, **kwargs):
return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs)
class PostDetailView(LoginRequiredMixin, DetailView):
model = Post
все круто и юзабельно, пока не нужно добавить свою логику в login_required. например, логгирование попыток входа.
а за DetailView — отдельное спасибо, не внимательно читал доки, не знал за него.
а за DetailView — отдельное спасибо, не внимательно читал доки, не знал за него.
развидьте предыдущий комментарий на счет попыток входа, фигню написал, туплю в конце недели.
Я показал лишь пример
И уже потом от него наследовать LoginRequired ;)
Главное — вовремя остановиться =)
LoginRequiredMixin
. Разумеется, можно сделать более общий микшин UserPassesTestMixin и организовать его по образу и подобию аналогичного декоратора. class LoginRequiredMixin(object):
@ method_decorator(login_required) # пробел после собачки надо убрать.
def dispatch(self, request, *args, **kwargs):
if self.test():
return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs)
else:
return self.test_failed()
def test_failed(self):
"Response в случае ошибки"
def test(self):
return True
И уже потом от него наследовать LoginRequired ;)
class LoginRequiredMixin(UserPassesTestMixin):
def test(self):
return self.request.user.is_authenticated()
Главное — вовремя остановиться =)
Классы сложнее функций, а композиция проще, чем наследование. Я выбираю более простой способ писать вьюхи. Я допускаю, что возможна ситуация когда код на CBV будет проще, но сам с таким не сталкивался.
В любом случае, считать функциональные вьюхи устаревшими попросту неправильно.
В любом случае, считать функциональные вьюхи устаревшими попросту неправильно.
С другой стороны необходимость кучи импортов раздражает. Я даже подумывал сделать спец. пакет, сложить туда всё дерьмо и делать:
from handy.shits import *
Вот что думает один из core developerов Django по поводу CBV: lukeplant.me.uk/blog/posts/djangos-cbvs-were-a-mistake/.
И это не только его мнение, классы сложны в поддержке и понимании. А сила Pythonа в том, что код легко понимается с одного взгляда.
И это не только его мнение, классы сложны в поддержке и понимании. А сила Pythonа в том, что код легко понимается с одного взгляда.
post = Post.objects.get(pk=request.GET['id'])
вы действительно используете такую конструкцию в реальных проектах? без валидации?
?id=ololotrololo';delete from auth_users;
во так например
Джанго экранирует подставляемые значения.
Не сообразил =)
Впрочем, удаления тут не произойдёт. Будет некрасивая «пятисотка», но да, это плохо. Согласен.
Впрочем, удаления тут не произойдёт. Будет некрасивая «пятисотка», но да, это плохо. Согласен.
А разве django не использует preparedStements для запросов?
Нет, не использует, он использует клиенты для бд, которые следуют Python Database API, в котором экранирование параметров осуществляется на клиенте средствами этих библиотек.
В результате SQL-инъекции при запросе через ORM не будет.
В результате SQL-инъекции при запросе через ORM не будет.
Ясно, получается, что в случае MySQL и MySQLdb мы не сможем использовать server-side prepared statements
Ну через ORM нет, а так там есть расширения, только зачем?
Запросы будут бегать быстрее, ведь не надо будет каждый раз строить план выполнения запроса
Зато планы будут неоптимальны, потому как будут строиться без знания конкретных значений.
Откуда такая информация? Как по вашему происходит выполнения запроса в СУБД?
Примерно так — пришедший запрос парсится в какое-то внутреннее представление, потом для него готовится план выполнения. При составлении плана могут использоваться значения в условиях, например, если какое-то условие выберет небольшое количество рядов (по прогнозам), то будет использоваться соответствующий индекс, если много, то просто последовательное сканирование.
Всё это относится к PostgreSQL:
wiki.postgresql.org/wiki/FAQ#Why_is_my_query_much_slower_when_run_as_a_prepared_query.3F
В MySQL возможно планирование попроще и выиграть на подготовленных запросах действительно можно.
wiki.postgresql.org/wiki/FAQ#Why_is_my_query_much_slower_when_run_as_a_prepared_query.3F
В MySQL возможно планирование попроще и выиграть на подготовленных запросах действительно можно.
Хм, очень странно такое поведение Postgres-а. Ересь какая-то. Просто очень плотно работал с oracle, для которого неиспользование PreparedStatement-ов приводит к очень грустным результатам. Может просто не надо делать оптимизацию на уровне компилирования запроса, а уж если очень хочется — строить общий план, а потом корректировать на основании полученных данных. По крайней мере мы избегаем компиляции запроса, что для сложных запросов даст припрост.
Ну в 9.1, вроде, добавили перестройку плана при получении реальных параметров, но я не вижу где тут можно много времени выиграть — парсинг запроса не такая уж сложная вещь.
Ну у вас получается разобраный запрос во внутреннем представлении и план на основе синтаксиса запроса. Уже будет выигрыш. А если запрос большой? Интерпретация его каждый раз тоже небесплатна
Ну да, но это не так уж и медленно, а вот если план неэффективен, то можно замедлиться сразу в тысячи раз.
Да и большинство юзкейсов покрывают INSERT… VALUES… и UPDATE… VALUES…
Да и большинство юзкейсов покрывают INSERT… VALUES… и UPDATE… VALUES…
Я так понял план неправильно считает постгре, и то как Вы написали-они поправили это дело. Mysql строит не весь план в случае prepared statements. Так что то, что preparedstatements медленные в постгре можно отнести к недальновидности разработчиков постгряшного оптимизатора.
Что касается юзкейсов — Вы почему то опустили селекты, которые в данном случае намного интереснее. Если у Вас селект в полстраницы — его разбирать уже не так быстро.
Что касается юзкейсов — Вы почему то опустили селекты, которые в данном случае намного интереснее. Если у Вас селект в полстраницы — его разбирать уже не так быстро.
500-ку можно сделать красивой. Выдавать 500 в н. ештатной ситуации вполне адекватно. Более того, если возникнет KeyError, то это будет означать, что проблема не здесь, а где-то ещё, и замалчивать её неправильно. DoesNotExists наоборот надо ловить, потому как оно отражает штатную ситуацию — устаревшую ссылку
500-ка это внутренняя ошибка сервера. Ключевое слово — внутренняя. То есть если БД упала, например, или место на диске закончилось. Как-то нехорошо, когда внутренняя ошибка провоцируется внешним воздействием.
Вопрос восприятия, у нас концепция такова, что 500я должна выдаваться если произошло что-то неожиданное, что не было предусмотрено. И одновременно уходит письмо разработчикам. Такой подход позволяет быстро находить и чинить многие баги.
В этом случае любой кшольник-кулхацкер, запустивший сканер поиска ошибок, просто засыпет спамом :-) А если отчёты будут отправляться синхронно, ещё и отказ в обслуживании вызовет
Когда запустит тогда и будем смотреть, и адекватно отвечать. Решать проблему до её возникновения в корне неверно. А если просто ловить все такие ошибки, то это может привести к замалчиванию неправильного поведения, битых ссылок и т.п.
Опыт показывает, что при замалчивании ошибок искать такие проколы по жалобам пользователей довольно сложно и небыстро.
Опыт показывает, что при замалчивании ошибок искать такие проколы по жалобам пользователей довольно сложно и небыстро.
Бывало такое, что просыпаешься, а на почте 1500 писем с одной и той же ошибкой. Ну и что? Ошибка быстро исправлена, вреда эти 1500 писем не принесли (Gmail и миллион бы пережил).
А если замалчивать ошибку, то они и дальше бы оставались на сайте. В конце концов что важней — работающий продукт или чистота инбокса?
А если замалчивать ошибку, то они и дальше бы оставались на сайте. В конце концов что важней — работающий продукт или чистота инбокса?
Уверен, что у большинства есть своя bulletproof-библиотека с полезными функциями. Но досадно, что народ с завидным упорством плодит всё новые и новые пакеты: django-annoying, django-extensions, handy. А ведь функциональность зачастую перекрывается. Тот же
И вместе с тем, очень многие из этих библиотек словно из прошлого века. В смысле, заточены под старые версии Джанги.
За упрощение жизни: многие в повседневной жизни используют CBV. Для них катастрофически не хватает микшинов с функциональностью, аналогичной декораторам. Или абстрактных моделей, в которых уже добавлены какие-то полезные колонки, которые приходится каждый раз добавлять руками (типа
render_to
или json_response
не реализовывал только ленивый.И вместе с тем, очень многие из этих библиотек словно из прошлого века. В смысле, заточены под старые версии Джанги.
За упрощение жизни: многие в повседневной жизни используют CBV. Для них катастрофически не хватает микшинов с функциональностью, аналогичной декораторам. Или абстрактных моделей, в которых уже добавлены какие-то полезные колонки, которые приходится каждый раз добавлять руками (типа
date_created
).> Для них катастрофически не хватает микшинов с функциональностью, аналогичной декораторам.
Эти иногда пользую github.com/brack3t/django-braces
Эти иногда пользую github.com/brack3t/django-braces
Спасибо. Но даже если и использовать, всё равно как-то грустно, что придётся подключать django-braces для микшинов, django-annoying для
Вот бы один метапакет, в котором это всё в одном месте было… Не совсем джанго-вэй, конечно, но его можно разбить на аппы и подключать только нужное:
Вроде и красиво, и зависимостей не плодит.
get_object_or_None
и django-extensions для чего-нибудь ещё.Вот бы один метапакет, в котором это всё в одном месте было… Не совсем джанго-вэй, конечно, но его можно разбить на аппы и подключать только нужное:
INSTALLED_APPS = (
'metapacket.views',
'metapacket.shortcuts',
'metapacket.decorators',
)
Вроде и красиво, и зависимостей не плодит.
Некрасиво, что декоратором render_to нельзя воспользоваться, не поставив пустые скобки. Посмотрите на login_required, например.
Декоратором
Начиная с 1.3 есть
render_to
вообще пользоваться моветон, на мой взгляд. Ладно бы жили в каменные времена версии 1.2, когда это было вынужденной мерой: тогда шорткаты были длиннее собственно функций, на которые ссылались.Начиная с 1.3 есть
django.shortcuts.render
. А в 1.2, кстати, можно было импортировать direct_to_template as render
. Получалось вполне себе универсально.Декоратор render_to принимает аргументы, поэтому нужны скобки.
login_required тоже. И хотя «Explicit is better than implicit», есть ещё и «Beautiful is better than ugly» (The Zen of Python). Кроме того мы находимся в контексте джанги, поэтому стоит соблюдать единообразие. И третий аргумент — такое поведение реализуется довольно просто: django/contrib/auth/decorators.py. Поэтому мне кажется, что такой подход будет удачнее.
Декораторы render_to и json_response есть в django-annoying. Полезная библиотека. Но насчет render_to — это уже действительно устарело. Сейчас можно написать просто:
По мне это как то более… не знаю как сказать. Нагляднее что ли.
return render(request, 'template/template.html', {'a':2,' b':3})
По мне это как то более… не знаю как сказать. Нагляднее что ли.
И если у тебя несколько выходов из вьюхи, то придётся имя шаблона повторять, либо вынести в переменную. Так что всё спорно.
Это просто другой способ смотреть на вещи — отдельно логика, отдельно рендеринг. Кроме того,
Это просто другой способ смотреть на вещи — отдельно логика, отдельно рендеринг. Кроме того,
@render_to()
предлагает более высокий уровень факторизации, например похожие страницы для зарегистрированных и для анонимов могут использовать общую логику:def _edit(request):
# логика редактирования какой-нибудь штуки
# будет использоваться повторно
# создаём вьюхи из логики и других аспектов
my_edit = login_required(render_to('my/edit.html')(_edit))
edit = render_to()(_edit)
Если у вьюхи несколько выходов, то почти наверняка лучше использовать CBV: отлаживать и писать тесты намного проще.
Если несколько выходов на один и тот же шаблон, значит у вас формируется несколько различных вариантов параметров контекста. Логично значит внутри условий добавлять в контекст параметры, а потом в конце функции отрендерить данный контекст при помощи шаблона.
Если же у вас посередине функции в рендомных местах стоят return-ы, то эта функция по определению нуждается в рефакторинге.
Если же у вас посередине функции в рендомных местах стоят return-ы, то эта функция по определению нуждается в рефакторинге.
Ну структурном программировании можно долго спорить, я не считаю его такой уж безусловно правильной идеей. По мне, код должен отражать, то как удобно думать о решении, и если удобней думать «если так, то сразу выкидываем, если сяк, то выводим сообщение, а иначе — обычная обработка», то и код должен выглядеть именно так.
Каждый раз когда мы не выходим из функции сразу, мы вынуждены вводить новый элемент состояния (переменную/флажок), который занимает внимание, читающего такой код, программиста, а это весьма ограниченный ресурс.
Каждый раз когда мы не выходим из функции сразу, мы вынуждены вводить новый элемент состояния (переменную/флажок), который занимает внимание, читающего такой код, программиста, а это весьма ограниченный ресурс.
Хотя шорткат
render()
, конечно, уменьшает надобность в @render_to()
.… и ни одного теста?)
Буду занудствовать.
1. Вот такое:
при всем удобстве, в корне неправильно. Декораторам, меняющим синтаксис вызываемых функций не место в этом мире. Почему — рассказывать долго и холиварно.
2. last_modified — а если в вьюха уже установила Last-Modified?
3. class StripWhitespace(object):
компиляцию регеспов вынести в модуль.
4. render_to_email; if settings.DEBUG:
Зачем? есть же console.EmailBackend
В общем, еще пилить и пилить :)
Ну а в целом — кнопка watch уже нажата, удачного развития!
1. Вот такое:
<hh user=render_to>()
def foo(request):
return {
}
при всем удобстве, в корне неправильно. Декораторам, меняющим синтаксис вызываемых функций не место в этом мире. Почему — рассказывать долго и холиварно.
2. last_modified — а если в вьюха уже установила Last-Modified?
3. class StripWhitespace(object):
компиляцию регеспов вынести в модуль.
4. render_to_email; if settings.DEBUG:
Зачем? есть же console.EmailBackend
В общем, еще пилить и пилить :)
Ну а в целом — кнопка watch уже нажата, удачного развития!
1. Один из вариантов декораторов — это композиция, и естественно они будут менять семантику декорируемой функции.
2. Такой проблемы не возникало, а следовательно и решать её преждевременно.
4. console.EmailBackend выводит на консоль уже кодированное письмо, т.е. русский текст не прочитаешь. Решение конечно ad-hoc, правильнее было бы свой EmailBackend написать.
foo
здесь уже не вьюха, а только её часть и если думать о ней так, то вполне логично, что и выглядит она по другому.2. Такой проблемы не возникало, а следовательно и решать её преждевременно.
4. console.EmailBackend выводит на консоль уже кодированное письмо, т.е. русский текст не прочитаешь. Решение конечно ad-hoc, правильнее было бы свой EmailBackend написать.
В целом спасибо за конструктив )
Честно говоря, я против того, чтобы декоратор менял тип и логику выходных данных функций. Декоратор — как приправа, должен делать функцию «вкуснее», однако в его отсутствии блюдо все равно должно быть съедомным.
Например,
легко может быть заменено на:
При этом сохранив оригинальный дух и стиль кодирования Django (View возвращает объект Response) и снизив порог вхождения для новичков.
Например,
ajax
def enable_post(request):
...
raise ajax.error('permission_denied')
легко может быть заменено на:
def enable_post(request):
...
return ErrorAjaxResponse('permission_denied')
При этом сохранив оригинальный дух и стиль кодирования Django (View возвращает объект Response) и снизив порог вхождения для новичков.
Никто не запрещает, но мне удобно думать, что у меня отдельно есть обработчик бизнес логики, а отдельно другие аспекты — контроль доступа, обработка ошибок и представление. То, что я могу их отделить — однозначно хорошо, как это делать в виде утилит и шорткатов, которые вызываются по мере надобности, в виде композиции функций с использованием синтаксиса декораторов или с помощью наследования классов дело вкуса.
P.S. Можно использовать
P.S. Можно использовать
<source lang="python">...</source>
для подстветки кода и @
для создания собак.Разделение логики и представления это хорошо. Тут все верно. Но причем тут декораторы?
Т.е. когда у Вас есть здание и вы его декорируете — это значит что вы делаете красивым готовое решение. Вы же не поручаете декоративным гипсовым колоннам поддерживать потолок. Без декора здание будет некрасивым — однако оно будет, и оно будет работать.
А для разделения логики и представления есть модули, инкапсуляция, наследование, функциональные паттерны и другие вещи. Да и return SomeCostomResponse() даже короче в написании чем raise SomeCustomError в функции с дополнительной оберткой.
Декор — (от лат. decorare — украшать) — дополнительные элементы в живописных композиционных сюжетах.
Т.е. когда у Вас есть здание и вы его декорируете — это значит что вы делаете красивым готовое решение. Вы же не поручаете декоративным гипсовым колоннам поддерживать потолок. Без декора здание будет некрасивым — однако оно будет, и оно будет работать.
А для разделения логики и представления есть модули, инкапсуляция, наследование, функциональные паттерны и другие вещи. Да и return SomeCostomResponse() даже короче в написании чем raise SomeCustomError в функции с дополнительной оберткой.
Кстати для шаблонов есть вполне красивое решение от самой джанги:
Оно очевидно, документировано и через наследование позволяет делать еще более крутые вещи.
class MyCustomView(TemplateView):
template_name = 'my_custom_template.html'
def get_context_data(self, **kwargs):
return {
'bar': Bar.objects.all()
}
Оно очевидно, документировано и через наследование позволяет делать еще более крутые вещи.
Ну может декораторы и не совсем правильное название, но они делают дело, да и не я их называл. Так что аргумент не принимается.
Что же качается return и raise, то где вы увидели SomeCustomError()? А использование исключений позволяет выносить отдельные вещи в подфункции, декораторы и возвращать {«success»: true} просто по успешному завершению функции. В остальном же дело вкуса и спорить об этом глупо.
Что же качается return и raise, то где вы увидели SomeCustomError()? А использование исключений позволяет выносить отдельные вещи в подфункции, декораторы и возвращать {«success»: true} просто по успешному завершению функции. В остальном же дело вкуса и спорить об этом глупо.
Зачем все это в одном пакете? Почему не в нескольких? Вообще же никакой связности.
Связность есть — мелкие нужные штуки, слабая, конечно, но поддерживать кучу пакетов, а соответственно привносить кучу сущностей — тоже сомнительное решение.
Хм, а в чем именно сомнительность? Всегда был уверен, что куча маленьких узкоспециализированных аппов лучше одного монструозного.
Еще, по поводу StripWhitespace middleware. Чем {% spaceless %} не угодил?
Еще, по поводу StripWhitespace middleware. Чем {% spaceless %} не угодил?
Он решает несколько другую задачу, да и засорять свои шаблоны не хочется. Использование middleware чище.
Этот тег отлично наследуется, так что достаточно одного раза в базовом шаблоне. Но даже если вы уверены, что middleware чище, почему бы не использовать
django.utils.html.strip_spaces_between_tags
, зачем писать эту логику самостоятельно?> DAYS = zip(range(7), 'Sun Mon Tue Wed Thu Fri Sat'.split())
можно заменить на
DAYS = tuple(enumerate('Sun Mon Tue Wed Thu Fri Sat'.split()))
можно заменить на
DAYS = tuple(enumerate('Sun Mon Tue Wed Thu Fri Sat'.split()))
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Упрощая жизнь c Django