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. Для кеша есть два варианта:

  1. SESSION_ENGINE="django.contrib.sessions.backends.cache" - простой производительный енджин который напрямую хранит данные в кеше. Потенциальный недостаток в том что данные в сессий будут сбравсываться при рестарте кеш-сервера либо при его переполнении.
  2. 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 недели.