воскресенье, 16 декабря 2012 г.

django-lfs: пара тройка неприятных багов

Хочу рассказать о нескольких неприятных моментах (читай "ошибках"), с которыми совсем недавно столкнулся при использовании django-lfs:

Ошибки при сохранении картинок с длинными именами

Для хранения картинок в django-lfs используется специальный тип поля, пронаследованный от стандартного ImageFile. В этом стандартном типе дефолтная длина файла картинки 100 символов.
В модели , которая используется для хранения картинок товаров , например, max_length явно не указывается. В итоге при попытке отобразить превьюхи (thumbs) оригинальной картинки с длинным именем ( >= 100 символов), случается поначалу непонятный IndexOutOfRange..Казалось бы причем тут массивы...

Вот такой замечательный код есть..

class ImageWithThumbsFieldFile(ImageFieldFile):
    """
    See ImageWithThumbsField for usage example
    """
    def __init__(self, *args, **kwargs):
        super(ImageWithThumbsFieldFile, self).__init__(*args, **kwargs)
        self.sizes = self.field.sizes

        if self.sizes:
            def get_size(self, size):
                if not self:
                    return ''
                else:
                    split = self.url.rsplit('.', 1)
                    thumb_url = '%s.%sx%s.%s' % (split[0], w, h, split[1])
                    return thumb_url

падает, как нетрудно догадаться,  в строчке

                    thumb_url = '%s.%sx%s.%s' % (split[0], w, h, split[1])

Причина в том, чтo в rsplit передается усеченное имя файла.. Как видим, никакими дополнительными проверками авторы себя не обременили, поэтому дело и доходит до "пятисоток"..

Запретить пользователям не аплоадить такие картинки - нельзя.

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

А пока для своих проектов на django-lfs сделал небольшой костыль в виде max_length = 2048 в описании модели картинки.

Глюки с редактированием товаров в manage-интерфейсе

django-lfs позиционируется как шустрый фреймворк для ешопов. Шустрость его в частности основана на активном использовании кеширования бд.

Баг , который я сейчас опишу, как раз  и проявляется при использовании кеширования.

Допустим , вы используете кеширование в таблице БД. Выставляете время жизни объектов в кеше , например , в 60 секунд.

Пользователь магазина наконец-то решает заняться наполнением. Создает товар, редактирует "данные" и сохраняет их . Затем переходит на "категории" и вносит товар в нужные категории. "Сохранить категории". Лезет на сайт, а товара там нет... Лезет в интерфейс управления и видит, что "активно" , не выставлен, цены нет и вообще вкладка "данные" такая, как будто он к ней не прикасался.
Причина в том, что между сохранением "данных" и  сохранением  "категорий" не прошло минуты и при сохранении  категорий, товар взялся из кеша.

Вопрос, почему не был обновлен кеш при сохранении "данных" , пока открыт. Выясню и исправлю, ведь без кеширования как-то несолидно :)

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

UPD: 
Глюк с редактированием товаров исправляется отправкой сигнала product_changed в products.py (после сохранения формы данных),  а также с отправкой аналогичного сигнала при изменении категорий товара.


суббота, 1 декабря 2012 г.

django: Работаем с формами оптимально

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

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

В общем, всем django-разработчикам советую:  ОЗНАКОМИТЬСЯ


пятница, 30 ноября 2012 г.

django-lfs: добавляем произвольные формы в Page

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

Иногда надо, чтобы статическая страница включала в себя какую-нибудь форму. Ничего не мешает, конечно,  в html-код этой страницы впихнуть нужную конструкцию <form>...</form>  через wisywig, добавить соответствующий обработчик в urls и views,  а затем ненадолго успокоиться...
.. до тех пор , пока пользователь сайта в manage интерфейсе случайно не испохабит этот код формы , так что эта хрупкая конструкция перестанет работать...

Вот что я придумал, чтобы оживить модуль Page условно произвольными формами:

1. список форм

Создаем модуль (py-файл ) с классами нужных нам на данный момент форм

2. обновление модели

Добавляем в модель Page списочное поле , значениями списка которых являются классы из нашего набора форм

3. обработчики форм

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

4. актуализируем page_view

переделываем page_view так, чтобы брать из модели текущий используемый класс формы и передаем в шаблон соответствующую форму, созданную на лету

тут же , если POST, вызываем обработчик формы (для каждого класса формы свой)...

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

Таким образом, мы получаем возможность добавления произвольной формы из нашего набора в произвольную статическую страницу (Page) в django-lfs, что делает этот фреймворк для имагов гораздо более гибким...

p.s. код и тонкости (которые есть всегда) не публикую... если кого-то заинтересовала реализация этого, пишите - охотно поделюсь ....


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

понедельник, 19 ноября 2012 г.

foundation css framework + django-lfs

Популярный адаптивный фреймворк foundation идет со своей js-библиотекой, без которой этот фреймворк использовать бесполезно.
django-lfs также идет с несколькими нужными js-либами, среди которых lightbox, cookie...

Чтобы этот зоопарк библиотек заработал вместе foundation-овские js-файлы следует подключать до lfs-ных, но , естественно, после подключения jquery - иначе будут конфликты.

четверг, 15 ноября 2012 г.

Некоторые размышления об открытых e-commerce решениях

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

