Показаны сообщения с ярлыком программирование. Показать все сообщения
Показаны сообщения с ярлыком программирование. Показать все сообщения

21 ноября 2018 г.

Django не давать логиниться одновременно с двух разных мест

Другими словами, не разрешаются две параллельные сессии. При новом входе старый разлогинивается.
Может понадобиться для какого-нибудь портала в интранете или чего-то такого секюрного.
Решается всё довольно банально, есть специальный сигнал user_logged_in.
Ищем сессию для этого юзера, если нашли - сбрасываем в базе, сбрасываем в текущем engine (если это применимо).
Если что, у меня настроен один из стандартных:
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'
Код обработчика сигнала. Всё заполонил комментариями, думаю, там всё понятно.
from django.contrib.auth.signals import user_logged_in
from django.contrib import messages
from django.contrib.sessions.models import Session
from django.utils import timezone

def user_logged_in_handler(sender, request, user, **kwargs):
    # текущая сессия: request.session.session_key
    for session in Session.objects.filter(expire_date__gte=timezone.now()).exclude(session_key=request.session.session_key):
        if session.get_decoded().get('_auth_user_id', None) == str(user.id):
            # нашли какую-то сессию, делаем её expired
            session.expire_date = timezone.now()
            session.save()
            # далее нам надо очистить все данные сессии
            # сначала я пробовал так
            # SessionStore = session.get_session_store_class()
            # sesstor = SessionStore(session.session_key)
            # sesstor.flush()
            # где flush - это метод именно для моего бекенда cached_db
            # я ожидал, что для модели вернётся текущий настроенный в settings.SESSION_ENGINE бекенд, но в модели Session захардкожено (как ни странно?) db.SessionStore
            # в принципе можно напрямую тупо импортировать cached_db.SessionStore, но сделал проще и универсальнее:
            # request.session у нас уже содержит установленный мидлварей нужный SessionStore
            # с помощью "чужой" нашей новой SessionStore удаляем левый наш старый session_key, там удачно есть такой даже метод
            request.session.delete(session.session_key)
            # раз у нас доступен request, то при желании показываем юзеру сообщение
            messages.add_message(request, messages.INFO, 'Была закрыта дублирующаяся сессия на каком-то другом компьютере')

user_logged_in.connect(user_logged_in_handler)

5 марта 2014 г.

django: самодельный if-else тег

Здесь пример самодельного тега шаблонизатора django, выполняющего роль if-else-endif. Мой код подразумевает переменную 'current_statuskey' в контексте (кладётся напрямую или с помощью middleware). Писал для очень сложной страницы с кучей частей, имеющих разный вид в зависимости от статуса. Вообще там ещё у меня дополнительная логика, но это минимальный пример здесь. Использование в шаблоне:
{% ifstatus one,five,blabla %}
...
{% else %}
...
{% endifstatus %}
Можно было описать в десяток строк, но здесь больше и код очень сильно похож на стандартные теги ifequals и сопутствующие, откуда я его и адаптировал, чтобы получить более правильную и канонiчную реализацию.

13 ноября 2013 г.

django: contrib.comments работаем через ajax

Тут рассказ о том, как заставить стандартный django.contrib.comments работать через ajax. Цель была такова: минимальные изменения в клиентских view, чтобы работали стандартные templates перегружаемые, полноценная админка (обычная от приложения comments), да и вообще максимальное использование оригинального кода. Короче, в итоге после нескольких самопроизвольных переписываний (вместе с использованием подобного решения в проектах) получилась минимальная обёртка, которую по идее тоже можно вынести в отдельный app.

Да, скоро это уже не совсем актуально станет (django.contrib.comments устаревший и с версии 1.6 вынесен в отдельный проект), но решение отточилось на третьей версии и мне жалко его терять просто :) Пример тут для версии 1.5.

2 ноября 2013 г.

django: текущий пользователь в логах

