четверг, 18 июля 2013 г.

Классическая проблема верстки - выравнивание блоков

Часто встречается ситуация, когда некий контейнер "набивается" float-ом дочерними элементами . На макете все красиво и ровно. На практике, после того как начинается работа с реальным контентом (например содержимое категории товаров в магазине), то однотипные блоки получаются разными по высоте. Проблему можно решить принудительным транкейтом содержимого, подбором "максимальной" фиксированной высоты или javascript-ом. Пример кода, решающего подобную проблему (используется jQuery):

//делаем высоту всех .thumbnail одинаковой
   var  max_height = 0;
 $('.thumbnail').each(function(){
    h = $(this).height();
   if(h > max_height){
     max_height = h;
    };
   });
 $('.thumbnail').css('height',max_height);

Этот код хорошо отработает , например , в IE, а вот Chrome могут быть проблемы, если в .thumnail внутри есть изображение. На момент вычисления height оно еще может быть не загруженным и поэтому полученные величины height и width могут очень удивить верстальщика :) Поэтому код надо модифицировать , например, вот так:

$(document).ready(function(){
//делаем высоту всех .thumbnail одинаковой только после полной загрузки всей страницы
   var  max_height = 0;
 $('.thumbnail').each(function(){
    h = $(this).height();
   if(h > max_height){
     max_height = h;
    };
   });
 $('.thumbnail').css('height',max_height);
});

вторник, 9 июля 2013 г.

Unable to find vcvarsall.bat

Часто при установке некоторых python-библиотек (PIL, django-imagestore .. ) под windows возникает ошибка:

Unable to find vcvarsall.bat

Причина отсутствии или невозможности при установке найти нужный Сишный компилятор, который как правило есть  в поставке MS VS.

Для решения проблемы совсем необязательно ставить "студию". Можно обойтись компилятором mingw:


  1. качаем отсюда и ставим
  2. скачиваем нужную python - библиотеку и выполняем для ее setup.py :

setup.py install build --compiler=mingw32

Перед этим не забудьте прописать путь к mingw32.exe в PATH (например C:\mingw\bin)

четверг, 4 июля 2013 г.

django-rest-framework - некоторые особенности

Как и обещал ранее дамплю все грабли и особенности , которые возникают в ходе освоения этого фреймфорка.

Сразу отмечу, что авторы этого приложения большие молодцы - документация на фреймворк сделана в виде пошагового туториала - фактически онлайн учебника. На первых шагах все делается стандартными механизмами. И дальше, по мере продвижения, авторы упрощают код с помощью многочисленных "плюшек" , которые предоставляет их приложение. Советую для лучшего понимания и во избежание последующего недопонимания :) именно так и поступать, а не копипастить конечные синтаксические конструкции.
Хотя при этом могут и проблемы возникнуть . Например, на одном из шагов авторы предлагают реализовать rest-вьюхи  как обычные function-based django views, сопровождая их декоратором api_view. Этот шаг - исключение и его лучше пропустить, сразу перейдя к class based views , иначе возникнет ошибка:

Cannot apply DjangoModelPermissions on a view that does not have `.model` or `.queryset` property.
Но и после перехода в class-based views не забываем указывать model или queryset в полях вьюшного класса.

Unicode

Для того, чтобы в отдаваемом REST-ом контенте корректно отображался Unicode необходимо в settings явно прописать UnicodeJSONRenderer.
Например:

REST_FRAMEWORK = {
 
    'DEFAULT_RENDERER_CLASSES': (
        'rest_framework.renderers.UnicodeJSONRenderer',
    )
}


Выбор решения для организации API на django-сайте

Возникла задача в одном из моих django-проектов предоставить API для взаимодействия с некоей внешней системой.
Первоначальный выбор пал на django-piston. Реализовать операции read-доступа к объектам БД удалось довольно быстро, но вот с операцией создания объекта начались проблемы, частично решив которые и получив новые , решил выбрать другое решение.. Уж больно надоело патчить чужую библиотеку.
Другое решение - это Django Rest Framework. Знакомство с отзывами и документацией обещает менее нервную разработку. К непосредственной попытке использования приступлю на днях.
Постараюсь не забыть задокументировать здесь все особенности и подводные камни, если таковые найдутся :)



