summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/db/models/manager.py40
-rw-r--r--tests/othertests/manager_schema_manipulation.py131
2 files changed, 169 insertions, 2 deletions
diff --git a/django/db/models/manager.py b/django/db/models/manager.py
index 46a1710c1c..71a3ea5403 100644
--- a/django/db/models/manager.py
+++ b/django/db/models/manager.py
@@ -20,7 +20,9 @@ def ensure_default_manager(sender):
except FieldDoesNotExist:
pass
cls.add_to_class('objects', Manager())
-
+ elif cls._default_manager.model != cls:
+ # cls is an inherited model; don't want the parent manager
+ cls.add_to_class('objects', Manager())
dispatcher.connect(ensure_default_manager, signal=signals.class_prepared)
class Manager(object):
@@ -38,7 +40,9 @@ class Manager(object):
# TODO: Use weakref because of possible memory leak / circular reference.
self.model = model
setattr(model, name, ManagerDescriptor(self))
- if not hasattr(model, '_default_manager') or self.creation_counter < model._default_manager.creation_counter:
+ if not hasattr(model, '_default_manager') \
+ or self.creation_counter < model._default_manager.creation_counter \
+ or model._default_manager.model != model:
model._default_manager = self
#######################
@@ -102,6 +106,38 @@ class Manager(object):
def values(self, *args, **kwargs):
return self.get_query_set().values(*args, **kwargs)
+ #######################
+ # SCHEMA MANIPULATION #
+ #######################
+
+ def install(self, initial_data=False):
+ """Install my model's table, indexes and (if requested) initial data.
+
+ Returns a 2-tuple of the lists of statements executed and
+ statements pending. Pending statements are those that could
+ not yet be executed, such as foreign key constraints for
+ tables that don't exist at install time.
+ """
+ creator = self.model._meta.connection_info.get_creation_module()
+ run, pending = creator.builder.get_create_table(self.model)
+ run += creator.builder.get_create_indexes(self.model)
+ pending += creator.builder.get_create_many_to_many(self.model)
+ if initial_data:
+ run += creator.builder.get_initialdata(self.model)
+
+ for statement in run:
+ statement.execute()
+ return pending
+
+ def load_initial_data(self):
+ """Load initial data for my model into the database."""
+ pass # FIXME
+
+ def drop(self):
+ """Drop my model's table."""
+ pass # FIXME
+
+
class ManagerDescriptor(object):
# This class ensures managers aren't accessible via model instances.
# For example, Poll.objects works, but poll_obj.objects raises AttributeError.
diff --git a/tests/othertests/manager_schema_manipulation.py b/tests/othertests/manager_schema_manipulation.py
new file mode 100644
index 0000000000..e3c8fb37b9
--- /dev/null
+++ b/tests/othertests/manager_schema_manipulation.py
@@ -0,0 +1,131 @@
+"""
+# Django uses a model's default manager to perform schema manipulations such as
+# creating or dropping the model's table.
+
+>>> from django.db import models
+
+# default connection
+>>> class DA(models.Model):
+... name = models.CharField(maxlength=20)
+...
+... def __str__(self):
+... return self.name
+
+# connection django_test_db_a
+>>> class PA(models.Model):
+... name = models.CharField(maxlength=20)
+... # This creates a cycle in the dependency graph
+... c = models.ForeignKey('PC', null=True)
+...
+... def __str__(self):
+... return self.name
+...
+... class Meta:
+... db_connection = 'django_test_db_a'
+
+>>> class PB(models.Model):
+... name = models.CharField(maxlength=20)
+... a = models.ForeignKey(PA)
+...
+... def __str__(self):
+... return self.name
+...
+... class Meta:
+... db_connection = 'django_test_db_a'
+
+>>> class PC(models.Model):
+... name = models.CharField(maxlength=20)
+... b = models.ForeignKey(PB)
+...
+... def __str__(self):
+... return self.name
+...
+... class Meta:
+... db_connection = 'django_test_db_a'
+
+# connection django_test_db_b
+>>> class QA(models.Model):
+... name = models.CharField(maxlength=20)
+...
+... def __str__(self):
+... return self.name
+...
+... class Meta:
+... db_connection = 'django_test_db_b'
+
+>>> class QB(models.Model):
+... name = models.CharField(maxlength=20)
+... a = models.ForeignKey(QA)
+...
+... def __str__(self):
+... return self.name
+...
+... class Meta:
+... db_connection = 'django_test_db_b'
+
+# many-many
+>>> class QC(models.Model):
+... name = models.CharField(maxlength=20)
+...
+... def __str__(self):
+... return self.name
+...
+... class Meta:
+... db_connection = 'django_test_db_b'
+
+>>> class QD(models.Model):
+... name = models.CharField(maxlength=20)
+... qcs = models.ManyToManyField(QC)
+...
+... def __str__(self):
+... return self.name
+...
+... class Meta:
+... db_connection = 'django_test_db_b'
+
+# Using the manager, models can be installed individually, whether they
+# use the default connection or a named connection.
+
+>>> DA.objects.install()
+[]
+>>> QA.objects.install()
+[]
+>>> QB.objects.install()
+[]
+>>> DA.objects.all()
+[]
+>>> list(QA.objects.all())
+[]
+>>> list(QB.objects.all())
+[]
+>>> QA(name="something").save()
+>>> QA.objects.all()
+[<QA: something>]
+
+# The `install()` method returns a tuple, the first element of which is a
+# list of statements that were executed, and the second, pending
+# statements that could not be executed because (for instance) they are
+# meant to establish foreign key relationships to tables that don't
+# exist. These are bound to the model's connection and should
+# be executed after all models in the app have been installed.
+
+# NOTE: pretend db supports constraints for this test
+>>> real_cnst = PA._meta.connection_info.backend.supports_constraints
+>>> PA._meta.connection_info.backend.supports_constraints = True
+>>> result = PA.objects.install()
+>>> result
+[BoundStatement('ALTER TABLE "othertests_pa" ADD CONSTRAINT "c_id_referencing_othertests_pc_id" FOREIGN KEY ("c_id") REFERENCES "othertests_pc" ("id");')]
+
+# NOTE: restore real constraint flag
+>>> PA._meta.connection_info.backend.supports_constraints = real_cnst
+
+# Models with many-many relationships will also have pending statement
+# lists. Like other pending statements, these should be executed after
+# all models in the app have been installed.
+
+>>> QC.objects.install()
+[]
+>>> QD.objects.install()
+[BoundStatement('CREATE TABLE "othertests_qd_qcs" (...);')]
+
+"""