Публикации с меткой «статьи»

На большой дороге

ICFP contest 2009

Продолжая добрую традицию подробно описывать контесты, начатую с Sapka contest, предлагаю вашему вниманию отчёт о ICFPC 09

Введение

ICFP Contest - командный контест, который проводится один раз в году. Количество участников в команде не ограничено. Задание одно, на весь контест отводится 72 часа(3 суток). Контест делится на lightning round(оцениваются решения, полученные в первые 24 часа) и main round(оцениваются все отосланные решения).

Команда

Страницу команды Concrete mixers можно найти здесь. Т.е., 4 человека, но после lightning A2K отошел от дел. С одним из оставшихся участников - xa4a - я уже участвовал в Sapka, и мы там даже взяли призовое место на Lightning. Со вторым из оставшихся - Murkt - до этого работать вместе не приходилось, но мы вроде неплохо сработались.

Инструменты

Основной язык - Python. В качестве системы контроля версий использовали Mercurial, в качестве хостинга - bitbucket. Для визуализации был использован pygame(также были попытки использовать Qt, но в итоге остался вариант с pygame). Для общения использовали конференцию в jabber.

Задание

Оригинальное задание (последнюю версию) можно скачать здесь. Кратко - нужно было писать управляющие программы для спутника для выполнения разных задач. Поведение спутника и окружающей его вселенной эмулировалось в бинарниках, которые предоставляли организаторы. Бинарники можно было запустить на виртуальной машине, спецификации которой были также предоставлены. Список маневров, которые нужно было выполнять со спутником:
  • Перевод спутника с одной круговой орбиты на другую
  • Рандеву с другим спутником, двигающимся по круговой орбите
  • Аналогичное рандеву, но начальная орбита и конечная могут быть эллиптическими
  • Упрощенное рандеву с 11-ю спутниками на произвольных орбитах. Также здесь присутствует Луна и заправочная станция
Таким образом, большая часть задания связана с орбитальными маневрами - сплошная математика и физика.

Ночь первая (26.06-27.06)

Получили задание, прочитали, пообсуждали, устранили непонимания, распределили задания. Где-то через два часа у нас наконец появился парсер бинарников, еще через час было две VM, из которых мы выбрали лучшую. Еще через два часа я сделал первый тестовый солвер с визуализатором, а к этому моменту xa4a уже залил нужные формулы для hohmann transfer, A2K доделывал логгер, который был нужен для формирования сабмишенов.
Т.о., через 5 часов после начала я приступил к прикручиванию формул к солверу, однако это оказалось не так просто. Murkt с чувством выполненного долга(написанная им VM работала, хоть и медленно) пошел спать, а мы с xa4a и A2K еще часа два пытались починить логгер и формулы, A2K писал визуализатор на Qt. Результат первой ночи - готова VM, визуализатор, основная инфраструктура, однако очков всё еще 0

День первый (27.06)

С утра Murkt и xa4a подхимичили формулы и у нас в нашем симуляторе появились первые очки. Однако при попытке сабмита тут же стало понятно, что написанный ночью логгер ошибочен и я взялся его переделывать. В 14:20 был "EPIC WIN!!!!!"(с)Murkt - первые очки, полученные после исправления логгера. Задачи 1001-1004 в этот момент времени перешли в статус решенных. И пока xa4a и Murkt продолжали изыскания с задачами 2001-2004, я в течение часа многократно ускорил VM путем генерации и последующей интерпретации кода на Python. Также скриншот, сохранившийся с этого временного участка:
В дальнейшем мы все вместе пытались побороть задачи 2001-2004. Напомню, в этих задачах нужно было не просто перелететь на другую орбиту, как в 1001-1004, а и попасть еще и в ту же точку этой орбиты, что и другой спутник. Для того, чтобы этого добиться, мы ввели понятие hohmann delay - время ожидания, нужное, чтобы после него при hohmann transfer попасть в нужную точку. В 18:40 Murkt получил первые очки этим методом. Однако метод оказался совсем не стабильным - решение он давал, но давал и большие погрешности. Поэтому оставшееся до окончания lightning время мы работали в двух направлениях - устранение погрешности и 3001-3004. Ничего существенно добиться мы не успели и lightning закончили с результатом где-то 945 баллов. В top lightning'a мы,естественно не попали, и место в lightning на данном этапе узнать невозможно, хотя и очень интересно.
Ниже - визуализатор, который писал A2K, но который так и не был использован:

Ночь вторая (27.06-28.06)

