пятница, 20 апреля 2012 г.

KIckstart, виджеты vkontakte - и пара седых волос

Сегодня попросили запилить виджет ВКонтакте для отображения ифрейма одной из ВК-групп.
Ранее это я делал сотни раз - ну примитивнейшая задачка, которая сводится лишь к небольшому копипасту. Подключил нужный openapi.js от ВКонтакте.
Поместил нужный код (div+script) в нужное место нужного сайдбара и обновил страницу. Увидел два идентичных блока с данными группы. "Ха, ерунда" , подумал я , будучи уверен, что скопировал лишний div или что-то вроде того...
 Увидился, когда код оказался в порядке. И началось... Ну сплошная мистика - откуда второй блок - неясно.
 Забегая вперед - на пальцах и сейчас не объясню , откуда...  Но в итоге нашел "виновника"...
На данном сайте используется CSS - фреймворк Kickstart. Довольно практичная и удобная штука, надо сказать. Ну так вот. Этот фреймфорк идет вместе со своим js-тулкитом, в котором заплен функционал для простеньких слайдшоу, некоторая хитрая работа с DOM и т.д. Файл подключен в <head>. В консоль ошибками не плюется. Сам фреймворк отлажен и пользуется популярностью у существенного количества людей. Но вот ВКонтактовский виджет зачем-то клонирует) В итоге пришлось <script> , рисующий вконтактовский ифрейм помещать непосредственно перед </body> , чтобы не дать никому шанса все испортить...
Виджетов теперь ровно столько, сколько надо - один :)

p.s. какой именно код kickstart.js все испохабил я пока не понял.. будет время (а это врядли ) - раскопаю :)

среда, 18 апреля 2012 г.

django: совместимость с 1.4 : settings.py - ImproperlyConfigured: Error importing template source loader

В продолжении предыдущего поста о совместимости с django 1.4

Может возникнуть следующая проблема с загрузкой шаблонов при рендеринге:

ImproperlyConfigured: Error importing template source loader django.template.loaders.app_directories.load_template_source: "'module' object has no attribute 'load_template_source'"


Решение: закомментировать TEMPLATE_LOADERS в settings.py и скопипастить аналогичные настройки из settings.py , сгенерированного django-admin от версии 1.4

django: совместимость с 1.4 : diario - ImportError: No module named feeds

Не так давно вышел официальный релиз django 1.4
До его выхода кто-то дальновидно заранее начинал работать с пре-релиз версией и портировать на нее свои существующие проекты.

Те, кто предпочел использовать в повседневновсти стабильную версию 1.3 теперь либо продолжают ее использовать, либо потихоньку (ведь никто не гонит) , переводят проекты на 1.4.

При этом у каждого НЕИЗБЕЖНО возникнут большие и малые трудности..

Дело в том, что весь зоопарк сторонних django-приложений еще не успел актуализироваться вслед за измененями в django, которые появились в версии 1.4

В этом и быть может последующих постах буду более менее существенные моменты отражать - быть может этот опыт пригодиться кому-то быстрее решить проблему миграции..

1. django-diario - неплохое приложение для организаци блога. В нем я наткнулся на ошибку импорта в 13-й строке файла diario/feeds/entries.py:

  from django.contrib.syndication.feeds import Feed
ImportError: No module named feeds

Действительно, в версии 1.4 нет файла feeds - вместо него используется views - видимо так правильнее ) Имена сущностей те же.. то есть, теперь надо

from django.contrib.syndication.views import Feed


Проверил - в текущей версии diario проблема не решена , поэтому пришлось пока "ручками" поправить.

2. еще одно замечание про diario
Из-за того же рефакторинга django.contrib.syndication изменился и способ работы с фидами, которые предоставляет diario.
Если раньше вы подключали url-ы для фидов примерно так:

from diario.feeds.entries import RssEntriesFeed, AtomEntriesFeed
entries_feeds = {
        'rss': RssEntriesFeed,
        'atom': AtomEntriesFeed,
    }
#...
urlpatterns = patterns(
    url(r'^pub/(?P(rss|atom))/$', 'django.contrib.syndication.views.feed', {'feed_dict': 
entries_feeds}),
)


то теперь это надо делать так:

from diario.feeds.entries import RssEntriesFeed, AtomEntriesFeed
#...
urlpatterns = patterns(
    url(r'^pub/(?Prss)/$', RssEntriesFeed()),
    url(r'^pub/(?Patom)/$', AtomEntriesFeed()),
)



Удачи!