среда, 29 мая 2013 г.

django: форма обратной связи с аттачами и проверкой на вирусы

Предыстория

Недавно получил от одного своего постоянного клиента заказ на следующую доработку: "нужно доработать форму обратной связи для возможности отправки файлов клиентами".

В процессе обсуждений  выяснилось, что файлов надо аттачить "сколько хочешь".

А чтобы заказчик не "словил вирусняк" через эти самые аттачи , я решил реализовать сканирование.

Было 2 варианта:

  • настроить на серваке (linux) какой-нибудь , желательно бесплатный , антивирусный демон 
  • использовать API какого-нибудь авторитетного онлайн сервиса
Я остановился на втором варианте, тем более что был немного знаком с Virustotal. 
Бегло ознакомился с доками на его API . 

Проверил работоспособность его тестовых примеров  - все ок. 

И приступил к "допиливанию" имеющейся формы обратной связи.


Как только причешу код , чтобы было не стыдно опубликовать на bitbucket, то детально опишу реализацию.

Кому не терпится - пишите, торопИте :)

понедельник, 29 апреля 2013 г.

django-lfs: особенности работы со скидками

Текущая стабильная версия 0.7.7

В django-lfs есть два механизма работы со скидками:


  1. назначать скидку каждому товару отдельно в карточке товара
  2. создавать глобальные скидки, которые действуют в зависимости от критериев (например общая сумма покупки в корзине) - данные скидки применяются не к отдельным  товарам ,  а к всему содержимому корзины.
Особенность в том, что при наличии обоих скидок они суммируются.

При использовании данной и более младших версий django-lfs вместе с django >= 1.4 есть проблема с интерфейсом управления -  а именно бага при работе с критериями. Суть ошибки и как  ее решить  я уже описывал здесь.

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

Доработать не трудно. 

  1. добавляем нужное поле в Discount 
  2. добавляем колонку в таблицу discount_discount (если база уже существует) или пересоздаем базу (если старого контента не жалко :)
  3. добавляем нужный html-шаблон для страницы со списком скидок
  4. добавляем view, которая будет рендерить этот шаблон
  5. добавляем url, по которому это view будет "дергаться"
кому нужен патч - пишите.

пятница, 12 апреля 2013 г.

django-lfs: установка на Windows 7

Установка django-lfs  подробно описана здесь

Но у установки под windows есть несколько особенностей, которые не описаны на официальном сайте

Исходные данные:

  • windows7 64 bin
  • python 2.7 , установленный , например в c:\Python27
Требуется установить django-lfs (на текущий момент версия 0.7.7)

  1. качаем инсталлятор http://pypi.python.org/pypi/django-lfs
  2. распаковываем куда нам нужно
  3. cd lfs-installer
  4. качаем правильный bootstrap.py 
  5. python bootstrap.py
  6. качаем win-инсталлятор для библиотеки PIL (так как установщик ругнется на отсутствие батника vcvarsall.bat)
  7. создаем ключ в реестре HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Python\PythonCore\2.7\InstallPath со значением, которое копируем из ключа  HKEY_LOCAL_MACHINE\SOFTWARE\Python\PythonCore\2.7\InstallPath  - без этой манипуляции инсталлятор PIL "ругнется"  Python27 not found
  8. удаляем Pillow из списка зависимостей djanfo-lfs (файл: lfs-installer\eggs\django_lfs-0.7.7-py2.7.egg\EGG-INFO\requires)
  9. устанавливаем PIL
  10. bin/buildout.exe -v
  11. указываем параметры доступа к базе данных (предполагается, что она уже создана)  в settings to lfs_project/settings.py
  12. $ bin/django syncdb
  13. $ bin/django lfs_init
  14. $ bin/django collectstatic
  15. $ bin/django runserver
  16. Открываем дефолтный магазин http://localhost:8000 и торгуем :)

