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

[soc2009/multidb] Added a using option to a Model's Meta class. This allows you to select the default database for a specific model, in addition to the global default

git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/multidb@11135 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Alex Gaynor 2009-07-01 05:01:59 +00:00
parent 04e3fc8de2
commit 5d462b9ec1
8 changed files with 65 additions and 14 deletions

View File

@ -26,7 +26,4 @@ that need to be done. I'm trying to be as granular as possible.
4) Wait on the merge of the m2m stuff. 4) Wait on the merge of the m2m stuff.
5) Add the ``using`` Meta option. Tests and docs(these are to be assumed at
each stage from here on out).
6) Time permitting add support for a ``DatabaseManager``. 6) Time permitting add support for a ``DatabaseManager``.

View File

@ -419,8 +419,7 @@ class Model(object):
need for overrides of save() to pass around internal-only parameters need for overrides of save() to pass around internal-only parameters
('raw', 'cls', and 'origin'). ('raw', 'cls', and 'origin').
""" """
if using is None: using = using or self._meta.using or DEFAULT_DB_ALIAS
using = DEFAULT_DB_ALIAS
connection = connections[using] connection = connections[using]
assert not (force_insert and force_update) assert not (force_insert and force_update)
if cls is None: if cls is None:
@ -563,8 +562,7 @@ class Model(object):
parent_obj._collect_sub_objects(seen_objs) parent_obj._collect_sub_objects(seen_objs)
def delete(self, using=None): def delete(self, using=None):
if using is None: using = using or self._meta.using or DEFAULT_DB_ALIAS
using = DEFAULT_DB_ALIAS
connection = connections[using] 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) 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)

View File

