1. from django.apps import apps
    
  2. from django.conf import settings
    
  3. from django.db import connection
    
  4. from django.test import TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature
    
  5. 
    
  6. from .models.tablespaces import (
    
  7.     Article,
    
  8.     ArticleRef,
    
  9.     Authors,
    
  10.     Reviewers,
    
  11.     Scientist,
    
  12.     ScientistRef,
    
  13. )
    
  14. 
    
  15. 
    
  16. def sql_for_table(model):
    
  17.     with connection.schema_editor(collect_sql=True) as editor:
    
  18.         editor.create_model(model)
    
  19.     return editor.collected_sql[0]
    
  20. 
    
  21. 
    
  22. def sql_for_index(model):
    
  23.     return "\n".join(
    
  24.         str(sql) for sql in connection.schema_editor()._model_indexes_sql(model)
    
  25.     )
    
  26. 
    
  27. 
    
  28. # We can't test the DEFAULT_TABLESPACE and DEFAULT_INDEX_TABLESPACE settings
    
  29. # because they're evaluated when the model class is defined. As a consequence,
    
  30. # @override_settings doesn't work, and the tests depend
    
  31. class TablespacesTests(TransactionTestCase):
    
  32.     available_apps = ["model_options"]
    
  33. 
    
  34.     def setUp(self):
    
  35.         # The unmanaged models need to be removed after the test in order to
    
  36.         # prevent bad interactions with the flush operation in other tests.
    
  37.         self._old_models = apps.app_configs["model_options"].models.copy()
    
  38. 
    
  39.         for model in Article, Authors, Reviewers, Scientist:
    
  40.             model._meta.managed = True
    
  41. 
    
  42.     def tearDown(self):
    
  43.         for model in Article, Authors, Reviewers, Scientist:
    
  44.             model._meta.managed = False
    
  45. 
    
  46.         apps.app_configs["model_options"].models = self._old_models
    
  47.         apps.all_models["model_options"] = self._old_models
    
  48.         apps.clear_cache()
    
  49. 
    
  50.     def assertNumContains(self, haystack, needle, count):
    
  51.         real_count = haystack.count(needle)
    
  52.         self.assertEqual(
    
  53.             real_count,
    
  54.             count,
    
  55.             "Found %d instances of '%s', expected %d" % (real_count, needle, count),
    
  56.         )
    
  57. 
    
  58.     @skipUnlessDBFeature("supports_tablespaces")
    
  59.     def test_tablespace_for_model(self):
    
  60.         sql = sql_for_table(Scientist).lower()
    
  61.         if settings.DEFAULT_INDEX_TABLESPACE:
    
  62.             # 1 for the table
    
  63.             self.assertNumContains(sql, "tbl_tbsp", 1)
    
  64.             # 1 for the index on the primary key
    
  65.             self.assertNumContains(sql, settings.DEFAULT_INDEX_TABLESPACE, 1)
    
  66.         else:
    
  67.             # 1 for the table + 1 for the index on the primary key
    
  68.             self.assertNumContains(sql, "tbl_tbsp", 2)
    
  69. 
    
  70.     @skipIfDBFeature("supports_tablespaces")
    
  71.     def test_tablespace_ignored_for_model(self):
    
  72.         # No tablespace-related SQL
    
  73.         self.assertEqual(sql_for_table(Scientist), sql_for_table(ScientistRef))
    
  74. 
    
  75.     @skipUnlessDBFeature("supports_tablespaces")
    
  76.     def test_tablespace_for_indexed_field(self):
    
  77.         sql = sql_for_table(Article).lower()
    
  78.         if settings.DEFAULT_INDEX_TABLESPACE:
    
  79.             # 1 for the table
    
  80.             self.assertNumContains(sql, "tbl_tbsp", 1)
    
  81.             # 1 for the primary key + 1 for the index on code
    
  82.             self.assertNumContains(sql, settings.DEFAULT_INDEX_TABLESPACE, 2)
    
  83.         else:
    
  84.             # 1 for the table + 1 for the primary key + 1 for the index on code
    
  85.             self.assertNumContains(sql, "tbl_tbsp", 3)
    
  86. 
    
  87.         # 1 for the index on reference
    
  88.         self.assertNumContains(sql, "idx_tbsp", 1)
    
  89. 
    
  90.     @skipIfDBFeature("supports_tablespaces")
    
  91.     def test_tablespace_ignored_for_indexed_field(self):
    
  92.         # No tablespace-related SQL
    
  93.         self.assertEqual(sql_for_table(Article), sql_for_table(ArticleRef))
    
  94. 
    
  95.     @skipUnlessDBFeature("supports_tablespaces")
    
  96.     def test_tablespace_for_many_to_many_field(self):
    
  97.         sql = sql_for_table(Authors).lower()
    
  98.         # The join table of the ManyToManyField goes to the model's tablespace,
    
  99.         # and its indexes too, unless DEFAULT_INDEX_TABLESPACE is set.
    
  100.         if settings.DEFAULT_INDEX_TABLESPACE:
    
  101.             # 1 for the table
    
  102.             self.assertNumContains(sql, "tbl_tbsp", 1)
    
  103.             # 1 for the primary key
    
  104.             self.assertNumContains(sql, settings.DEFAULT_INDEX_TABLESPACE, 1)
    
  105.         else:
    
  106.             # 1 for the table + 1 for the index on the primary key
    
  107.             self.assertNumContains(sql, "tbl_tbsp", 2)
    
  108.         self.assertNumContains(sql, "idx_tbsp", 0)
    
  109. 
    
  110.         sql = sql_for_index(Authors).lower()
    
  111.         # The ManyToManyField declares no db_tablespace, its indexes go to
    
  112.         # the model's tablespace, unless DEFAULT_INDEX_TABLESPACE is set.
    
  113.         if settings.DEFAULT_INDEX_TABLESPACE:
    
  114.             self.assertNumContains(sql, settings.DEFAULT_INDEX_TABLESPACE, 2)
    
  115.         else:
    
  116.             self.assertNumContains(sql, "tbl_tbsp", 2)
    
  117.         self.assertNumContains(sql, "idx_tbsp", 0)
    
  118. 
    
  119.         sql = sql_for_table(Reviewers).lower()
    
  120.         # The join table of the ManyToManyField goes to the model's tablespace,
    
  121.         # and its indexes too, unless DEFAULT_INDEX_TABLESPACE is set.
    
  122.         if settings.DEFAULT_INDEX_TABLESPACE:
    
  123.             # 1 for the table
    
  124.             self.assertNumContains(sql, "tbl_tbsp", 1)
    
  125.             # 1 for the primary key
    
  126.             self.assertNumContains(sql, settings.DEFAULT_INDEX_TABLESPACE, 1)
    
  127.         else:
    
  128.             # 1 for the table + 1 for the index on the primary key
    
  129.             self.assertNumContains(sql, "tbl_tbsp", 2)
    
  130.         self.assertNumContains(sql, "idx_tbsp", 0)
    
  131. 
    
  132.         sql = sql_for_index(Reviewers).lower()
    
  133.         # The ManyToManyField declares db_tablespace, its indexes go there.
    
  134.         self.assertNumContains(sql, "tbl_tbsp", 0)
    
  135.         self.assertNumContains(sql, "idx_tbsp", 2)