20.12.09

[django] Как поиметь юзера везде

Часто текущего юзера хочется иметь там, где его по умолчанию нету. Например, в формах. Для этого нужно засунуть этого юзера в тред-локалс, а потом оттуда доставать в нужном месте.

Я уже давно стырил откуда-то такой вот код для этого самого всовывания/высовывания:

# -*- coding: utf-8 -*-
try:
    from threading import local
except ImportError:
    from django.utils._threading_local import local

_thread_locals = local()

def set_current_user(user):
    setattr(_thread_locals, 'user', user)

def get_current_user():
    return getattr(_thread_locals, 'user', None)


Положим этот код, например в common.user, тогда прослойка для засовывания будет такой:

from common.user import set_current_user

class CurrentUserMiddleware(object):
    def process_request(self, request):
        set_current_user(getattr(request, 'user', None))

Вот и всё. Теперь в любом месте имеем текущего юзера:

from common.user import get_current_user

user = get_current_user()
...

8 коммент.:

Александр Кошелев комментирует...

В посте не хватает упоминания, что так делать не стоит и что нужно всегда объект текущего пользователя передавать явно.

Imbolc комментирует...

Потому, что я так не считаю.

Да, это нарушает некоторые стереотипы проектирования. Но все эти стереотипы создавались для удобства в конечном итоге. И все они несовершенны в плане этого самого удобства. Но это несовершенство не так бросается в глаза, если позволять себе разумные отступления от правил.

В данном случае отступление мне кажется разумным. В тех проектах, где юзер часто передаётся в формы и модели, такой подход делает код короче и понятнее.

xcat комментирует...

Никогда не задумывались о том, что обработка запросов от пользователей может идти в одном потоке?
Для примера посмотрите в асинхронные фреймворки twisted или tornado.

Imbolc комментирует...

Да, всякое может быть, конечно. Однако, посте речь о Джанго. Она синхронна. И запусти её под Торнадо, например, код поста работать будет.

xcat комментирует...

Действительно сейчас она синхронна. Но думаю это вопрос времени. И тогда Ваш подход не то что сломается, а станет местом жутких практически невылавливаемых граблей.
А самое главное, что Вы найдете причину подобного поведения. А вот ваши последователи нет :(

neithere комментирует...

Источник (django wiki)

Imbolc комментирует...

> это вопрос времени
Вероятность того, что mysql и sqlite станут асинхронными в обозримом будущем - ничтожна. Вероятность того, что Джанга от них откажется такая же.

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


> вот ваши последователи нет

Я не претендую на роль всеобщего пастыря. Если меня кто-то и беспокоит, то это люди похожие на меня. И не потому, что я плохой такой. Просто, я не могу понять непохожего на меня человека. А следовательно, не могу и помочь ему.

Так вот, люди похожие на меня, разбираются в том, что делают. Именно поэтому, код не лежит где-то в виде модуля, а составляет основу поста.

xcat комментирует...

Не совсем так: для того чтобы сделать ORM асинхронный не нужно отказываться от поддержки mysql -- можно просто написать асинхронную DB библиотеку(хоть на тех же тредах).

Что касается, что плохого хранить пользователя( или любую другую инфу) в контексте треда -- тоже самое что и в глобальной переменной. Ваш код становится нереентерабельным. Результат будет зависим от внешних переменных.

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