вторник, 23 октября 2012 г.

Решение проблемы с PIL на хостинге Джино (Jino)

Сегодня потребовалось перенести один из django-проектов на свежеорганизоавнный хостинг "Джино".
Предварительное знакомство с опциями тарифного плана оставило хорошее впечатление - здесь вам и virtualenv и ssh .
Файлы проекта были перенесены успешно, wsgi-скрипт был настроен также за минуту согласно рекомендациям хостера. Чуть подольше ставил нужные либы в виртуальное окружение.
Сайт оказался работоспособным удивительно быстро.
Но первая и пока последняя трудность возникла почти сразу - тестовая загрузка картинки закончилась неудачно.
Немного дебага показало "INFO decoder jpeg not available".
Сразу стало все ясно - PIL в виртуальном окружении установился криво - а точнее без поддержки libjpeg (и не только ее ). Пляски с переменными окружения и указыванием путей к libjpeg не помогли.. И неудивительно , libjpeg-dev на сервере не оказалось.
Ситуацию спасла предустановленная PIL , которая оказалась на сервере - подкладываем ее в virtualenv и все работает )

суббота, 20 октября 2012 г.

sqlite - не поддерживается изменение колонок

Многие , кто только начинает использовать sqlite в работе, часто натыкаются на то, что не могут провести привычную для других СУБД операцию изменений параметров колонки.
Например, у поля в данный момент ограничение NOT NULL, которое вам мешает и вы хотите пометить ее как NULL.
Пробуете применить ALTER TABLE и ... облом.
Перепроверять синтаксис запроса - бесполезно :) sqlite (по-крайней мере текущая 3-я версия ) эту операцию попросту не поддерживает.
Выход один: пересоздать таблицу. Ну а чтобы не потерять данные, предварительно копируете их в резервную таблицу:

  create table tms as select * from table_to_backup; 

после пересоздания возвращаете данные на место

  insert into new_Table_version select * from table_to_backup;

django-lfs: ошибка при управлении остатками

Если при добавлении в корзину товара вы натолкнулись на следующее сообщение об ошибке


TypeError at /product-form-dispatcher

unsupported operand type(s) for /: 'float' and 'NoneType'
Request Method:POST
Request URL:http://test/product-form-dispatcher
Django Version:1.4.1
Exception Type:TypeError
Exception Value:
unsupported operand type(s) for /: 'float' and 'NoneType'
Exception Location:/home/projects/eshop/pydocs/deploy/../lfs/catalog/models.py in get_amount_by_packages, line 780
Python Executable:/usr/sbin/uwsgi
то скорее всего дело в том, что для данного товара вы не указали packing_unit . Этот параметр необязателен, и такое поведение - несомненный баг. Открываем catalog/models.py и правим багу в функции get_amount_of_packages:


780,781c780,781 
< packages = math.ceil(quantity / pu) 
< return packages * pu 
--- 
> packages = math.ceil(quantity / self.packing_unit) 
> return packages * self.packing_unit

вторник, 9 октября 2012 г.

jquery: выравниваем высоту элементов

Наверняка, многие сталкивались подобной ситуацией:

Есть футер, в нем несколько горизонтально располагающихся друг за другом блоков..
Например блоки быстрых ссылок.
У этих блоков свой фон, который отличается от основного фона.
Содержимое блоков (читай, высота) заранее неизвестны.

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

Кто-то пытается решить проблему подбором min-height побольше, что лишь снижает вероятность возникновения проблемы, но не решает ее.

Кто-то , возможно, знает как сверстать одинаковую высоту (заранее неизвестную) для произвольного содержимого блоков.

Кто-то использует старые добрые таблицы )

Я же использую незатейливый js-код и ставшую уже классикой - jquery

Например ,  у описанных блоков класс footer-block. Сделать их равной высоты можно примерно так:


        <script>
          var maxh=0;
          $('.footer-block').each(
            function(){
              var h = $(this).height();
              if( h  > maxh){
                maxh = h;
              }
            }
          );
          $('.footer-container').css('height',maxh);
        </script>


django:использование MultipleChoiceField в моделях

Сегодня натолкнулся на проблему... В django foms есть замечательное поле MultipleChoiceField. Позволяет, как не трудно догадаться, показывать пользователю контрол с возможностью множественного выбора.
Описывается , например, так (в классе формы):