Удобно в логах иметь текущего залогиненого юзера, чтобы видно было на ком падает, удобнее разбираться. Для логгера "django.request" передаётся экстра-параметр request. Его можно вытащить и залоггировать. Например, с помощью кастомного фильтра. Фильтр добавляет в вывод форматтера поле "user" с именем (строковое представление#id) юзера если оно есть. Фильтр вешается на нужные handlers. А в соответствующие formatters на этих handlers добавляется поле "user". В фильтре проверка на существование атрибутов request и request.user обязательна, если текущий formatter повешен на что-либо, кроме логгера "django.request", иначе будет падать в попытке найти поле с ид "user".

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.

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 и ниже.

30 мая 2013 г.

django: отправка email шаблонами из FlatPage

Приспичило сделать в django отправку email с удобной настройкой формата(-ов) письма. Понятно, что при этом должны работать какие-то переменные в шаблоне, куда удобно подставится что-то заранее предусмотренное. Ну и придумал для этого использовать FlatPage, которые идут «искаропки» и уже нормально редактируются в админке (я прикрутил туда ещё и TinyMCE). Плюс надо бы письма слать в формате html, для пущей красоты.

17 ноября 2012 г.

django: пролучить комментарии для объекта

Комментарии в django.contrib.comments, как известно, привязаны к моделям приложений через джанговский contenttypes. В документации рассказано как отрендерить и получить список в шаблонах, но механизм работы более подробно не рассмотрен для того, чтобы работать с комментариями в своих вьюшках. Ничего тут сложного нету, можно получить ContentType и вытащить все комментарии, но есть более удобный метод:
from django.contrib.comments.models import Comment

project_comments = Comment.objects.for_model(Project).filter(object_pk=project.id)
Его тоже можно разглядеть и изучить в исходниках django. Короче, с помощью for_model у CommentManager можно покороче и поудобнее сделать это всё. Содержимое метода вполне очевидно:
def for_model(self, model):
    """
    QuerySet for all comments for a particular model (either an instance or a class).
    """
    ct = ContentType.objects.get_for_model(model)
    qs = self.get_query_set().filter(content_type=ct)
    if isinstance(model, models.Model):
        qs = qs.filter(object_pk=force_unicode(model._get_pk_val()))
    return qs
Также можно обратить внимание, что если есть возможность, то можно передать не модельку, а конкретный экземпляр, тогда фильтровать по object_pk не придётся.

21 сентября 2011 г.

Java regexp: при использовании appendReplacement или replaceAll внезапно происходит Illegal group reference

Это странное поведение некоторых методов regexp с некоторыми случайными параметрами одно время убило мне пару часов отладки. Сам виноват, ибо проглядел в доках указание на специальное значение символов '$' и '\'. Не подумал, что из-за них может выкидываться java.lang.IllegalArgumentException, про который нет ничего в явадоках этих методов.

21 июня 2010 г.

Apache FOP: Встраивание в Java

Как встраивать FOP в приложения Java
Мой перевод статьи с "официального" сайта Apache FOP: Embedding.

4 декабря 2009 г.

Java: краткая инициализация List

А знаете ли вы, что методы с произвольным числом параметров (в отличие от c++ удобно реализованные) и класс Arrays даёт нам в Java возможность быстрой инициализации коллекций почти так же удобно, как в каком-нибудь php:
List<string> list = Arrays.asList("1", "2", "3");
А autoboxing (автоматическое преобразование примитивных типов во врапперы и обратно) позволяет то же самое сделать и с примитивами!
List<integer> list = Arrays.asList(1, 2, 3);
Чудеса, да и только. В java7 (по слухам) добавят специальные свистелки-перделки для этого. Помимо всякого другого бесполезного годобра.

14 января 2009 г.

linux: подсчитать количество строк кода в проекте

Например, чтобы подсчитать количество всех строк в файлах *.java внутри какого-либо каталога (рекурсивно, разумеется) надо сделать что-то типа такого:
$ find ~/workspace/project1/src -type f -name *.java -exec cat {} \; | wc -l
426610

Системные property в j2me, System.getProperty()

What are the defined J2ME system property names?
http://developers.sun.com/techtopics/mobility/midp/questions/properties/index.html

29 декабря 2008 г.

создать маленький windows exe

Пример под микрософтовский компилятор

nano.cpp:
#include <windows.h>

#pragma comment(linker,"/SUBSYSTEM:WINDOWS /ENTRY:winMain /MERGE:.rdata=.text")
#pragma comment(lib, "kernel32.lib")
#pragma comment(lib, "user32.lib")

void WINAPI winMain()
{
MessageBox(0,"Hello, World!",":)",MB_ICONINFORMATION);
}

Компилировать командной строкой, например, такой:
cl.exe /O1 nano.cpp