Среди подобных решений есть вылизанные и очень массовые, можно сказать , попсовые типа joomla, а есть варианты для хардкорщиков и тех, кто изначально планирует "пилить" под себя.

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

Перенос данных из sqlite в mysql

sqlite позволяет легко сдампить содержимое бд в файл - для этого используется команда .dump.

Попытка сразу экпортировать этот файл в mysql гарантированно завершится ошибками.

Причина в том, что синтаксис sql - запросов несколько отличается у этих субд.

Таким образом дамп необходимо преобразовать к виду, пригодному для загрузки в mysql.

Кроме того, может возникнуть проблема с целочисленными PRIMARY KEY после эскпорта, которая будет сопровождаться сообщением типа 'Duplicate entry '0' for key 'PRIMARY''

Причина в том, что в дампе запросы CREATE TABLE не содержат опции AUTO_INCREMENT для PRIMARY_KEY полей. В итоге данные залиты, приложение/сайт работает на чтение, но при попытке что-либо сохранить возникает такая ошибка.

Тут можно либо модифицировать соответствующие поля, добавив им эту опцию. Либо допилить дамп, добавив в него AUTO_INCREMENT для нужных полей

  `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY

и после этого пересоздать и перезалить базу.


Если кому нужен скрипт для конвертации sqlite-формата в mysql - обращайтесь. Поделюсь с удовольствием.


четверг, 8 ноября 2012 г.

Oracle восстановить пароль для sysdba

Если забыт пароль для system Ораклового инстанса, то всегда можно его поменять так:


  1. заходим на сервер , где крутится oracle
  2. переходим под пользователя oracle
  3. sqlplus /nolog
  4. >connect / as sysdba
  5. alter user system identified by new_password

среда, 7 ноября 2012 г.

Немного о том, как я перепиливал django-lfs

Некоторое время назад, когда передо мной встала задача создания интернет-магазина на django я разрывался между несколькими вариантами дальнейших действий:
    1. django-satchmo
    2. django-lfs 
    3. django-shop (недоделанный)
    4. сделать все самому с нуля
Будь у меня побольше время на тот проект, то , скорее всего , я бы остановился на варианте "сделай сам". 
Но в итоге выбор пал на django-lfs, потому что он мне показался проще в установке и сразу из коробки , там что-то даже полноценно работало =)

Довольно скоро после начала попыток создания данного конкретного магазина на lfs стало ясно, что допиливать придется , и допиливать много. Вот краткий список того, что было доделано-переделано:
  1. добавлен механизм новостей (на основе переработанного под SEO модуля django-diario)
  2. добавлен ротатор баннеров
  3. добавлены товары-"новинки"
  4. упрощена работа с адресами
  5. добавлен механизм персональных скидок зарегенным пользователям магазина
  6. расширен модуль "способы доставки" (для возможности иметь адрес доставки опциональным  в зависимости от способа доставки)
  7. добавлен модуль "FAQ"
  8. добавлен модуль "Рекламные кампании"
и еще куча доработок и переделок по - мелочи - все это, естественно, с поддержкой в manage интерфейсе, что требовало дополнительного кропотливого "встраивания".

В перспективе еще много фич задумано...

В целом lfs довольно функционален и много, чего умеет. При знакомстве с внутренней архитектурой, порой, я был недоволен, но приходилось встраиваться в то, что есть. Пилить пришлось много. 

Не уверен, что пришлось бы меньше потеть, возьми я ,  кпримеру satchmo. И наоборот, уверен, что с учетом трудозатрат на ознакомление и допиливание с нуля проект делался бы дольше, начни я делать все с нуля. Правда и "костылей" бы в нем не было, какие , признаться есть сейчас и ожидают очередни на рефакторинг =)

Бытует мнение , что всякий подобный продукт в любом случае придется кастомизировать под заказчика, и не всегда будет складываться так, что архитектура продукта позволит это сделать безболезненно. Это касается не только django-магазинов. Думаю, для всяких джумл с виртуемартами это справедливо в той же или даже большей степени. Что косвенно подтверждают многочисленные отзывы php-разработчиков.

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

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


понедельник, 5 ноября 2012 г.

Django - отдаем статику с nginx-ом

django в DEBUG=False режиме не отдает статику и по этому начинающие разработчики зачастую напарываются на многочисленные 404 после отключения отладочного режима.
Напомню, что для боевого режима статика должна собираться командой collectstatic в отдельную папку (python manage.py collectstatic) - именно на эту директорию и следует натравить nginx (если вы его конечно используете):

location /static/ {
alias /home/projects/test_project/static;
expires 10d;
}

пятница, 2 ноября 2012 г.

Установка DBD::Oracle на linux

Если при установке CPAN-модуля возникли проблемы подобные этой:
Can't load '/root/.cpan/build/DBD-Oracle-1.52-2Zd5mX/blib/arch/auto/DBD/Oracle/Oracle.so' for module DBD::Oracle: libclntsh.so.11.1: невозможно открыть разделяемый объектный файл: Нет такого файла или каталога at /usr/lib64/perl5/DynaLoader.pm line 200
на стадии make или make test, то скорее всего не установлены переменные окружения: ORACLE_HOME и LD_LIBRARY_PATH
$ sudo -s
# export ORACLE_HOME=путь к домашней директории ORACLE
# export LD_LIBRARY_PATH=$ORACLE_HOME/lib
# cpan DBD::Oracle