Header

Мы создаём веб сайты и веб приложения

Design is not just what it looks like and feels like

Design is how it works

Давайте сделаем что-то интересное!

пятница, 5 августа 2011 г.

Как заставить django 1.3 делать авторелоад (autoreload) сервера разработки для pycharm

Пока что есть такая проблема. Django не хочет делать авторелоад сервера при работе в pycharm.
Лечется это просто:
Находим фаил django/utils/autoreload.py
В нем находим метод ensure_echo_on и заменяем его на этот.
def ensure_echo_on():
    if termios:
        fd = sys.stdin
        if fd.isatty():
            attr_list = termios.tcgetattr(fd)
            if not attr_list[3] & termios.ECHO:
                attr_list[3] |= termios.ECHO
                termios.tcsetattr(fd, termios.TCSANOW, attr_list)
Готово!
Взято отсюда

вторник, 28 июня 2011 г.

Ошибки при установке emencia.django.newsletter

emencia.django.newsletter - свободное django приложение для рассылкок.
К сожалению на текущий момент с его установкой возникают некоторые сложности.
Ставим при помощи pip
pip install emencia.django.newsletter
далее указываем все необходимые настройки и выполняем syncdb.
Итак первая ошибка:
line 6, in from django.db.models.query import QuerySet, parse_lookup
ImportError: cannot import name parse_lookup
Ошибка происходит в приложении django-tagging версии 0.2.1
Почему все так получилось я не исследовал, решение банальное, обновить tagging до девелоперской версии
pip install -e svn+http://django-tagging.googlecode.com/svn/trunk/#egg=django-tagging
опять выполняем syncdb, получаем следующее:
File ".../envi/lib/python2.6/site-packages/dateutil/rrule.py", line 13, in module import _thread
ImportError: No module named _thread
Ошибка происходит в апликейшене dateutil. Я не стал разбираться почему модуль назвали _thread а не thread, если кто знает расскажите в комментах, в общем лечится это путем замены _thread на thread в файле dateutil/rrule.py.

воскресенье, 19 июня 2011 г.

Правый фильтр в django admin и ошибка типа "Filtering by property_values__value not allowed"

Переодически возникает задача когда нужно фильтровать спикок changelist модели по каким нибудь хитрым условиям, и стандартного функционала django admin не хватает.
Например есть такая структура:
class Product(models.Model):
 name = models.CharField(_(u"Name"), max_length=255, blank=True)
  
class PropertyOption(models.Model):
 name = models.CharField(_(u"Name"), max_length=255, blank=True)
 product = models.ForeignKey(Product, verbose_name=_(u"Product"), related_name="productoptions")
К списку товаров необходимо приделать правый фильтр по PropertyOption.
Сразу хочется отправиться в фаил admin и в классе ProductAdmin написать что-нибудь вроде list_filter = ('productoptions',), но к сожалению будет ошибка
'ProductAdmin.list_filter[0]' refers to field 'productoptions' that is missing from model 'Product'. 
У модели товара нету поля productoptions, есть только менеджер объектов с таким именем... в любом случае пока что это не прокатывает, поэтому нужно искать другой вариант. Например создать свой собственный фильтр, унаследовав его от класса ChoicesFilterSpec. В итоге получиться что-то вроде этого:
class PropertyOptionFilterSpec(ChoicesFilterSpec):
    def __init__(self, request, **kwargs):
        self.lookup_kwarg = 'productoptions__pk'
        self.lookup_val = request.GET.get(self.lookup_kwarg, None)
        self.lookup_choices = PropertyOption.objects.all()

    def choices(self, cl):
        yield { 'selected': self.lookup_val is None,
                'query_string': cl.get_query_string({}, [self.lookup_kwarg,]),
                'display': u'Все'}
        for option in self.lookup_choices:
            yield  {'selected': smart_unicode(option.pk) == self.lookup_val,
                    'query_string': cl.get_query_string({self.lookup_kwarg: option.pk}),
                    'display': option.name }
                    
    # незабываем переопределить title иначе при рендеринге будет ошибка              
    def title(self):
        return u'параметрам'
Теперь возникает задача как этот фильтр применить. Т.к. нет поля к которому мы бы могли его прицепить, нужно использовать другой подход, а именно:
  • Переопределить метод changelist_view у ProductAdmin добавив туда эксземпляр нашего фильтра
  • Переопределить шаблон списка товаров, который рендерит changelist_view
ProductAdmin получается такой:
class ProductAdmin(admin.ModelAdmin):

    def changelist_view(self, request, extra_context=None):
        po_filter = PropertyOptionFilterSpec(request)
        return super(ProductAdmin, self).changelist_view(request, extra_context={ 'po_filter': po_filter, })