Появилась Луна и задачи 4001-4004. Часа через четыре усилиями Murkt и xa4a были существенно проапгрейджены решения наших 8 задач и получено 1127.44746 очков. Приблизительно этого хотелось добиться в lightning, но не успели, а жаль. Также xa4a очень красиво переделал солверы (стало чем-то похоже на twisted). С 3х до 6 утра я сделал алгоритм подгазовки через phasing - он работал идеально и позволял проходить 2001-2004 даже без hohmann delay.

День второй (28.06)

Murkt почистил код, а мы с xa4a пытались найти параметры эллиптических орбит и у нас никак не получалось вывести формулу, которая бы работала для всех задач 3001-3004. В поисках решения были привлечены ЧМ и придумано несколько странных методов определения. Однако это ни к чему не привело, а уравнение орбиты было выведено xa4a чуть позже, когда я отсутствовал.

Ночь третья (28.06-29.06)

Murkt сделал naive chase - подгазовку, которая плевала на всю астрофизику и просто заставляла суптник лететь к цели, сжигая при этом топливо(занятный пример представлен ниже).
Этот naive chase отлично работал на небольших расстояниях. Совместив его с hohmann elliptic transfer, который к этому моменту я докрутил до состояния бета, у нас получилось решить все задачи из 3001-3004. Также я пытался сделать phasing для эллиптических орбит, однако из-за каких-то погрешностей точность phasingа оказалась меньше, чем нужно. Тем не менее, применение одной итерации phasing, а потом naive chase привело к увеличению очков.

День третий (29.06)

Весь день был проведен в попытках улучшить переходы по эллиптическим орбитам для 3001-3004, чтобы затем использовать в 4001-4004. Я попытался еще ускорить виртуальную машину посредством psyco (работало отлично, но только на 32-битных системах) и cython(работало везде, но компиляция была слишком долгой, кеширование скомпилированного нужно было делать, а профит был меньше, чем с psyco, т.о. в итоге от него отказались). В 13:04 был эпический "OMFG", когда мы заметили, что в Orbital Mechanics (описание ее смотрите ниже) есть матлабовский код, в котором есть готовые нужные нам формулы и алгоритмы - только копируй, исправляй и пользуйся. По этому коду xa4a переделал определение параметров орбиты, а я пытался сделать smart chase - через уравнение Ламберта. Однако, по-моему мнению, это было лишним - код нормально работать отказывался, а определение параметров орбиты вообще стало работать хуже, чем было. Smart chase также работал хуже, чем naive chase. Я попытался вывести hohmann elliptic delay - аналог hohmann delay но для произвольных эллиптических орбит, однако и этот алгоритм нам не очень пригодился. В этот момент - оставалось часа 3 до конца контеста - мы решили, что нужно хотя бы какие-нибудь Score получить на задачах 4001-4004. Murkt сделал простой солвер, который работал также, как 3001-3004(hohmann elliptic transfer+naive chase), однако ему не хватало топлива. Остальные три часа мы пытались подстроить naive chase, так чтобы он экономил топливо. Итог - пойманы три спутника в 4001(третий спутник был пойман мной за 7 минут до окончания, скриншот смотрите ниже) и два спутника в 4002.
Итог контеста - Weighted Total Score 2852.2285 (14 problems solved), 21 место, если последние 4 часа контеста все участники прохлаждались :)

О разном

  • Репозиторий с исходниками можно найти здесь
  • Orbital mechanics - кодовое название книги Howard Curtis "Orbital mechanics for engineering students" - для нас она стала Библией астрофизики
  • Отчёт от Murkt
  • Отчёт от xa4a

Организаторам

  • Слишком много версий заданий. Последние я даже не читал - надоело
  • Математика - это круто, и я не жалею, что участвовал в контесте, но всё же хотелось увидеть programming contest

Выводы

Как ни странно, текущий раздел не последний в этом отчёте. Тех, кто интересуется математикой, могут также прочитать и следующий раздел. Здесь же хотелось отметить, что фана от ICFPC 09 было всё же меньше, чем от Sapka (возможно потому, что Sapka была первым моим подобным контестом), однако море удовольствия от решения математических задачек я всё же получил. Будем надеяться, что в следующем году я тоже смогу поучаствовать.

О математике

За время контеста я получил(вывел сам, подсмотрел в Orbital Mechanics) множество формул. Здесь небольшой список, что было проделано(список не включает достижения остальных участников соревнования):