@ -21,7 +21,7 @@ get_verbose_name = lambda class_name: re.sub('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|
DEFAULT_NAMES = ('verbose_name', 'db_table', 'ordering', DEFAULT_NAMES = ('verbose_name', 'db_table', 'ordering',
'unique_together', 'permissions', 'get_latest_by', 'unique_together', 'permissions', 'get_latest_by',
'order_with_respect_to', 'app_label', 'db_tablespace', 'order_with_respect_to', 'app_label', 'db_tablespace',
'abstract', 'managed', 'proxy') 'abstract', 'managed', 'proxy', 'using')
class Options(object): class Options(object):
def __init__(self, meta, app_label=None): def __init__(self, meta, app_label=None):
@ -47,6 +47,7 @@ class Options(object):
self.proxy_for_model = None self.proxy_for_model = None
self.parents = SortedDict() self.parents = SortedDict()
self.duplicate_targets = {} self.duplicate_targets = {}
self.using = None
# To handle various inheritance situations, we need to track where # To handle various inheritance situations, we need to track where
# managers came from (concrete or abstract base classes). # managers came from (concrete or abstract base classes).
@ -487,4 +488,3 @@ class Options(object):
Returns the index of the primary key field in the self.fields list. Returns the index of the primary key field in the self.fields list.
""" """
return self.fields.index(self.pk) return self.fields.index(self.pk)

View File

@ -33,12 +33,14 @@ class QuerySet(object):
""" """
def __init__(self, model=None, query=None): def __init__(self, model=None, query=None):
self.model = model self.model = model
connection = connections[DEFAULT_DB_ALIAS] using = model._meta.using or DEFAULT_DB_ALIAS
connection = connections[using]
self.query = query or sql.Query(self.model, connection) self.query = query or sql.Query(self.model, connection)
self._result_cache = None self._result_cache = None
self._iter = None self._iter = None
self._sticky_filter = False self._sticky_filter = False
self._using = connections.alias_for_connection(self.query.connection) self._using = (query and
connections.alias_for_connection(self.query.connection) or using)
######################## ########################
# PYTHON MAGIC METHODS # # PYTHON MAGIC METHODS #

View File

@ -103,7 +103,7 @@ model handling are exactly the same as normal. This includes
unmanaged model, then the intermediate table for the many-to-many join unmanaged model, then the intermediate table for the many-to-many join
will also not be created. However, a the intermediary table between one will also not be created. However, a the intermediary table between one
managed and one unmanaged model *will* be created. managed and one unmanaged model *will* be created.
If you need to change this default behavior, create the intermediary If you need to change this default behavior, create the intermediary
table as an explicit model (with ``managed`` set as needed) and use the table as an explicit model (with ``managed`` set as needed) and use the
:attr:`ManyToManyField.through` attribute to make the relation use your :attr:`ManyToManyField.through` attribute to make the relation use your
@ -210,6 +210,17 @@ set of fields::
unique_together = ("driver", "restaurant") unique_together = ("driver", "restaurant")
``using``
---------
.. attribute:: Options.using
The alias for the default database to be used for this model. If this is not
provided the default is ``'default'``. If it is porvided it can be overidden
at the ``QuerySet`` level with the ``using()`` method.
.. versionadded:: TODO
``verbose_name`` ``verbose_name``
---------------- ----------------
@ -232,4 +243,3 @@ The plural name for the object::
verbose_name_plural = "stories" verbose_name_plural = "stories"
If this isn't given, Django will use :attr:`~Options.verbose_name` + ``"s"``. If this isn't given, Django will use :attr:`~Options.verbose_name` + ``"s"``.

View File

@ -25,6 +25,15 @@ thing to note is that your primary database should have the alias
``'default'``, and any additional databases you have can have whatever alias ``'default'``, and any additional databases you have can have whatever alias
you choose. you choose.
Selecting a Database for a ``Model``
====================================
In addition to the global default database for all models, it is possible to
select a default database on a per-model level. This is done using the
``using`` option in a model's inner ``Meta`` class. When provided this
database becomes the default database for all lookups, saves, and deletes for
this model. It can be overiden on a per-query basis as described below.
Selecting a Database for a ``QuerySet`` Selecting a Database for a ``QuerySet``
======================================= =======================================

View File

@ -1,4 +1,5 @@
from django.db import models from django.conf import settings
from django.db import models, DEFAULT_DB_ALIAS
class Book(models.Model): class Book(models.Model):
title = models.CharField(max_length=100) title = models.CharField(max_length=100)
@ -9,3 +10,15 @@ class Book(models.Model):
class Meta: class Meta:
ordering = ('title',) ordering = ('title',)
if len(settings.DATABASES) > 1:
article_using = filter(lambda o: o != DEFAULT_DB_ALIAS, settings.DATABASES.keys())[0]
class Article(models.Model):
title = models.CharField(max_length=100)
def __unicode__(self):
return self.title
class Meta:
ordering = ('title',)
using = article_using

View File

@ -6,6 +6,13 @@ from django.test import TestCase
from models import Book from models import Book
try:
# we only have these models if the user is using multi-db, it's safe the
# run the tests without them though.
from models import Article, article_using
except ImportError:
pass
class ConnectionHandlerTestCase(TestCase): class ConnectionHandlerTestCase(TestCase):
def setUp(self): def setUp(self):
settings.DATABASES['__test_db'] = { settings.DATABASES['__test_db'] = {
@ -71,3 +78,18 @@ class QueryTestCase(TestCase):
months = Book.objects.dates('published', 'month').using(db) months = Book.objects.dates('published', 'month').using(db)
self.assertEqual(sorted(o.month for o in months), [5, 12]) self.assertEqual(sorted(o.month for o in months), [5, 12])
if len(settings.DATABASES) > 1:
class MetaUsingTestCase(TestCase):
def test_meta_using_queries(self):
a = Article.objects.create(title="Django Rules!")
self.assertEqual(Article.objects.get(title="Django Rules!"), a)
for db in connections:
if db == article_using:
self.assertEqual(Article.objects.using(db).get(title="Django Rules!"), a)
else:
self.assertRaises(Article.DoesNotExist,
lambda: Article.objects.using(db).get(title="Django Rules!"))
a.delete()
self.assertRaises(Article.DoesNotExist,
lambda: Article.objects.get(title="Django Rules!"))