EQUIPMENT_VARIANTS = ( 
 ('pm',u'Посудомоечная машина'),
 ('sm',u'Стиральная машина'),
 ('vr',u'Варка'),
 ('du',u'Духовка'),
 ('mi',u'Микроволновка'),
 ('ho',u'Холодильник'),
 ('vy',u'Вытяжка'), 
) 

equipment = forms.MultipleChoiceField(choices=EQUIPMENT_VARIANTS, required=False)   

Все хорошо - форма рисуется, данные принимаются. Надо сохранить в БД.

Обнаруживается, что для Models поля-аналога нет.

Нормализовать БД, создавая новую словарную таблицу, не планирую и не хочу.

Хочу просто хранить список строк в одном поле.

Что ж, в соответствующей модели объявляем такое поле:

 equipment = models.CharField( max_length="1024", null=True, blank=True)

Привязываем форму к модели , используя forms.ModelForm.
Теперь данные сохраняются в БД. Но есть проблема. В БД они в таком виде: "[u'pm', u'sm']"

Когда все это затеивал знал, что для ChoiceField (вернее для CharField с аттрибутом choices) есть замечательная функция-хелпер get_FOO_display.

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

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

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

#не забыть import re 

def get_equipment_list(self): 
    p = re.compile('\'(\w+)\'') 
    result = [] 
    for e in p.findall(self.equipment): 
       for v in EQUIPMENT_VARIANTS: 
          if v[0] == e: 
             result.append(v[1]) 
    return result 

Функция получает список мнемоник значения поля equipment и формирует список значений опций из EQUIPMENT_VARIANTS - этот массив опций пришлось сделать видимым не только в классе формы , но и в классе модели.

Теперь в django-шаблоне можно спокойно получить для объекта результат множественного выбора из одного из его полей (в данном случае equipment).

Ну а дальше отрисовать этот список как угодно.

Надеюсь, кому-то сэкономит время.

вторник, 24 июля 2012 г.

django: ошибка при отправке e-mail

Если у Вас не отправляются письма из вашего django-проекта и при этом в debug-режиме показывается сообщения типа

'utf8' codec can't decode byte 0xcf in position 32: invalid continuation byte

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

Переименуйте комп , перезагрузите его и пробуйте отправить сообщенеи еще раз )

Удачи.

пятница, 8 июня 2012 г.

django-lfs: опыт использования. часть 3 (ошибка при работе скидками)

Этот пост небольшой и посвящен одной единственной проблемке.

django 1.4
python 2.7

В интерфейсе управления (manage) создаем объект - скидку (discount).

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

Открываем js-консоль и видим там 500 Internal server error в ответ на запрос, который делается при нажатии на "добавить критерий".

Копируем проблемный url и открываем его в отдельной вкладке (убедитесь, что settings.DEBUG=True) , чтобы увидеть стек и понять в чем причина 500-ки.


На картинке не видно, но  в стеке нас отправляют исправлять lfs/manage/criteria/views.py:49

Смотрим,  а там действительно бага - указан некорректный формат для функции strftime.
Вместо %s надо указывать %S.

#       "id": "%s%s" % (now.strftime("%s"), now.microsecond),
         "id": "%s%s" % (now.strftime("%S"), now.microsecond),

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

вторник, 5 июня 2012 г.

django-lfs: опыт использования. часть 2 (темы)

Итак, нам удалось запустить тестовый сервер тестового проекта. Понятно, что стандартный внешний вид нас не устроит.
Что можно сделать, чтобы его изменить?
Надо воспользоваться так называемым механизмом "схем". Схема - это обычное django приложение , которое надо создать самому, создать в нем структуру шаблонов, аналогичную структуре шаблонов стандартной lfs-схемы (см. приложение django-lfstheme)

cd /path/to/lfs_project
python manage.py startapp custom_lfstheme
cp -r /path/to/lfstheme/application/templates ./custom_lfstheme

далее в settings.py надо добавить зависимость от этого приложения (INSTALLED_APPS), поместив его ДО 'lfstheme':

INSTALLED_APPS = (
#...
    "custom_lfstheme",
    "lfstheme",
#...
)

Вот в общем-то вся подготовка к кастомизации внешнего вида. Далее надо просто видоизменять (переписывать с нуля) скопированные шаблоны.

