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:
parent
04e3fc8de2
commit
5d462b9ec1
3
TODO.txt
3
TODO.txt
@ -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``.
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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 #
|
||||||
|
@ -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"``.
|
||||||
|
|
||||||
|
@ -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``
|
||||||
=======================================
|
=======================================
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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!"))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user