mirror of
https://github.com/django/django.git
synced 2024-12-22 17:16:24 +00:00
Optimized Model instantiation a bit.
* Avoid some unnecessary attribute lookups, e.g. access signals directly rather than from module * Alias some repeat accesses inside the method to use the slightly faster local lookups * Use tuple to iterate remaining kwargs as it's faster to construct * Cache Field.get_default() to avoid running through all the logic on every call * Use a cached list of the properties on the model class to avoid repeat isinstance() calls
This commit is contained in:
parent
f94475e526
commit
d2a26c1a90
@ -16,7 +16,6 @@ from django.db import (
|
|||||||
DEFAULT_DB_ALIAS, DJANGO_VERSION_PICKLE_KEY, DatabaseError, connection,
|
DEFAULT_DB_ALIAS, DJANGO_VERSION_PICKLE_KEY, DatabaseError, connection,
|
||||||
connections, router, transaction,
|
connections, router, transaction,
|
||||||
)
|
)
|
||||||
from django.db.models import signals
|
|
||||||
from django.db.models.constants import LOOKUP_SEP
|
from django.db.models.constants import LOOKUP_SEP
|
||||||
from django.db.models.deletion import CASCADE, Collector
|
from django.db.models.deletion import CASCADE, Collector
|
||||||
from django.db.models.fields.related import (
|
from django.db.models.fields.related import (
|
||||||
@ -25,6 +24,9 @@ from django.db.models.fields.related import (
|
|||||||
from django.db.models.manager import Manager
|
from django.db.models.manager import Manager
|
||||||
from django.db.models.options import Options
|
from django.db.models.options import Options
|
||||||
from django.db.models.query import Q
|
from django.db.models.query import Q
|
||||||
|
from django.db.models.signals import (
|
||||||
|
class_prepared, post_init, post_save, pre_init, pre_save,
|
||||||
|
)
|
||||||
from django.db.models.utils import make_model_tuple
|
from django.db.models.utils import make_model_tuple
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
from django.utils.deprecation import RemovedInDjango20Warning
|
from django.utils.deprecation import RemovedInDjango20Warning
|
||||||
@ -366,7 +368,7 @@ class ModelBase(type):
|
|||||||
manager.auto_created = True
|
manager.auto_created = True
|
||||||
cls.add_to_class('objects', manager)
|
cls.add_to_class('objects', manager)
|
||||||
|
|
||||||
signals.class_prepared.send(sender=cls)
|
class_prepared.send(sender=cls)
|
||||||
|
|
||||||
def _requires_legacy_default_manager(cls): # RemovedInDjango20Warning
|
def _requires_legacy_default_manager(cls): # RemovedInDjango20Warning
|
||||||
opts = cls._meta
|
opts = cls._meta
|
||||||
@ -465,7 +467,13 @@ class ModelState(object):
|
|||||||
class Model(six.with_metaclass(ModelBase)):
|
class Model(six.with_metaclass(ModelBase)):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
signals.pre_init.send(sender=self.__class__, args=args, kwargs=kwargs)
|
# Alias some things as locals to avoid repeat global lookups
|
||||||
|
cls = self.__class__
|
||||||
|
opts = self._meta
|
||||||
|
_setattr = setattr
|
||||||
|
_DEFERRED = DEFERRED
|
||||||
|
|
||||||
|
pre_init.send(sender=cls, args=args, kwargs=kwargs)
|
||||||
|
|
||||||
# Set up the storage for instance state
|
# Set up the storage for instance state
|
||||||
self._state = ModelState()
|
self._state = ModelState()
|
||||||
@ -474,27 +482,27 @@ class Model(six.with_metaclass(ModelBase)):
|
|||||||
# overrides it. It should be one or the other; don't duplicate the work
|
# overrides it. It should be one or the other; don't duplicate the work
|
||||||
# The reason for the kwargs check is that standard iterator passes in by
|
# The reason for the kwargs check is that standard iterator passes in by
|
||||||
# args, and instantiation for iteration is 33% faster.
|
# args, and instantiation for iteration is 33% faster.
|
||||||
if len(args) > len(self._meta.concrete_fields):
|
if len(args) > len(opts.concrete_fields):
|
||||||
# Daft, but matches old exception sans the err msg.
|
# Daft, but matches old exception sans the err msg.
|
||||||
raise IndexError("Number of args exceeds number of fields")
|
raise IndexError("Number of args exceeds number of fields")
|
||||||
|
|
||||||
if not kwargs:
|
if not kwargs:
|
||||||
fields_iter = iter(self._meta.concrete_fields)
|
fields_iter = iter(opts.concrete_fields)
|
||||||
# The ordering of the zip calls matter - zip throws StopIteration
|
# The ordering of the zip calls matter - zip throws StopIteration
|
||||||
# when an iter throws it. So if the first iter throws it, the second
|
# when an iter throws it. So if the first iter throws it, the second
|
||||||
# is *not* consumed. We rely on this, so don't change the order
|
# is *not* consumed. We rely on this, so don't change the order
|
||||||
# without changing the logic.
|
# without changing the logic.
|
||||||
for val, field in zip(args, fields_iter):
|
for val, field in zip(args, fields_iter):
|
||||||
if val is DEFERRED:
|
if val is _DEFERRED:
|
||||||
continue
|
continue
|
||||||
setattr(self, field.attname, val)
|
_setattr(self, field.attname, val)
|
||||||
else:
|
else:
|
||||||
# Slower, kwargs-ready version.
|
# Slower, kwargs-ready version.
|
||||||
fields_iter = iter(self._meta.fields)
|
fields_iter = iter(opts.fields)
|
||||||
for val, field in zip(args, fields_iter):
|
for val, field in zip(args, fields_iter):
|
||||||
if val is DEFERRED:
|
if val is _DEFERRED:
|
||||||
continue
|
continue
|
||||||
setattr(self, field.attname, val)
|
_setattr(self, field.attname, val)
|
||||||
kwargs.pop(field.name, None)
|
kwargs.pop(field.name, None)
|
||||||
|
|
||||||
# Now we're left with the unprocessed fields that *must* come from
|
# Now we're left with the unprocessed fields that *must* come from
|
||||||
@ -539,28 +547,28 @@ class Model(six.with_metaclass(ModelBase)):
|
|||||||
# field.name instead of field.attname (e.g. "user" instead of
|
# field.name instead of field.attname (e.g. "user" instead of
|
||||||
# "user_id") so that the object gets properly cached (and type
|
# "user_id") so that the object gets properly cached (and type
|
||||||
# checked) by the RelatedObjectDescriptor.
|
# checked) by the RelatedObjectDescriptor.
|
||||||
if rel_obj is not DEFERRED:
|
if rel_obj is not _DEFERRED:
|
||||||
setattr(self, field.name, rel_obj)
|
_setattr(self, field.name, rel_obj)
|
||||||
else:
|
else:
|
||||||
if val is not DEFERRED:
|
if val is not _DEFERRED:
|
||||||
setattr(self, field.attname, val)
|
_setattr(self, field.attname, val)
|
||||||
|
|
||||||
if kwargs:
|
if kwargs:
|
||||||
for prop in list(kwargs):
|
property_names = opts._property_names
|
||||||
|
for prop in tuple(kwargs):
|
||||||
try:
|
try:
|
||||||
# Any remaining kwargs must correspond to properties or
|
# Any remaining kwargs must correspond to properties or
|
||||||
# virtual fields.
|
# virtual fields.
|
||||||
if (isinstance(getattr(self.__class__, prop), property) or
|
if prop in property_names or opts.get_field(prop):
|
||||||
self._meta.get_field(prop)):
|
if kwargs[prop] is not _DEFERRED:
|
||||||
if kwargs[prop] is not DEFERRED:
|
_setattr(self, prop, kwargs[prop])
|
||||||
setattr(self, prop, kwargs[prop])
|
|
||||||
del kwargs[prop]
|
del kwargs[prop]
|
||||||
except (AttributeError, FieldDoesNotExist):
|
except (AttributeError, FieldDoesNotExist):
|
||||||
pass
|
pass
|
||||||
if kwargs:
|
if kwargs:
|
||||||
raise TypeError("'%s' is an invalid keyword argument for this function" % list(kwargs)[0])
|
raise TypeError("'%s' is an invalid keyword argument for this function" % list(kwargs)[0])
|
||||||
super(Model, self).__init__()
|
super(Model, self).__init__()
|
||||||
signals.post_init.send(sender=self.__class__, instance=self)
|
post_init.send(sender=cls, instance=self)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_db(cls, db, field_names, values):
|
def from_db(cls, db, field_names, values):
|
||||||
@ -816,8 +824,10 @@ class Model(six.with_metaclass(ModelBase)):
|
|||||||
cls = cls._meta.concrete_model
|
cls = cls._meta.concrete_model
|
||||||
meta = cls._meta
|
meta = cls._meta
|
||||||
if not meta.auto_created:
|
if not meta.auto_created:
|
||||||
signals.pre_save.send(sender=origin, instance=self, raw=raw, using=using,
|
pre_save.send(
|
||||||
update_fields=update_fields)
|
sender=origin, instance=self, raw=raw, using=using,
|
||||||
|
update_fields=update_fields,
|
||||||
|
)
|
||||||
with transaction.atomic(using=using, savepoint=False):
|
with transaction.atomic(using=using, savepoint=False):
|
||||||
if not raw:
|
if not raw:
|
||||||
self._save_parents(cls, using, update_fields)
|
self._save_parents(cls, using, update_fields)
|
||||||
@ -829,8 +839,10 @@ class Model(six.with_metaclass(ModelBase)):
|
|||||||
|
|
||||||
# Signal that the save is complete
|
# Signal that the save is complete
|
||||||
if not meta.auto_created:
|
if not meta.auto_created:
|
||||||
signals.post_save.send(sender=origin, instance=self, created=(not updated),
|
post_save.send(
|
||||||
update_fields=update_fields, raw=raw, using=using)
|
sender=origin, instance=self, created=(not updated),
|
||||||
|
update_fields=update_fields, raw=raw, using=using,
|
||||||
|
)
|
||||||
|
|
||||||
save_base.alters_data = True
|
save_base.alters_data = True
|
||||||
|
|
||||||
|
@ -92,6 +92,10 @@ def _empty(of_cls):
|
|||||||
return new
|
return new
|
||||||
|
|
||||||
|
|
||||||
|
def return_None():
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
@total_ordering
|
@total_ordering
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class Field(RegisterLookupMixin):
|
class Field(RegisterLookupMixin):
|
||||||
@ -771,13 +775,18 @@ class Field(RegisterLookupMixin):
|
|||||||
"""
|
"""
|
||||||
Returns the default value for this field.
|
Returns the default value for this field.
|
||||||
"""
|
"""
|
||||||
|
return self._get_default()
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def _get_default(self):
|
||||||
if self.has_default():
|
if self.has_default():
|
||||||
if callable(self.default):
|
if callable(self.default):
|
||||||
return self.default()
|
return self.default
|
||||||
return self.default
|
return lambda: self.default
|
||||||
|
|
||||||
if not self.empty_strings_allowed or self.null and not connection.features.interprets_empty_strings_as_nulls:
|
if not self.empty_strings_allowed or self.null and not connection.features.interprets_empty_strings_as_nulls:
|
||||||
return None
|
return return_None
|
||||||
return ""
|
return six.text_type # returns empty string
|
||||||
|
|
||||||
def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH, limit_choices_to=None):
|
def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH, limit_choices_to=None):
|
||||||
"""Returns choices with a default blank choices included, for use
|
"""Returns choices with a default blank choices included, for use
|
||||||
|
@ -883,3 +883,14 @@ class Options(object):
|
|||||||
@has_auto_field.setter
|
@has_auto_field.setter
|
||||||
def has_auto_field(self, value):
|
def has_auto_field(self, value):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def _property_names(self):
|
||||||
|
"""
|
||||||
|
Return a set of the names of the properties defined on the model.
|
||||||
|
Internal helper for model initialization.
|
||||||
|
"""
|
||||||
|
return frozenset({
|
||||||
|
attr for attr in
|
||||||
|
dir(self.model) if isinstance(getattr(self.model, attr), property)
|
||||||
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user