четверг, 25 апреля 2013 г.

Миграция с Posterous на Blogger

Как вы, наверное, знаете Twitter сначала купил Posterous, а потом его закрыл. И так как на Posterous'е был мой блог, я перенес все посты из него сюда. Сорри, если RSS фид обновился слишком рьяно.

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

зы. Так как этот блог стал моим единственным, то я сменил его название на более честное. Впредь тут будут появлятся информация и по Flask'у, и по Django, но в большинстве случаев думаю просто по Python'у.

четверг, 20 декабря 2012 г.

Запускаем gunicorn из Python'а

Иногда бывает надо запустить gunicorn внутри Python скрипта, например, в manage.py. Конечно всегда можно воспользоваться subprocess.call:

import subprocess


app = 'package.module:app'
host, port = '0.0.0.0', 8000
subprocess.call('gunicorn -b {}:{:d} -w 4 {}'.format(host, port, app))

Но как-то это не комильфо подумал я и решил найти более труЪ-способ :)

Решение пришло не сразу, но пришло, надо всего лишь переопределить sys.argv и вызвать метод run,

import sys

from gunicorn.app.wsgiapp import run


app = 'package.module:app'
host, port = '0.0.0.0', 8000
sys.argv = [
    sys.argv[0],
    '-b', '{}:{:d}'.format(host, port),
    '-w', '4',
    app
]
run()

Не очень круто вышло, не находите? А все потому что gunicorn еще использует устаревший optparse для парсинга аргументов с коммандной строки, а там задавать список аргументов в parse_args совсем не обязательно, ведь по дефолту берется список sys.argv[1:].

Так что на таком простом примере лично мне ясно видно, насколько optparse устарел и как вашему приложению, которое его еще использует надо сломя голову переезжать на argparse! И это я еще не рассказал вам о бесподобном управлении под-коммандами в argparse :)

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

Чиним gunicorn'овский Internal Server Error

Если вдруг, после запуска gunicorn перестал работать и показывает ошибку похожую на:

Internal Server Error

Traceback:

Traceback (most recent call last):
  File "/Users/playpauseandstop/Projects/project/env/lib/python2.7/site-packages/gunicorn/workers/async.py", line 45, in handle
    self.handle_request(req, client, addr)
  File "/Users/playpauseandstop/Projects/project/env/lib/python2.7/site-packages/gunicorn/workers/async.py", line 73, in handle_request
    resp, environ = wsgi.create(req, sock, addr, self.address, self.cfg)
  File "/Users/playpauseandstop/Projects/project/env/lib/python2.7/site-packages/gunicorn/http/wsgi.py", line 161, in create
    path_info = path_info.split(script_name, 1)[1]
IndexError: list index out of range

то это говорит о том, что какая-то сволочьое-то приложение установило переменную окружения SCRIPT_NAME, которую gunicorn не может нормально обработать. Чтобы пофиксить, просто удаляем переменную окружения,

$ unset SCRIPT_NAME

и перегружаем страницу (ну можно еще перегрузить gunicorn для пущей важности). That's all!

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

Валидация Django моделей, пять советов

Не думаю, что для кого-то открою Америку, сказав что в Django есть валидация не только для форм, а и для моделей :) Однако судя по моей практике это чуть спорное, но весьма полезное решение не спешат повсеместно использовать и городят свои велосипеды для проверки значений при создании/обновлении моделей. Поэтому хочу поделиться несколькими простыми советами по этой теме.

Во-первых, храните валидаторы отдельно от моделей. Хранение всех валидаторов в models.py неимоверно раздувает и без того не маленький модуль моделей (а иногда это и пакет), хранение же валидаторов в validators.py решает эту проблему и логично выносит все операции по проверке значений/уникальности модели в подходящее для этого место. Ну и сюда же, не пишите сложные проверки в Model.clean, Model.validate_unique методах, просто вызывайте необходимые функции валидации из validators.py, например:

models.py

from django.db import models

from .validators import validate_complex_case, validate_unique


class Card(models.Model):
    ...
    def clean(self):
        validate_complex_case(instance)

    def validate_unique(self, exclude=None):
        validate_unique(self)
        return super(Card, self).validate_unique(exclude)

validators.py

from django.core.exceptions import ValidationError


def validate_complex_case(instance):
    if instance.name == 'XXX' and instance.path != '/path/to/XXX':
        raise ValidationError('Please provide proper path for the card.')


def validate_unique(instance):
    manager = instance._default_manager
    other = manager.filter(name=instance.name)

    if other.count():
        message = 'Card with this Name already exists.'
        raise ValidationError({'__all__': [message]})

Во-вторых, непонятно зачем, но ошибки в проверке на уникальность модели нужно обворачивать в message_dict, использование простого сообщения приводит к AttributeError. Хотя с другой стороны это повышает возможности по написанию ошибок в разных полях моделей. Можно, например, подсветить какие именно поля не уникальны и уже сохранены в БД.

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

from django.core.validators import MaxLengthValidator, MinLengthValidator
from django.db import models

from .validators import validate_markup


class Card(models.Model):
    ...
    comment = models.TextField(validators=[
        MinLengthValidator(10), MaxLengthValidator(1000), validate_markup
    ])

В-четвертых, валидация модели автоматически не вызывается, когда вы хотите сохранить модель не из админ-панели или ModelForm'ы. Т.е., просто добавление валидаторов в поля модели и задание методов clean или validate_unique не гарантирует, что неправильные данные не сохранятся в базе данных после Model.objects.create или instance.save(). Для того, чтобы обезопасить себя и включить валидацию и для моделей, используйте сигналы:

from django.db import models
from django.db.models import signals
from django.dispatch import receiver


class Card(models.Model)
    ...


@receiver(signals.pre_save, sender=Card)
def validate_card(instance):
    instance.full_clean()

Ну и в-пятых,

  • Не забывайте про стандартные валидаторы Django
  • Импортируйте исключение ошибки валидации как from django.core.exceptions import ValidationError
  • Для моделей нельзя писать методы clean_FIELD как для форм
  • Валидация модели при вызове instance.full_clean() идет в следующей последовательности: валидация полей (instance.clean_fields()), вызов instance.clean(), проверка на уникальность (instance.validate_unique())

зы. Код и принципы данного материала актуальны для Django 1.3 ветки (да я так и не пересел на 1.4 или 1.5 потому что не вижу в этом особого смысла), может в новых версиях что-то поменялось, уточняйте в документации :)

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

Learn Python

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

Так что встречайте, курсы по углубленному изучению Python в Киеве. Курсы будут проходить в 2 потока по 12 занятий, один из которых, web, буду вести я, а второй, advanced - Андрей. Каждое занятие будет длиться 2 часа, по часу на теоретический материал и на разбор домашнего задания. Главной целью web потока будет создание рабочего веб-приложения и обучение безболезненой работы в команде, используя фреймворки Django или Flask. В свою очередь в advanced потоке будут убиты два зайца, с одной стороны курсы будут направлены на помощь в переходе с Python 2.x на Python 3.x, а с другой стороны на детальное изучение как работает веб на примере создания собственного простого веб-сервера.

Курсы направлены на программистов уже знакомых с Python'ом, имеющих определенный опыт его применения и которые хотят более детально залезть под "капот" и разобраться как же все устроено. Не говорю, что курсы полностью не подходят для новичков, главное, конечно, стремление, но поверьте, не подготовленным программистам может прийтись очень тяжело. Также еще раз упомяну, что каждую неделю будут задаваться домашние задание, выполнение которых будет необходимым. Так что курсы направлены и на усидчивых программистов тоже :)

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

Больше информации доступно на сайте, Learn Python.

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