Вчера выложил в открытый доступ под лицензией BSD свой новый велосипедик для Django.
Называется творение "django-supergeneric". Исходный код доступен тут: https://github.com/lig/django-supergeneric
Причиной написания этого стало желание перестать постоянно писать одно и то же. Почти всегда для каждой модели приходится писать похожие друг на друга, как близнецы-братья, view для просмотра списка объектов, одного объекта, создания объекта, редактирования объекта и удаления объекта. Понятно, что есть generic views, но их тоже надо конфигурировать, причем многие параметры повторяются. Да, и каждый раз для всех пяти view надо прописать urlpatterns, которые тоже для всех объектов очень похожи.
Вот поэтому и появился django-supergeneric.
Пример использования можно посмотреть в демо-проекте: https://github.com/lig/django-supergeneric/tree/master/project
Upd: страничка на ohloh: https://www.ohloh.net/p/django-supergeneric, плюсуйте, кто будет пользоваться, если не лень.
18 авг. 2011 г.
22 дек. 2010 г.
Пам-парам, по "монгам"
Пару месяце назад я начал активно использовать MongoDB как для своих открытых проектов, так и по работе. Надо сказать, что этот относительно кратковременный опыт весьма и весьма положительный.
Я не хочу здесь пересказывать документацию. Однако, хочу описать некоторые вкусности, неочевидности и, конечно, подводные камни, с которыми я столкнулся в самом начале пути. Возможно, кому-то это сэкономит нервы в начале освоения этой документ-ориентированной базы данных.
В основном я использую MongoDB из Python, для чего использую PyMongo напрямую или MongoEngine (подобие ORM для MongoDB с несколькими фишками для использования его с Django). И вот как раз с взаимодействием с MongoDB из Python-а и связаны пара подводных камушков. Точнее с параллельным использованием JavaScript встроенного в MongoDB и PyMongo.
Вообще говоря, "питоновский" тип DateTime прекрасно обрабатывается MongoDB. Внимательный читатель документации сразу обратит внимание (я этого не сделал), что PyMongo нужно передавать дату/время в UTC, т.е. нужно использовать datetime.utcnow(), а не привычное большинству datetime.now(). Это первый подводный камень, который, впрочем, вполне очевиден и в явном виде отражен в документации.
Есть еще один подводный камень связанный с хранением времени. Предположим, что, по каким-то причинам, мы хотим хранить время в формате unixtimestamp, т.е. вещественное количество секунд прошедших с 1.01.1970 с тремя знаками после запятой или даже просто целым числом. Это может быть нужно чтобы упростить и ускорить запись журнала каких-либо событий в MongoDB. Пока мы работаем с этими числами через PyMongo, все хорошо. Но потом встает необходимость оперировать с этим значением времени из функции JavaScript, например, при выполнении операции Map/Reduce или просто в консоли MongoDB. Пусть в базе лежит такой документ (timestamp получен сейчас, т.е., как минимум, в 2010 году):
{ "_id" : ObjectId("4d125e16f52cb12e01d5d041"), "timestamp" : 1293038527, "name" : "jump" }
Постоянная ссылка на этот кусок кода: http://paste.nophp.ru/1Ot. Код раскрашен с помощью Paste.NoPHP.ru.
Пусть нам нужно получить год, месяц и число, в который произошло событие. Вот с чего начал я:
db.events.find().forEach(function (event) {
var date = new Date(event.timestamp);
print(date.getFullYear(), date.getMonth(), date.getDate());
})
Постоянная ссылка на этот кусок кода: http://paste.nophp.ru/1Ov. Код раскрашен с помощью Paste.NoPHP.ru.
Какого же было мое удивление, когда я увидел в ответ "1970 0 16", т.е. нулевой (!) месяц и всего 16-й день от начала эпохи. Чтение документации по JavaScript Date Object открыло мне глаза прежде всего на то, что в JavaScript timestamp - это целое число миллисекунд прошедших от начала эпохи. И код превратился в такой:
db.events.find().forEach(function (event) {
var date = new Date(event.timestamp * 1000);
print(date.getFullYear(), date.getMonth(), date.getDate());
})
Постоянная ссылка на этот кусок кода: http://paste.nophp.ru/1Ow. Код раскрашен с помощью Paste.NoPHP.ru.
Этот код вернул уже "2010 11 22", но сегодня 22.12.2010. Да, да, да, вспоминаем нулевой месяц. Из той же документации по JavaScript Date Object я узнаю, что, оказывается, именно месяц считается в JavaScript начиная с нуля (зачем???), тогда код приобретает окончательный вид:
db.events.find().forEach(function (event) {
var date = new Date(event.timestamp * 1000);
print(date.getFullYear(), date.getMonth() + 1, date.getDate());
})
Постоянная ссылка на этот кусок кода: http://paste.nophp.ru/1Ox. Код раскрашен с помощью Paste.NoPHP.ru.
Теперь ответ правильный: "2010 12 22".
На сегодня, думаю, этого достаточно. В следующий раз поделюсь опытом использования MapReduce в MongoDB. Покажу как возвращать из Reduce списки значений и какого подхода к расчету значений в Reduce следует избегать.
20 дек. 2010 г.
Git mass push. Как заслать во все "ремоуты".
Думаю, все, кто работал с git-ом, сталкивались с ситуацией, когда удаленных репозиториев несколько и хотелось бы сделать "git push" во все. Например, у меня проекты лежат одновременно на sf.net, github.com и в собственном приватном хранилище.
Если положить скрипт в файл "~/bin/git-mass-push", то его можно будет запускать командой "git mass-push", для которой будет работать стандартное автодополнение git-а. При этом, этой команде можно передать любые параметры, которые принимает "git push" после имени репозитория, что видно из кода.
Я для себя написал маленький скриптик на bash:
#~/bin/bash
git remote show | while read repo_name
do
git push $repo_name $*
done
Постоянная ссылка на этот кусок кода: http://paste.nophp.ru/1Nu. Код раскрашен с помощью Paste.NoPHP.ru.
Пользуемся, радуемся;)
30 марта 2010 г.
Yammy? Dummy? ... Botty!:) Простой бот на xmpppy
Сейчас появляется всё больше сервисов, использующих протокол XMPP (он же Jabber).
Например: juick.com, facebook.com, identi.ca.
Часто хочется написать своего бота, чтобы общаться с такими сервисами, может быть написать свой сервис или просто автоматически отвечать своим друзьям на глупые сообщения;)
В этом может помочь библиотека xmpppy.
Я использую вот такой класс, в качестве основы для моих ботов:
Как видно, в коде используются настройки бота. Они выглядят вот так:
Для любого нового бота теперь нужно создать класс-наследник BaseBot, в котором установить атрибут bot_name и реализовать методы messageHandler и presenceHandler. В словаре BOT должен существовать ключ соответствующий этому bot_name с настройками для этого бота.
Запуск бота осуществляется так:
, где MyBot — класс вашего бота, а параметр debug говорит сам за себя.
That's all folks!
Например: juick.com, facebook.com, identi.ca.
Часто хочется написать своего бота, чтобы общаться с такими сервисами, может быть написать свой сервис или просто автоматически отвечать своим друзьям на глупые сообщения;)
В этом может помочь библиотека xmpppy.
Я использую вот такой класс, в качестве основы для моих ботов:
class BotBase(object, xmpp.Client):
bot_name = None
class InvalidCommandError(Exception):
pass
def __init__(self, *args, **kwargs):
self.bot_settings = BOT[self.bot_name]
self.__class__.__name__ = 'Client'
xmpp.Client.__init__(self, server=self.bot_settings['HOST'],
*args, **kwargs)
def connect(self):
xmpp.Client.connect(self, use_srv=self.bot_settings['SERVER'])
return self.auth(self.bot_settings['USERNAME'],
self.bot_settings['PASSWWORD'], self.bot_settings['XMPP_RESOURCE'])
def run(self):
self.UnregisterDisconnectHandler(self.DisconnectHandler)
self.RegisterDisconnectHandler(self.reconnectAndReauth)
self.RegisterHandler('message', self.messageHandler)
self.RegisterHandler('presence', self.presenceHandler)
self.sendInitPresence(0)
while self.Process(1): pass
self.disconnect()
def messageHandler(self, connection, message):
raise NotImplementedError
def presenceHandler(self, connection, presence):
raise NotImplementedError
Как видно, в коде используются настройки бота. Они выглядят вот так:
BOT['dummy'] = {
'HOST': 'serge.matveenko.ru',
'USERNAME': 'dummy',
'PASSWWORD': 'dummy5',
'XMPP_RESOURCE': 'mybot-0.1',
'USER_AGENT': 'MyBot/',
'SERVER': 'serge.matveenko.ru',
'JID': 'lig@serge.matveenko.ru',
}
Для любого нового бота теперь нужно создать класс-наследник BaseBot, в котором установить атрибут bot_name и реализовать методы messageHandler и presenceHandler. В словаре BOT должен существовать ключ соответствующий этому bot_name с настройками для этого бота.
Запуск бота осуществляется так:
bot = MyBot.initBot(debug=False)
if not bot.connect():
raise IOError('Can not auth with server.')
bot.run()
, где MyBot — класс вашего бота, а параметр debug говорит сам за себя.
That's all folks!
26 февр. 2010 г.
26 нояб. 2009 г.
Bash в bash-е, в bash-е, ..., в bash-е.
Думаю, всем знакома ситуация, когда, во время компиляции какого-либо ПО, выполнение `./configure` останавливается с сообщением о неудовлетворенной зависимости. И приходится установить что-нибудь еще и не одно, и не только из репозиториев, а еще и скомпилить что-нибудь другое, а там тоже зависимости.
Если всё это делать в одном терминале, то неизбежны: путаница в зависимостях, безуспешное копание в истории ввода и прочие пляски. Можно, конечно, для каждой задачи открывать новую вкладку в терминале или новый терминал, но тут может вмешаться параллельная задача и порядок задач будет нарушен, и открутить его в обратную сторону будет уже очень сложно - опять бардак.
Достаточно давно я выработал для таких случаев очень удобный, на мой взгляд, способ - рекурсивный запуск `bash`. Если для установки или сборки какого-либо приложения требуется установить или собрать что-нибудь другое, то в момент возникновения такой необходимости я запускаю `bash` прямо в этом же терминале. Это можно делать несколько раз, потом выходить из последнего запущенного экземпляра и получать действие, которое потребовало удовлетворить его зависимости предпоследним в истории. Примерно вот так:
Однако, мне всегда не давало покоя, что я не вижу уровень вложенности `bash`. И вот недавно с небольшой посторонней помощью дошел до решения.
Небольшая добавка в `~/.bashrc`, которая добавляет в приглашение командной строки отображение уровня рекурсии начиная с ноля:
После добавления этого кода в `~/.bashrc` первый листинг будет выглядеть вот так:
Вот так-то лучше:)
Если всё это делать в одном терминале, то неизбежны: путаница в зависимостях, безуспешное копание в истории ввода и прочие пляски. Можно, конечно, для каждой задачи открывать новую вкладку в терминале или новый терминал, но тут может вмешаться параллельная задача и порядок задач будет нарушен, и открутить его в обратную сторону будет уже очень сложно - опять бардак.
Достаточно давно я выработал для таких случаев очень удобный, на мой взгляд, способ - рекурсивный запуск `bash`. Если для установки или сборки какого-либо приложения требуется установить или собрать что-нибудь другое, то в момент возникновения такой необходимости я запускаю `bash` прямо в этом же терминале. Это можно делать несколько раз, потом выходить из последнего запущенного экземпляра и получать действие, которое потребовало удовлетворить его зависимости предпоследним в истории. Примерно вот так:
lig@host:~/soft/application$ ./configure Failed "dependency" required lig@host:~/soft/application$ bash lig@host:~/soft/application$ sudo aptitude install dependency lig@host:~/soft/application$ exit lig@host:~/soft/application$ ./configure Success lig@host:~/soft/application$ Постоянная ссылка на этот кусок кода: http://paste.nophp.ru/e. Код раскрашен с помощью Paste.NoPHP.ru.
Однако, мне всегда не давало покоя, что я не вижу уровень вложенности `bash`. И вот недавно с небольшой посторонней помощью дошел до решения.
Небольшая добавка в `~/.bashrc`, которая добавляет в приглашение командной строки отображение уровня рекурсии начиная с ноля:
if [ -z $recursion ]; then recursion="0"; export recursion; else recursion=$(($recursion+1)); fi; PS1='$recursion:'$PS1 Постоянная ссылка на этот кусок кода: http://paste.nophp.ru/c. Код раскрашен с помощью Paste.NoPHP.ru.
После добавления этого кода в `~/.bashrc` первый листинг будет выглядеть вот так:
0:lig@host:~/soft/application$ ./configure Failed "dependency" required 0:lig@host:~/soft/application$ bash 1:lig@host:~/soft/application$ sudo aptitude install dependency 1:lig@host:~/soft/application$ exit 0:lig@host:~/soft/application$ ./configure Success 0:lig@host:~/soft/application$ Постоянная ссылка на этот кусок кода: http://paste.nophp.ru/f. Код раскрашен с помощью Paste.NoPHP.ru.
Вот так-то лучше:)
24 нояб. 2009 г.
«Вредная» монополия Microsoft
По мотивам новости «Microsoft откроет свои магазины рядом с магазинами Apple» (news.techlabs.by) и обсуждения в рассылке SPb LUG.
А теперь вспомним, что конкуренция - это хорошо. А почему? А потому что конкуренция - это двигатель прогресса. Конкуренция заставляет соперничать и разрабатывать новое, полезное пользователю.
Но, если Microsoft - монополист, то им не надо будет развивать свои продукты? Это же хорошо. Это значит, что Open Source победит тогда и только тогда, когда перестанет лезть в коммерческие и области. Тогда, когда мы найдем для него некоммерческий рынок сбыта, такой же емкий, как комерческий.
Если главный путь распространения коммерческого ПО - это магазины, значит ПО с открытым исходным кодом надо раздавать с другой стороны двери, т.е. на улице. Да, так и только так.
Монопо́лия (от греч. μονο (mono) — один и πωλέω (poleo) — продаю) — фирма (ситуация на рынке, на котором действует такая фирма), действующая в условиях отсутствия значимых конкурентов (выпускающая товар(ы) и/или оказывающая услуги, не имеющие близких заменителей). Первые в истории монополии создавались сверху санкциями государства, когда одной фирме давалось привилегированное право торговли тем или иным товаром.
(wikipedia)Тут надо понять что есть рынок в нашем случае. Рынок операционных систем? Вряд ли. Скорее рынок коммерческого ПО. А Linux - это ПО с открытым исходным кодом, т.е. MS не конкурент Linux и Open Source в целом. Ух ты!
А теперь вспомним, что конкуренция - это хорошо. А почему? А потому что конкуренция - это двигатель прогресса. Конкуренция заставляет соперничать и разрабатывать новое, полезное пользователю.
Но, если Microsoft - монополист, то им не надо будет развивать свои продукты? Это же хорошо. Это значит, что Open Source победит тогда и только тогда, когда перестанет лезть в коммерческие и области. Тогда, когда мы найдем для него некоммерческий рынок сбыта, такой же емкий, как комерческий.
Если главный путь распространения коммерческого ПО - это магазины, значит ПО с открытым исходным кодом надо раздавать с другой стороны двери, т.е. на улице. Да, так и только так.
Ярлыки:
distribution,
linux,
microsoft,
monopoly,
opensource
Подписаться на:
Сообщения (Atom)