Кодовое имяОписание
hohmann delayПозволял найти время, которое нужно подождать на текущей орбите, чтобы при hohmann transfer на целевую орбиту попасть в нужную точку. Я выводил из равенства конечных углов, где конечные углы - функции времени. Этот hohmann delay не был использован в решении
phasingБыл подсмотрен в Orbital Mechanics и немного подправлен, чтобы была возможность совершать подстройку из любой точки орбиты, а не только из перигея. Вывод можно посмотреть в Orbital Mechanics
orbital equation v.1Позволял находить уравнение орбиты по двум точкам. Был выведен из системы двух уравнений орбиты в разных точках. Работал идеально на орбитах, apse line которых совпадала с осью абсцисс
orbital equation v.2К v.1 был добавлен еще один параметр - угол поворота орбиты. Параметры должны были находиться теперь уже по трём точкам. Решение искалось численно, потому что косинусы. Довести до ума так и не получилось, возможно, в моих предположениях была какая-то ошибка
orbital equation v.3Было использовано уравнение орбиты в векторах. Также должно было определять по трём точкам и находить угловой момент и вектор эксцентриситета. Не был доведен до ума, потому что см. ниже
orbital equation v.4Был подсмотрен в википедии в статье про кеплеровские орбиты. Вероятно, я где-то ошибся, но и эта формула выдавала неправильные результаты, хотя по этой же статье xa4a смог сделать рабочую версию
hohmann elliptic transferПочти то же самое, что и hohmann transfer, только без упрощений, возможных для круговых орбит. Вывод можно посмотреть в Orbital Mechanics. Было использовано для 3001-3004
elliptic phasingТо же самое, что и phasing, но для эллиптических орбит. Работало хуже, чем phasing, но работало для некоторых задач из 3001-3004. Вывод можно посмотреть в Orbital Mechanics
Smart chaseChasing maneuver по Orbital Mechanics. Работал не очень, использован не был

На большой дороге

Sapka contest

Не так давно закончился контест Sapka, который проходил с 13-го по 20-е марта 2009 года. Я принимал в этом контесте участие и не могу не поделиться впечатлениями, ведь их было очень-очень-очень много.

Введение

Sapka - контест, больше похожий на ICFPC, чем на контесты, проводимые ACM. А именно - задание здесь одно, марафонного типа, которое может решить практически любой хороший программист, но только в ситуации, когда у него в распоряжении будет неограниченное количество времени. К тому же задания как в ICFPC, так и в Sapka обычно намного интересней, подаются в игровой форме и больше требуют не знания алгоритмов, а умения напрягать мозг, концентрировать усилия и бороться. Для меня контест Sapka стал первым контестом подобного типа.

Команда

Sapka и ICFPC - командные соревнования. Естественно, участвовать можно и одному, но для одного человека объем работы очень велик, да и вместе веселее. Поэтому очень важным является выбор команды, настройка средств коммуникации, распределение ролей и т.д.
Я до самого конца не был уверен, буду ли я участвовать в Sapka, поэтому команду я нашел лишь за день до соревнований, да и за день мы никак не готовились к участию. Как ни странно, но ничего страшного из-за этого не случилось, были и покрупней просчёты :) Итак, команда под названием "a" изначально состояла из трёх человек - Вашего покорного слуги, xa4a и A2K. Но у последнего участника под конец соревнования были завалы с учебой, поэтому написанием самого решения занимались всего два человека.

Начало соревнования

Оригинальное задание можно прочитать на сайте Sapka. Фан начинается уже здесь. Если кратко - то нам даётся некий сервер некой игры, в которую надо стучаться по телнету. Нужно написать клиента для этой игры. Всё! Никакого точного ТЗ, только упоминание о том, что сначала сервер нужно сконфигурировать. И вот с таким багажом знаний все участники и вступают в игру.
Лично для себя я всё время участия могу поделить на 3 этапа:

Этап 1. Хак сервера :)

То, что сервер написан на Java, и то, что защита там игрушечная, впоследствии позволило многим командам получить все нужные для конфигурации данные в течение первых 1-2х часов соревнования. Это также вызвало бурные дискуссии и недовольство тех, кто не смог сервер хакнуть. В гугл-группе даже жаловались, что из-за того, что в команде не было ни одного джависта, у них не получилось похачить сервер и поэтому они не смогли "заняться собственно программированием"(с). Для тех, кто все еще считает, что для взлома сервера обязательно нужны знания Java - следующий абзац.
Обладая минимальными знаниями Java и обладая к Java большой нелюбовью, я пересилил себя и приступил к "взлому" сервера. Вся процедура взлома заключалась вот в чём: разархивировать jar; увидеть, что там есть какой-то loader; натравить на него jad (найден в google по словам Java Decompiler); открыть Eclipse; запихать туда расшифрованный код; пройтись по коду и найти место, где происходит дешифрация; добавить запись в файл прямо там(код записи в файл был также найден в google ;));скомпилировать и использовать получившийся код для дешифрации файлов; после этого натравливать jad на дешифрованные файлы. Чтобы долго не лазить по обфусцированному коду, я взял и банально расшифровал самые большие файлы, которые были в архиве. И тут же налетел на нужные файлы - на описания логики игр. Чуть погодя(почему погодя - смотрите ниже) был написан и генератор конфигурационных токенов(простым копированием существующего кода). Вы всё еще считаете, что для взлома сервера нужно было знание Java? ;)
На самом деле, я занимался исследованием сервера намного дольше, чем это могло показаться. Во-первых, я вначале пытался достать конфигурационные токены прямо из памяти, но успеха не добился. Во-вторых, команда активно ковыряла квестовую часть Сапки, и я также принимал в этом участие.