а шаблон для списка admin/-my_app_name-/product/change_list.html я сделал таким
{% extends "admin/change_list.html" %}
{% load admin_list i18n %}

{% block filters %}
      <div id="changelist-filter">
        <h2>{% trans 'Filter' %}</h2>
        {% if cl.has_filters %}
            {% for spec in cl.filter_specs %}{% admin_list_filter cl spec %}{% endfor %}
        {% endif %}
        {# Последним рендерим наш фильтр #}
        {% admin_list_filter cl po_filter %}
      </div>
{% endblock %}

На этом этапе можно попробвать фильтр в дейсвии. Заходим в список товаров, все отлично рендериться, но если попробовать воспользоваться фильтром выскочит ошибка:
SuspiciousOperation at /admin/ecko/product/
Filtering by productoptions__pk not allowed
Это происходит потому что в классе ModelAdmin есть такой специальный метод lookup_allowed, который занимается тем что проверяет позволено ли параметру в запросе учавстовать в обращении к базе или нет, в нашем случае я не стал заморачиваться и просто застваил его всегда возвращать true. В конечном итоге ProductAdmin стал выглядеть так:
class ProductAdmin(admin.ModelAdmin):

    def lookup_allowed(self, *args, **kwargs):
        return True

    def changelist_view(self, request, extra_context=None):
        po_filter = PropertyOptionFilterSpec(request)
        return super(ProductAdmin, self).changelist_view(request, extra_context={ 'po_filter': po_filter, })
После этого всё должно заработать.

среда, 6 апреля 2011 г.

fatal error: Python.h: No such file or directory

Пытался ставить в ubuntu библиотеку
sudo pip install python-Levenshtein
При попытке скомпилиться вылетала ошибка
Levenshtein.c:99: fatal error: Python.h: No such file or directory
compilation terminated.
Лечение очень простое, нужно поставить пакет python-dev (Header files, a static library and development tools for building Python modules, extending the Python interpreter or embedding Python in applications.)
sudo apt-get install python-dev

вторник, 25 января 2011 г.

Yandex Market: проблемы с валидацией YML файла.

Оказывается не все отражено в документации по YML файлу.
Если указываешь в теге offer type="vendor.model" то для описания товара нельзя использовать тег <name>...</name>, иначе будет ошибка. Если убрать type="vendor.model" и оставить  <typeprefix>...</typeprefix>, тоже будет ошибка.

вторник, 26 октября 2010 г.

Django: Как передавать переменные в шаблонный тэг.

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

Для примера реализуем тег pagination который будет получать какой-то список объектов и отдавать объект Paginator. Ситнаксис тэга будет такой:
{% pagination objects as pagination_objects %}.
Итак код:
  1. def do_pagination(parser, token):
  2.     bits = token.split_contents()
  3.     if len(bits) != 5:
  4.         raise TemplateSyntaxError('%r syntax is %s obj_list num as varname ' % (bits[0], bits[0]))
  5.     if bits[3] != 'as':
  6.         raise TemplateSyntaxError('%r required "as" beetwen arguments' % bits[0])
  7.    
  8.     obj_list = parser.compile_filter(bits[1])
  9.     page_size = bits[2]
  10.     return_name = bits[-1]
  11.     return PaginationNode(obj_list, return_name, page_size)

Разберем по порядку:
  • Метод do_pagination разбирает строку тэга, делает необходимые проверки и возвращает экземпляр класса PaginationNode
  • bits - здесь содержится массив разобранной строки.
  • if len(bits) != 5: ... тут идут проверки синтаксиса шаблона
  • obj_list = parser.compile_filter(bits[1]) - вот здесь важный момент, в obj_list получаем переменную типа template.Variable при помощи метода compile_filter(), она нужна только затем чтобы потом вызвать у неё метод resolve(context) и получить саму переменную из контекста шаблона, об этом далее

Теперь реализуем класс PaginationNode который будет создавать в контексте шаблона столь необходимый пэйджинг.
  1. class PaginationNode(template.Node):
  2.     def __init__(self, obj_list, return_name, page_size):
  3.         self.obj_list = obj_list
  4.         self.return_name = return_name
  5.         self.page_size = page_size
  6.        
  7.     def render(self, context):
  8.         actual_obj_list = self.obj_list.resolve(context)
  9.         context[self.return_name] = Paginator(actual_obj_list, int(self.page_size))
  10.         return ''
  11.  
  • actual_obj_list = self.obj_list.resolve(context) - вот здесь второй важный момент, actual_obj_list после вызова метода resolve(context) содержит в себе настоящий список объектов с которым мы можем творить чо хотим.
  • context[self.return_name] = Paginator(actual_obj_list, int(self.page_size)) - вот тут собственно мы и творим то что хотели, т.е. создаём объект пэйджинга и записываем его в контекст шаблона