пятница, 29 марта 2013 г.

django-lfs: wysiwyg и копипаст контента с других сайтов

В каждом новом веб-проекте встает дилемма - использовать ли wysiwyg в административной части сайта. С одной стороны хочется избавить себя от будущего утомительного контент-менеджмента, который не очень дорого оплачивается (если вообще оплачивается),  а с другой - дашь пользователю полноценный wysiwyg - он вмиг сломает всю верстку и в итоге тебя же и обвинит: "ты мол программист, ты должен был все учесть"...

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

В общем это проблема не новая. Есть еще одна - смежная с ней. Часто начинающие "контент-менеджеры" сайтов (они же и их владельцы по-совместительству :) не брезгуют простым копипастом контента с сайтов конкурентов, сайтов с понравившимся контентом.. Им невдомек, что есть понятие "копирайт", что редактор "не умеет" превращать чужие картинки в свои, что он копирует буквально все, в том числе и внешние ссылки.

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

Везде , где можно был переопредлен обработчик save моделей и в этом обработчике вызывался метод, который скачивает картинки в media, меняет пути на свои локальные, чистит внешние ссылки.

Планирую еще и PIL прикрутить к этому делу, чтобы вотермарки накладывать. Любой каприз, как говорится, за ваши деньги.

p.s. но тырить контент с чужих сайтов, не оставляя ссылок - нехорошо , о чем все мои клиенты честно предупреждаются.


p.p.s если кого интересует, то могу код причесать и выложить на какой-нибудь bitbucket - пишите.

суббота, 23 марта 2013 г.

Хостинг jino и bitbucket

Не хочу давать ни рекламы  , ни антирекламы , но у меня возникает следующая проблема с хостингом jino.
Клонирование репозитория с bitbucket в данный момент невозможно, так как hg clone отваливается по tcp-таймауту. Проблема перманентная, то есть не связана с какими-то временными трудностями на любой из сторон.
Вариант с клонированием через ssh не проходит, так как на jino нет ssh-клиента.
Вот и приходится обновлять исходники через третий сервер (

Саппорт пока не ответил на просьбу устранить эту проблему или вообще объясить как-то причину .
Ждем.

понедельник, 4 марта 2013 г.

lightbox vs elastislide

В одном из веб-проектов потребовалось реализовать галерею изображений.

Ранее уже успешно использовал плагин jquery.elastislide.js.

Применил и на этот раз.

Кроме того, надо было сделать так, чтобы по клику на фотку она открывалась в "полный рост" - наиболее популярным решением сейчас является для этого lightbox , его я и попытался применить.

Все сделал по манам, верстка правильная, ошибок в консоли  js нет, но изображение по клику не открывается - lightbox не фурычит.

Сразу заподозрил конфликт обработчиков событий. Так и оказалось.

Чтобы его преодолеть в опциях создания слайдера необходимо обнулить обработчик onClick изображений:

$('#carousel').elastislide({imageW :300,margin: 30,minItems: 2, onClick: null});



воскресенье, 24 февраля 2013 г.

Небольшая ремарка по django-imagestore

Тому, кто хочет организовать фотогалерею на django, могу посоветовать в  качестве основного приложения django-imagestore .
Приличный функционал, возможность расширения засчет переопределения моделей альбомов и фотографий, более-менее удобная админка.
Но как всегда не без греха. Как минимум одна проблема в этом модуле есть - а именно проблема каскадного удаления..
Допустим, у вас есть альбом, в нем много фотографий.

Вы из админки удаляете фотографию (не выключаете из альбома, а просто удаляете).
Мелким шрифтом , конечно, показывается предупреждение о том, что будут удалены все сущности. Но  кто их читает ?)

Вот и получается - хотели удалить фотку, а удалили весь альбом.




