2011-07-27 9 views
7

Podano model Django o nazwie BlogPost. Początkowo jest kodowany bez numeru Meta.verbose_name. W chwili ./manage.py syncdb, automatycznie tworzony jest ContentType o nazwie "wpis na blogu". W pewnym późniejszym momencie dodano Meta.verbose_name z "wpisu na blogu".Gdy zmieni się nazwa pliku verbose, w jaki sposób automatycznie aktualizować ContentType modelu?

Teraz istnieje rozbieżność: ContentType nazywa się „blogu”, podczas gdy model przechodzi przez gadatliwym nazwą „Blog post”, różnica ta jest przedstawiona w dowolnej ramy za pomocą relacji rodzajowe, np w administratorze komentarzy. Chciałbym poprawić tę sytuację, zmieniając nazwę ContentType, ale nie chciałbym tego zrobić ręcznie (z oczywistych względów) lub poprzez migrację (ponieważ nie migruję niczego innego, Meta.verbose_name jest po prostu zmiana kodu).

Jak zaktualizować nazwę ContentType po zmianie Meta.verbose_name?

Odpowiedz

10

Odpowiadając na własne pytanie: Udało mi się to zrobić małym sygnałem post_migrate. Jeśli nie korzystasz z South, prawdopodobnie możliwe jest użycie sygnału post_syncdb w ten sam sposób. Wszelkie uwagi na temat tego kodu są mile widziane.

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") 
+0

Działa doskonale dla mnie, thx! BTW, powinieneś oznaczyć to jako odpowiedź :) – Dave

+0

Jeśli używasz specjalnych znaków w swojej pełnej nazwie, chcesz użyć 'new_name = cl._meta.verbose_name.decode ('utf-8')' –

+0

Jeśli jesteś zastanawiasz się jak ja ** gdzie umieścić ten kod **, jest to miejsce na poziomie aplikacji '__init __. py'. –

1

Innym podejściem jest przesłonić ContentType.__str__ sposób, jak to wygląda następująco:

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) 

Tak, można go przerobić, jeśli nie potrzebują pewnego rodzaju wstecznej kompatybilności:

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 

To trochę trudne, ale uważam, że jest bardziej proste. Zauważ, że ponieważ Django 1.4.1 force_text jest używany zamiast force_unicode.