Понятие "Сессий" основано на том, что состояние пользователя каким-то образом сохраняется, когда он переходит с одной страницы на другую. Вспомните, что HTTP не сохраняет состояний, поэтому только браузер или ваше приложение может "запомнить" то, что нужно запомнить.
Куки - это пары данных по типу "ключ-значение", которые сохраняются в браузере пользователя до истечения какого-то определенного срока. Они применимы практически для любой задачи, но чаще всего их используют, чтобы сохранить пользователя в том же месте веб-страницы, если он потеряет интернет-соединение, или чтобы хранить простые настройки отображения сайта. Вы можете также хранить в них данные корзины пользователя или даже пароли, но это не очень хорошая идея, не стоит хранить в обычных куках браузера информацию, которая должна быть защищенной или сохраняться между сессиями браузера. Пользователь может легко потерять данные, очистив кэш, или украсть/использовать незащищенные данные из куков.
Куки добавляются в request/response для хранения абсолютно разных данных. Например, стандартная джанго авторизация добавляет куку с данными о пользователя, что бы можно было определить кто именно делает запрос. Поэтому там и нужны csrf токены в формах или просто токены в рест запросах, так как перехватить значение куки при запросе очень просто, а мы должны быть уверенны, что запрос пришел именно от авторизированного пользователя (Куки хранит информацию, кто это, а токены позозволяют проверить, что это был именно этот пользователь.).
Задумайтесь о том, каким образом браузеры следят, что пользователь залогинен, когда страница перезагружается. HTTP запросы не имеют состояний, так как же вы определите, что запрос пришел именно от залогинненого пользователя? Вот почему важны куки -- они позволяют вам отслеживать пользователя от запроса к запросу, пока не истечет их срок действия.
Особый случай - это когда вы хотите отслеживать данные пользовательской "сессии", которая включает все, что пользователь делает, пока вы хотите "запоминать" это, обычно до тех пор, пока пользователь не закроет окно браузера. В этом случае каждая страница, которую пользователь посетил до закрытия браузера будет частью одной сессии.
Если упростить, сессия это набор запросов от одного и того же пользователя (Или от разных в рамках одного процесса).
В случае с джанго, при стандартных настройках, сессия хранит набор куки, которые храняться в формате JSON. (А значит, что данные можно сериализовать)
В джанго сессия всегда храниться в реквесте, request.session в виде словаря.
Рассмотрим несколько примеров.
>>> request.session[0] = 'bar'
>>> # subsequent requests following serialization & deserialization
>>> # of session data
>>> request.session[0] # KeyError
>>> request.session['0']
'bar'Данные хранятся в формате JSON, а значит что ключи будут преобразованы в строки.
Допустим вам нужно "запомнить" коментировал ли этот пользователь только что статью, что бы не позволить написать большое кол-во коментарие подряд. Конечно можно сохранить эти данные в базе, но зачем? Проще воспользоваться сессией :
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!')Сохраним это состояние в сессии и будем перепроверять именно его.
Допустим вам нужно хранить сколько времени назад пользователь последний раз совершал действие после логина.
request.session['last_action'] = timezone.now()Теперь мы можем проверить когда было выполнено последниее действие и добавить любую нужную нам логику (например в мидлвар).
Если нам нужно воспользоваться сессией вне мест где есть доступ к реквесту:
>>> from django.contrib.sessions.backends.db import SessionStore
>>> s = SessionStore()
>>> # stored as seconds since epoch since datetimes are not serializable in JSON.
>>> s['last_login'] = 1376587691
>>> s.create()
>>> s.session_key
'2b1189a188b44ad18c35e113ac6ceead'
>>> s = SessionStore(session_key='2b1189a188b44ad18c35e113ac6ceead')
>>> s['last_login']
1376587691Мы можем получить сессию по ключу (Любая созданная джанго сессия автоматически хранит переменную session_key) по которой получить нужные нам данные.
Если мы не знаем нужный ключ в нашей сессии, мы можем получить сессию в виде словаря при помощи метода .get_decoded()
>>> s.session_data
'KGRwMQpTJ19hdXRoX3VzZXJfaWQnCnAyCkkxCnMuMTExY2ZjODI2Yj...'
>>> s.get_decoded()
{'user_id': 42}Сохранение данных в сессии происходит только тогда когда меняется значение request.session :
# Session is modified.
request.session['foo'] = 'bar'
# Session is modified.
del request.session['foo']
# Session is modified.
request.session['foo'] = {}
# Gotcha: Session is NOT modified, because this alters
# request.session['foo'] instead of request.session.
request.session['foo']['bar'] = 'baz'В последнем случае данные не будут сохранены, т.к. модифицируется не request.session, а request.session['foo']
Это поведение можно изменить, если добавить настройку в settings.py SESSION_SAVE_EVERY_REQUEST = True тогда запись в сессию будет происходить каждый запрос, а не только в момент изменения.
Для очистки данных сессии можно воспользоваться мендж командой python manage.py clearsessions
Некоторые настройки можно поменять и перенастроить, как и полностью кастомизировать любые действия с сессиями. Подробнее об этом Тут
Офииальная документация Тут
Что такое кеш?
Кеш - промежуточный буфер с быстрым доступом к нему, содержащий информацию, которая может быть запрошена с наибольшей вероятностью. Доступ к данным в кэше осуществляется быстрее, чем выборка исходных данных из более медленной памяти или удалённого источника, однако её объём существенно ограничен по сравнению с хранилищем исходных данных.
Если упростить, то это хранилище для часто запрашиваемых данных.
Предположим мы разрабатываем новостной сайт, и знаем, что новости у нас обновляются раз в час. Как мы помним некоторые методы, например GET являются идемпотентными, а это значит, что на одинаковые запросы должны отвечать одинаково (пока данные не изменятся), так вот в течении часа, пока новости не обновятся, абсолютно кажный пользователь заходящий на сайт, будет видеть один и тот же набор статей, а значит нам не обязательно каждый раз доставать этот набор из базы данных, мы можем закешировать его!
Для использования кеша, мы можем воспользоваться огромным кол-вом заранее заготовленных решений для кеша.
Как часто бывает в джанго кеш настраивается через settings.py
Хранение данных с использованием Memcached (Стандартный кеш для джанго), на отдельном для этого порту 11211:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
}
}Хранение в файле сокета (временный файл хранилище в юникс системах):
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': 'unix:/tmp/memcached.sock',
}
}Хранение на несольких серверах, для уменьшения нагрузки:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11211',
]
}Можно хранить кеш прям в базе данных, для этого нужно указать таблицу в которую складывать кеш:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'my_cache_table',
}
}Для использования кеша через базу, таблицу нужно предварительно создать, сделать это можно при помощи менедж команды:
python manage.py createcachetableМожно хранить кеш в обычном файле:
Линукс\мак:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',
}
}Виндовс:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': 'c:/foo/bar',
}
}Можно хранить в оперативной памяти сервера:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
}
}Есть упрощенная схема кеширования для разработки:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
}
}Как и всёё остальное, кеш можно кастомизировать написав собственные классы для управления кешем:
CACHES = {
'default': {
'BACKEND': 'path.to.backend',
}
}Любой тип кеширования подерживает большое кол-во доп настроек, подробно о которых в документации.
###Как же этим пользоваться?
Существует два основных способа использовать кеш.
Кешировать весь сайт, или кешировать конкретную вью.
Для того что бы кешировать весь сайт, нужно добавить две мидлвары, до и после CommonMiddleware:
MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware',
]Время кеширование или ограничения на кеш выставляются через переменные settings.py, подробно в документации.
Для того, что бы кешировать, отдельный метод или класс используется декоратор cache_page
from django.views.decorators.cache import cache_page
@cache_page(60 * 15)
def my_view(request):
...В скобках указывается время которое кеш должен хранится, обычно записывается в виде умножения на секунды\минуты, для простоты чтения (15*60 это 15 минут, никакой разницы от того что бы записать 900, но так проще воспринимать на вид).
Чаще всего декоратор используется в урлах:
from django.views.decorators.cache import cache_page
urlpatterns = [
path('foo/<int:code>/', cache_page(60 * 15)(my_view)),
]Для кеширования class base view кешуреется весь класс:
from django.views.decorators.cache import cache_page
url(r'^my_url/?$', cache_page(60*60)(MyView.as_view())),Так же можно закешировать часть темплейта при помощи темплейт тега cache:
{% load cache %}
{% cache 500 sidebar %}
.. sidebar ..
{% endcache %}В кеш можно записать любые кастомные данные, если это необходимо (Данные будут доступны для всех сессий).
from django.core.cache import cache
cache.set('my_key', 'hello, world!', 30)
cache.get('my_key')
'hello, world!'
# Wait 30 seconds for 'my_key' to expire...
cache.get('my_key')
None
cache.set('add_key', 'Initial value')
cache.add('add_key', 'New value')
# .add() сработает только если в указанном ключе ничего не было
cache.get('add_key')
'Initial value'
cache.get_or_set('my_new_key', 'my new value', 100)
'my new value'
import datetime
cache.get_or_set('some-timestamp-key', datetime.datetime.now)
datetime.datetime(2014, 12, 11, 0, 15, 49, 457920)
cache.set('a', 1)
cache.set('b', 2)
cache.set('c', 3)
cache.get_many(['a', 'b', 'c'])
{'a': 1, 'b': 2, 'c': 3}
cache.set_many({'a': 1, 'b': 2, 'c': 3})
cache.get_many(['a', 'b', 'c'])
{'a': 1, 'b': 2, 'c': 3}
cache.delete('a')
cache.delete_many(['a', 'b', 'c'])
cache.clear()
cache.touch('a', 10) # Обновить время храненияИ многие другие тонкости и ньюансы, например декоратор from django.views.decorators.cache import never_cache который можно использовать, что бы не кешировать данные, если вы уже кешируете весь сайт. И многое другое, подробности в документации.
