mirror of
				https://github.com/django/django.git
				synced 2025-10-31 01:25:32 +00:00 
			
		
		
		
	Fixed #24099 -- Removed contenttype.name deprecated field
This finsishes the work started on #16803. Thanks Simon Charette, Tim Graham and Collin Anderson for the reviews.
This commit is contained in:
		
				
					committed by
					
						 Markus Holtermann
						Markus Holtermann
					
				
			
			
				
	
			
			
			
						parent
						
							374c2419e5
						
					
				
				
					commit
					b4ac232907
				
			| @@ -69,7 +69,7 @@ | ||||
|                 {% endif %} | ||||
|                 <br/> | ||||
|                 {% if entry.content_type %} | ||||
|                     <span class="mini quiet">{% filter capfirst %}{% trans entry.content_type.name %}{% endfilter %}</span> | ||||
|                     <span class="mini quiet">{% filter capfirst %}{{ entry.content_type }}{% endfilter %}</span> | ||||
|                 {% else %} | ||||
|                     <span class="mini quiet">{% trans 'Unknown content' %}</span> | ||||
|                 {% endif %} | ||||
|   | ||||
| @@ -110,8 +110,9 @@ def create_permissions(app_config, verbosity=2, interactive=True, using=DEFAULT_ | ||||
|     for perm in perms: | ||||
|         if len(perm.name) > permission_name_max_length: | ||||
|             raise exceptions.ValidationError( | ||||
|                 "The verbose_name of %s is longer than %s characters" % ( | ||||
|                     perm.content_type, | ||||
|                 "The verbose_name of %s.%s is longer than %s characters" % ( | ||||
|                     perm.content_type.app_label, | ||||
|                     perm.content_type.model, | ||||
|                     verbose_name_max_length, | ||||
|                 ) | ||||
|             ) | ||||
|   | ||||
| @@ -568,7 +568,7 @@ class PermissionTestCase(TestCase): | ||||
|         models.Permission._meta.verbose_name = "some ridiculously long verbose name that is out of control" * 5 | ||||
|  | ||||
|         six.assertRaisesRegex(self, exceptions.ValidationError, | ||||
|             "The verbose_name of permission is longer than 244 characters", | ||||
|             "The verbose_name of auth.permission is longer than 244 characters", | ||||
|             create_permissions, auth_app_config, verbosity=0) | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -52,24 +52,20 @@ class LoadDataWithNaturalKeysAndMultipleDatabasesTestCase(TestCase): | ||||
|         default_objects = [ | ||||
|             ContentType.objects.db_manager('default').create( | ||||
|                 model='examplemodela', | ||||
|                 name='example model a', | ||||
|                 app_label='app_a', | ||||
|             ), | ||||
|             ContentType.objects.db_manager('default').create( | ||||
|                 model='examplemodelb', | ||||
|                 name='example model b', | ||||
|                 app_label='app_b', | ||||
|             ), | ||||
|         ] | ||||
|         other_objects = [ | ||||
|             ContentType.objects.db_manager('other').create( | ||||
|                 model='examplemodelb', | ||||
|                 name='example model b', | ||||
|                 app_label='app_b', | ||||
|             ), | ||||
|             ContentType.objects.db_manager('other').create( | ||||
|                 model='examplemodela', | ||||
|                 name='example model a', | ||||
|                 app_label='app_a', | ||||
|             ), | ||||
|         ] | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| from django.apps import apps | ||||
| from django.db import DEFAULT_DB_ALIAS, router | ||||
| from django.db.migrations.loader import is_latest_migration_applied | ||||
| from django.utils.encoding import smart_text | ||||
| from django.utils import six | ||||
| from django.utils.six.moves import input | ||||
|  | ||||
| @@ -50,7 +49,6 @@ def update_contenttypes(app_config, verbosity=2, interactive=True, using=DEFAULT | ||||
|  | ||||
|     cts = [ | ||||
|         ContentType( | ||||
|             name=smart_text(model._meta.verbose_name_raw), | ||||
|             app_label=app_label, | ||||
|             model=model_name, | ||||
|         ) | ||||
|   | ||||
| @@ -0,0 +1,41 @@ | ||||
| # -*- coding: utf-8 -*- | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| from django.db import models, migrations | ||||
|  | ||||
|  | ||||
| def add_legacy_name(apps, schema_editor): | ||||
|     ContentType = apps.get_model('contenttypes', 'ContentType') | ||||
|     for ct in ContentType.objects.all(): | ||||
|         try: | ||||
|             ct.name = apps.get_model(ct.app_label, ct.model)._meta.object_name | ||||
|         except: | ||||
|             ct.name = ct.model | ||||
|         ct.save() | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|  | ||||
|     dependencies = [ | ||||
|         ('contenttypes', '0001_initial'), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.AlterModelOptions( | ||||
|             name='contenttype', | ||||
|             options={'verbose_name': 'content type', 'verbose_name_plural': 'content types'}, | ||||
|         ), | ||||
|         migrations.AlterField( | ||||
|             model_name='contenttype', | ||||
|             name='name', | ||||
|             field=models.CharField(max_length=100, null=True), | ||||
|         ), | ||||
|         migrations.RunPython( | ||||
|             migrations.RunPython.noop, | ||||
|             add_legacy_name, | ||||
|         ), | ||||
|         migrations.RemoveField( | ||||
|             model_name='contenttype', | ||||
|             name='name', | ||||
|         ), | ||||
|     ] | ||||
| @@ -1,11 +1,13 @@ | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| import warnings | ||||
|  | ||||
| from django.apps import apps | ||||
| from django.db import models | ||||
| from django.db.utils import OperationalError, ProgrammingError | ||||
| from django.utils.translation import ugettext_lazy as _ | ||||
| from django.utils.encoding import force_text | ||||
| from django.utils.encoding import python_2_unicode_compatible | ||||
| from django.utils.deprecation import RemovedInDjango20Warning | ||||
| from django.utils.encoding import force_text, python_2_unicode_compatible | ||||
|  | ||||
|  | ||||
| class ContentTypeManager(models.Manager): | ||||
| @@ -34,6 +36,14 @@ class ContentTypeManager(models.Manager): | ||||
|         key = (opts.app_label, opts.model_name) | ||||
|         return self.__class__._cache[self.db][key] | ||||
|  | ||||
|     def create(self, **kwargs): | ||||
|         if 'name' in kwargs: | ||||
|             del kwargs['name'] | ||||
|             warnings.warn( | ||||
|                 "ContentType.name field doesn't exist any longer. Please remove it from your code.", | ||||
|                 RemovedInDjango20Warning, stacklevel=2) | ||||
|         return super(ContentTypeManager, self).create(**kwargs) | ||||
|  | ||||
|     def get_for_model(self, model, for_concrete_model=True): | ||||
|         """ | ||||
|         Returns the ContentType object for a given model, creating the | ||||
| @@ -66,7 +76,6 @@ class ContentTypeManager(models.Manager): | ||||
|             ct, created = self.get_or_create( | ||||
|                 app_label=opts.app_label, | ||||
|                 model=opts.model_name, | ||||
|                 defaults={'name': opts.verbose_name_raw}, | ||||
|             ) | ||||
|         self._add_to_cache(self.db, ct) | ||||
|         return ct | ||||
| @@ -108,7 +117,6 @@ class ContentTypeManager(models.Manager): | ||||
|             ct = self.create( | ||||
|                 app_label=opts.app_label, | ||||
|                 model=opts.model_name, | ||||
|                 name=opts.verbose_name_raw, | ||||
|             ) | ||||
|             self._add_to_cache(self.db, ct) | ||||
|             results[ct.model_class()] = ct | ||||
| @@ -148,7 +156,6 @@ class ContentTypeManager(models.Manager): | ||||
|  | ||||
| @python_2_unicode_compatible | ||||
| class ContentType(models.Model): | ||||
|     name = models.CharField(max_length=100) | ||||
|     app_label = models.CharField(max_length=100) | ||||
|     model = models.CharField(_('python model class name'), max_length=100) | ||||
|     objects = ContentTypeManager() | ||||
| @@ -157,21 +164,16 @@ class ContentType(models.Model): | ||||
|         verbose_name = _('content type') | ||||
|         verbose_name_plural = _('content types') | ||||
|         db_table = 'django_content_type' | ||||
|         ordering = ('name',) | ||||
|         unique_together = (('app_label', 'model'),) | ||||
|  | ||||
|     def __str__(self): | ||||
|         # self.name is deprecated in favor of using model's verbose_name, which | ||||
|         # can be translated. Formal deprecation is delayed until we have DB | ||||
|         # migration to be able to remove the field from the database along with | ||||
|         # the attribute. | ||||
|         # | ||||
|         # We return self.name only when users have changed its value from the | ||||
|         # initial verbose_name_raw and might rely on it. | ||||
|         model = self.model_class() | ||||
|         if not model or self.name != model._meta.verbose_name_raw: | ||||
|         return self.name | ||||
|         else: | ||||
|  | ||||
|     @property | ||||
|     def name(self): | ||||
|         model = self.model_class() | ||||
|         if not model: | ||||
|             return self.model | ||||
|         return force_text(model._meta.verbose_name) | ||||
|  | ||||
|     def model_class(self): | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| from __future__ import unicode_literals | ||||
|  | ||||
| import warnings | ||||
|  | ||||
| from django.contrib.contenttypes.models import ContentType | ||||
| from django.contrib.contenttypes.views import shortcut | ||||
| from django.contrib.sites.shortcuts import get_current_site | ||||
| @@ -230,11 +232,10 @@ class ContentTypesTests(TestCase): | ||||
|         is defined anymore. | ||||
|         """ | ||||
|         ct = ContentType.objects.create( | ||||
|             name='Old model', | ||||
|             app_label='contenttypes', | ||||
|             model='OldModel', | ||||
|         ) | ||||
|         self.assertEqual(six.text_type(ct), 'Old model') | ||||
|         self.assertEqual(six.text_type(ct), 'OldModel') | ||||
|         self.assertIsNone(ct.model_class()) | ||||
|  | ||||
|         # Make sure stale ContentTypes can be fetched like any other object. | ||||
| @@ -243,9 +244,6 @@ class ContentTypesTests(TestCase): | ||||
|         ct_fetched = ContentType.objects.get_for_id(ct.pk) | ||||
|         self.assertIsNone(ct_fetched.model_class()) | ||||
|  | ||||
|  | ||||
| class MigrateTests(TestCase): | ||||
|  | ||||
|     @skipUnlessDBFeature('can_rollback_ddl') | ||||
|     def test_unmigrating_first_migration_post_migrate_signal(self): | ||||
|         """ | ||||
| @@ -260,3 +258,22 @@ class MigrateTests(TestCase): | ||||
|                 call_command("migrate", "contenttypes", "zero", verbosity=0) | ||||
|         finally: | ||||
|             call_command("migrate", verbosity=0) | ||||
|  | ||||
|     def test_name_deprecation(self): | ||||
|         """ | ||||
|         ContentType.name has been removed. Test that a warning is emitted when | ||||
|         creating a ContentType with a `name`, but the creation should not fail. | ||||
|         """ | ||||
|         with warnings.catch_warnings(record=True) as warns: | ||||
|             warnings.simplefilter('always') | ||||
|             ContentType.objects.create( | ||||
|                 name='Name', | ||||
|                 app_label='contenttypes', | ||||
|                 model='OldModel', | ||||
|             ) | ||||
|         self.assertEqual(len(warns), 1) | ||||
|         self.assertEqual( | ||||
|             str(warns[0].message), | ||||
|             "ContentType.name field doesn't exist any longer. Please remove it from your code." | ||||
|         ) | ||||
|         self.assertTrue(ContentType.objects.filter(model='OldModel').exists()) | ||||
|   | ||||
| @@ -164,6 +164,9 @@ details on these changes. | ||||
| * ``GeoQuerySet`` aggregate methods ``collect()``, ``extent()``, ``extent3d()``, | ||||
|   ``makeline()``, and ``union()`` will be removed. | ||||
|  | ||||
| * Ability to specify ``ContentType.name`` when creating a content type instance | ||||
|   will be removed. | ||||
|  | ||||
| .. _deprecation-removed-in-1.9: | ||||
|  | ||||
| 1.9 | ||||
|   | ||||
| @@ -59,7 +59,7 @@ The ``ContentType`` model | ||||
| .. class:: ContentType | ||||
|  | ||||
|     Each instance of :class:`~django.contrib.contenttypes.models.ContentType` | ||||
|     has three fields which, taken together, uniquely describe an installed | ||||
|     has two fields which, taken together, uniquely describe an installed | ||||
|     model: | ||||
|  | ||||
|     .. attribute:: app_label | ||||
| @@ -74,12 +74,19 @@ The ``ContentType`` model | ||||
|  | ||||
|         The name of the model class. | ||||
|  | ||||
|     Additionally, the following property is available: | ||||
|  | ||||
|     .. attribute:: name | ||||
|  | ||||
|         The human-readable name of the model. This is taken from the | ||||
|         The human-readable name of the content type. This is taken from the | ||||
|         :attr:`verbose_name <django.db.models.Field.verbose_name>` | ||||
|         attribute of the model. | ||||
|  | ||||
| .. versionchanged:: 1.8 | ||||
|  | ||||
|     Before Django 1.8, the ``name`` property was a real field on the | ||||
|     ``ContentType`` model. | ||||
|  | ||||
| Let's look at an example to see how this works. If you already have | ||||
| the :mod:`~django.contrib.contenttypes` application installed, and then add | ||||
| :mod:`the sites application <django.contrib.sites>` to your | ||||
| @@ -96,9 +103,6 @@ created with the following values: | ||||
| * :attr:`~django.contrib.contenttypes.models.ContentType.model` | ||||
|   will be set to ``'site'``. | ||||
|  | ||||
| * :attr:`~django.contrib.contenttypes.models.ContentType.name` | ||||
|   will be set to ``'site'``. | ||||
|  | ||||
| .. _the verbose_name attribute: ../model-api/#verbose_name | ||||
|  | ||||
| Methods on ``ContentType`` instances | ||||
|   | ||||
| @@ -1101,6 +1101,10 @@ Miscellaneous | ||||
|   the newly introduced ``hints`` parameter for these operations, but it can | ||||
|   also be used to disable migrations from running on a particular database. | ||||
|  | ||||
| * The ``name`` field of :class:`django.contrib.contenttypes.models.ContentType` | ||||
|   has been removed by a migration and replaced by a property. That means it's | ||||
|   not possible to query or filter a ``ContentType`` by this field any longer. | ||||
|  | ||||
| .. _deprecated-features-1.8: | ||||
|  | ||||
| Features deprecated in 1.8 | ||||
|   | ||||
| @@ -1974,7 +1974,7 @@ class AdminViewStringPrimaryKeyTest(TestCase): | ||||
|         self.assertContains(response, should_contain) | ||||
|         should_contain = "Model with string primary key"  # capitalized in Recent Actions | ||||
|         self.assertContains(response, should_contain) | ||||
|         logentry = LogEntry.objects.get(content_type__name__iexact=should_contain) | ||||
|         logentry = LogEntry.objects.get(content_type__model__iexact='modelwithstringprimarykey') | ||||
|         # http://code.djangoproject.com/ticket/10275 | ||||
|         # if the log entry doesn't have a content type it should still be | ||||
|         # possible to view the Recent Actions part | ||||
| @@ -1989,8 +1989,8 @@ class AdminViewStringPrimaryKeyTest(TestCase): | ||||
|  | ||||
|     def test_logentry_get_admin_url(self): | ||||
|         "LogEntry.get_admin_url returns a URL to edit the entry's object or None for non-existent (possibly deleted) models" | ||||
|         log_entry_name = "Model with string primary key"  # capitalized in Recent Actions | ||||
|         logentry = LogEntry.objects.get(content_type__name__iexact=log_entry_name) | ||||
|         log_entry_model = "modelwithstringprimarykey"  # capitalized in Recent Actions | ||||
|         logentry = LogEntry.objects.get(content_type__model__iexact=log_entry_model) | ||||
|         model = "modelwithstringprimarykey" | ||||
|         desired_admin_url = "/test_admin/admin/admin_views/%s/%s/" % (model, iri_to_uri(quote(self.pk))) | ||||
|         self.assertEqual(logentry.get_admin_url(), desired_admin_url) | ||||
|   | ||||
| @@ -11,7 +11,7 @@ from django.core import checks | ||||
| from django.db import connections, models | ||||
| from django.test import TestCase, override_settings | ||||
| from django.test.utils import captured_stdout | ||||
| from django.utils.encoding import force_str | ||||
| from django.utils.encoding import force_str, force_text | ||||
|  | ||||
| from .models import Author, Article, SchemeIncludedURL | ||||
|  | ||||
| @@ -87,7 +87,7 @@ class ContentTypesViewsTests(TestCase): | ||||
|         ct = ContentType.objects.get_for_model(ModelCreatedOnTheFly) | ||||
|         self.assertEqual(ct.app_label, 'my_great_app') | ||||
|         self.assertEqual(ct.model, 'modelcreatedonthefly') | ||||
|         self.assertEqual(ct.name, 'a model created on the fly') | ||||
|         self.assertEqual(force_text(ct), 'modelcreatedonthefly') | ||||
|  | ||||
|  | ||||
| class IsolatedModelsTestCase(TestCase): | ||||
| @@ -364,7 +364,7 @@ class GenericRelationshipTests(IsolatedModelsTestCase): | ||||
| class UpdateContentTypesTests(TestCase): | ||||
|     def setUp(self): | ||||
|         self.before_count = ContentType.objects.count() | ||||
|         ContentType.objects.create(name='fake', app_label='contenttypes_tests', model='Fake') | ||||
|         ContentType.objects.create(app_label='contenttypes_tests', model='Fake') | ||||
|         self.app_config = apps.get_app_config('contenttypes_tests') | ||||
|  | ||||
|     def test_interactive_true(self): | ||||
|   | ||||
| @@ -29,7 +29,7 @@ class TaggedItem(models.Model): | ||||
|     content_object = GenericForeignKey() | ||||
|  | ||||
|     class Meta: | ||||
|         ordering = ["tag", "content_type__name"] | ||||
|         ordering = ["tag", "content_type__model"] | ||||
|  | ||||
|     def __str__(self): | ||||
|         return self.tag | ||||
|   | ||||
| @@ -28,8 +28,3 @@ class ContentTypeTests(TestCase): | ||||
|             self.assertEqual(six.text_type(company_type), 'Company') | ||||
|         with translation.override('fr'): | ||||
|             self.assertEqual(six.text_type(company_type), 'Société') | ||||
|  | ||||
|     def test_field_override(self): | ||||
|         company_type = ContentType.objects.get(app_label='i18n', model='company') | ||||
|         company_type.name = 'Other' | ||||
|         self.assertEqual(six.text_type(company_type), 'Other') | ||||
|   | ||||
		Reference in New Issue
	
	Block a user