среда, 28 марта 2012 г.

BOOTSTRAP CSS FRAMEWORK: проблема с carousel ( Cannot read property 'end' of undefined)

Использую в одном из проектов css-фреймворк bootstrap от twitter.
Вчера обнаружил очередное обновление, вместе с которым появился js-плагин для слайдшоу.

Подключил его отдельным скриптом bootstrap-crousel.js и при прокрутке первого же тестового элемента слайдшоу получил ошибку в js-консоли:

Cannot read property 'end' of undefined
со ссылкой на 109 строку bootstrap-carousel.js

Немного попыхтев обнаружил, что этот плагин не совсем самостоятелен и зависит от других плагинов этого фреймворка.

В частности от bootsrap-transitions.js.

Пришлось подключить и его.

А вообще , проще всего подключить весь bootstrap.js во избежание подобных багов в будущем.

четверг, 22 марта 2012 г.

nginx: unknown directive "uwsgi_pass"

Постепенно начинаю отказываться от apache как основного веб-сервера для своих проектов.
Альтернатива? Буду банален - nginx.

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

Итак, первая "проблема":
На Debian 6 установил nginx из репозитория .
Настроил на взаимодействие с django проектом через uwsgi.
Первый запуск веб-сервера завершился ошибкой: nginx: unknown directive "uwsgi_pass"

проблема оказалась в том, что дефолтный веб-сервер в репозитории squeeze-а не содержит по дефолту модуль для работы с uwsgi.

Это изменено начиная с nginx версии 0.8. В репозитории 0.7.67 - так что все объяснимо.

Качаю исходники последнего стабильного релиза nginx:


cd /usr/src/nginx
wget http://nginx.org/download/nginx-1.1.9.tar.gz
tar xfz nginx-1.1.9.tar.gz
cd nginx-1.1.9
#подсмотрел рекомендуемые опции конфигурирования в инете :)
./configure --pid-path=/var/run/nginx.pid \
--conf-path=/etc/nginx/nginx.conf \
--sbin-path=/usr/local/sbin \
--user=www-data \
--group=www-data \
--http-log-path=/var/log/nginx/access.log \
--error-log-path=/var/log/nginx/error.log \
--with-http_stub_status_module \
--with-ipv6 \
--with-http_ssl_module \
--with-http_realip_module \
--with-sha1-asm \
--with-sha1=/usr/lib \
--http-fastcgi-temp-path=/var/tmp/nginx/fcgi/ \
--http-proxy-temp-path=/var/tmp/nginx/proxy/ \
--http-client-body-temp-path=/var/tmp/nginx/client/ \
--with-http_geoip_module \
--with-http_gzip_static_module \
--with-http_sub_module \
--with-http_addition_module \
--with-file-aio \
--without-mail_smtp_module

#модулей много заюзали, так что у вас configure может ругнуться на отсутствие необходимых 
#для сборки библиотек..
#ставим все, что нужно

apt-get install libpcre3 libpcre3-dev openssl libssl-dev  libgeoip-dev libgeoip1

#после этого конфигурируется без ошибок

make
make install
mkdir /var/tmp/nginx #ибо при конфигурировании мы попросили nginx писать времянки именно сюда)
chown www-data /var/tmp/nginx #вместо www-data подставьте нужного пользователя


Да, дефолтную версию nginx лучше удалить.

вторник, 20 марта 2012 г.

django: range - цикл в шаблоне

Встроенный в django шаблонный движок , как оказалось, не поддерживает циклы по диапазону значений.
Например , в шаблон передается некоторое число и необходимо организовать цикл от 0 до этого числа - 1
Средствами встроенных шаблонных тегов и фильтров сделать это , судя по всему , нельзя. Но есть простое решение : передавть в шаблон не число а значение, возвращаемое встроенной python-функцией range.

Например там, где было

...
context['num'] = Somemodel.objects.count(),
...

надо сделать

...
context['num'] = range(Somemodel.objects.count()),
...

после чего в шаблоне можно уже делать вот так:

...
{%for i in num%}
...
{%endfor%}
...

четверг, 23 февраля 2012 г.

django: Internal Server Error при debug = False

Сегодня напоролся на неприятный "баг" в django-flatpages.
Суть его в том, что при отключенном дебаге (settings.debug = False) возникает ошибка при попытке отобразить url с flatpage.

Включаешь дебаг - страницы отображаются . Отключаешь - видишь 500-тку. Мистика.

