Djnago предоставляет возможность хранить и получать данные для каждого пользователя сайта c помощтю сессий. Сессия - это словарь с парами ключ/значение который ассоциирован с каждым сеансом работы с сайтом, обычно от первого визита сайта в браузере и до тех пор пока не истечет срок годности куки в браузере либо до тех пор пока юзер вручную их не очистит. Куки - это данные, которые сервер отправляет на хранение в браузер а потом получает назад с каждым запросом от браузера. В них хранится только id сессии (маленькая строка), а сами данные сессии хранятся на сервере. Это детальнее и будет рассмотрено в статье.
Чтобы включить сессии нужно что бы было прописано django.contrib.sessions.middleware.SessionMiddleware в MIDDLEWARE (по дефолту вкл.), а также django.contrib.sessions в INSTALLED_APPS (для сессий в бд). Добавление последнего в INSTALLED_APPS (если оно еще не добавлено) при миграции добавит таблицу в бд:
CREATE TABLE "django_session" ("session_key" varchar(40) NOT NULL PRIMARY KEY, "session_data" text NOT NULL, "expire_date" datetime NOT NULL); CREATE INDEX "django_session_de54fa62" ON "django_session" ("expire_date")
По дефоту джанго хранит сессии в бд используя модель django.contrib.sessions.models.Session, хотя можно настроить на кеш/фс изменяя настройку SESSION_ENGINE. Для кеша есть два варианта:
- SESSION_ENGINE="django.contrib.sessions.backends.cache" - простой производительный енджин который напрямую хранит данные в кеше. Потенциальный недостаток в том что данные в сессий будут сбравсываться при рестарте кеш-сервера либо при его переполнении.
- SESSION_ENGINE="django.contrib.sessions.backends.cached_db" - надежный кеш который каждый раз при записи пишет еще и в БД а чтение из БД происходит лишь тогда когда кеш пуст.
Как это работает
В первый раз когда нужно что-то сохранить в сессию (вы просто пишете request.session['somekey']="someval") джанго смотрит есть ли в реквест куки sessionid. Если нету то он генерирует его и выдает в респонсе то есть устанавливает куки в браузер.
В это же время джанго создает в БД запись со словарем ключ значение, который вы сохраняли (если подробнее то перед сохранением в БД этот словарь сериализируется в текстовый JSON и шифруется ключем SECRET_KEY, который указан в settings)
После этого на протяжении всего експаери (по дефолту как видим там 14 дней) браузер будет выдавать серверу в реквесте:
Исходя из этого когда вы захотите использовать сохраненные данные, вы лезете в словарь request.session, и джанго сразу достает запись из БД по ключу присланному из куки, расшифровывает и десериализирует словарь.
Пример хранения значения из инпута в сессии
@csrf_exempt def mainview(request): logger = logging.getLogger(__name__) if request.method == "POST" and \ 'val' in request.POST: request.session['val'] = request.POST['val'].encode() t = Template( "Store in session: " "<form method='POST' url='/'>" " <input name='val' value='{{ val }}'></input>" " <button>Store</button>" "</form>") args = {"val": request.session.get("val", "")} resp = t.render(Context(args)) return HttpResponse(resp)
Как сделать что бы юзер/аноним не комментил дважжы (пример из док-ии):
def post_comment(request, new_comment): if request.session.get('has_commented', False): return HttpResponse("You've already commented.") c = comments.Comment(comment=new_comment) c.save() request.session['has_commented'] = True return HttpResponse('Thanks for your comment!')
Простой логин/логаут используя сессии:
def login(request): m = Member.objects.get(username=request.POST['username']) if m.password == request.POST['password']: request.session['member_id'] = m.id return HttpResponse("You're logged in.") else: return HttpResponse("Your username and password didn't match.") def logout(request): try: del request.session['member_id'] except KeyError: pass return HttpResponse("You're logged out.")
Установка тестового куки что бы узнать поддерживается ли куки браузером:
from django.http import HttpResponse from django.shortcuts import render def login(request): if request.method == 'POST': if request.session.test_cookie_worked(): request.session.delete_test_cookie() return HttpResponse("You're logged in.") else: return HttpResponse("Please enable cookies and try again.") request.session.set_test_cookie() return render(request, 'foo/login_form.html')
Сохранение
Как вы поняли сессии автоматически сохраняются при изменении дикта request.session, НО! если в дикте есть mutable объекты (словарь, лист, сет), то при внутреннем их изменении внешний словарь не меняется! (то есть айди мутебл объекта не меняется). По-этому чтобы пересохранить сессию нужно вручную сделать:
request.session.modified = True
Время жизни
В настройках можно задать такую опцию как SESSION_EXPIRE_AT_BROWSER_CLOSE , по дефолту она False. Эта настройка приведет к тому что при установки куки sessionid в браузер, експаери будет указано в значение "Session" что принуждает браузер к очистке куки после выхода с сайта.
Но стоит помнить что браузеры (например хром) могут игнорировать очистку куки с Session експаери при помощи спец настройки (On Startup -> Continue where you left off.).
Если же вы не используете сброс при закрытии браузера, контролировать время жизни можно при помощи настройки SESSION_COOKIE_AGE, в секундах, по дефолту 2 недели.