Ну и для того, чтобы пополнить список "несовместимостей" с django 1.4: Может вылезти и обязательно вылезет следующая ошибка (при использовании) admin интерфейса lfs:
You cannot add messages without installing 
django.contrib.messages.middleware.MessageMiddleware
Лечится просто - добавляем нужные миддлваре в settings.py проекта:

MIDDLEWARE_CLASSES = (
#...
    'django.contrib.messages.middleware.MessageMiddleware',
#...
)

среда, 30 мая 2012 г.

django: flatpages error "Cannot use None as a query value" (En)

If you see "Cannot use None as a query value" error message in Django admin tying to save new Flatpage object, then just fill all mandatory fields. Probable you forgot to specify 'site' field.
It's very strange not to see standart django error message but the stack trace.. And it's a bug in Flatpages field validation code.
Moreover, this bug has been fixed (Ticket #18324).
p.s. stable version (c)

понедельник, 28 мая 2012 г.

django-lfs: опыт использования. часть 1 ( установка )

Потребовалось сделать небольшой проект - интернет-магазин на django.
Остановил свой выбор на решении django-lfs.
Беглый осмотр исходников навел на мысль о том, что поддержки новой django 1.4 пока нет - взять хотя бы структуру проекта. Она явно не соответствует той структуре , которую создает django_admin.
Также вижу, что в settings.py не все так, как принято теперь в дефолтном джанго-приложении.
Есть соблазн использовать django 1.3, но спортивный интерес и желание разобраться со всеми проблемами совместимости, которые вылезут, подталкивает начинать именно с 1.4...
Итак, что у нас имеется?
  1. Linux Debian Lenny 32 bit
  2. Python 2.7
  3. virtualenv
  4. django-lfs-installer-0.7.4.tar.gz
  5. MySQL
Сразу готовимся с будущему копипасту )) Создаем фейковый проектик из-под django 1.4 для того, чтобы под рукой иметь эталон. Очень будет полезно для приведения lfs-ного settings к потребному для 1.4 виду. Сделали? Двигаемся дальше.
Далее создадим бд (предполагается что сервер mysql уже крутится на сервере). Логинимся на локальный mysql-сервер под рутом субд и выполняем sql:

--создание базы данных
create database shop_db;
--создание пользователя и выдача ему всех прав на эту бд
grant all privileges on shop_db.* to shop_admin@localhost identified by 'very_hard_to_brake_password' with grant option;

Начнем.
Фазу установки виртуального окружения , распаковки архива, пожалуй , опущу. Отмечу только, что перед синхронизацией БД (syncdb) не забываем прописать настройки доступа к СУБД в settings.py. Остальные первичные телодвижения, которые надо сделать неплохо описаны здесь.
Естественно ни у кого никогда сразу все не ставится. Первое, что мне потребовалось доставить - модуль gunicorn :

#не забываем ставить пакеты при активированном вирт.окружении
easy_install gunicorn

Второе, на что ругнулся скрипт синхронизации БД - отсутствие MySQLdb. ИЗИ_инсталлами он не ставится. Качаем с surceforge последний тарболл MySQL-python-1.2.3.tar.gz , распаковываем, и ставим

wget "длинный дайрект линк с сорцфорж"
tar xvfz MySQL-python-1.2.3.tar.gz
cd MySQL-python-1.2.3
python ./setup.py install

Итак, база синхронизована (syncdb), магазин проиницализирован (lfs_init), тестовый сервер запущен. Открываем. И, судя по всему, видим первый "баг совместимости". В данном случае совместимости дефолтных настроек от джанги старых версий с новой джангой.

Module "django.core.context_processors" does not define a "auth" callable request processor

Исправляется просто. Открываем settings.py, ищем список TEMPLATE_CONTEXT_PROCESSORS , комментируем (если страшно)или удаляем :

TEMPLATE_CONTEXT_PROCESSORS = (
#...some processors here
    'django.contrib.auth.context_processors.auth',
#    'django.core.context_processors.auth',
#...some processors here
)

Запускаем тестовый сервер. Обновляем страницу. И, "следующий сказал заведующий". Другая ошибка:

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

Помните, я выше советовал создать проект-пустышку ? открываем его settings.py, копируем целиком оттуда список TEMPLATE_LOADERS и заменяем им аналог в settings.py нашего lfs-ного магазина.

#список шаблонозагрузчиков должен выглядеть так
TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.Loader',
    'django.template.loaders.app_directories.Loader',
#     'django.template.loaders.eggs.Loader',
)

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