diff --git a/django/db/models/base.py b/django/db/models/base.py index 33ed01a9ef..bb02d7a00c 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -165,33 +165,6 @@ class ModelBase(type): class Model(object): __metaclass__ = ModelBase - def _get_pk_val(self, meta=None): - if not meta: - meta = self._meta - return getattr(self, meta.pk.attname) - - def _set_pk_val(self, value): - return setattr(self, self._meta.pk.attname, value) - - pk = property(_get_pk_val, _set_pk_val) - - def __repr__(self): - return smart_str(u'<%s: %s>' % (self.__class__.__name__, unicode(self))) - - def __str__(self): - if hasattr(self, '__unicode__'): - return force_unicode(self).encode('utf-8') - return '%s object' % self.__class__.__name__ - - def __eq__(self, other): - return isinstance(other, self.__class__) and self._get_pk_val() == other._get_pk_val() - - def __ne__(self, other): - return not self.__eq__(other) - - def __hash__(self): - return hash(self._get_pk_val()) - def __init__(self, *args, **kwargs): dispatcher.send(signal=signals.pre_init, sender=self.__class__, args=args, kwargs=kwargs) @@ -264,6 +237,59 @@ class Model(object): raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0] dispatcher.send(signal=signals.post_init, sender=self.__class__, instance=self) + def from_sequence(cls, values): + """ + An alternate class constructor, primarily for internal use. + + Creates a model instance from a sequence of values (which corresponds + to all the non-many-to-many fields in creation order. If there are more + fields than values, the remaining (final) fields are given their + default values. + + ForeignKey fields can only be initialised using id values, not + instances, in this method. + """ + dispatcher.send(signal=signals.pre_init, sender=cls, args=values, + kwargs={}) + obj = Empty() + obj.__class__ = cls + field_iter = iter(obj._meta.fields) + for val, field in izip(values, field_iter): + setattr(obj, field.attname, val) + for field in field_iter: + setattr(obj, field.attname, field.get_default()) + dispatcher.send(signal=signals.post_init, sender=cls, instance=obj) + return obj + + from_sequence = classmethod(from_sequence) + + def __repr__(self): + return smart_str(u'<%s: %s>' % (self.__class__.__name__, unicode(self))) + + def __str__(self): + if hasattr(self, '__unicode__'): + return force_unicode(self).encode('utf-8') + return '%s object' % self.__class__.__name__ + + def __eq__(self, other): + return isinstance(other, self.__class__) and self._get_pk_val() == other._get_pk_val() + + def __ne__(self, other): + return not self.__eq__(other) + + def __hash__(self): + return hash(self._get_pk_val()) + + def _get_pk_val(self, meta=None): + if not meta: + meta = self._meta + return getattr(self, meta.pk.attname) + + def _set_pk_val(self, value): + return setattr(self, self._meta.pk.attname, value) + + pk = property(_get_pk_val, _set_pk_val) + def save(self): """ Save the current instance. Override this in a subclass if you want to @@ -533,6 +559,9 @@ def get_absolute_url(opts, func, self, *args, **kwargs): # MISC # ######## +class Empty(object): + pass + if sys.version_info < (2, 5): # Prior to Python 2.5, Exception was an old-style class def subclass_exception(name, parent, unused): diff --git a/django/db/models/query.py b/django/db/models/query.py index 1bafa64805..e1e2bb19f2 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -153,7 +153,7 @@ class QuerySet(object): obj, _ = get_cached_row(self.model, row, index_start, max_depth, requested=requested) else: - obj = self.model(*row[index_start:]) + obj = self.model.from_sequence(row[index_start:]) for i, k in enumerate(extra_select): setattr(obj, k, row[i]) yield obj @@ -646,7 +646,7 @@ def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0, restricted = requested is not None index_end = index_start + len(klass._meta.fields) - obj = klass(*row[index_start:index_end]) + obj = klass.from_sequence(row[index_start:index_end]) for f in klass._meta.fields: if (not f.rel or (not restricted and f.null) or (restricted and f.name not in requested) or f.rel.parent_link):