Fixed #26207 -- Replaced dynamic classes with non-data descriptors for deferred instance loading.

This commit is contained in:
Anssi Kääriäinen 2016-02-02 11:33:09 +02:00 committed by Tim Graham
parent dac075e910
commit 7f51876f99
17 changed files with 104 additions and 240 deletions

View file

@ -73,13 +73,12 @@ when loading from the database.
The ``db`` argument contains the database alias for the database the model
is loaded from, ``field_names`` contains the names of all loaded fields, and
``values`` contains the loaded values for each field in ``field_names``. The
``field_names`` are in the same order as the ``values``, so it is possible to
use ``cls(**(zip(field_names, values)))`` to instantiate the object. If all
of the model's fields are present, then ``values`` are guaranteed to be in
the order ``__init__()`` expects them. That is, the instance can be created
by ``cls(*values)``. It is possible to check if all fields are present by
consulting ``cls._deferred`` - if ``False``, then all fields have been loaded
from the database.
``field_names`` are in the same order as the ``values``. If all of the model's
fields are present, then ``values`` are guaranteed to be in the order
``__init__()`` expects them. That is, the instance can be created by
``cls(*values)``. If any fields are deferred, they won't appear in
``field_names``. In that case, assign a value of ``django.db.models.DEFERRED``
to each of the missing fields.
In addition to creating the new model, the ``from_db()`` method must set the
``adding`` and ``db`` flags in the new instance's ``_state`` attribute.
@ -87,14 +86,20 @@ In addition to creating the new model, the ``from_db()`` method must set the
Below is an example showing how to record the initial values of fields that
are loaded from the database::
from django.db.models import DEFERRED
@classmethod
def from_db(cls, db, field_names, values):
# default implementation of from_db() (could be replaced
# with super())
if cls._deferred:
instance = cls(**zip(field_names, values))
else:
instance = cls(*values)
# Default implementation of from_db() (subject to change and could
# be replaced with super()).
if len(values) != len(cls._meta.concrete_fields):
values = list(values)
values.reverse()
values = [
values.pop() if f.attname in field_names else DEFERRED
for f in cls._meta.concrete_fields
]
new = cls(*values)
instance._state.adding = False
instance._state.db = db
# customization to store the original field values on the instance
@ -114,6 +119,12 @@ The example above shows a full ``from_db()`` implementation to clarify how that
is done. In this case it would of course be possible to just use ``super()`` call
in the ``from_db()`` method.
.. versionchanged:: 1.10
In older versions, you could check if all fields were loaded by consulting
``cls._deferred``. This attribute is removed and
``django.db.models.DEFERRED`` is new.
Refreshing objects from database
================================

View file

@ -783,6 +783,12 @@ Miscellaneous
Web servers already implement this behavior. Responses retrieved using the
Django test client continue to have these "response fixes" applied.
* ``Model.__init__()`` now receives ``django.db.models.DEFERRED`` as the value
of deferred fields.
* The ``Model._deferred`` attribute is removed as dynamic model classes when
using ``QuerySet.defer()`` and ``only()`` is removed.
.. _deprecated-features-1.10:
Features deprecated in 1.10