1
0
mirror of https://github.com/django/django.git synced 2025-07-05 10:19:20 +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(): if manager.filter(pk=pk_val).extra(select={'a': 1}).values('a').order_by():
# It does already exist, so do an UPDATE. # It does already exist, so do an UPDATE.
if non_pks: 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] 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(**dict(values)) manager.filter(pk=pk_val)._update(values)
else: else:
record_exists = False record_exists = False
if not pk_set or not record_exists: if not pk_set or not record_exists:
if not pk_set: 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: 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: if meta.order_with_respect_to:
field = 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 record_exists = False
update_pk = bool(meta.has_auto_field and not pk_set) update_pk = bool(meta.has_auto_field and not pk_set)
if values: if values:
# Create a new record. # Create a new record.
result = manager._insert(__return_id=update_pk, **dict(values)) result = manager._insert(values, return_id=update_pk)
else: else:
# Create a new record with defaults for everything. # Create a new record with defaults for everything.
result = manager._insert(__return_id=update_pk, result = manager._insert([(meta.pk, connection.ops.pk_default_value())], return_id=update_pk, raw_values=True)
__raw_values=True, pk=connection.ops.pk_default_value())
if update_pk: if update_pk:
setattr(self, meta.pk.attname, result) setattr(self, meta.pk.attname, result)

View File

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

View File

@ -287,6 +287,19 @@ class QuerySet(object):
self._result_cache = None self._result_cache = None
update.alters_data = True 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 # # PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #
################################################## ##################################################
@ -692,13 +705,13 @@ def delete_objects(seen_objs):
transaction.commit_unless_managed() 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 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 the InsertQuery class and is how Model.save() is implemented. It is not
part of the public API. part of the public API.
""" """
query = sql.InsertQuery(__model, connection) query = sql.InsertQuery(model, connection)
query.insert_values(kwargs, __raw_values) query.insert_values(values, raw_values)
return query.execute_sql(__return_id) return query.execute_sql(return_id)

View File

@ -194,11 +194,27 @@ class UpdateQuery(Query):
self.execute_sql(None) self.execute_sql(None)
def add_update_values(self, values): 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(): for name, val in values.iteritems():
field, model, direct, m2m = self.model._meta.get_field_by_name(name) field, model, direct, m2m = self.model._meta.get_field_by_name(name)
if not direct or m2m: if not direct or m2m:
raise FieldError('Cannot update model field %r (only non-relations and foreign keys permitted).' % field) 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. # FIXME: Some sort of db_prep_* is probably more appropriate here.
if field.rel and isinstance(val, Model): if field.rel and isinstance(val, Model):
val = val.pk val = val.pk
@ -279,17 +295,11 @@ class InsertQuery(Query):
parameters. This provides a way to insert NULL and DEFAULT keywords parameters. This provides a way to insert NULL and DEFAULT keywords
into the query, for example. 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 # keys() and values() return items in the same order, providing the
# dictionary hasn't changed between calls. So the dual iteration here # dictionary hasn't changed between calls. So the dual iteration here
# works as intended. # works as intended.
placeholders, values = [], [] placeholders, values = [], []
for name, val in insert_values.iteritems(): for field, val in insert_values:
if name == 'pk':
name = self.model._meta.pk.name
# Getting the Field associated w/the name.
field = func(name)
if hasattr(field, 'get_placeholder'): if hasattr(field, 'get_placeholder'):
# Some fields (e.g. geo fields) need special munging before # Some fields (e.g. geo fields) need special munging before
# they can be inserted. # they can be inserted.