db.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. import logging
  2. from django.contrib.sessions.backends.base import CreateError, SessionBase, UpdateError
  3. from django.core.exceptions import SuspiciousOperation
  4. from django.db import DatabaseError, IntegrityError, router, transaction
  5. from django.utils import timezone
  6. from django.utils.functional import cached_property
  7. class SessionStore(SessionBase):
  8. """
  9. Implement database session store.
  10. """
  11. def __init__(self, session_key=None):
  12. super().__init__(session_key)
  13. @classmethod
  14. def get_model_class(cls):
  15. # Avoids a circular import and allows importing SessionStore when
  16. # django.contrib.sessions is not in INSTALLED_APPS.
  17. from django.contrib.sessions.models import Session
  18. return Session
  19. @cached_property
  20. def model(self):
  21. return self.get_model_class()
  22. def _get_session_from_db(self):
  23. try:
  24. return self.model.objects.get(
  25. session_key=self.session_key, expire_date__gt=timezone.now()
  26. )
  27. except (self.model.DoesNotExist, SuspiciousOperation) as e:
  28. if isinstance(e, SuspiciousOperation):
  29. logger = logging.getLogger("django.security.%s" % e.__class__.__name__)
  30. logger.warning(str(e))
  31. self._session_key = None
  32. def load(self):
  33. s = self._get_session_from_db()
  34. return self.decode(s.session_data) if s else {}
  35. def exists(self, session_key):
  36. return self.model.objects.filter(session_key=session_key).exists()
  37. def create(self):
  38. while True:
  39. self._session_key = self._get_new_session_key()
  40. try:
  41. # Save immediately to ensure we have a unique entry in the
  42. # database.
  43. self.save(must_create=True)
  44. except CreateError:
  45. # Key wasn't unique. Try again.
  46. continue
  47. self.modified = True
  48. return
  49. def create_model_instance(self, data):
  50. """
  51. Return a new instance of the session model object, which represents the
  52. current session state. Intended to be used for saving the session data
  53. to the database.
  54. """
  55. return self.model(
  56. session_key=self._get_or_create_session_key(),
  57. session_data=self.encode(data),
  58. expire_date=self.get_expiry_date(),
  59. )
  60. def save(self, must_create=False):
  61. """
  62. Save the current session data to the database. If 'must_create' is
  63. True, raise a database error if the saving operation doesn't create a
  64. new entry (as opposed to possibly updating an existing entry).
  65. """
  66. if self.session_key is None:
  67. return self.create()
  68. data = self._get_session(no_load=must_create)
  69. obj = self.create_model_instance(data)
  70. using = router.db_for_write(self.model, instance=obj)
  71. try:
  72. with transaction.atomic(using=using):
  73. obj.save(
  74. force_insert=must_create, force_update=not must_create, using=using
  75. )
  76. except IntegrityError:
  77. if must_create:
  78. raise CreateError
  79. raise
  80. except DatabaseError:
  81. if not must_create:
  82. raise UpdateError
  83. raise
  84. def delete(self, session_key=None):
  85. if session_key is None:
  86. if self.session_key is None:
  87. return
  88. session_key = self.session_key
  89. try:
  90. self.model.objects.get(session_key=session_key).delete()
  91. except self.model.DoesNotExist:
  92. pass
  93. @classmethod
  94. def clear_expired(cls):
  95. cls.get_model_class().objects.filter(expire_date__lt=timezone.now()).delete()