mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +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:
		| @@ -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) | ||||
|         }) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user