Loading django/db/backends/__init__.py +0 −3 Original line number Diff line number Diff line Loading @@ -520,9 +520,6 @@ class BaseDatabaseFeatures(object): # at the end of each save operation? supports_forward_references = True # Does the backend allow very long model names without error? supports_long_model_names = True # Is there a REAL datatype in addition to floats/doubles? has_real_datatype = False supports_subqueries_in_group_by = True Loading django/db/backends/mysql/base.py +0 −1 Original line number Diff line number Diff line Loading @@ -171,7 +171,6 @@ class DatabaseFeatures(BaseDatabaseFeatures): has_select_for_update = True has_select_for_update_nowait = False supports_forward_references = False supports_long_model_names = False # XXX MySQL DB-API drivers currently fail on binary data on Python 3. supports_binary_field = six.PY2 supports_microsecond_precision = False Loading django/db/models/base.py +72 −1 Original line number Diff line number Diff line Loading @@ -12,7 +12,7 @@ from django.conf import settings from django.core import checks from django.core.exceptions import (ObjectDoesNotExist, MultipleObjectsReturned, FieldError, ValidationError, NON_FIELD_ERRORS) from django.db import (router, transaction, DatabaseError, from django.db import (router, connections, transaction, DatabaseError, DEFAULT_DB_ALIAS, DJANGO_VERSION_PICKLE_KEY) from django.db.models.deletion import Collector from django.db.models.fields import AutoField, FieldDoesNotExist Loading Loading @@ -1068,6 +1068,7 @@ class Model(six.with_metaclass(ModelBase)): if not cls._meta.swapped: errors.extend(cls._check_fields(**kwargs)) errors.extend(cls._check_m2m_through_same_relationship()) errors.extend(cls._check_long_column_names()) clash_errors = cls._check_id_field() + cls._check_field_name_clashes() errors.extend(clash_errors) # If there are field name clashes, hide consequent column name Loading Loading @@ -1451,6 +1452,76 @@ class Model(six.with_metaclass(ModelBase)): ) return errors @classmethod def _check_long_column_names(cls): """ Check that any auto-generated column names are shorter than the limits for each database in which the model will be created. """ errors = [] allowed_len = None db_alias = None # Find the minimum max allowed length among all specified db_aliases. for db in settings.DATABASES.keys(): # skip databases where the model won't be created if not router.allow_migrate(db, cls): continue 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 if allowed_len is None: return errors for f in cls._meta.local_fields: _, column_name = f.get_attname_column() # Check if auto-generated name for the field is too long # for the database. if (f.db_column is None and column_name is not None and len(column_name) > allowed_len): errors.append( checks.Error( 'Autogenerated column name too long for field "%s". ' 'Maximum length is "%s" for database "%s".' % (column_name, allowed_len, db_alias), hint="Set the column name manually using 'db_column'.", obj=cls, id='models.E018', ) ) for f in cls._meta.local_many_to_many: # Check if auto-generated name for the M2M field is too long # for the database. for m2m in f.rel.through._meta.local_fields: _, rel_name = m2m.get_attname_column() if (m2m.db_column is None and rel_name is not None and len(rel_name) > allowed_len): errors.append( checks.Error( 'Autogenerated column name too long for M2M field ' '"%s". Maximum length is "%s" for database "%s".' % (rel_name, allowed_len, db_alias), hint=("Use 'through' to create a separate model " "for M2M and then set column_name using " "'db_column'."), obj=cls, id='models.E019', ) ) return errors ############################################ # HELPER FUNCTIONS (CURRIED MODEL METHODS) # Loading docs/ref/checks.txt +5 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,11 @@ Models * **models.E016**: ``index_together/unique_together`` refers to field ``<field_name>`` which is not local to model ``<model>``. * **models.E017**: Proxy model ``<model>`` contains model fields. * **models.E018**: Autogenerated column name too long for field ``<field>``. Maximum length is ``<maximum length>`` for database ``<alias>``. * **models.E019**: Autogenerated column name too long for M2M field ``<M2M field>``. Maximum length is ``<maximum length>`` for database ``<alias>``. Fields ~~~~~~ Loading docs/releases/1.8.txt +22 −0 Original line number Diff line number Diff line Loading @@ -305,6 +305,28 @@ Now to implement the same behavior, you have to create an ``parser.add_argument`` to add any custom arguments, as parser is now an :py:class:`argparse.ArgumentParser` instance. Model check ensures auto-generated column names are within limits specified by database ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A field name that's longer than the column name length supported by a database can create problems. For example, with MySQL you'll get an exception trying to create the column, and with PostgreSQL the column name is truncated by the database (you may see a warning in the PostgreSQL logs). A model check has been introduced to better alert users to this scenario before the actual creation of database tables. If you have an existing model where this check seems to be a false positive, for example on PostgreSQL where the name was already being truncated, simply use :attr:`~django.db.models.Field.db_column` to specify the name that's being used. The check also applies to the columns generated in an implicit ``ManyToManyField.through`` model. If you run into an issue there, use :attr:`~django.db.models.ManyToManyField.through` to create an explicit model and then specify :attr:`~django.db.models.Field.db_column` on its column(s) as needed. Miscellaneous ~~~~~~~~~~~~~ Loading Loading
django/db/backends/__init__.py +0 −3 Original line number Diff line number Diff line Loading @@ -520,9 +520,6 @@ class BaseDatabaseFeatures(object): # at the end of each save operation? supports_forward_references = True # Does the backend allow very long model names without error? supports_long_model_names = True # Is there a REAL datatype in addition to floats/doubles? has_real_datatype = False supports_subqueries_in_group_by = True Loading
django/db/backends/mysql/base.py +0 −1 Original line number Diff line number Diff line Loading @@ -171,7 +171,6 @@ class DatabaseFeatures(BaseDatabaseFeatures): has_select_for_update = True has_select_for_update_nowait = False supports_forward_references = False supports_long_model_names = False # XXX MySQL DB-API drivers currently fail on binary data on Python 3. supports_binary_field = six.PY2 supports_microsecond_precision = False Loading
django/db/models/base.py +72 −1 Original line number Diff line number Diff line Loading @@ -12,7 +12,7 @@ from django.conf import settings from django.core import checks from django.core.exceptions import (ObjectDoesNotExist, MultipleObjectsReturned, FieldError, ValidationError, NON_FIELD_ERRORS) from django.db import (router, transaction, DatabaseError, from django.db import (router, connections, transaction, DatabaseError, DEFAULT_DB_ALIAS, DJANGO_VERSION_PICKLE_KEY) from django.db.models.deletion import Collector from django.db.models.fields import AutoField, FieldDoesNotExist Loading Loading @@ -1068,6 +1068,7 @@ class Model(six.with_metaclass(ModelBase)): if not cls._meta.swapped: errors.extend(cls._check_fields(**kwargs)) errors.extend(cls._check_m2m_through_same_relationship()) errors.extend(cls._check_long_column_names()) clash_errors = cls._check_id_field() + cls._check_field_name_clashes() errors.extend(clash_errors) # If there are field name clashes, hide consequent column name Loading Loading @@ -1451,6 +1452,76 @@ class Model(six.with_metaclass(ModelBase)): ) return errors @classmethod def _check_long_column_names(cls): """ Check that any auto-generated column names are shorter than the limits for each database in which the model will be created. """ errors = [] allowed_len = None db_alias = None # Find the minimum max allowed length among all specified db_aliases. for db in settings.DATABASES.keys(): # skip databases where the model won't be created if not router.allow_migrate(db, cls): continue 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 if allowed_len is None: return errors for f in cls._meta.local_fields: _, column_name = f.get_attname_column() # Check if auto-generated name for the field is too long # for the database. if (f.db_column is None and column_name is not None and len(column_name) > allowed_len): errors.append( checks.Error( 'Autogenerated column name too long for field "%s". ' 'Maximum length is "%s" for database "%s".' % (column_name, allowed_len, db_alias), hint="Set the column name manually using 'db_column'.", obj=cls, id='models.E018', ) ) for f in cls._meta.local_many_to_many: # Check if auto-generated name for the M2M field is too long # for the database. for m2m in f.rel.through._meta.local_fields: _, rel_name = m2m.get_attname_column() if (m2m.db_column is None and rel_name is not None and len(rel_name) > allowed_len): errors.append( checks.Error( 'Autogenerated column name too long for M2M field ' '"%s". Maximum length is "%s" for database "%s".' % (rel_name, allowed_len, db_alias), hint=("Use 'through' to create a separate model " "for M2M and then set column_name using " "'db_column'."), obj=cls, id='models.E019', ) ) return errors ############################################ # HELPER FUNCTIONS (CURRIED MODEL METHODS) # Loading
docs/ref/checks.txt +5 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,11 @@ Models * **models.E016**: ``index_together/unique_together`` refers to field ``<field_name>`` which is not local to model ``<model>``. * **models.E017**: Proxy model ``<model>`` contains model fields. * **models.E018**: Autogenerated column name too long for field ``<field>``. Maximum length is ``<maximum length>`` for database ``<alias>``. * **models.E019**: Autogenerated column name too long for M2M field ``<M2M field>``. Maximum length is ``<maximum length>`` for database ``<alias>``. Fields ~~~~~~ Loading
docs/releases/1.8.txt +22 −0 Original line number Diff line number Diff line Loading @@ -305,6 +305,28 @@ Now to implement the same behavior, you have to create an ``parser.add_argument`` to add any custom arguments, as parser is now an :py:class:`argparse.ArgumentParser` instance. Model check ensures auto-generated column names are within limits specified by database ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A field name that's longer than the column name length supported by a database can create problems. For example, with MySQL you'll get an exception trying to create the column, and with PostgreSQL the column name is truncated by the database (you may see a warning in the PostgreSQL logs). A model check has been introduced to better alert users to this scenario before the actual creation of database tables. If you have an existing model where this check seems to be a false positive, for example on PostgreSQL where the name was already being truncated, simply use :attr:`~django.db.models.Field.db_column` to specify the name that's being used. The check also applies to the columns generated in an implicit ``ManyToManyField.through`` model. If you run into an issue there, use :attr:`~django.db.models.ManyToManyField.through` to create an explicit model and then specify :attr:`~django.db.models.Field.db_column` on its column(s) as needed. Miscellaneous ~~~~~~~~~~~~~ Loading