А если на альбом ссылалась еще какая-нибудь сущность, то и она успешно удалится.
На подобное я напарывался в django-lfs.
Чтобы решить эту проблему , необходимо либо пропатчить поле Image.album, добавив в него  on_delete=models.SET_NULL.

 Либо переопределить Image ,  как это позволяет делать imagestore и уже аналогично решить эту проблему на уровне своей модели.

воскресенье, 17 февраля 2013 г.

Слайды: кастомизация интерфейса django-admin

На просторах интернета попалась следующая презентация, которая может задать вектор начинающему джанговоду в направлении того, как можно изменить стандартный интерфейс админки django.

Не могу не поделиться



вторник, 12 февраля 2013 г.

How to improve Test::Class to conrol test ttl

To  make Test::Class control test-suites time-to-live you could inherit Test::Class and redefine runtests method..

For example:

package TTL::Test::Class;
use base qw(Test::Class);
use Test::More;
use POSIX ":sys_wait_h";
use constant DEFAULT_TIMEOUT => 60;
# переопределяем метод запуска тестов 
sub runtests{
    my $t = DEFAULT_TIMEOUT;                                           
    my $pid = fork;                         
    my $child_ret_code;                           
    if( $pid > 0 ){ # parent process                                         
       #child waiting loop
       my $ok_flag = 0;                                                  
       for(my $i=0; $i < $t; $i++){                                       
          if(waitpid($pid, WNOHANG)){                                           
               $child_ret_code = $?/256;                                       
               ok($child_ret_code);                                             
               $ok_flag = 1;                                                  
               last;                                                            
          };                                                                              
          sleep(1);
       };
       if( not $ok_flag ){
         diag "Killing test by its timetolive...";
         kill TERM =&gt; 
         $pid;
         return $ok_flag;
       };
       return $child_ret_code;                                                  
    } elsif( $pid == 0 ) { #child                                               
        exit $self->SUPER::runtests;                                         
    } elsif ( not defined($pid) ) { #unsuccessfull fork                         
        die "Cannot fork child process: $!";                                    
   }                                                                            
} 
1;

I think, we don't need SIGCHILD here..

source

вторник, 5 февраля 2013 г.

django-sitetree: key error 'request'

Сообщение об ошибке при работе с django-sitetree  в админке django :

говорит о том , что необходимо добавить

KeyError at /admin/sitetree/tree/1/

'request'
Request Method: GET
Request URL: http://localhost:8080/admin/sitetree/tree/1/
Django Version: 1.4.3
Exception Type: KeyError
Exception Value:
'request'


"django.core.context_processors.request", в TEMPLATE_CONTEXT_PROCESSORS

только  и всего

четверг, 31 января 2013 г.

django-lfs: EOFError при работе в режиме кеширования

На одном моем сервере "крутится" магазинчик под управлением Django-lfs

  • django 1.4
  • nginx 1.1.9
  • uwsgi 1.2.3
Время от времени на тревожный email приходят сообщения об ошибке вида:

EOFError

TRACEBACK:
  File "..../pydocs/env/lib/python2.7/site-
packages/Django-1.4.1-py2.7.egg/django/core/handlers/base.py", line 111, in get_response
    response = callback(request, *callback_args, **callback_kwargs)

  File "..../pydocs/deploy/../lfs/catalog/views.py", line 267, in category_view
    inline = category_products(request, slug, start)

  File "...../pydocs/deploy/../lfs/catalog/views.py", line 358, in category_products
    temp = cache.get(cache_key)

  File "....../pydocs/env/lib/python2.7/site-packages/Django-1.4.1-py2.7.egg/django/core/cache/backends/db.py", line 75, in get
    return pickle.loads(base64.decodestring(value))

Ошибка "мигающая".

Пока лишь понятно, что копать в сторону python модуля pickle .

Поверхностный анализ кода этого pickle пока не помог в поиске причины.

Ждет меня дебаг продолжительный, судя по всему :)