1
0
mirror of https://github.com/django/django.git synced 2025-10-23 21:59:11 +00:00

Fixed #34901 -- Added async-compatible interface to session engines.

Thanks Andrew-Chen-Wang for the initial implementation which was posted
to the Django forum thread about asyncifying contrib modules.
This commit is contained in:
Jon Janzen
2023-10-16 18:50:20 -07:00
committed by Mariusz Felisiak
parent 33c06ca0da
commit f5c340684b
12 changed files with 975 additions and 9 deletions

View File

@@ -125,6 +125,10 @@ Minor features
error messages with their traceback via the newly added
:ref:`sessions logger <django-contrib-sessions-logger>`.
* :class:`django.contrib.sessions.backends.base.SessionBase` and all built-in
session engines now provide async API. The new asynchronous methods all have
``a`` prefixed names, e.g. ``aget()``, ``akeys()``, or ``acycle_key()``.
:mod:`django.contrib.sitemaps`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -196,54 +196,156 @@ You can edit it multiple times.
Example: ``'fav_color' in request.session``
.. method:: get(key, default=None)
.. method:: aget(key, default=None)
*Asynchronous version*: ``aget()``
Example: ``fav_color = request.session.get('fav_color', 'red')``
.. versionchanged:: 5.1
``aget()`` function was added.
.. method:: aset(key, value)
.. versionadded:: 5.1
Example: ``await request.session.aset('fav_color', 'red')``
.. method:: update(dict)
.. method:: aupdate(dict)
*Asynchronous version*: ``aupdate()``
Example: ``request.session.update({'fav_color': 'red'})``
.. versionchanged:: 5.1
``aupdate()`` function was added.
.. method:: pop(key, default=__not_given)
.. method:: apop(key, default=__not_given)
*Asynchronous version*: ``apop()``
Example: ``fav_color = request.session.pop('fav_color', 'blue')``
.. versionchanged:: 5.1
``apop()`` function was added.
.. method:: keys()
.. method:: akeys()
*Asynchronous version*: ``akeys()``
.. versionchanged:: 5.1
``akeys()`` function was added.
.. method:: values()
.. method:: avalues()
*Asynchronous version*: ``avalues()``
.. versionchanged:: 5.1
``avalues()`` function was added.
.. method:: has_key(key)
.. method:: ahas_key(key)
*Asynchronous version*: ``ahas_key()``
.. versionchanged:: 5.1
``ahas_key()`` function was added.
.. method:: items()
.. method:: aitems()
*Asynchronous version*: ``aitems()``
.. versionchanged:: 5.1
``aitems()`` function was added.
.. method:: setdefault()
.. method:: asetdefault()
*Asynchronous version*: ``asetdefault()``
.. versionchanged:: 5.1
``asetdefault()`` function was added.
.. method:: clear()
It also has these methods:
.. method:: flush()
.. method:: aflush()
*Asynchronous version*: ``aflush()``
Deletes the current session data from the session and deletes the session
cookie. This is used if you want to ensure that the previous session data
can't be accessed again from the user's browser (for example, the
:func:`django.contrib.auth.logout()` function calls it).
.. versionchanged:: 5.1
``aflush()`` function was added.
.. method:: set_test_cookie()
.. method:: aset_test_cookie()
*Asynchronous version*: ``aset_test_cookie()``
Sets a test cookie to determine whether the user's browser supports
cookies. Due to the way cookies work, you won't be able to test this
until the user's next page request. See `Setting test cookies`_ below for
more information.
.. versionchanged:: 5.1
``aset_test_cookie()`` function was added.
.. method:: test_cookie_worked()
.. method:: atest_cookie_worked()
*Asynchronous version*: ``atest_cookie_worked()``
Returns either ``True`` or ``False``, depending on whether the user's
browser accepted the test cookie. Due to the way cookies work, you'll
have to call ``set_test_cookie()`` on a previous, separate page request.
have to call ``set_test_cookie()`` or ``aset_test_cookie()`` on a
previous, separate page request.
See `Setting test cookies`_ below for more information.
.. versionchanged:: 5.1
``atest_cookie_worked()`` function was added.
.. method:: delete_test_cookie()
.. method:: adelete_test_cookie()
*Asynchronous version*: ``adelete_test_cookie()``
Deletes the test cookie. Use this to clean up after yourself.
.. versionchanged:: 5.1
``adelete_test_cookie()`` function was added.
.. method:: get_session_cookie_age()
Returns the value of the setting :setting:`SESSION_COOKIE_AGE`. This can
be overridden in a custom session backend.
.. method:: set_expiry(value)
.. method:: aset_expiry(value)
*Asynchronous version*: ``aset_expiry()``
Sets the expiration time for the session. You can pass a number of
different values:
@@ -266,7 +368,14 @@ You can edit it multiple times.
purposes. Session expiration is computed from the last time the
session was *modified*.
.. versionchanged:: 5.1
``aset_expiry()`` function was added.
.. method:: get_expiry_age()
.. method:: aget_expiry_age()
*Asynchronous version*: ``aget_expiry_age()``
Returns the number of seconds until this session expires. For sessions
with no custom expiration (or those set to expire at browser close), this
@@ -279,7 +388,7 @@ You can edit it multiple times.
- ``expiry``: expiry information for the session, as a
:class:`~datetime.datetime` object, an :class:`int` (in seconds), or
``None``. Defaults to the value stored in the session by
:meth:`set_expiry`, if there is one, or ``None``.
:meth:`set_expiry`/:meth:`aset_expiry`, if there is one, or ``None``.
.. note::
@@ -295,7 +404,14 @@ You can edit it multiple times.
expires_at = modification + timedelta(seconds=settings.SESSION_COOKIE_AGE)
.. versionchanged:: 5.1
``aget_expiry_age()`` function was added.
.. method:: get_expiry_date()
.. method:: aget_expiry_date()
*Asynchronous version*: ``aget_expiry_date()``
Returns the date this session will expire. For sessions with no custom
expiration (or those set to expire at browser close), this will equal the
@@ -304,22 +420,47 @@ You can edit it multiple times.
This function accepts the same keyword arguments as
:meth:`get_expiry_age`, and similar notes on usage apply.
.. versionchanged:: 5.1
``aget_expiry_date()`` function was added.
.. method:: get_expire_at_browser_close()
.. method:: aget_expire_at_browser_close()
*Asynchronous version*: ``aget_expire_at_browser_close()``
Returns either ``True`` or ``False``, depending on whether the user's
session cookie will expire when the user's web browser is closed.
.. versionchanged:: 5.1
``aget_expire_at_browser_close()`` function was added.
.. method:: clear_expired()
.. method:: aclear_expired()
*Asynchronous version*: ``aclear_expired()``
Removes expired sessions from the session store. This class method is
called by :djadmin:`clearsessions`.
.. versionchanged:: 5.1
``aclear_expired()`` function was added.
.. method:: cycle_key()
.. method:: acycle_key()
*Asynchronous version*: ``acycle_key()``
Creates a new session key while retaining the current session data.
:func:`django.contrib.auth.login()` calls this method to mitigate against
session fixation.
.. versionchanged:: 5.1
``acycle_key()`` function was added.
.. _session_serialization:
Session serialization
@@ -475,6 +616,10 @@ Here's a typical usage example::
request.session.set_test_cookie()
return render(request, "foo/login_form.html")
.. versionchanged:: 5.1
Support for setting test cookies in asynchronous view functions was added.
Using sessions out of views
===========================
@@ -694,16 +839,26 @@ the corresponding session engine. By convention, the session store object class
is named ``SessionStore`` and is located in the module designated by
:setting:`SESSION_ENGINE`.
All ``SessionStore`` classes available in Django inherit from
:class:`~backends.base.SessionBase` and implement data manipulation methods,
namely:
All ``SessionStore`` subclasses available in Django implement the following
data manipulation methods:
* ``exists()``
* ``create()``
* ``save()``
* ``delete()``
* ``load()``
* :meth:`~backends.base.SessionBase.clear_expired`
* :meth:`~.SessionBase.clear_expired`
An asynchronous interface for these methods is provided by wrapping them with
``sync_to_async()``. They can be implemented directly if an async-native
implementation is available:
* ``aexists()``
* ``acreate()``
* ``asave()``
* ``adelete()``
* ``aload()``
* :meth:`~.SessionBase.aclear_expired`
In order to build a custom session engine or to customize an existing one, you
may create a new class inheriting from :class:`~backends.base.SessionBase` or
@@ -713,6 +868,11 @@ You can extend the session engines, but doing so with database-backed session
engines generally requires some extra effort (see the next section for
details).
.. versionchanged:: 5.1
``aexists()``, ``acreate()``, ``asave()``, ``adelete()``, ``aload()``, and
``aclear_expired()`` methods were added.
.. _extending-database-backed-session-engines:
Extending database-backed session engines