Ticket #3438: models-init.patch

File models-init.patch, 5.5 KB (added by (removed), 8 years ago)

django.db.models.base.Model.init refactoring

  • django/db/models/base.py

    old new  
    1313from django.utils.datastructures import SortedDict
    1414from django.utils.functional import curry
    1515from django.conf import settings
     16from itertools import izip
    1617import types
    1718import sys
    1819import os
     
    9091
    9192    def __init__(self, *args, **kwargs):
    9293        dispatcher.send(signal=signals.pre_init, sender=self.__class__, args=args, kwargs=kwargs)
    93         for f in self._meta.fields:
    94             if isinstance(f.rel, ManyToOneRel):
    95                 try:
    96                     # Assume object instance was passed in.
    97                     rel_obj = kwargs.pop(f.name)
    98                 except KeyError:
     94        # there is a rather weird disparity here; if kwargs, it's set, then args overrides it.
     95        # should be one or the other, don't duplicate the work
     96        # the reason for the kwargs check is that standard iterator passes in by args, literally,
     97        # the row- with this check, instantiation for iteration is 33% faster.
     98        args_len = len(args)
     99        if args_len > len(self._meta.fields):
     100            # daft, but matches old exception sans the err msg.
     101            raise IndexError("number of args exceeds number of fields")
     102
     103        fields_iter = iter(self._meta.fields)
     104        if not kwargs:
     105            # ordering of the izip calls matter- izip throws StopIteration when an iter throws it
     106            # meaning, if the first iter throws it, the second is *not* consumed from
     107            # we rely on this, thus don't change the order without changing the logic.
     108            for val, field in izip(args, fields_iter):
     109                setattr(self, field.attname, val)
     110        else:
     111            # slower...
     112            for val, field in izip(args, fields_iter):
     113                setattr(self, field.attname, val)
     114                kwargs.pop(field.name, None)
     115                # maintain compatibility with existing calls, daft as it is.
     116                if isinstance(field.rel, ManyToOneRel):
     117                    kwargs.pop(field.attname, None)
     118       
     119        # now we're left with the unprocessed fields that *must* come from keywords, or default.
     120       
     121        for field in fields_iter:
     122            if kwargs:
     123                if isinstance(field.rel, ManyToOneRel):
    99124                    try:
    100                         # Object instance wasn't passed in -- must be an ID.
    101                         val = kwargs.pop(f.attname)
     125                        # Assume object instance was passed in.
     126                        rel_obj = kwargs.pop(field.name)
    102127                    except KeyError:
    103                         val = f.get_default()
    104                 else:
    105                     # Object instance was passed in.
    106                     # Special case: You can pass in "None" for related objects if it's allowed.
    107                     if rel_obj is None and f.null:
    108                         val = None
    109                     else:
    110128                        try:
    111                             val = getattr(rel_obj, f.rel.get_related_field().attname)
    112                         except AttributeError:
    113                             raise TypeError, "Invalid value: %r should be a %s instance, not a %s" % (f.name, f.rel.to, type(rel_obj))
    114                 setattr(self, f.attname, val)
     129                            # Object instance wasn't passed in -- must be an ID.
     130                            val = kwargs.pop(field.attname)
     131                        except KeyError:
     132                            val = field.get_default()
     133                    else:
     134                        # Object instance was passed in.
     135                        # Special case: You can pass in "None" for related objects if it's allowed.
     136                        if rel_obj is None and field.null:
     137                            val = None
     138                        else:
     139                            try:
     140                                val = getattr(rel_obj, field.rel.get_related_field().attname)
     141                            except AttributeError:
     142                                raise TypeError("Invalid value: %r should be a %s instance, not a %s" %
     143                                    (field.name, field.rel.to, type(rel_obj)))
     144                else:
     145                    val = kwargs.pop(field.attname, field.get_default())
    115146            else:
    116                 val = kwargs.pop(f.attname, f.get_default())
    117                 setattr(self, f.attname, val)
    118         for prop in kwargs.keys():
    119             try:
    120                 if isinstance(getattr(self.__class__, prop), property):
    121                     setattr(self, prop, kwargs.pop(prop))
    122             except AttributeError:
    123                 pass
     147                val = field.get_default()
     148            setattr(self, field.attname, val)
     149
    124150        if kwargs:
    125             raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0]
    126         for i, arg in enumerate(args):
    127             setattr(self, self._meta.fields[i].attname, arg)
     151            for prop in kwargs.keys():
     152                try:
     153                    if isinstance(getattr(self.__class__, prop), property):
     154                        setattr(self, prop, kwargs.pop(prop))
     155                except AttributeError:
     156                    pass
     157            if kwargs:
     158                raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0]
    128159        dispatcher.send(signal=signals.post_init, sender=self.__class__, instance=self)
    129160
    130161    def add_to_class(cls, name, value):
Back to Top