15 декабря 2013 г.
проект django project на shared hosting хостинге mod_wsgi
Историческая заметка по большей части. Черновик схемы настройки проекта на django 1.4 на хостинге с mod_wsgi.
13 ноября 2013 г.
django: contrib.comments работаем через ajax
Тут рассказ о том, как заставить стандартный django.contrib.comments работать через ajax. Цель была такова: минимальные изменения в клиентских view, чтобы работали стандартные templates перегружаемые, полноценная админка (обычная от приложения comments), да и вообще максимальное использование оригинального кода. Короче, в итоге после нескольких самопроизвольных переписываний (вместе с использованием подобного решения в проектах) получилась минимальная обёртка, которую по идее тоже можно вынести в отдельный app.
Да, скоро это уже не совсем актуально станет (django.contrib.comments устаревший и с версии 1.6 вынесен в отдельный проект), но решение отточилось на третьей версии и мне жалко его терять просто :) Пример тут для версии 1.5.
Да, скоро это уже не совсем актуально станет (django.contrib.comments устаревший и с версии 1.6 вынесен в отдельный проект), но решение отточилось на третьей версии и мне жалко его терять просто :) Пример тут для версии 1.5.
Теги:
программирование,
django,
python
2 ноября 2013 г.
django: текущий пользователь в логах
Удобно в логах иметь текущего залогиненого юзера, чтобы видно было на ком падает, удобнее разбираться. Для логгера "django.request" передаётся экстра-параметр request. Его можно вытащить и залоггировать. Например, с помощью кастомного фильтра. Фильтр добавляет в вывод форматтера поле "user" с именем (строковое представление#id) юзера если оно есть. Фильтр вешается на нужные handlers. А в соответствующие formatters на этих handlers добавляется поле "user". В фильтре проверка на существование атрибутов request и request.user обязательна, если текущий formatter повешен на что-либо, кроме логгера "django.request", иначе будет падать в попытке найти поле с ид "user".
См. также про логгирование в файл django.
class RequestPushUserFilter(logging.Filter): def filter(self, record): if hasattr(record, 'request'): if hasattr(record.request, 'user'): record.user = u'%s#%s' % (record.request.user, record.request.user.pk) else: record.user = '?' else: record.user = '-' return True LOGGING = { ... 'filters': { ... 'request_pushuser_filter': { '()': 'project.settings.RequestPushUserFilter', }, }, ... 'formatters': { 'standard': { 'format': "[%(asctime)s] %(levelname)s [%(name)s:%(module)s:%(lineno)s] [%(user)s] %(message)s", 'datefmt': '%d.%m.%Y %H:%M:%S', }, }, ... 'handlers': { 'logfile': { ... 'filters': ['request_pushuser_filter'], ... 'formatter': 'standard', }, ...В итоге в логах будет примерно так:
[31.10.2013 00:43:20] ERROR [django.request:base:212] [Driver John#1] Internal Server Error: блабла
См. также про логгирование в файл django.
Теги:
программирование,
трюки,
django,
logging,
python
31 октября 2013 г.
django: csrf при ajax-запросах
Несколько раз встречал вопросы на счёт csrf при самодельных ajax-запросах (например, через jquery как в примере ниже), и несколько вариантов странных даже видел. Я делаю проще - в основную страницу (например, в шаблон base.html или как он там у вас назван) выношу стандартный csrf_token в качестве javascript-переменной и во всех последующих скриптах удобно его использую.
Секюрность не страдает, конечно, т.к. сам токен не представляет интереса. Потом при запросах, например, через jquery.ajax можно просто передавать как параметр с именем "csrfmiddlewaretoken" (аналогично тому как он передаётся через скрытый input в обычных формах)
Секюрность не страдает, конечно, т.к. сам токен не представляет интереса. Потом при запросах, например, через jquery.ajax можно просто передавать как параметр с именем "csrfmiddlewaretoken" (аналогично тому как он передаётся через скрытый input в обычных формах)
$.ajax({ type: 'POST', url: '...', data : { ..., 'csrfmiddlewaretoken' : csrf_token, }, ... });
21 октября 2013 г.
linux: русский в консоли archlinux
Возникла проблема со шрифтами в виртуальной консоли. Квадратики вместо кириллицы. В графическом эмуляторе терминала, разумеется, всё нормально, а в /dev/ttyX беда. Выяснилось, что всё портит systemd, сначала загружая шрифты и настраивая их согласно vconsole.conf как и положено, а потом подгружая drm-модуль видеокарты, который создаёт новый фреймбуфер (например, у меня
/dev/fb0
), в котором уже никаких настроек не делается.
7 октября 2013 г.
django: логгирование в файл и перехват warnings
В django иногда удобно вести файловые логи в приложении. По умолчанию настроен только один handler — mail_admins (отправка на мыло админам), что не всегда хорошо, а иногда невозможно (в принципиальном интранете, например). Вторая задача возникла — хорошо бы отлавливать туда же и все варнинги из warnings.warn, которые в том числе и django иногда вываливает.
Первая задача тривиальна и описана в документации. Создаём новый handler RotatingFileHandler и добавляем в нужные логгеры (очевидно, что речь про settings.py):
Первая задача тривиальна и описана в документации. Создаём новый handler RotatingFileHandler и добавляем в нужные логгеры (очевидно, что речь про settings.py):
LOGGING = { ... 'handlers': { ... 'logfile': { 'level': 'WARNING', 'class': 'logging.handlers.RotatingFileHandler', 'filename': rel('log', 'logfile.log'), 'maxBytes': 1000000, 'backupCount': 666, 'formatter': 'standard', }, ... }, 'loggers': { 'django.request': { 'handlers': ['mail_admins', 'logfile'], 'level': 'WARNING', 'propagate': True, }, ... }, }вторая задача тоже просто решается. Делается
import logging logging.captureWarnings(True)После этого все варнинги начинаются писаться также в логгер с именем "py.warnings", откуда их и логируем:
... 'loggers': { ... 'py.warnings': { 'handlers': ['console', 'logfile'], 'level': 'WARNING', 'propagate': True, }, ... },
6 октября 2013 г.
django: удобные относительные пути в settings
В settings-файле джанги удобно придумать какой-то порядок, потому что рутинных записей всяких копится целая куча. Например, для работы с путями (которые почти все относительны) использую небольшой трюк.
import os # путь корня этого проекта (там где manage лежит) PROJECT_DEPLOY_PATH = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) rel = lambda *path: os.path.join(PROJECT_DEPLOY_PATH, *path)Без лямбды функция rel может выглядеть как-то так:
def rel(*path): return os.path.join(PROJECT_DEPLOY_PATH, *path)И далее просто используется:
STATIC_ROOT = rel('static')Или для путей из нескольких подпапок (/static/files/)
STATIC_ROOT = rel('static','files')
21 сентября 2013 г.
django: нормальные bootstrap3 инпуты в autocomplete_light
В bootstrap 3 поля автозаполнения от django-модуля autocomplete_light выглядят непотребно из-за требования иметь красивым инпутам формы обязательный класс "form-control". Никаких возможностей кастомизации через autocomplete_light_registry.py и т.п. нету, т.к. класс намертво захардкожен (widget.html):
{% block input %} {# a text input, that is the 'autocomplete input' #} {% endblock %}Пришлось сделать патчик в js и всем полям с class="autocomplete" добавить ещё и класс "form-control" (используется jquery):
if ($(".autocomplete").length) { $(".autocomplete").addClass( "form-control" ); }Если используется другой вариант хардкода (типа насильное назначение вообще всем input, либо хардкодом в css), то неактуально. Я люблю чистые решения, но чище этого ничего не смог придумать.
Теги:
трюки,
django,
javascript,
python
16 сентября 2013 г.
swt jface button dropdown popup menu
Хочу я, чтобы при нажатии на кнопку вываливалось меню. Ну типа как Start-меню в винде. Всё очень просто - по нажатию кнопки создаём и разворачиваем в месте тыкания мышкой.
import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Menu; ... final Button mainMenuButton = new Button(composite, SWT.PUSH); mainMenuButton.setText("¿?"); mainMenuButton.setLayoutData( ... ); mainMenuButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent event) { Menu dropMenu = createMainMenu( getShell() ); Point point = mainMenuButton.toDisplay(new Point(event.x, event.y)); dropMenu.setLocation( point.x, point.y+mainMenuButton.getBounds().height ); dropMenu.setVisible(true); } });Далее на чистом SWT как-то так:
public static Menu createMainMenu( final Shell shell ) { Menu dropMenu = new Menu(shell, SWT.POP_UP); ... MenuItem item0 = new MenuItem(dropMenu, SWT.PUSH); item0.setText(...); item0.addListener(SWT.Selection, ...); .... return dropMenu; }На JFace как-то так:
public Menu createMainMenu( final Shell shell ) { MenuManager popManager = new MenuManager(); popManager.add(new ActionOne(shell)); ... popManager.add(new Separator()); ... Menu dropMenu = popManager.createContextMenu(shell); return dropMenu; }
29 августа 2013 г.
django upload_to windows 123 bad chars
Использовал свою реализацию upload_to-метода для формирования пути сохранения файлов в FileField-поле модели на основании заголовка сущности (чтобы файлики сохранялись в подпапки контрагентов). При разработке под linux всё было отлично, но в продакшене на одной windows-машине периодически валилась загрузка файлов
File "C:\python27\lib\site-packages\django\core\files\storage.py", line 168, in _save os.makedirs(directory) File "C:\python27\lib\os.py", line 150, in makedirs makedirs(head, mode) File "C:\python27\lib\os.py", line 157, in makedirs mkdir(name, mode) WindowsError: [Error 123] Синтаксическая ошибка в имени файла,: u'C:\\inetpub\\wwwroot\\ZooDjangoProject\\media\\file\\\blabla "blabla"'Выяснилось, что дело в кавычках в имени файлов, который есть запретный символ в винде. Пришлось вспомнить и остальные ограничения, после чего родилось прижившееся экспресс-решение.
def removebadchars(value): for c in r'\/:*?"<>|': value = value.replace(c, '') return value ... filename = removebadchars(filename)
27 августа 2013 г.
Версия приложения через makefile прямиком из git
Так как код я держу в той или иной VCS (последнее время чаще всего в git) я подумал, что было бы круто версию приложений брать из репозитория, а конкретно — из тегов. Для небольших утилит особенно удобно. Некоторые похожие решения из интернета натолкнули меня на такое решение. Всё довольно просто, пример для обычного gnu-makefile и исходника на Си.
Теги:
руководство,
трюки,
c,
git,
makefile
26 августа 2013 г.
django admin запрет редактирования модели
Запрещение в админке django редактирования какой-либо сущности. В моём случае это оповещения об оплатах платёжной системы. В ModelAdmin есть методы
has_add_permission
, has_change_permission
, has_delete_permission
с очевидным предназначением. Правда, если все они вернут в каком-то случае False, то модель вообще не отобразится в списке сущностей админки и по прямой ссылке тоже не будет работать. Так что все поля вместо has_change_permission надо сделать readonly.class SuccessNotificationAdmin(admin.ModelAdmin): ... readonly_fields = ('order', 'sum', ) def has_add_permission(self, request): return False #def has_change_permission(self, request, obj=None): # return False def has_delete_permission(self, request, obj=None): return False admin.site.register(SuccessNotification, SuccessNotificationAdmin)
Теги:
django
25 августа 2013 г.
django favicon.ico robots.txt
Есть некоторая проблема с такого рода файлами в django. Они предполагаются находящимися в корне, а статика как правило раздаётся из «подкаталога» (например, /static/). Да, в случае favicon.ico его можно указать в meta-тегах страницы, но в некоторых случаях, например, при отдаче 500 ошибки или в любом другом случае без учёта корневого шаблона с meta-тегами, некоторые браузеры всё равно запрашивают /favicon.ico из корня, напрягая логи WARN-ошибками 404. Потому удобно в urls.py перенаправлять редиректом на реальные расположения файлов.
url(r'^favicon\.ico$', RedirectView.as_view(url=settings.STATIC_URL + 'images/favicon.ico')), url(r'^robots\.txt$', RedirectView.as_view(url=settings.STATIC_URL + 'robots.txt')),В случае robots.txt можно отдавать как шаблон, если удобнее (можно и mime задать, начиная с 1.5, емнип)
url(r'^robots\.txt$', TemplateView.as_view(template_name='robots.txt', content_type='text/plain')),Для старых версий django это будут процедурные вьюшки redirect_to и direct_to_template соответственно, приводить код тут не буду за бессмысленностью.
Теги:
руководство,
django
12 июля 2013 г.
linux eclipse crash libsoup
Eclipse крашится вместе с JRE (причём любой, опен или сановской) на новом libsoup в момент попытки вывода всплывающего окошка почти в любом редакторе.
з.ы. баг на багтрекере: https://bugs.eclipse.org/bugs/show_bug.cgi?id=405786
C [libsoup-2.4.so.1+0x6d9b1] soup_session_feature_detach+0x11Пришлось порыться и перепробовать несколько вариантов, но нагуглил решение. Помогает добавление
-Dorg.eclipse.swt.browser.DefaultType=mozillaв eclipse.ini или параметры запуска.
з.ы. баг на багтрекере: https://bugs.eclipse.org/bugs/show_bug.cgi?id=405786
11 июля 2013 г.
svn-проект в git-проект (с googlecode на github)
Просто памятка. Переносил несколько проектов с разных svn-репозиториев (googlecode, например). Делается просто, да и материал в сети есть на этот счёт. Выкачиваем репозиторий и превращаем его в git-svn репозиторий:
Дальше можно отфильтровать дерево коммитов, исключив пустые коммиты, которые при конвертации образуются по разным причинам, т.к. не всем изменениям в svn-коммитах соответствуют изменения в git-коммитах (изменения в директориях, например).
git svn clone https://projectsvn.googlecode.com/svn/trunk --authors-file=/home/user/authors --no-metadataФайл authors нужен, чтобы сопоставить svn-юзеров с git-юзерами в новом репозитории, у меня он был такой:
user@gmail.com = darkbarker <user@gmail.com> (no author) = darkbarker <user@gmail.com>"(no author)" нужен для коммитов без автора (например, инициальный коммит svn-сервера).
Дальше можно отфильтровать дерево коммитов, исключив пустые коммиты, которые при конвертации образуются по разным причинам, т.к. не всем изменениям в svn-коммитах соответствуют изменения в git-коммитах (изменения в директориях, например).
git filter-branch --commit-filter 'git_commit_non_empty_tree "$@"' HEADЭто отпочкует ветку от низа дерева и перенесёт туда master, оставив старые коммиты в старой ветке git-svn. Ну а дальше как обычно, заливаем в новый существующий git-репозиторий (надо создать его, конечно).
git remote add origin git@github.com:user/projectgit.git git push origin masterДля bitbucket то же самое потребуется сделать:
git remote add origin https://user@bitbucket.org/user/projectgit.git git push origin master
Теги:
руководство,
git,
subversion,
svn
9 июля 2013 г.
midnight commander user menu hardlink
Очень бесит создание хардлинков в midnight commander -- запрашивает пустым окошком имя линка и кладёт его в текущую папку (актуальная версия 4.8.7). Так как я довольно часто использую хардлинки для общих файлов разных проектов, написал мелкий скриптик на user menu. Редактируется через "редактировать файл маню -> пользовательский".
+ t t H Hardlink to other panel set %t while [ -n "$1" ]; do ln "$1" "%D" shift doneТеперь выделяем файлы, нажимаем F2, "Hardlink to other panel" и линки с теми же именами оказываются на противоположной панели. Круто.
27 июня 2013 г.
django: что-то типа select_related для m2m
Заметка о проблемах со множественными выборками из БД при обходе списка сущностей в случае many-to-many связей.
Приходит в голову решение в лоб:
Проблема
У нас есть такая схема:# категория афиши class CategoryPlaybill(models.Model): ... # афиша class Playbill(models.Model): categories = models.ManyToManyField(CategoryPlaybill, ...) ... # проведение афиши class ConductingPlaybill(models.Model): playbill = models.ForeignKey(Playbill, ...) ...Итак, есть типы событий. Есть события по многие-ко-многим (событие может входить в несколько категорий). А есть их так называемые проведения — событие с конкретной датой, временем, местом. Мне надо получить проведения событий с разбивкой по категориям: категория1=>(список проведений), категория2 =>(список проведений).
Приходит в голову решение в лоб:
cond_playbill_list = ConductingPlaybill.objects.select_related().all() cond_playbill_map = {} for cond_playbill in cond_playbill_list: cats = cond_playbill.playbill.categories.all() for cat in cats: if cat not in cond_playbill_map: cond_playbill_map[cat] = [] cond_playbill_map[cat].append(cond_playbill)Но это решение ожидаемо даёт кучу запросов в БД на предмет
SELECT FROM categoryplaybill WHERE playbill_categories.playbill_id = N
для каждой итерации внешнего цикла. Дело в том, что select_related не тянет playbill_categories и ниже.
Теги:
проблемы,
программирование,
трюки,
django,
python
django: шаблонизатор не отображает defaultdict
Проблема
Шаблонизатор Django некорректно отображает словарь, созданный через defaultdict. На этот счёт есть тикет #16335.21 июня 2013 г.
django: QuerySet расширяемый Manager
Приспособился использовать небольшой QuerySetManager для удобства добавления налету методов в QuerySet модели. Т.е. просто прописыванием в классе модели соответствующий QuerySet расширяется, довольно удобно, если надо писать сложные менеджеры для модели. Сам класс выглядит так:
Используется как-то так:
from django.db import models class QuerySetManager(models.Manager): def get_query_set(self): return self.model.QuerySet(self.model) def __getattr__(self, attr, *args): return getattr(self.get_query_set(), attr, *args)Внутрь класса где-то рекомендовалось добавить полем "
use_for_related_fields=True
", но работает без него. Зато не работает без __getattr__, пишет: 'RelatedManager' object has no attribute 'useractive', вопреки ещё какой-то рекомендации из инета. В итоге имеем некий хак.Используется как-то так:
class Clazz(models.Model): ... objects = QuerySetManager() ... class QuerySet(models.query.QuerySet): def useractive(self,date): return self.filter( Q(active=True) & (Q(date__isnull=True)|Q(date__gte=date)) )И потом во вьюшках:
Clazz.objects.useractive(now).order_by('date')
30 мая 2013 г.
django: отправка email шаблонами из FlatPage
Приспичило сделать в django отправку email с удобной настройкой формата(-ов) письма. Понятно, что при этом должны работать какие-то переменные в шаблоне, куда удобно подставится что-то заранее предусмотренное. Ну и придумал для этого использовать FlatPage, которые идут «искаропки» и уже нормально редактируются в админке (я прикрутил туда ещё и TinyMCE). Плюс надо бы письма слать в формате html, для пущей красоты.
Теги:
программирование,
руководство,
django,
python
20 мая 2013 г.
django: минимальное количество formset inline
Захотелось мне в django, чтобы formset содержал не менее некоторого количества полностью заполненных форм. С нормальной валидацией где нужно, а не в середине вьюшки, например. На момент 1.4.x ничего особо удобного на этот счёт не было (возможно, это странно), хотя какие-то реквесты в багзилле я находил. На самом деле довольно просто ручками всё решается наследованием и переопределением InlineFormSet при конструировании FormSet через factory (а я так использую всегда).
Допустим, мы конструируем FormSet таким образом (обратите внимание на formset=...):
Допустим, мы конструируем FormSet таким образом (обратите внимание на formset=...):
ConductingClazzRequestMorePersonFormSet = inlineformset_factory(ConductingClazzRequest, ConductingClazzRequestPerson, extra=1, formset=ConductingClazzRequestPersonFormsetBase, form=ConductingClazzRequestPersonForm)
Теги:
руководство,
django,
python
17 апреля 2013 г.
Баг в camera slideshow при одном слайде
Использовал довольно приятную крутилку баннеров (слайдшоу) camera slideshow (http://www.pixedelic.com/plugins/camera/). Но один косяк очень сильно напрягал всё время — при единственном слайде работает некорректно: показ этого слайда перемежается показом пустого слайда.
Теги:
javascript,
jquery,
web
14 апреля 2013 г.
django: не разрешать формам в formset быть пустыми
В django при использовании formset по умолчанию* действует такая штука: при сохранении всего formset незаполненные формы (полностью незаполненные) в нём игнорируются и валидируются/сохраняются только остальные. Если по какой-то причине это неудобно (у меня, например, нужно было заполнять минимум N форм в формсете), то можно отключить, установив empty_permitted = False.
* На самом деле ставится некоторым (extra) формам при конструировании formset, что в общем-то логично.
В документации на этот счёт сказано: «The formset is smart enough to ignore extra forms that were not changed».
Можно намертво прописать в конструкторе формы, например.
* На самом деле ставится некоторым (extra) формам при конструировании formset, что в общем-то логично.
В документации на этот счёт сказано: «The formset is smart enough to ignore extra forms that were not changed».
Можно намертво прописать в конструкторе формы, например.
class BlablaForm(forms.ModelForm): class Meta: model = Blabla def __init__(self, *arg, **kwarg): super(BlablaForm, self).__init__(*arg, **kwarg) self.empty_permitted = False
7 апреля 2013 г.
Самодельный упрощённый accordion на jquery
Нужна была схлопывалка дивов, с заголовками, раскрывающая содержимое по очереди. Изначально был код на jquery-ui через accordion, но мучение со стилями достало. Т.к. стили наворачиваются хардкорно, а переопределять все в css получается в несколько раз длиннее, чем сам код.
Потом переделал тупо на jquery, получилось в несколько раз короче.
$( "#accordion" ).accordion({ active: false, collapsible: true, header: ".accordion_later", heightStyle: "content", beforeActivate: function( event, ui ){ ui.newHeader.hide(); ui.oldHeader.show(); } });Рабочий пример: http://jsfiddle.net/Fx4nA/
Потом переделал тупо на jquery, получилось в несколько раз короче.
$('#accordion .collapsible').hide(); $('#accordion .accordion_later').bind('click', function (e) { // show all "more..." $('#accordion .accordion_later').show(); // close all collapsible $('#accordion .collapsible').slideUp(); // hide current "more..." $(e.target).hide(); // open current collapsible $(e.target).next('div').slideDown(); });Рабочий пример: http://jsfiddle.net/8qtXH/
Теги:
javascript,
jquery,
jqueryui,
web
5 апреля 2013 г.
jquery ui dialog без заголовка
Убрать заголовок можно переопределив класс ui-dialog-titlebar, но чтобы не испортить все диалоги сразу можно выборочно через dialogClass.
Потом скрываем и всё.
dialogdiv.dialog({ ... dialogClass: 'dialog-wo-title', ...
Потом скрываем и всё.
.dialog-wo-title .ui-dialog-titlebar { display:none; }Или убираем всё кроме кнопки закрытия (которая остаётся справа)
.dialog-wo-title .ui-dialog-titlebar { width: 0px; float: right; }
Теги:
css,
javascript,
jquery,
jqueryui,
web
22 марта 2013 г.
mysql 5.6 grant ERROR 2013 (HY000) Lost connection to MySQL server during query
После перехода с 5.5 на 5.6 при попытках некоторых операций, например, GRANT вываливает:
ERROR 2013 (HY000): Lost connection to MySQL server during queryВсё потому что нужно не забывать про:
# mysql_upgrade
Теги:
mysql
Подписаться на:
Сообщения (Atom)