Skip to content

Commit d4956cb

Browse files
committed
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
1 parent fcfa8b2 commit d4956cb

File tree

4 files changed

+47
-22
lines changed

4 files changed

+47
-22
lines changed

django/db/models/base.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -308,29 +308,28 @@ def save_base(self, raw=False, cls=None):
308308
if manager.filter(pk=pk_val).extra(select={'a': 1}).values('a').order_by():
309309
# It does already exist, so do an UPDATE.
310310
if non_pks:
311-
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]
312-
manager.filter(pk=pk_val).update(**dict(values))
311+
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]
312+
manager.filter(pk=pk_val)._update(values)
313313
else:
314314
record_exists = False
315315
if not pk_set or not record_exists:
316316
if not pk_set:
317-
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)]
317+
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)]
318318
else:
319-
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]
319+
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]
320320

321321
if meta.order_with_respect_to:
322322
field = meta.order_with_respect_to
323-
values.append(('_order', manager.filter(**{field.name: getattr(self, field.attname)}).count()))
323+
values.append((meta.get_field_by_name('_order')[0], manager.filter(**{field.name: getattr(self, field.attname)}).count()))
324324
record_exists = False
325325

326326
update_pk = bool(meta.has_auto_field and not pk_set)
327327
if values:
328328
# Create a new record.
329-
result = manager._insert(__return_id=update_pk, **dict(values))
329+
result = manager._insert(values, return_id=update_pk)
330330
else:
331331
# Create a new record with defaults for everything.
332-
result = manager._insert(__return_id=update_pk,
333-
__raw_values=True, pk=connection.ops.pk_default_value())
332+
result = manager._insert([(meta.pk, connection.ops.pk_default_value())], return_id=update_pk, raw_values=True)
334333

335334
if update_pk:
336335
setattr(self, meta.pk.attname, result)

django/db/models/manager.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,11 @@ def update(self, *args, **kwargs):
123123
def reverse(self, *args, **kwargs):
124124
return self.get_query_set().reverse(*args, **kwargs)
125125

126-
def _insert(self, **kwargs):
127-
return insert_query(self.model, **kwargs)
126+
def _insert(self, values, **kwargs):
127+
return insert_query(self.model, values, **kwargs)
128+
129+
def _update(self, values, **kwargs):
130+
return self.get_query_set()._update(values, **kwargs)
128131

129132
class ManagerDescriptor(object):
130133
# This class ensures managers aren't accessible via model instances.

django/db/models/query.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,19 @@ def update(self, **kwargs):
287287
self._result_cache = None
288288
update.alters_data = True
289289

290+
def _update(self, values):
291+
"""
292+
A version of update that accepts field objects instead of field names.
293+
Used primarily for model saving and not intended for use by general
294+
code (it requires too much poking around at model internals to be
295+
useful at that level).
296+
"""
297+
query = self.query.clone(sql.UpdateQuery)
298+
query.add_update_fields(values)
299+
query.execute_sql(None)
300+
self._result_cache = None
301+
_update.alters_data = True
302+
290303
##################################################
291304
# PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #
292305
##################################################
@@ -692,13 +705,13 @@ def delete_objects(seen_objs):
692705

693706
transaction.commit_unless_managed()
694707

695-
def insert_query(__model, __return_id=False, __raw_values=False, **kwargs):
708+
def insert_query(model, values, return_id=False, raw_values=False):
696709
"""
697710
Inserts a new record for the given model. This provides an interface to
698711
the InsertQuery class and is how Model.save() is implemented. It is not
699712
part of the public API.
700713
"""
701-
query = sql.InsertQuery(__model, connection)
702-
query.insert_values(kwargs, __raw_values)
703-
return query.execute_sql(__return_id)
714+
query = sql.InsertQuery(model, connection)
715+
query.insert_values(values, raw_values)
716+
return query.execute_sql(return_id)
704717

django/db/models/sql/subqueries.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -194,11 +194,27 @@ def clear_related(self, related_field, pk_list):
194194
self.execute_sql(None)
195195

196196
def add_update_values(self, values):
197-
from django.db.models.base import Model
197+
"""
198+
Convert a dictionary of field name to value mappings into an update
199+
query. This is the entry point for the public update() method on
200+
querysets.
201+
"""
202+
values_seq = []
198203
for name, val in values.iteritems():
199204
field, model, direct, m2m = self.model._meta.get_field_by_name(name)
200205
if not direct or m2m:
201206
raise FieldError('Cannot update model field %r (only non-relations and foreign keys permitted).' % field)
207+
values_seq.append((field, model, val))
208+
return self.add_update_fields(values_seq)
209+
210+
def add_update_fields(self, values_seq):
211+
"""
212+
Turn a sequence of (field, model, value) triples into an update query.
213+
Used by add_update_values() as well as the "fast" update path when
214+
saving models.
215+
"""
216+
from django.db.models.base import Model
217+
for field, model, val in values_seq:
202218
# FIXME: Some sort of db_prep_* is probably more appropriate here.
203219
if field.rel and isinstance(val, Model):
204220
val = val.pk
@@ -279,17 +295,11 @@ def insert_values(self, insert_values, raw_values=False):
279295
parameters. This provides a way to insert NULL and DEFAULT keywords
280296
into the query, for example.
281297
"""
282-
func = lambda x: self.model._meta.get_field_by_name(x)[0]
283298
# keys() and values() return items in the same order, providing the
284299
# dictionary hasn't changed between calls. So the dual iteration here
285300
# works as intended.
286301
placeholders, values = [], []
287-
for name, val in insert_values.iteritems():
288-
if name == 'pk':
289-
name = self.model._meta.pk.name
290-
# Getting the Field associated w/the name.
291-
field = func(name)
292-
302+
for field, val in insert_values:
293303
if hasattr(field, 'get_placeholder'):
294304
# Some fields (e.g. geo fields) need special munging before
295305
# they can be inserted.

0 commit comments

Comments
 (0)