1
0
mirror of https://github.com/django/django.git synced 2025-07-05 18:29:11 +00:00

queryset-refactor: Added faster paths for updates and inserts that are done

from other core code. This saves a round-trip from field object to field name
and back to field object when we already have the right information to hand.


git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@7437 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Malcolm Tredinnick 2008-04-20 08:47:21 +00:00
parent fcfa8b204a
commit d4956cb427
4 changed files with 47 additions and 22 deletions

View File

@ -308,29 +308,28 @@ class Model(object):
if manager.filter(pk=pk_val).extra(select={'a': 1}).values('a').order_by():
# It does already exist, so do an UPDATE.
if non_pks:
values = [(f.name, f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, False))) for f in non_pks]
manager.filter(pk=pk_val).update(**dict(values))
values = [(f, None, f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, False))) for f in non_pks]
manager.filter(pk=pk_val)._update(values)
else:
record_exists = False
if not pk_set or not record_exists:
if not pk_set:
values = [(f.name, f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True))) for f in meta.local_fields if not isinstance(f, AutoField)]
values = [(f, f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True))) for f in meta.local_fields if not isinstance(f, AutoField)]
else:
values = [(f.name, f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True))) for f in meta.local_fields]
values = [(f, f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True))) for f in meta.local_fields]
if meta.order_with_respect_to:
field = meta.order_with_respect_to
values.append(('_order', manager.filter(**{field.name: getattr(self, field.attname)}).count()))
values.append((meta.get_field_by_name('_order')[0], manager.filter(**{field.name: getattr(self, field.attname)}).count()))
record_exists = False
update_pk = bool(meta.has_auto_field and not pk_set)
if values:
# Create a new record.
result = manager._insert(__return_id=update_pk, **dict(values))
result = manager._insert(values, return_id=update_pk)
else:
# Create a new record with defaults for everything.
result = manager._insert(__return_id=update_pk,
__raw_values=True, pk=connection.ops.pk_default_value())
result = manager._insert([(meta.pk, connection.ops.pk_default_value())], return_id=update_pk, raw_values=True)
if update_pk:
setattr(self, meta.pk.attname, result)

View File

@ -123,8 +123,11 @@ class Manager(object):
def reverse(self, *args, **kwargs):
return self.get_query_set().reverse(*args, **kwargs)
def _insert(self, **kwargs):
return insert_query(self.model, **kwargs)
def _insert(self, values, **kwargs):
return insert_query(self.model, values, **kwargs)
def _update(self, values, **kwargs):
return self.get_query_set()._update(values, **kwargs)
class ManagerDescriptor(object):
# This class ensures managers aren't accessible via model instances.

View File

@ -287,6 +287,19 @@ class QuerySet(object):
self._result_cache = None
update.alters_data = True
def _update(self, values):
"""
A version of update that accepts field objects instead of field names.
Used primarily for model saving and not intended for use by general
code (it requires too much poking around at model internals to be
useful at that level).
"""
query = self.query.clone(sql.UpdateQuery)
query.add_update_fields(values)
query.execute_sql(None)
self._result_cache = None
_update.alters_data = True
##################################################
# PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #
##################################################
@ -692,13 +705,13 @@ def delete_objects(seen_objs):
transaction.commit_unless_managed()
def insert_query(__model, __return_id=False, __raw_values=False, **kwargs):
def insert_query(model, values, return_id=False, raw_values=False):
"""
Inserts a new record for the given model. This provides an interface to
the InsertQuery class and is how Model.save() is implemented. It is not
part of the public API.
"""
query = sql.InsertQuery(__model, connection)
query.insert_values(kwargs, __raw_values)
return query.execute_sql(__return_id)
query = sql.InsertQuery(model, connection)
query.insert_values(values, raw_values)
return query.execute_sql(return_id)

View File

@ -194,11 +194,27 @@ class UpdateQuery(Query):
self.execute_sql(None)
def add_update_values(self, values):
from django.db.models.base import Model
"""
Convert a dictionary of field name to value mappings into an update
query. This is the entry point for the public update() method on
querysets.
"""
values_seq = []
for name, val in values.iteritems():
field, model, direct, m2m = self.model._meta.get_field_by_name(name)
if not direct or m2m:
raise FieldError('Cannot update model field %r (only non-relations and foreign keys permitted).' % field)
values_seq.append((field, model, val))
return self.add_update_fields(values_seq)
def add_update_fields(self, values_seq):
"""
Turn a sequence of (field, model, value) triples into an update query.
Used by add_update_values() as well as the "fast" update path when
saving models.
"""
from django.db.models.base import Model
for field, model, val in values_seq:
# FIXME: Some sort of db_prep_* is probably more appropriate here.
if field.rel and isinstance(val, Model):
val = val.pk
@ -279,17 +295,11 @@ class InsertQuery(Query):
parameters. This provides a way to insert NULL and DEFAULT keywords
into the query, for example.
"""
func = lambda x: self.model._meta.get_field_by_name(x)[0]
# keys() and values() return items in the same order, providing the
# dictionary hasn't changed between calls. So the dual iteration here
# works as intended.
placeholders, values = [], []
for name, val in insert_values.iteritems():
if name == 'pk':
name = self.model._meta.pk.name
# Getting the Field associated w/the name.
field = func(name)
for field, val in insert_values:
if hasattr(field, 'get_placeholder'):
# Some fields (e.g. geo fields) need special munging before
# they can be inserted.