Этап 2. Квест

Все конфигурационные токены можно было достать, пройдя текстовый квест, общаясь по telnet с сервером. Это было очень увлекательно, поэтому даже когда я уже достаточно продвинулся в процессе взлома, мы всё равно продолжали решать задачки. Так были найдены практически все токены, кроме DNA (сгенерировать токен оказалось намного проще, чем решить эту задачу). Также были подсмотрены в коде сервера секретные пароли, так что токены, которые они дают, также были получены путём хака.
В общем, описать квестовую часть практически невозможно - это было незабываемо! :) Внутри был Брейнфак, Forth-подобный язык, "шашки"(за них отдельное спасибо, я был просто поражен, когда обыграл его) + несколько задачек на преобразование текста. Я постарался не выдавать никаких секретов в описании, поэтому если вы не видели квеста - it's worth to try it out! Даже несмотря на то, что игра завершена, удовольствие вам гарантировано.

Этап 3. Программирование бота

После прохождения квеста стало ясно, что нам нужно написать клиент к bomberman-like игре. В качестве основного инструмента был выбран язык Python. Краткое описание процесса:
xa4a - написание парсера для сообщений от сервера
xa4a - создание визуализатора (на pygame)
tilarids(/me) - улучшение визуализатора
xa4a - создание keyboard controller
xa4a - рефакторинг, фикс багов, random AI
tilarids(/me) - добавлено избегание опасных мест
tilarids(/me) - добавление уничтожителя стен

Здесь небольшая врезка - кончился lightning раунд. На lightning раунд был отправлен бот, который не ставил бомб, а только убегал от опасностей. Причина - за убийство себя давали -1000 очков, а бот был страшен в своей наглости и часто себя убивал.
Дальнейшая разработка заключалась практически в улучшении, рефакторингом и пофиксах существующих алгоритмов.
xa4a - большой рефакторинг, добавлен нормальный механизм переключения состояний
tilarids(/me) - добавлен учёт времени взрыва бомб, простейшее начисление очков(в соответствии с идеей A2K), пофикс багов
xa4a - добавление охоты за бонусами, пофикс багов, рефакторинг бомб
tilarids(/me) - добавление охоты за противником
xa4a - завершение рефакторинга бомб, добавление учета подрыва бомбами друг друга, пофикс багов
tilarids(/me) - пофикс багов, пофикс учета коллапса мира, сабмит

На этом уже подошел к концу сам контест. Мы засабмитили бота, который умеет всё, что нужно для победы, но, к сожалению, он всё же не бессмертен и частенько умирает.
Краткое описание алгоритмов. Бот - простейший автомат, с такими состояниями как "беги", "ищи, куда поставить", "ставь", "охоться за бонусами", etc. Переход из одного состояния в другое был жестко зашит - никакой сложно логики выбора состояния не было - она была просто лишней. Для нахождения путей был применен волновой алгоритм, который учитывал опасности на пути. Для нахождения места, куда ставить, также использовался волновой алгоритм, на котором помечались цели с указанием очков за эти цели. После того, как алгоритм отрабатывал, выбиралась цель с набольшим количеством очков, такая, что если рядом с целью поставить бомбу, то мы сможем оттуда убежать(для проверки, можем ли мы убежать, также использовался волновой алгоритм). В случае, когда бомбу поставить не было возможности, включалась охота за бонусами.
Для тех, кто хочет посмотреть, как это выглядело:

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

Благодарности

Хотелось бы выразить огромнейшую благодарность организаторам. Sapka - это было потрясающе! Такого количества фана я не получал уже очень давно. Спасибо вам за то, что вы сделали!
Также хотелось поблагодарить команду - мне показалось, что работали мы достаточно слаженно, и я очень рад, что мне довелось участвовать в Sapka в команде именно с таким составом.