Оказалось, что для решения проблемы потребовалось разместить в templates/flatpages файл default.html , наследующий базовый шаблон. Ни один из моих url не использует этот дефолтный шаблон, но тем не менее он оказывается краеугольным камнем проекта.

Глупо да? Я тоже так думаю...

вторник, 3 января 2012 г.

Отладка django+mod_wsgi: помощь при головной боли начинающего

Одной из первых проблем , с которыми сталкиваются начинающие django-разработчики при развертывании своих творений - это их отладка в продакшене. Например, такой вариант: Apache+mod_wsgi+django.
Вот в кратце список того, с чем пришлось столкнуться мне:
Предусловия:
1. В наличии имелся виртуальный сервер с Debian 5 Lenny , Apache2, python2.5 (из коробки) + доустановленный python 2.7.
2. Среди виртуальных хостов уже крутился один django-проект, использующий python2.5 и развернутый при помощи mod_python.
3. Доустановка нужного python 2.7 не доставила проблем, как и разработка и отладка с помощью встроенного в django веб-сервера. Когда настала пора деплоить проект на имеющийся apache, выбор пал на mod_wsgi.
4. Создан виртуальный хост и питоновский обработчик wsgi запросов от апача - все по мануалу, ошибиться было трудно.

Далее о многочисленных граблях, на которые пришлось наступить:

1. Установил mos_wsgi из репозитория - ничего не заработало.
Это потому что в репозитории lenny есть только модули , собранные для версии 2.5
Пришлось собирать mod_wsgi из исходников с нужной (2.7) версией python
2. Далее сглупил, собрав mod_wsgi без флага --enable-shared.
3. Потратил время, пытаясь запустить apache2 с виртуальными хостами, использующими разную версию python. Один - использует mod_python и сам интерпретатор версии 2.5. Другой (который пытаюсь развернуть) - только что собранный mod_wsgi для python 2.7. В такой конфигурации и без использования virtualenv так и не удалось заставить работать оба сайта. Причина оказалась не очень явная - mod_python всегда загружался первым и тянул за собой либы для 2.5 версии. И последующие ухищрения с настройками mod_wsgi и окружения не помогали. Решения было два: начать использовать virtualenv или пересобрать mod_python для версии 2.7 и запустить старый сайт с 2.7 версией python. Я выбрал второй вариант.
4. Долго пытался собрать mod_python версии 2.7.*, пока не понял, что бьюсь башкой о баг. Скачал версию 3.x.x , успешно собрал и установил , теперь уже не забывая про сборку именно шаред-библиотеки. После этого связку apache2+mod_wsgi можно было считать работающей.
5. Долго пытался отлаживать (путем print в стандартные потоки) причины 500-ток, пока не набрел на совет, как обернуть wsgi-приложение так, чтобы спроксировать дебаг-информацию в mod_wsgi. Вот какое решение (django.wsgi) мне помогло увидеть долгожданную дебаг-информацию в стиле django в окне броузера:

import os
import sys
sys.stdout = sys.stderr
# Add the virtual Python environment site-packages directory to the path
import site
sys.path.insert(0,'/www/djangoprojects/')
sys.path.insert(0,'/www/djangoprojects/testproject/')

print sys.path
os.environ['DJANGO_SETTINGS_MODULE'] = 'yourapp.settings'
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

from django.conf import settings

# Debug middleware
if settings.DEBUG:
    print >> sys.stderr, "Using Paste error middleware"

from paste.exceptions.errormiddleware import ErrorMiddleware
application = ErrorMiddleware(application, debug=True, show_exceptions_in_wsgi_errors=True)
print "here"
if settings.SESSION_FILE_PATH:
    try:
        os.makedirs(settings.SESSION_FILE_PATH)
    except OSError:
        pass



Предварительно надо установить paste:

sudo easy_install paste


6. После того, как появился нормальный дебаг в броузере первпя проблему, которую пришлось решить это Unable to Open Database File. Было ясно , что процессам апача не хватает прав на работу с файлом бд (речь идет о sqlite). Пермишены были добавлены, но ошибка осталась. Некоторое время ушло на понимание того, что надо дать еще права на директорию, в которой находится файл БД - такова особенность работы orm с данной СУБД.
7. после этого были подправлены некоторые директивы import по файлам проекта
8. статику тоже не сразу увидел (css, js) - не были настроены алиасы в виртуальном хосте. Вот, что было нужно добавить:
Alias /mediaurl /path/to/files

Order allow,deny
Allow from all


Вуаля, apache2+mod_wsgi-3.3.1+python2.7+django-1.3+sqlite успешно заработало.

