mirror of
https://github.com/django/django.git
synced 2025-06-05 11:39:13 +00:00
Fixed #34112 -- Added async-compatible interface to Model methods.
Thanks Adam Johnson for the review.
This commit is contained in:
parent
6103059592
commit
d5bcdf858d
1
AUTHORS
1
AUTHORS
@ -141,6 +141,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Bernd Schlapsi
|
Bernd Schlapsi
|
||||||
Bernhard Essl <me@bernhardessl.com>
|
Bernhard Essl <me@bernhardessl.com>
|
||||||
berto
|
berto
|
||||||
|
Bhuvnesh Sharma <bhuvnesh875@gmail.com>
|
||||||
Bill Fenner <fenner@gmail.com>
|
Bill Fenner <fenner@gmail.com>
|
||||||
Bjørn Stabell <bjorn@exoweb.net>
|
Bjørn Stabell <bjorn@exoweb.net>
|
||||||
Bo Marchman <bo.marchman@gmail.com>
|
Bo Marchman <bo.marchman@gmail.com>
|
||||||
|
@ -4,6 +4,8 @@ import warnings
|
|||||||
from functools import partialmethod
|
from functools import partialmethod
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
|
|
||||||
|
from asgiref.sync import sync_to_async
|
||||||
|
|
||||||
import django
|
import django
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@ -737,6 +739,9 @@ class Model(metaclass=ModelBase):
|
|||||||
|
|
||||||
self._state.db = db_instance._state.db
|
self._state.db = db_instance._state.db
|
||||||
|
|
||||||
|
async def arefresh_from_db(self, using=None, fields=None):
|
||||||
|
return await sync_to_async(self.refresh_from_db)(using=using, fields=fields)
|
||||||
|
|
||||||
def serializable_value(self, field_name):
|
def serializable_value(self, field_name):
|
||||||
"""
|
"""
|
||||||
Return the value of the field name for this instance. If the field is
|
Return the value of the field name for this instance. If the field is
|
||||||
@ -810,6 +815,18 @@ class Model(metaclass=ModelBase):
|
|||||||
|
|
||||||
save.alters_data = True
|
save.alters_data = True
|
||||||
|
|
||||||
|
async def asave(
|
||||||
|
self, force_insert=False, force_update=False, using=None, update_fields=None
|
||||||
|
):
|
||||||
|
return await sync_to_async(self.save)(
|
||||||
|
force_insert=force_insert,
|
||||||
|
force_update=force_update,
|
||||||
|
using=using,
|
||||||
|
update_fields=update_fields,
|
||||||
|
)
|
||||||
|
|
||||||
|
asave.alters_data = True
|
||||||
|
|
||||||
def save_base(
|
def save_base(
|
||||||
self,
|
self,
|
||||||
raw=False,
|
raw=False,
|
||||||
@ -1111,6 +1128,14 @@ class Model(metaclass=ModelBase):
|
|||||||
|
|
||||||
delete.alters_data = True
|
delete.alters_data = True
|
||||||
|
|
||||||
|
async def adelete(self, using=None, keep_parents=False):
|
||||||
|
return await sync_to_async(self.delete)(
|
||||||
|
using=using,
|
||||||
|
keep_parents=keep_parents,
|
||||||
|
)
|
||||||
|
|
||||||
|
adelete.alters_data = True
|
||||||
|
|
||||||
def _get_FIELD_display(self, field):
|
def _get_FIELD_display(self, field):
|
||||||
value = getattr(self, field.attname)
|
value = getattr(self, field.attname)
|
||||||
choices_dict = dict(make_hashable(field.flatchoices))
|
choices_dict = dict(make_hashable(field.flatchoices))
|
||||||
|
@ -132,6 +132,9 @@ value from the database::
|
|||||||
>>> obj.field # Loads the field from the database
|
>>> obj.field # Loads the field from the database
|
||||||
|
|
||||||
.. method:: Model.refresh_from_db(using=None, fields=None)
|
.. method:: Model.refresh_from_db(using=None, fields=None)
|
||||||
|
.. method:: Model.arefresh_from_db(using=None, fields=None)
|
||||||
|
|
||||||
|
*Asynchronous version*: ``arefresh_from_db()``
|
||||||
|
|
||||||
If you need to reload a model's values from the database, you can use the
|
If you need to reload a model's values from the database, you can use the
|
||||||
``refresh_from_db()`` method. When this method is called without arguments the
|
``refresh_from_db()`` method. When this method is called without arguments the
|
||||||
@ -188,6 +191,10 @@ all of the instance's fields when a deferred field is reloaded::
|
|||||||
A helper method that returns a set containing the attribute names of all those
|
A helper method that returns a set containing the attribute names of all those
|
||||||
fields that are currently deferred for this model.
|
fields that are currently deferred for this model.
|
||||||
|
|
||||||
|
.. versionchanged:: 4.2
|
||||||
|
|
||||||
|
``arefresh_from_db()`` method was added.
|
||||||
|
|
||||||
.. _validating-objects:
|
.. _validating-objects:
|
||||||
|
|
||||||
Validating objects
|
Validating objects
|
||||||
@ -406,6 +413,9 @@ Saving objects
|
|||||||
To save an object back to the database, call ``save()``:
|
To save an object back to the database, call ``save()``:
|
||||||
|
|
||||||
.. method:: Model.save(force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS, update_fields=None)
|
.. method:: Model.save(force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS, update_fields=None)
|
||||||
|
.. method:: Model.asave(force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS, update_fields=None)
|
||||||
|
|
||||||
|
*Asynchronous version*: ``asave()``
|
||||||
|
|
||||||
For details on using the ``force_insert`` and ``force_update`` arguments, see
|
For details on using the ``force_insert`` and ``force_update`` arguments, see
|
||||||
:ref:`ref-models-force-insert`. Details about the ``update_fields`` argument
|
:ref:`ref-models-force-insert`. Details about the ``update_fields`` argument
|
||||||
@ -416,6 +426,10 @@ method. See :ref:`overriding-model-methods` for more details.
|
|||||||
|
|
||||||
The model save process also has some subtleties; see the sections below.
|
The model save process also has some subtleties; see the sections below.
|
||||||
|
|
||||||
|
.. versionchanged:: 4.2
|
||||||
|
|
||||||
|
``asave()`` method was added.
|
||||||
|
|
||||||
Auto-incrementing primary keys
|
Auto-incrementing primary keys
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
@ -644,6 +658,9 @@ Deleting objects
|
|||||||
================
|
================
|
||||||
|
|
||||||
.. method:: Model.delete(using=DEFAULT_DB_ALIAS, keep_parents=False)
|
.. method:: Model.delete(using=DEFAULT_DB_ALIAS, keep_parents=False)
|
||||||
|
.. method:: Model.adelete(using=DEFAULT_DB_ALIAS, keep_parents=False)
|
||||||
|
|
||||||
|
*Asynchronous version*: ``adelete()``
|
||||||
|
|
||||||
Issues an SQL ``DELETE`` for the object. This only deletes the object in the
|
Issues an SQL ``DELETE`` for the object. This only deletes the object in the
|
||||||
database; the Python instance will still exist and will still have data in
|
database; the Python instance will still exist and will still have data in
|
||||||
@ -660,6 +677,10 @@ Sometimes with :ref:`multi-table inheritance <multi-table-inheritance>` you may
|
|||||||
want to delete only a child model's data. Specifying ``keep_parents=True`` will
|
want to delete only a child model's data. Specifying ``keep_parents=True`` will
|
||||||
keep the parent model's data.
|
keep the parent model's data.
|
||||||
|
|
||||||
|
.. versionchanged:: 4.2
|
||||||
|
|
||||||
|
``adelete()`` method was added.
|
||||||
|
|
||||||
Pickling objects
|
Pickling objects
|
||||||
================
|
================
|
||||||
|
|
||||||
|
@ -239,6 +239,10 @@ Models
|
|||||||
* :class:`F() <django.db.models.F>` expressions that output ``BooleanField``
|
* :class:`F() <django.db.models.F>` expressions that output ``BooleanField``
|
||||||
can now be negated using ``~F()`` (inversion operator).
|
can now be negated using ``~F()`` (inversion operator).
|
||||||
|
|
||||||
|
* ``Model`` now provides asynchronous versions of some methods that use the
|
||||||
|
database, using an ``a`` prefix: :meth:`~.Model.adelete`,
|
||||||
|
:meth:`~.Model.arefresh_from_db`, and :meth:`~.Model.asave`.
|
||||||
|
|
||||||
Requests and Responses
|
Requests and Responses
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -91,10 +91,20 @@ Detailed notes can be found in :ref:`async-queries`, but in short:
|
|||||||
* ``async for`` is supported on all QuerySets (including the output of
|
* ``async for`` is supported on all QuerySets (including the output of
|
||||||
``values()`` and ``values_list()``.)
|
``values()`` and ``values_list()``.)
|
||||||
|
|
||||||
|
Django also supports some asynchronous model methods that use the database::
|
||||||
|
|
||||||
|
async def make_book(...):
|
||||||
|
book = Book(...)
|
||||||
|
await book.asave(using="secondary")
|
||||||
|
|
||||||
Transactions do not yet work in async mode. If you have a piece of code that
|
Transactions do not yet work in async mode. If you have a piece of code that
|
||||||
needs transactions behavior, we recommend you write that piece as a single
|
needs transactions behavior, we recommend you write that piece as a single
|
||||||
synchronous function and call it using :func:`sync_to_async`.
|
synchronous function and call it using :func:`sync_to_async`.
|
||||||
|
|
||||||
|
.. versionchanged:: 4.2
|
||||||
|
|
||||||
|
Asynchronous model interface was added.
|
||||||
|
|
||||||
Performance
|
Performance
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
25
tests/async/test_async_model_methods.py
Normal file
25
tests/async/test_async_model_methods.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from .models import SimpleModel
|
||||||
|
|
||||||
|
|
||||||
|
class AsyncModelOperationTest(TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(cls):
|
||||||
|
cls.s1 = SimpleModel.objects.create(field=0)
|
||||||
|
|
||||||
|
async def test_asave(self):
|
||||||
|
self.s1.field = 10
|
||||||
|
await self.s1.asave()
|
||||||
|
refetched = await SimpleModel.objects.aget()
|
||||||
|
self.assertEqual(refetched.field, 10)
|
||||||
|
|
||||||
|
async def test_adelete(self):
|
||||||
|
await self.s1.adelete()
|
||||||
|
count = await SimpleModel.objects.acount()
|
||||||
|
self.assertEqual(count, 0)
|
||||||
|
|
||||||
|
async def test_arefresh_from_db(self):
|
||||||
|
await SimpleModel.objects.filter(pk=self.s1.pk).aupdate(field=20)
|
||||||
|
await self.s1.arefresh_from_db()
|
||||||
|
self.assertEqual(self.s1.field, 20)
|
Loading…
x
Reference in New Issue
Block a user