summaryrefslogtreecommitdiff
path: root/django/db/models/base.py
diff options
context:
space:
mode:
authorRussell Keith-Magee <russell@keith-magee.com>2009-12-22 15:18:51 +0000
committerRussell Keith-Magee <russell@keith-magee.com>2009-12-22 15:18:51 +0000
commitff60c5f9de3e8690d1e86f3e9e3f7248a15397c8 (patch)
treea4cb0ebdd55fcaf8c8855231b6ad3e1a7bf45bee /django/db/models/base.py
parent7ef212af149540aa2da577a960d0d87029fd1514 (diff)
Fixed #1142 -- Added multiple database support.
This monster of a patch is the result of Alex Gaynor's 2009 Google Summer of Code project. Congratulations to Alex for a job well done. Big thanks also go to: * Justin Bronn for keeping GIS in line with the changes, * Karen Tracey and Jani Tiainen for their help testing Oracle support * Brett Hoerner, Jon Loyens, and Craig Kimmerer for their feedback. * Malcolm Treddinick for his guidance during the GSoC submission process. * Simon Willison for driving the original design process * Cal Henderson for complaining about ponies he wanted. ... and everyone else too numerous to mention that helped to bring this feature into fruition. git-svn-id: http://code.djangoproject.com/svn/django/trunk@11952 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django/db/models/base.py')
-rw-r--r--django/db/models/base.py77
1 files changed, 49 insertions, 28 deletions
diff --git a/django/db/models/base.py b/django/db/models/base.py
index 5b727a059f..3464ae6712 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -9,7 +9,7 @@ from django.db.models.fields.related import OneToOneRel, ManyToOneRel, OneToOneF
from django.db.models.query import delete_objects, Q
from django.db.models.query_utils import CollectedObjects, DeferredAttribute
from django.db.models.options import Options
-from django.db import connection, transaction, DatabaseError
+from django.db import connections, transaction, DatabaseError, DEFAULT_DB_ALIAS
from django.db.models import signals
from django.db.models.loading import register_models, get_model
import django.utils.copycompat as copy
@@ -230,6 +230,13 @@ class ModelBase(type):
signals.class_prepared.send(sender=cls)
+class ModelState(object):
+ """
+ A class for storing instance state
+ """
+ def __init__(self, db=None):
+ self.db = db
+
class Model(object):
__metaclass__ = ModelBase
_deferred = False
@@ -237,6 +244,9 @@ class Model(object):
def __init__(self, *args, **kwargs):
signals.pre_init.send(sender=self.__class__, args=args, kwargs=kwargs)
+ # Set up the storage for instane state
+ self._state = ModelState()
+
# There is a rather weird disparity here; if kwargs, it's set, then args
# overrides it. It should be one or the other; don't duplicate the work
# The reason for the kwargs check is that standard iterator passes in by
@@ -404,7 +414,7 @@ class Model(object):
return getattr(self, field_name)
return getattr(self, field.attname)
- def save(self, force_insert=False, force_update=False):
+ def save(self, force_insert=False, force_update=False, using=None):
"""
Saves the current instance. Override this in a subclass if you want to
control the saving process.
@@ -416,18 +426,20 @@ class Model(object):
if force_insert and force_update:
raise ValueError("Cannot force both insert and updating in "
"model saving.")
- self.save_base(force_insert=force_insert, force_update=force_update)
+ self.save_base(using=using, force_insert=force_insert, force_update=force_update)
save.alters_data = True
- def save_base(self, raw=False, cls=None, origin=None,
- force_insert=False, force_update=False):
+ def save_base(self, raw=False, cls=None, origin=None, force_insert=False,
+ force_update=False, using=None):
"""
Does the heavy-lifting involved in saving. Subclasses shouldn't need to
override this method. It's separate from save() in order to hide the
need for overrides of save() to pass around internal-only parameters
('raw', 'cls', and 'origin').
"""
+ using = using or self._state.db or DEFAULT_DB_ALIAS
+ connection = connections[using]
assert not (force_insert and force_update)
if cls is None:
cls = self.__class__
@@ -458,7 +470,7 @@ class Model(object):
if field and getattr(self, parent._meta.pk.attname) is None and getattr(self, field.attname) is not None:
setattr(self, parent._meta.pk.attname, getattr(self, field.attname))
- self.save_base(cls=parent, origin=org)
+ self.save_base(cls=parent, origin=org, using=using)
if field:
setattr(self, field.attname, self._get_pk_val(parent._meta))
@@ -476,11 +488,11 @@ class Model(object):
if pk_set:
# Determine whether a record with the primary key already exists.
if (force_update or (not force_insert and
- manager.filter(pk=pk_val).exists())):
+ manager.using(using).filter(pk=pk_val).exists())):
# It does already exist, so do an UPDATE.
if force_update or non_pks:
values = [(f, None, (raw and getattr(self, f.attname) or f.pre_save(self, False))) for f in non_pks]
- rows = manager.filter(pk=pk_val)._update(values)
+ rows = manager.using(using).filter(pk=pk_val)._update(values)
if force_update and not rows:
raise DatabaseError("Forced update did not affect any rows.")
else:
@@ -489,27 +501,33 @@ class Model(object):
if not pk_set:
if force_update:
raise ValueError("Cannot force an update in save() with no primary key.")
- 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)]
+ values = [(f, f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True), connection=connection))
+ for f in meta.local_fields if not isinstance(f, AutoField)]
else:
- 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]
+ values = [(f, f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True), connection=connection))
+ for f in meta.local_fields]
if meta.order_with_respect_to:
field = meta.order_with_respect_to
- values.append((meta.get_field_by_name('_order')[0], manager.filter(**{field.name: getattr(self, field.attname)}).count()))
+ values.append((meta.get_field_by_name('_order')[0], manager.using(using).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(values, return_id=update_pk)
+ result = manager._insert(values, return_id=update_pk, using=using)
else:
# Create a new record with defaults for everything.
- result = manager._insert([(meta.pk, connection.ops.pk_default_value())], return_id=update_pk, raw_values=True)
+ result = manager._insert([(meta.pk, connection.ops.pk_default_value())], return_id=update_pk, raw_values=True, using=using)
if update_pk:
setattr(self, meta.pk.attname, result)
- transaction.commit_unless_managed()
+ transaction.commit_unless_managed(using=using)
+
+ # Store the database on which the object was saved
+ self._state.db = using
+ # Signal that the save is complete
if origin and not meta.auto_created:
signals.post_save.send(sender=origin, instance=self,
created=(not record_exists), raw=raw)
@@ -572,7 +590,9 @@ class Model(object):
# delete it and all its descendents.
parent_obj._collect_sub_objects(seen_objs)
- def delete(self):
+ def delete(self, using=None):
+ using = using or self._state.db or DEFAULT_DB_ALIAS
+ connection = connections[using]
assert self._get_pk_val() is not None, "%s object can't be deleted because its %s attribute is set to None." % (self._meta.object_name, self._meta.pk.attname)
# Find all the objects than need to be deleted.
@@ -580,7 +600,7 @@ class Model(object):
self._collect_sub_objects(seen_objs)
# Actually delete the objects.
- delete_objects(seen_objs)
+ delete_objects(seen_objs, using)
delete.alters_data = True
@@ -594,7 +614,7 @@ class Model(object):
param = smart_str(getattr(self, field.attname))
q = Q(**{'%s__%s' % (field.name, op): param})
q = q|Q(**{field.name: param, 'pk__%s' % op: self.pk})
- qs = self.__class__._default_manager.filter(**kwargs).filter(q).order_by('%s%s' % (order, field.name), '%spk' % order)
+ qs = self.__class__._default_manager.using(self._state.db).filter(**kwargs).filter(q).order_by('%s%s' % (order, field.name), '%spk' % order)
try:
return qs[0]
except IndexError:
@@ -603,17 +623,16 @@ class Model(object):
def _get_next_or_previous_in_order(self, is_next):
cachename = "__%s_order_cache" % is_next
if not hasattr(self, cachename):
- qn = connection.ops.quote_name
- op = is_next and '>' or '<'
+ op = is_next and 'gt' or 'lt'
order = not is_next and '-_order' or '_order'
order_field = self._meta.order_with_respect_to
- # FIXME: When querysets support nested queries, this can be turned
- # into a pure queryset operation.
- where = ['%s %s (SELECT %s FROM %s WHERE %s=%%s)' % \
- (qn('_order'), op, qn('_order'),
- qn(self._meta.db_table), qn(self._meta.pk.column))]
- params = [self.pk]
- obj = self._default_manager.filter(**{order_field.name: getattr(self, order_field.attname)}).extra(where=where, params=params).order_by(order)[:1].get()
+ obj = self._default_manager.filter(**{
+ order_field.name: getattr(self, order_field.attname)
+ }).filter(**{
+ '_order__%s' % op: self._default_manager.values('_order').filter(**{
+ self._meta.pk.name: self.pk
+ })
+ }).order_by(order)[:1].get()
setattr(self, cachename, obj)
return getattr(self, cachename)
@@ -627,14 +646,16 @@ class Model(object):
# ORDERING METHODS #########################
-def method_set_order(ordered_obj, self, id_list):
+def method_set_order(ordered_obj, self, id_list, using=None):
+ if using is None:
+ using = DEFAULT_DB_ALIAS
rel_val = getattr(self, ordered_obj._meta.order_with_respect_to.rel.field_name)
order_name = ordered_obj._meta.order_with_respect_to.name
# FIXME: It would be nice if there was an "update many" version of update
# for situations like this.
for i, j in enumerate(id_list):
ordered_obj.objects.filter(**{'pk': j, order_name: rel_val}).update(_order=i)
- transaction.commit_unless_managed()
+ transaction.commit_unless_managed(using=using)
def method_get_order(ordered_obj, self):