summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorAnubhav Joshi <anubhav9042@gmail.com>2014-06-10 17:34:19 +0530
committerTim Graham <timograham@gmail.com>2014-06-17 16:16:02 -0400
commit91f1b6dcdc5da47d7794a55e3114820407a5bd62 (patch)
treef27e7176a448035362fdc95caa1e32013ef84ba1 /tests
parente4708385fde278fbbff5428f756632455c2bcaf5 (diff)
Fixed #13711 -- Model check added to ensure that auto-generated column name is within limits of the database.
Thanks russellm for report and Tim Graham for review.
Diffstat (limited to 'tests')
-rw-r--r--tests/backends/models.py22
-rw-r--r--tests/backends/tests.py9
-rw-r--r--tests/invalid_models_tests/test_models.py143
3 files changed, 155 insertions, 19 deletions
diff --git a/tests/backends/models.py b/tests/backends/models.py
index 2a94c249ca..b550212d0b 100644
--- a/tests/backends/models.py
+++ b/tests/backends/models.py
@@ -4,7 +4,7 @@ from django.contrib.contenttypes.fields import (
GenericForeignKey, GenericRelation
)
from django.contrib.contenttypes.models import ContentType
-from django.db import models, connection
+from django.db import models
from django.utils.encoding import python_2_unicode_compatible
@@ -31,17 +31,15 @@ class SchoolClass(models.Model):
day = models.CharField(max_length=9, blank=True)
last_updated = models.DateTimeField()
-# Unfortunately, the following model breaks MySQL hard.
-# Until #13711 is fixed, this test can't be run under MySQL.
-if connection.features.supports_long_model_names:
- class VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ(models.Model):
- class Meta:
- # We need to use a short actual table name or
- # we hit issue #8548 which we're not testing!
- verbose_name = 'model_with_long_table_name'
- primary_key_is_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.AutoField(primary_key=True)
- charfield_is_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.CharField(max_length=100)
- m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.ManyToManyField(Person, blank=True)
+
+class VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ(models.Model):
+ class Meta:
+ # We need to use a short actual table name or
+ # we hit issue #8548 which we're not testing!
+ verbose_name = 'model_with_long_table_name'
+ primary_key_is_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.AutoField(primary_key=True)
+ charfield_is_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.CharField(max_length=100)
+ m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.ManyToManyField(Person, blank=True)
class Tag(models.Model):
diff --git a/tests/backends/tests.py b/tests/backends/tests.py
index 9742358a59..40b0b55082 100644
--- a/tests/backends/tests.py
+++ b/tests/backends/tests.py
@@ -379,26 +379,23 @@ class LongNameTest(TestCase):
check it is. Refs #8901.
"""
- @skipUnlessDBFeature('supports_long_model_names')
def test_sequence_name_length_limits_create(self):
"""Test creation of model with long name and long pk name doesn't error. Ref #8901"""
- models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ.objects.create()
+ models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ.objects.create()
- @skipUnlessDBFeature('supports_long_model_names')
def test_sequence_name_length_limits_m2m(self):
"""Test an m2m save of a model with a long name and a long m2m field name doesn't error as on Django >=1.2 this now uses object saves. Ref #8901"""
- obj = models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ.objects.create()
+ obj = models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ.objects.create()
rel_obj = models.Person.objects.create(first_name='Django', last_name='Reinhardt')
obj.m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.add(rel_obj)
- @skipUnlessDBFeature('supports_long_model_names')
def test_sequence_name_length_limits_flush(self):
"""Test that sequence resetting as part of a flush with model with long name and long pk name doesn't error. Ref #8901"""
# A full flush is expensive to the full test, so we dig into the
# internals to generate the likely offending SQL and run it manually
# Some convenience aliases
- VLM = models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
+ VLM = models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
VLM_m2m = VLM.m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.through
tables = [
VLM._meta.db_table,
diff --git a/tests/invalid_models_tests/test_models.py b/tests/invalid_models_tests/test_models.py
index 9cbaa8449c..8550dc3065 100644
--- a/tests/invalid_models_tests/test_models.py
+++ b/tests/invalid_models_tests/test_models.py
@@ -1,13 +1,36 @@
# -*- encoding: utf-8 -*-
from __future__ import unicode_literals
+import unittest
+
+from django.conf import settings
from django.core.checks import Error
-from django.db import models
+from django.db import models, connections
from django.test.utils import override_settings
from .base import IsolatedModelsTestCase
+def get_max_column_name_length():
+ allowed_len = None
+ db_alias = None
+
+ for db in settings.DATABASES.keys():
+ connection = connections[db]
+ max_name_length = connection.ops.max_name_length()
+ if max_name_length is None:
+ continue
+ else:
+ if allowed_len is None:
+ allowed_len = max_name_length
+ db_alias = db
+ elif max_name_length < allowed_len:
+ allowed_len = max_name_length
+ db_alias = db
+
+ return (allowed_len, db_alias)
+
+
class IndexTogetherTests(IsolatedModelsTestCase):
def test_non_iterable(self):
@@ -252,6 +275,124 @@ class FieldNamesTests(IsolatedModelsTestCase):
]
self.assertEqual(errors, expected)
+ max_column_name_length, column_limit_db_alias = get_max_column_name_length()
+
+ @unittest.skipIf(max_column_name_length is None,
+ "The database doesn't have a column name length limit.")
+ def test_M2M_long_column_name(self):
+ """
+ #13711 -- Model check for long M2M column names when database has
+ column name length limits.
+ """
+ allowed_len, db_alias = get_max_column_name_length()
+
+ # A model with very long name which will be used to set relations to.
+ class VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz(models.Model):
+ title = models.CharField(max_length=11)
+
+ # Main model for which checks will be performed.
+ class ModelWithLongField(models.Model):
+ m2m_field = models.ManyToManyField(
+ VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz,
+ related_name="rn1"
+ )
+ m2m_field2 = models.ManyToManyField(
+ VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz,
+ related_name="rn2", through='m2msimple'
+ )
+ m2m_field3 = models.ManyToManyField(
+ VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz,
+ related_name="rn3",
+ through='m2mcomplex'
+ )
+ fk = models.ForeignKey(VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz, related_name="rn4")
+
+ # Models used for setting `through` in M2M field.
+ class m2msimple(models.Model):
+ id2 = models.ForeignKey(ModelWithLongField)
+
+ class m2mcomplex(models.Model):
+ id2 = models.ForeignKey(ModelWithLongField)
+
+ long_field_name = 'a' * (self.max_column_name_length + 1)
+ models.ForeignKey(
+ VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
+ ).contribute_to_class(m2msimple, long_field_name)
+
+ models.ForeignKey(
+ VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz,
+ db_column=long_field_name
+ ).contribute_to_class(m2mcomplex, long_field_name)
+
+ errors = ModelWithLongField.check()
+
+ # First error because of M2M field set on the model with long name.
+ m2m_long_name = "verylongmodelnamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz_id"
+ expected = [
+ Error(
+ ('Autogenerated column name too long for M2M field "%s". '
+ 'Maximum length is "%s" for database "%s".'
+ % (m2m_long_name, self.max_column_name_length, self.column_limit_db_alias)),
+ hint=("Use 'through' to create a separate model for "
+ "M2M and then set column_name using 'db_column'."),
+ obj=ModelWithLongField,
+ id='models.E019',
+ )
+ ]
+
+ # Second error because the FK specified in the `through` model
+ # `m2msimple` has auto-genererated name longer than allowed.
+ # There will be no check errors in the other M2M because it
+ # specifies db_column for the FK in `through` model even if the actual
+ # name is longer than the limits of the database.
+ expected.append(
+ Error(
+ ('Autogenerated column name too long for M2M field "%s_id". '
+ 'Maximum length is "%s" for database "%s".'
+ % (long_field_name, self.max_column_name_length, self.column_limit_db_alias)),
+ hint=("Use 'through' to create a separate model for "
+ "M2M and then set column_name using 'db_column'."),
+ obj=ModelWithLongField,
+ id='models.E019',
+ )
+ )
+
+ self.assertEqual(errors, expected)
+
+ @unittest.skipIf(max_column_name_length is None,
+ "The database doesn't have a column name length limit.")
+ def test_local_field_long_column_name(self):
+ """
+ #13711 -- Model check for long column names
+ when database does not support long names.
+ """
+ allowed_len, db_alias = get_max_column_name_length()
+
+ class ModelWithLongField(models.Model):
+ title = models.CharField(max_length=11)
+
+ long_field_name = 'a' * (self.max_column_name_length + 1)
+ long_field_name2 = 'b' * (self.max_column_name_length + 1)
+ models.CharField(max_length=11).contribute_to_class(ModelWithLongField, long_field_name)
+ models.CharField(max_length=11, db_column='vlmn').contribute_to_class(ModelWithLongField, long_field_name2)
+
+ errors = ModelWithLongField.check()
+
+ # Error because of the field with long name added to the model
+ # without specifying db_column
+ expected = [
+ Error(
+ ('Autogenerated column name too long for field "%s". '
+ 'Maximum length is "%s" for database "%s".'
+ % (long_field_name, self.max_column_name_length, self.column_limit_db_alias)),
+ hint="Set the column name manually using 'db_column'.",
+ obj=ModelWithLongField,
+ id='models.E018',
+ )
+ ]
+
+ self.assertEqual(errors, expected)
+
def test_including_separator(self):
class Model(models.Model):
some__field = models.IntegerField()