Организаторам

  • Всё же сервер надо защищать лучше. Чтобы взлом сервера был отдельной трудной задачей, а не заменял прохождение квеста. Например, можно было бы некоторые задания составить так, чтобы понадобилось их решать программно в момент запуска клиента
  • Побольше бы заданий наподобие fifth. Задачки на преобразование текста были занятные, но по количеству фана намного менее содержательные
  • Я буду очень рад поучаствовать в Sapka 2010 ;)

Себе на заметку

  • Если желаешь выиграть, нужно пользоваться даже относительно нечестными методами. Если бы мы не тратили время на квест, когда уже были почти все токены, на лайтнинг можно было бы успеть отправить уже активного бота
  • Количество участников в подобных соревнованиях - не главное. Но хорошо, если есть участники, которые могут потратить время на поиск и исправление багов
  • Возможно, стоило попробовать переписать всё вообще без состояний, чтобы получить простого, как пробка, но зато бессмертного/безбажного бота. Но после драки кулаками не машут :)
  • Pygame - отличная платформа для визуализации чего-либо

На большой дороге

Reia - скриптовый язык для виртуальной машины Erlang`а

Лично я считаю Erlang одним из самых простых яызков программирования, а среди знакомых мне функциональных языков - самым простым. К тому же на Erlang благодаря его направленности на создание конкурентных приложений написано уже множество проектов, таких как Yaws, CouchDB, ejabberd, которые являются для него наилучшей рекламой.
Таким образом, Erlang - функциональный язык с простым и понятным синтаксисом, который нашёл свою нишу, и если вы интересуетесь созданием масштабируемых конкурентных систем - вам стоит выучить его. Однако из-за того, что Erlang - функциональный, его синтаксис и стиль понятен не всем - он слишком отличается от императивных языков(таких как С и подобные) и даже от Ruby/Python, которые включают в себя частицы функционального подхода.
Если Вы столкнулись с такой проблемой - обратите внимание на Reia - скриптовый Ruby/Python like язык для виртуальной машины BEAM(эта виртуальная машина используется также и в Erlang). Язык Reia совместим с Erlang и может использоваться для создания конкурентых приложений, однако используя при этом скриптовый синтаксис. Вот простейший пример:
module Foo
def bar
receive
when msg
["Received ",msg].join().puts()
bar()
pid = Process.spawn(fun {Foo.bar()})
pid ! "Hello"
pid ! "World"
Результат:
Received Hello
Received World
В язык заложено очень много возможностей(как по мне - слишком много :) ): отсылка сообщений процессам и объектам, встроенные регулярные выражения, pattern matching, асинхронные вызовы функции, лямбда функции, а также многое-многое другое. Многое из этого не реализовано(например, циклы), но часть функциональности уже существует и работает, как можно увидеть из примера.

Выводы

  1. Reia - многообещающий ЯП, которому, однако, не хватает разработчиков. Возможно, если реализации будет уделяться больше времени, то этот ЯП станет мостиком, по которому толпы приверженцев императивного подхода ринутся в страну Erlang
  2. Как по мне, синтаксис Reia перегружен. Этот вывод только подкрепляет моё убеждение, что Erlang - отлично спроектирован и очень элегантен

На большой дороге

Знакомьтесь: Geany!

Введение

Я уже писал, что мне очень нравится редактор SciTE и поэтому я его постоянно использую, например, при программировании на Python. Однако в GTK версии есть несколько недостатков:
  • Открытие большого числа вкладок невозможно - не работает прокрутка и multiline
  • Глюки с юникодом - если написать \что-то при редактировании TeX документа, получим несуразные символы. Эти же несуразные неудаляемые символы также иногда появляются в строке поиска
  • Нет нормальной интеграции с shell. В итоге для простейшей проверки конструкции в ipython приходится переключаться на терминал
Естественно, можно было бы подправить это в самом SciTE, но зачем, если всё уже сделано? Представляю вам Geany - простейший редактор с замашками IDE(которые, в принципе, не мешают :) ) основанный на том же движке, что и SciTE - на Scintilla.

На этом скриншоте он уже немного подконфигурирован для моего удобства. Возможности Geany:
  • Подсветка, фолдинг - аналогично SciTE
  • Нормальные табы
  • Поддержка VTE
  • Symbol explorer
  • Плагины(в числе встроенных FileBrowser, SplitWindow и т.д.)
  • Автодополнение по символам(неплохое, но для Python хотелось бы лучше. Geany - Open Source, так что возможно это желание будет реализовано)
  • Автозакрытие тегов
  • Сессии
Более подробно хотелось бы остановится на поддержке VTE. Благодаря ей в Geany есть полноценный терминал! Насколько это удобно можно понять, прочитав следующий раздел.

Конфигурирование

В этом разделе я хочу дать описание моего рабочего окружения в Geany. Оно не блещет уникальностью, но весьма удобно. Конечный внешний вид - на скриншоте выше. По пунктам:
  1. Ставим. Я просто выполнил "emerge -av geany". Думаю, в остальных Linux дистрибутивах его можно поставить сходным же образом. Для Windows пользователей - есть инсталляторы
  2. Убираем Sidebar, дабы сэкономить площадь
  3. Задаем комбинации клавиш для удобного перемещения по табам
  4. Настраиваем терминал - самая интересная часть. Я выбрал себе темную темку и запустил внутри screen - в итоге я могу переключаться между логами сервера, ipython и дополнительными консолями. Для меня терминал в Geany - самый важный инструмент. В нём я работаю с git и hg, в нём я отлаживаю приложение, в нём же я и лажу по файлам("geany file_name" открывает файл в новой вкладке). Таким образом, терминал мне заменяет File Browser, Debugger и VCS Inegration
  5. Настраиваем шрифт, остальные комбинации клавиш и интерфейс по вкусу
У меня в screen не заработала клавиша Backspace, пришлось биндить. Ниже - кусок конфигурационного файла screen (.screenrc), который делает screen юзабльным:
bindkey -d ^@ stuff ^? # пофикс backspace
hardstatus on
hardstatus alwayslastline
hardstatus string "%{Gk}| %-w%{+u}%n %t%{-}%+w |%=(%l) %d/%m %c"
До сих пор нормально не работает скролл на мышке, прокрутка вверх генерит "^[[A". Если у кого-то есть уже решение - поделитесь :) Если разберусь сам - проапдейчу.

Выводы

Geany - отличный редактор, полностью покрывающий мои запросы. На данный момент я использую его для Python разработки и редактирования TeX файлов. Для С++ же я предпочитаю IDE Anjuta, хоть Geany можно использовать и здесь.

livedev.org

Загрузка файлов в Django. FileField & upload_to.

На момент выхода версии 1.0 Django, одним из самых значительных изменений стал механизм загрузки (upload) и хранения (storage) файлов. Загрузка файлов без FileField в модели базы данных конечно очень редкий случай, поэтому начнем именно с одного из обязательных параметров FileField — upload_to.

Главная инновация — это гибкая возможность настройки этого самого upload_to. Раньше можно было только передавать строку с кодированными strftime параметрами. Тем самым можно было разбивать загруженные файлы по директориям, создаваемыми по дате, типа uploads/mp3/2008-12/uploaded.mp3. Сейчас параметром upload_to может быть и callable, т.е. к примеру функция, которая должна возвращать полный путь с именем файла, куда будет сохраняться загруженный файл. Функция должна принимать два параметра: instance и filename.

  • instance — это экземпляр объекта модели, которой и принадлежит поле с типом FileField.
  • filename — юникодное имя файла.

Эта функция будет запускаться перед сохранением в базу, поэтому если объект новый (вы не редактируете существующий объект, а создаете новый), то поле pk/id будет пустым и соотвественные Django сигналы еще не выполнены. Думаю, что с примером кода все будет наиболее хорошо понятно.

Итак функция-callback, которую мы будем использовать в upload_path:

def make_upload_path(instance, filename):
    """Generates upload path for FileField"""
    return u"uploads/%s/%s" % (instance.category.slug, filename)

Теперь пример модели с FileField:

class Upload(models.Model):
    user = models.ForeignKey(User)
    file = models.FileField(upload_to=make_upload_path)
    category = models.ForeignKey(Category)
    uploaded_date = models.DateTimeField(auto_now_add=True)

Все просто. Можно использовать множество параметров вашей модели для создания директорий и упорядочивания файлов в них. И самое главное upload_to — самый простой способ закачивания файлов с русскими (и другими юникодными) именами. Т.к. по-умолчанию Django заменяет их на символ подчеркивания, что приводит к появлению файлов типа __________.jpg. Если вы сделаете самую простую функцию с вовращением вида return u"uploads/mypath/%s" % (filename), то получите русские имена файлов после закачки.

Официальная документация Django по FileField.

livedev.org

Django signals по-новому

На пути к 1.0 релизу Django претерпевал немало радикальных изменений. Одно из них рефакторинг системы сигналов.

Если вы первый раз читаете и не в курсе «что это такое и с чем его едят», то скажу в двух словах. Это система реагирования на события приложения. Любой JavaScript или прикладной UI программист хорошо знаком с системой событий (event) — клик мышкой, нажатие горячей клавиши и т.п. Для программистов серверной части веба все выглядит немного по другому. Есть HTTP-запрос и есть его обработчик, анализируется как правило URL на предмет «кому отправлять запрос». Но на самом деле это та же система сигналов-событий, только узкопрофилированная под обработку HTTP-запросов.

Оказывается серверная часть веб-приложения тоже может, и я уверен, просто должна генерировать намного больший спектр сигналов, чем просто обработку URL и данных запроса. С чем успешно и справляется Django. Теперь немного прозы. Какие же события может ловить Django «из-коробки».

  • pre_init — перед запуском метода-конструктора __init__() модели;
  • post_init — после выполнения метода __init__() модели;
  • pre_save — перед сохранением экземпляра модели в базу;
  • post_save — после успешного сохранения экземпляра модели в базу;
  • pre_delete, post_delete — перед и после удаления экземпляра модели из базы;
  • post_syncdb — генерируется админкой Django после установки нового приложения (INSTALLED_APPS);
  • request_started, request_finished, got_request_exception — генерируются при обработке HTTP-запросов;
  • template_rendered — генерируется только в режиме тестирования приложения.

Итак сигналы *_init, _*save, *_delete и post_syncdb генерируются системой моделей Django и их можно импортировать из django.db.models.signals. Сигналы обработки HTTP-запросов из django.core.signals. И тестовый template_rendered из django.test.signals.

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

Код модели:

class MimeType(models.Model):
    """
    Mime Types table
    """
    name = models.CharField(max_length=200)
    slug = models.SlugField()

    def __unicode__(self):
        return self.name

class Item(models.Model):
    """
    Main file
    """
    name = models.CharField(max_length=200)
    slug = models.SlugField()
    file = models.FileField(upload_to='files', blank=True)
    mime = models.ForeignKey(MimeType, blank=True, null=True)
    upload_date = models.DateTimeField(auto_now_add=True)

    def __unicode__(self):
        return self.name

    def set_mime(self, mime):
        obj, created = MimeType.objects.get_or_create(name=mime, slug=slugify(mime))
        self.mime = obj

Посмотрите на метод set_mime модели Item. Он создает новый объект MimeType или использует если он уже создан. При этом не сохраняя модель Item. Теперь я пишу функцию-callback, которая будет вызываться по событию сохранения модели Item.

import mimetypes
import os.path

# init mime types dict
mimetypes.init()

def add_mime_type(instance, **kwargs):
    """
    Adding mime-type to uploaded file (for future use).
    Would be called on post-save.
    """
    if not hasattr(instance, 'mime') or not hasattr(instance, 'file'):
        raise Exception("Object %s does not have 'mime' attribute! Can't set mime-type!" % instance)
    if instance.file:
        extension = os.path.splitext(instance.file.path)[1].lower()
        instance.set_mime(mimetypes.types_map[extension])

Итак, функция-callback add_mime_type принимает параметр instance, который является экземпляром модели, сгенерировавшим сигнал (в нашем случае модели Item). Вторым параметром принимает словарь (dictionary). Кстати, это и есть один из моментов обратной несовместимости. После рефакторинга каждая функция-callback должна принимать параметр **kwargs.

Теперь третий самый простой шаг — связывание модели с сигналом. В самом конце файла моделей добавилась строчка:

models.signals.pre_save.connect(add_mime_type, sender=Item)

Теперь перед каждым сохранением объектов Item, будет выполняться проверка и установка Mime-типа.

Кстати, я советую если у вас больше одного сигнала, выносите их в отдельный файл signals.py или можно по-старинке писать в файле моделей (что ИМХО иногда мешает чтению кода).

Почитать про сигналы можно еще в официальной доке: Django Signals и Django Built-in signal reference. Почитать про рефаторинг можно на странице Backwards Incompatible Changes и в соответствующем коммите 8223.

Александр Кошелев тоже немного раньше написал по теме, статья «А вы поймали новые сигналы?» с хорошим примером создания абстрактного сигнала.

«На сегодня все. Вопросы в студию» :-)

Метки

.net .NET C# .sort 1.2 2009 2010 404 error admin ajax amazon analytics and apache api archlinux asp.net async asynchronous autocomplete bash blender blog blogengine blogs book bootstrap bot bpython buildout byteflow bzr C c plus plus C++ cache cbv Chaco checkio chrome ci ckeditor class based views clojure closure cms cms с удобной админкой code coding style collectd COM comet competition conference ConfigParser contest Context continuous integration CouchDB coverage CppCMS cpyext cpython crud csrf CSS ctypes curl custom model fields cx_freeze cython database db dbm dbqueries debian debug debugging decorator decorators deploy deployment descriptor design dev devconf developers development diveintopython Django django 1.2 django 1.3 django advent django framework django template django trunk django weblog django-admin-tools django-cms django-compressor django-hosts django-piston django-registration django-sphinx django.admin djangoadvent djangocms djangodash doc documentation drupal e-legion eclipse EGit emacs encoding Enthought epoll erlang event exception ExtJS fabric facebook fastcgi finaloption fixtures fonts forms formset fp framework freebsd freeswitch fs2web ftp fun funcparserlib functional gae gamin gandi generic views gettext gevent gil git github gitosis Google Google App Engine google picasa Google Translate google wave Google Web Toolkit grab grablab greenlet gtd gui haskell hg hgshelve highlighter host hosting how-to howto html html5lib Hudson humor i18n icfpc ide idiomatic image-scripting improvements Internet interpreter ipython ironpython izmenimsya.ru jabber java javascript jenkins jetbrains JIT job jquery json jstree jython kde kiev kiyv kyivpy l10n ldap library libs Life Links linux Linux & Unix LLVM logging logs lxml Mac OS X magic mail markdown Matplotlib Mayavi maybe mediavirus meetup memcache Memcached memory messages metaclass middleware migration mikrotik mkd model models mod_python mod_wsgi mongodb monitoring mptt musicmans.ru musicx mvc my-projects mysql netCDF networkx newforms newforms-admin news nginx Nhibernate nix nose NoSQL numpy oop open source OpenID openoffice opster optimization oracle orm os pagination parsing path patterns pdf PDF-принтер PEP PEP8 performance performance optimization perl personality photo php picture-driven computing PIL pinax pingback pip plasma plone plugin plugins postgresql programming progress bar psycopg2 py2exe pybb pybbm pycamp pycharm pycon pycow pycurl pydev pygtk pylons PyNGL pypy pyqt PyQt4 pyrad pyramid PySide Python Python 2.5 python 2.7 python 3 python c api python speed python-mssql python3 pywinauto Qt Qt4 queue rabbitmq radius raw sql re redis redsolution redsolution cms regexp regular expressions release repoze.bfg RequestContext reusable apps robokassa rss ru ruby ruby-on-rails sample satchmo scalability SciPy scraping screencast search selenium self.error seo server setattr settings setuptools shell sikuli sms snippet socket.io software sorting south sphinx spider sql sqlalchemy sqlite ssh startup step-by-step subdomain subversion svn SyntaxHighlighter system tags tdd tddspry teh drama template templates templatetags test testing thinkpad threading threads tips tips and tricks tools tornadio tornado tornado server tricks tutorial tweepy twisted twitter typography uapycon Ubuntu ucsvlog uml Uncategorized unicode unit test unit testing UnitTest Unladen Swallow upload urllib urls utf-8 uwsgi validation vcs versioning video vim virtualenv Visual Studio vkontakte voip wave web web-devel web-services web-разработка webdev webfaction webkit webpy websockets webtest widget widgets Win API windows Wirbel work wrapper wsgi wxPython wxWidgets wysiwyg xapian xml xmonad xmpp xpath yandex youtube zip zomg zope [cdata[cbv]] [cdata[ci]] [cdata[class based views]] [cdata[continuous integration]] [cdata[django framework]] [cdata[django-sphinx]] [cdata[django]] [cdata[nginx]] [cdata[python]] [cdata[virtualenv]] [cdata[программирование]] автоматизация администрирование администрирование django админка алгоритмы архитектура атрибуты базы данных Без рубрики безопасность библиотеки блоге бот веб-разработка видео Визуализация данных вконтакте Все записи гвидо ван россум граббер графика графы декоратор декораторы дескриптор дескрипторы документация заметки игра жизнь идея интересное киев Клиентам книги конференция личное математика метаклассы модели модули монады морфология мысли невозможное новости о облачные вычисления обо мне Обработка данных оптимизация оптимизация кода Основная лента основы парсинг парсинг сайтов перевод песочница Питон поебень поиск правила кодирования программирование Проектирование производительность работа рабочее размышлизмы Разное разработка разработка приложений разработки регулярные выражения сайт событие события ссылки статьи тестирование тесты Тюмень убунтариум фигня философия формы форум Хабрахабр хакинг хостинг шаблоны шаблоны проектирования эксперимент Эксперименты юмор я пиарюсь Яндекс