Given is a Django model called BlogPost
. At first, it's coded without a Meta.verbose_name
. At ./manage.py syncdb
time, a ContentType
with a name "blog post" is created automatically. At some later point of time, Meta.verbose_name
of "Blog post" is added.
Now there is a discrepancy: ContentType
is called "blog post", while the model goes by verbose name of "Blog post", this difference is shown in any framework using generic relationships, e.g. in comments' admin. I would like to correct this situation by changing the name of the ContentType
, however, I wouldn't want to do that either by hand (for obvious reasons) or via a migration (since I don't migrate anything else, Meta.verbose_name
is just a code change).
How would you update the ContentType
's name upon Meta.verbose_name
change?
Answering own question: I've managed to do this with a small post_migrate
signal. If you are not using South, it's probably perfectly possible to use the post_syncdb
signal the same way. Any comments on this code are appreciated.
from django.contrib.contenttypes.models import ContentType
from django.utils.functional import Promise
from south.signals import post_migrate
# or if using django >=1.7 migrations:
# from django.db.models.signals import post_migrate
def update_contenttypes_names(**kwargs):
for c in ContentType.objects.all():
cl = c.model_class()
# Promises classes are from translated, mostly django-internal models. ignore them.
if cl and not isinstance(cl._meta.verbose_name, Promise):
new_name = cl._meta.verbose_name
if c.name != new_name:
print u"Updating ContentType's name: '%s' -> '%s'" % (c.name, new_name)
c.name = new_name
c.save()
post_migrate.connect(update_contenttypes_names, dispatch_uid="update_contenttypes")
Another approach is to override ContentType.__str__
method, as it looks like this:
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:
return force_unicode(model._meta.verbose_name)
So, you can rewrite it, if you don't need some kind of backwards compatibility:
from django.contrib.contenttypes.models import ContentType
from django.utils.encoding import force_unicode
def contenttype_as_str(self):
return force_unicode(self.model_class()._meta.verbose_name)
ContentType.__str__ = contenttype_as_str
It's a bit tricky, but I believe it's more straightforward. Note, that since Django 1.4.1 force_text
is used instead of force_unicode
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With