Да , был забавный момент. В настройке WSGIScriptAlias зачем-то сдуру указал 30 потоков использовать и забыл об этом успешно. Долго не мог понять , почему моя vrs-ка стала загибаться. Чуть было не снес все , что переустановил. Вовремя заметил эту самонадеянную настройку и ограничился всего лишь 4 мя потоками.

среда, 28 декабря 2011 г.

Django: "Кто на сайте" ?


В одном из проектов понадобилось реализовать функцию "Кто на сайте". Средней продолжительности копания привели к модулю django-onlineiser.


$sudo easy install django-onlineuser


далее в settings.py:

#добавляем приложение в список используемых в проекте
INSTALLED_APPS += ('oblineuser')

#регистрируем его middleware в соответствующем списке
MIDDLEWARE_CLASSES += ('onlineuser.middleware.OnlineUserMiddleware')



в нужном html шаблоне подгружаем теги этого приложения и там же отображаем html-код этой приблуды

{% load onlineuser_tags %}
Кто здесь?
{%onlineinfos%}


У меня, чтобы заработало пришлось немного модифицировать код класса из middleware.py, обернув имеющийся там код в try except

class OnlineUserMiddleware:
    def process_request(self, request):
        try:
            user = request.user
            ip=request.META['REMOTE_ADDR']
            if user.is_authenticated():
                o, created = Online.objects.get_or_create(user=user)
                o.session_key = ip
            else:
                o, created = Online.objects.get_or_create(ident=ip)
                o.session_key = ip
            if not created:
                o.save()
        except:
            return


В результате видим простейшую статистику о гостях и онлайн юзерах.

С первого взгляда все ок и автору приложения в любом случае спасибо.

Но чуть позже стало понятно 2 факта (помимо бага описанного выше):
1. сразу после логаута пользователя счетчик пользователей не уменьшается, а счетик гостей не увеличивается
2. после логина счетчик гостей не уменьшается, хотя счетчик пользователей увеличивается

Не совсем релевантно получается.

Я допилил это приложение с помощью сигналов о логине и логауте:


#этот код я добавил в middleware.py рассматриваемого приложения, хотя можно и любое другое удобное место 
from django.contrib.auth.signals import user_logged_in, user_logged_out
from django.dispatch import receiver
@receiver(user_logged_in)
def count_logins(sender,request,user,**kwargs):
    ip=request.META['REMOTE_ADDR']
    g=Online.objects.get(session_key=ip, user=None)
    if(g):
        g.delete() #удаляем гостя , после того , как он стал залогиненным юзером
    #создаем или обновляем запись в таблице приложения (этот код я скопипастил из имеющегося миддлваре-класса
    o, created = Online.objects.get_or_create(user=user) 
    o.session_key = ip
    if not created:
        o.save()    
 
@receiver(user_logged_out)
def count_logouts(sender,request,user,**kwargs):
    o=Online.objects.get(user=user) 
    o.delete() #сразу при логауте удаляем запись из таблицы



Если в ближайшее время не придумаю как более красиво это все дело улучшить, отпишу автору или посто форкну это приложение )

четверг, 1 декабря 2011 г.

Django: Добавляем свои собственные проверки объектов в админку



Если понадобилось дополнить стандарные проверки, которые делает Django при создании, редактировании, удалении объектов через панель администратора, то существует несколько извращенных способов.
Опишу самый простой, наглядный и незатратный.

Начиная с версии 1.2 в Django позволяет добавлять в модель метод clean, в котором и предлагается реализовывать логику нужных вам проверок.

Например у вас есть таблица "Матч" , в полях которой есть две ссылки (ForeignKey) на таблицу "Команда". То есть играют две команды ) Сама с собой команда, понятно, играть не может. По крайней мере в тех командных видах спорта , о которых доводилось слышать )

Код проверки будет примерно прост (добавить метод в класс модели "Матч"):


    def clean(self):
        from django.core.exceptions import ValidationError
        if(self.team1.id == self.team2.id):
            raise ValidationError('Команда не может играть сама c собой.')            

В результате , при попытке сохранения матча где команда намеревается рубиться сама с собой , админ увидит красненький баннер с соответствующим сообщением.

p.s. сейчас в голову пришла мыслишка: будет ли вызываться этот метод, при сохранении объекта не через админку..хм.. надо будет матчасть почитать или выяснить эмпирически.

Если кто уже в курсе - буду признателен за ответ. Хотя кому это я, читателей пока у меня судя по всему нет )))))