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,
|
||||
connections, router, transaction,
|
||||
)
|
||||
from django.db.models import signals
|
||||
from django.db.models.constants import LOOKUP_SEP
|
||||
from django.db.models.deletion import CASCADE, Collector
|
||||
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.options import Options
|
||||
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.utils import six
|
||||
from django.utils.deprecation import RemovedInDjango20Warning
|
||||
@ -366,7 +368,7 @@ class ModelBase(type):
|
||||
manager.auto_created = True
|
||||
cls.add_to_class('objects', manager)
|
||||
|
||||
signals.class_prepared.send(sender=cls)
|
||||
class_prepared.send(sender=cls)
|
||||
|
||||
def _requires_legacy_default_manager(cls): # RemovedInDjango20Warning
|
||||
opts = cls._meta
|
||||
@ -465,7 +467,13 @@ class ModelState(object):
|
||||
class Model(six.with_metaclass(ModelBase)):
|
||||
|
||||
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
|
||||
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
|
||||
# The reason for the kwargs check is that standard iterator passes in by
|
||||
# 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.
|
||||
raise IndexError("Number of args exceeds number of fields")
|
||||
|
||||
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
|
||||
# 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
|
||||
# without changing the logic.
|
||||
for val, field in zip(args, fields_iter):
|
||||
if val is DEFERRED:
|
||||
if val is _DEFERRED:
|
||||
continue
|
||||
setattr(self, field.attname, val)
|
||||
_setattr(self, field.attname, val)
|
||||
else:
|
||||
# Slower, kwargs-ready version.
|
||||
fields_iter = iter(self._meta.fields)
|
||||
fields_iter = iter(opts.fields)
|
||||
for val, field in zip(args, fields_iter):
|
||||
if val is DEFERRED:
|
||||
if val is _DEFERRED:
|
||||
continue
|
||||
setattr(self, field.attname, val)
|
||||
_setattr(self, field.attname, val)
|
||||
kwargs.pop(field.name, None)
|
||||
|
||||
# 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
|
||||
# "user_id") so that the object gets properly cached (and type
|
||||
# checked) by the RelatedObjectDescriptor.
|
||||
if rel_obj is not DEFERRED:
|
||||
setattr(self, field.name, rel_obj)
|
||||
if rel_obj is not _DEFERRED:
|
||||
_setattr(self, field.name, rel_obj)
|
||||
else:
|
||||
if val is not DEFERRED:
|
||||
setattr(self, field.attname, val)
|
||||
if val is not _DEFERRED:
|
||||
_setattr(self, field.attname, val)
|
||||
|
||||
if kwargs:
|
||||
for prop in list(kwargs):
|
||||
property_names = opts._property_names
|
||||
for prop in tuple(kwargs):
|
||||
try:
|
||||
# Any remaining kwargs must correspond to properties or
|
||||
# virtual fields.
|
||||
if (isinstance(getattr(self.__class__, prop), property) or
|
||||
self._meta.get_field(prop)):
|
||||
if kwargs[prop] is not DEFERRED:
|
||||
setattr(self, prop, kwargs[prop])
|
||||
if prop in property_names or opts.get_field(prop):
|
||||
if kwargs[prop] is not _DEFERRED:
|
||||
_setattr(self, prop, kwargs[prop])
|
||||
del kwargs[prop]
|
||||
except (AttributeError, FieldDoesNotExist):
|
||||
pass
|
||||
if kwargs:
|
||||
raise TypeError("'%s' is an invalid keyword argument for this function" % list(kwargs)[0])
|
||||
super(Model, self).__init__()
|
||||
signals.post_init.send(sender=self.__class__, instance=self)
|
||||
post_init.send(sender=cls, instance=self)
|
||||
|
||||
@classmethod
|
||||
def from_db(cls, db, field_names, values):
|
||||
@ -816,8 +824,10 @@ class Model(six.with_metaclass(ModelBase)):
|
||||
cls = cls._meta.concrete_model
|
||||
meta = cls._meta
|
||||
if not meta.auto_created:
|
||||
signals.pre_save.send(sender=origin, instance=self, raw=raw, using=using,
|
||||
update_fields=update_fields)
|
||||
pre_save.send(
|
||||
sender=origin, instance=self, raw=raw, using=using,
|
||||
update_fields=update_fields,
|
||||
)
|
||||
with transaction.atomic(using=using, savepoint=False):
|
||||
if not raw:
|
||||
self._save_parents(cls, using, update_fields)
|
||||
@ -829,8 +839,10 @@ class Model(six.with_metaclass(ModelBase)):
|
||||
|
||||
# Signal that the save is complete
|
||||
if not meta.auto_created:
|
||||
signals.post_save.send(sender=origin, instance=self, created=(not updated),
|
||||
update_fields=update_fields, raw=raw, using=using)
|
||||
post_save.send(
|
||||
sender=origin, instance=self, created=(not updated),
|
||||
update_fields=update_fields, raw=raw, using=using,
|
||||
)
|
||||
|
||||
save_base.alters_data = True
|
||||
|
||||
|
@ -92,6 +92,10 @@ def _empty(of_cls):
|
||||
return new
|
||||
|
||||
|
||||
def return_None():
|
||||
return None
|
||||
|
||||
|
||||
@total_ordering
|
||||
@python_2_unicode_compatible
|
||||
class Field(RegisterLookupMixin):
|
||||
@ -771,13 +775,18 @@ class Field(RegisterLookupMixin):
|
||||
"""
|
||||
Returns the default value for this field.
|
||||
"""
|
||||
return self._get_default()
|
||||
|
||||
@cached_property
|
||||
def _get_default(self):
|
||||
if self.has_default():
|
||||
if callable(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:
|
||||
return None
|
||||
return ""
|
||||
return return_None
|
||||
return six.text_type # returns empty string
|
||||
|
||||
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
|
||||
|
@ -883,3 +883,14 @@ class Options(object):
|
||||
@has_auto_field.setter
|
||||
def has_auto_field(self, value):
|
||||
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