Действительно, зачем треш в репе? У меня вообще папка public/ (в ней media и static/ хранятся) находится в .gitignore и создаётся при развёртывании динамически.
Так что тут тоже полностью согласен: трешу в репозитории не место :-)
Вы правы в том, что less и DRY — это хорошо. В остальном — нет :) less нужно компилировать ровно один раз, на своей девелоперской машине, при деплое. Заодно кучу css-файлов можно смержить в один, удалить лишние пробелы и гзипнуть на всякий случай.
С coffeescript проделываются аналогичные вещи: компиляция — мерж — yuicompressor… На выходе получается один минифицированный (или даже обфусцированный) js, который уже и отправляется на сервер.
Такая вот подготовленная статика молча лежит и каши не просит не компилируется. При желании её вообще можно на народ.ру закинуть.
Зачем катавасия с мемкешем? Если уж единовременно генерировать странички, можно сразу класть их в виде html-файлов в корень nginx'а (кстати, это уже было в Симпсонах). Заодно и память сэкономите, на ВДСах за её расходом приходится следить.
Да, чего-то я дал маху с просмотром исходников наискосок.
А Вы лучше не над поддержкой mod_geoip подумайте, а над интерфейсом способа локации. Чтоб и в базе можно было искать, и в бинарном файле и из окружения.
Ну так и нужно хранить пользовательскую географию в модели. А если её внезапно не оказалось, или назначать администратором, или пусть юзер при следующем заходе сам инициализирует геолокацию.
А уж как она выполняться будет — от настроек проекта зависит. Хоть sql-поиском в базе IpGeoBase, хоть напрямую из окружения nginx :-)
В request.META будет простое текстовое значение типа RU. Да, его легко вывести в шаблон, но на этом сфера его пользы и заканчивается.
Совсем другое дело django-geoip: {{ request.location }} это полноценная модель, понимаете? Причём кастомизируемая. Её можно и сохранять, и расширять, и даже внешние ключи на неё вешать.
Я пару комментариев назад слово «костыли» по отношению к употребил именно с этой позиции: он может передать лишь текстовую строку, в то время как django-geoip оперирует полноценным питоновским объектом.
Для того, чтобы с тем же комфортом работать с geoip'ом инжиникса, придётся писать много кода (как раз те самые обращения к request.META). Ну, или как меня осинило, предложить ребятам сделать поддержку mod_geoip, благо, насколько я понял их код, это делается элементарно с помощью storage.
Не поймите меня неправильно, я не спорю о том, что nginx — великолепная штука, встроенная нересурсоёмкая геолокация из коробки — это тоже замечательно и правильно.
Но случай с джангой скорее исключение. Во-первых, она сама по себе достаточно прожорлива, поэтому её запускают обычно на ВДС или в облаках. Но не на виртуальных хостингах.
ВО-вторых, как ни крути, работа с нативными питоновскими объектами удобнее, чем две строки, получаемые из внешней среды.
То есть Вы считаете, что размазать логику на два слоя — Джанги и фронтенда — это вовсе не костыль, а сделать один дополнительный запрос к БД — восьмой смертный грех? =)
Вы правы в том, что когда можно избежать лезть в базу — лучше не лезть. А ведь может случиться так, что информация понадобится не для редиректа на поддомен, а в логике самого проекта. Простейший пример:
<p>
Выберите город, {{ request.user }}. Текущее значение – <b>{{ request.location }}</b>
</p>
Можно получить эту информацию из nginx? Думаю, что можно. Но костыли, применяемые при этом, едва ли будут менее позорны, чем лишний запрос и красивый код :-)
Вы, ребята, молодцы. И блог у вас классный, и распространяемые приложения очень даже.
По поводу этого геолокатора: я правильно понял, что оно не кеширует результаты и при каждом явном обращении к request.location пытается определить, откуда пользователь?
Давайте всё же попробуем обойтись одним миксином безо всяких пачек? =)
class DecoratorChainingMixin(object):
def dispatch(self, *args, **kwargs):
decorators = getattr(self, 'decorators', [])
base = super(DecoratorChainingMixin, self).dispatch
for decorator in decorators:
base = decorator(base)
return base(*args, **kwargs)
А потом нам потребуется всего лишь перечислить требуемые декораторы в нужном порядке:
class MyView(DecoratorChainingMixin, UpdateItem):
form_class = ItemForm
model = Item
decorators = [login_required, never_cache, i_cherta_lysogo_v_stupe(foo='bar')]
Но это, что называется, низкий уровень. Если хочется абстракций, можно пытаться писать микшины с изменяемым поведением:
class UserPassesTestMixin(object):
def user_passes_test(self, user):
return user.is_authenticated()
def user_failed_test(self):
return redirect(LOGIN_URL)
def dispatch(self, request, *args, **kwargs):
if not self.user_passes_test(request.user):
return self.user_failed_test()
return super(UserPassesTestMixin, self).dispatch(request, *args, **kwargs)
Наследуемся и когда потребуется сменить логику проверки доступа пользователя, просто перекрываем user_passes_test. В том маловероятном случае, если не устраивает редирект на страницу логина, можно перекрыть и user_failed_test.
def form_valid(self, form):
# Мы используем ModelForm, а его метод save() возвращает инстанс
# модели, связанный с формой. Аргумент commit=False говорит о том, что
# записывать модель в базу рановато.
instance = form.save(commit=False)
# Теперь, когда у нас есть несохранённая модель, можно ей чего-нибудь
# накрутить. Например, заполнить внешний ключ на auth.User. У нас же
# блог, а не анонимный имижборд, правда?
instance.user = request.user
# А теперь можно сохранить в базу
instance.save()
return redirect(self.get_success_url())
И ещё: не надоело в каждой вьюхе писать что-то такое?
Особенно нелепо это выглядит на фоне class based, основная идея которого — избавить нас от рутины. Почему бы не сделать миксин, в котором все лишь раз прописать эту магическую формулу, а потом уже наследовать все вьюхи от него?
Можно вообще все данные отображения свести до одного класса и реализовать классический RESTful.
Мне кажется, вместо «можно» нужно употрелять слово «нужно». Собственно, они для того и писались.
Просто в данной статье хотелось подчеркнуть особенности использования, показания лучшие стороны в ООП подобных отображений
Самое грустное в том, что это неправильный пример. Я бы даже сказал — антипаттерн. Потому что (возможно, повторюсь):
много буквстрок, следовательно «одним взглядом» весь код не охватить и не понять.
написание собственного велосипеда вместо использования готового решения — это я в сторону пагинации. Вы на коленке реализовали то, что уже работает (и лучше :-)) из коробки.
логика разбора кверисета находится совсем не там, где ей надлежит быть
и таки да — перекрытие dispatch. Этого лучше по возможности избегать. Понятно, конечно, что далеко не всегда это пожелание осуществимо, но в данном случае — да.
Практическая полезность этого кода в том, что вы открыли dispatch — точку входа во вьюху. Молодым исследователям будет понятно, с чего начинать. Но я бы сделал приписку: «в чистом виде такой код к применению не рекомендуется» :-)
Мне кажется, что за формирование кверисета get_queryset() должен отвечать. А он тут статичен, зато вся логика по его созданию и изменению в зависимости от условий размазывается в dispatch(), который суть точка входа и желательно его по возможности вообще не трогать.
По-моему, рабочая вьюха с минимальной функциональностью делается так:
class PostView(ListView):
model = Post
Обратите внимание, никаких перекрытий dispatch() и прочего. Пагинацию тоже не надо использовать низкоуровнево: в ListView, примесью которого является MultipleObjectMixin, она (пагинация) работает по умолчанию (пруф и примеры)
Собственно, весь Ваш код можно переписать компактнее
class PostView(ListView):
"""
Просмотр списка статей, доступных текущему пользователю
"""
model = Post
context_object_name = 'posts'
template_name = 'posts.html'
def queryset(self):
qs = Post.objects.filter(is_delete=False).order_by('-created_at')
if not request.user.is_authenticated():
qs = qs.exclude(is_private=True)
return qs
class PostsIndex(PaginatedList):
"""
Список статей для главной страницы
"""
def get_queryset(self):
return super(PostsIndex, self).get_queryset().exclude(rating__lt=10)
Я не проверял, но мне кажется, что должно быть примерно так :)
А вообще, конечно, погорячились разработчики Джанги с обязательным введением CBV. Начнут ведь теперь лепить классы везде где надо и не надо
Так что тут тоже полностью согласен: трешу в репозитории не место :-)
С coffeescript проделываются аналогичные вещи: компиляция — мерж — yuicompressor… На выходе получается один минифицированный (или даже обфусцированный) js, который уже и отправляется на сервер.
Такая вот подготовленная статика молча лежит и
каши не проситне компилируется. При желании её вообще можно на народ.ру закинуть.Зачем Вам less на сервере? =)
А вообще статья хорошая, конечно =)
А Вы лучше не над поддержкой mod_geoip подумайте, а над интерфейсом способа локации. Чтоб и в базе можно было искать, и в бинарном файле и из окружения.
Интерфейсы вообще штука хорошая :-)
А уж как она выполняться будет — от настроек проекта зависит. Хоть sql-поиском в базе IpGeoBase, хоть напрямую из окружения nginx :-)
В
request.META
будет простое текстовое значение типаRU
. Да, его легко вывести в шаблон, но на этом сфера его пользы и заканчивается.Совсем другое дело django-geoip:
{{ request.location }}
это полноценная модель, понимаете? Причём кастомизируемая. Её можно и сохранять, и расширять, и даже внешние ключи на неё вешать.Я пару комментариев назад слово «костыли» по отношению к употребил именно с этой позиции: он может передать лишь текстовую строку, в то время как django-geoip оперирует полноценным питоновским объектом.
Для того, чтобы с тем же комфортом работать с geoip'ом инжиникса, придётся писать много кода (как раз те самые обращения к
request.META
). Ну, или как меня осинило, предложить ребятам сделать поддержку mod_geoip, благо, насколько я понял их код, это делается элементарно с помощью storage.Надо всего лишь предложить ребятам написать бэкенд для получения гео-локации из окружения и тогда мы оба будем правы!
Но случай с джангой скорее исключение. Во-первых, она сама по себе достаточно прожорлива, поэтому её запускают обычно на ВДС или в облаках. Но не на виртуальных хостингах.
ВО-вторых, как ни крути, работа с нативными питоновскими объектами удобнее, чем две строки, получаемые из внешней среды.
Можно получить эту информацию из nginx? Думаю, что можно. Но костыли, применяемые при этом, едва ли будут менее позорны, чем лишний запрос и красивый код :-)
По поводу этого геолокатора: я правильно понял, что оно не кеширует результаты и при каждом явном обращении к request.location пытается определить, откуда пользователь?
А потом нам потребуется всего лишь перечислить требуемые декораторы в нужном порядке:
Но это, что называется, низкий уровень. Если хочется абстракций, можно пытаться писать микшины с изменяемым поведением:
Наследуемся и когда потребуется сменить логику проверки доступа пользователя, просто перекрываем
user_passes_test
. В том маловероятном случае, если не устраивает редирект на страницу логина, можно перекрыть иuser_failed_test
.Это жесть и чревато ошибками. Лучше так:
И ещё: не надоело в каждой вьюхе писать что-то такое?
Особенно нелепо это выглядит на фоне class based, основная идея которого — избавить нас от рутины. Почему бы не сделать миксин, в котором все лишь раз прописать эту магическую формулу, а потом уже наследовать все вьюхи от него?
DetailView
методget_queryset()
возвращает объект модели, а не собственно кверисет?Не надо наследоваться от MultipleObjectMixin: ListView уже сделал это за Вас.
Мне кажется, вместо «можно» нужно употрелять слово «нужно». Собственно, они для того и писались.
Самое грустное в том, что это неправильный пример. Я бы даже сказал — антипаттерн. Потому что (возможно, повторюсь):
буквстрок, следовательно «одним взглядом» весь код не охватить и не понять.Практическая полезность этого кода в том, что вы открыли dispatch — точку входа во вьюху. Молодым исследователям будет понятно, с чего начинать. Но я бы сделал приписку: «в чистом виде такой код к применению не рекомендуется» :-)
PostView
следует читатьqueryset
, аget_queryset
request
, аself.request
get_queryset()
должен отвечать. А он тут статичен, зато вся логика по его созданию и изменению в зависимости от условий размазывается вdispatch()
, который суть точка входа и желательно его по возможности вообще не трогать.По-моему, рабочая вьюха с минимальной функциональностью делается так:
Обратите внимание, никаких перекрытий
dispatch()
и прочего. Пагинацию тоже не надо использовать низкоуровнево: в ListView, примесью которого является MultipleObjectMixin, она (пагинация) работает по умолчанию (пруф и примеры)Собственно, весь Ваш код можно переписать компактнее
Я не проверял, но мне кажется, что должно быть примерно так :)
А вообще, конечно, погорячились разработчики Джанги с обязательным введением CBV. Начнут ведь теперь лепить классы везде где надо и не надо