2010-12-07 6 views
55

J'ai un modèle que je voudrais contenir un nom de sujet et leurs initiales. (Les données sont quelque peu anonymisées et suivis par des initiales.)Champ de modèle Django par défaut basé sur un autre champ dans le même modèle

En ce moment, je l'ai écrit

class Subject(models.Model): 

    name = models.CharField("Name", max_length=30) 
    def subject_initials(self): 
     return ''.join(map(lambda x: '' if len(x)==0 else x[0], 
          self.name.split(' '))) 
    # Next line is what I want to do (or something equivalent), but doesn't work with 
    # NameError: name 'self' is not defined 
    subject_init = models.CharField("Subject Initials", max_length=5, default=self.subject_initials) 

Comme l'indique la dernière ligne, je préférerais être en mesure d'avoir les initiales sont stockés en fait dans le base de données comme un champ (indépendant du nom), mais qui est initialisé avec une valeur par défaut basée sur le champ de nom. Cependant, j'ai des problèmes car les modèles django ne semblent pas avoir un 'soi'.

Si je change la ligne à subject_init = models.CharField("Subject initials", max_length=2, default=subject_initials), je peux faire la syncdb, mais je ne peux pas créer de nouveaux sujets.

Est-ce possible dans django, ayant une fonction appelable donne un champ par défaut basé sur la valeur d'un autre champ?

(Pour les curieux, la raison pour laquelle je veux séparer séparément les initiales de mon magasin est dans de rares cas où des noms de famille étranges peuvent avoir des différences avec ceux que je suis en train de suivre. Mallory » initiales sont « JM » plutôt que « JO » et veut fixer le modifier comme un administrateur)

Répondre

53

Les modèles ont certainement un "soi"! C'est juste que vous essayez de définir un attribut d'une classe de modèle comme étant dépendant d'une instance de modèle; ce n'est pas possible, car l'instance n'existe pas (et ne peut pas exister) avant que vous définissiez la classe et ses attributs.

Pour obtenir l'effet souhaité, remplacez la méthode save() de la classe de modèle. Apportez les modifications nécessaires à l'instance, puis appelez la méthode de la superclasse pour effectuer la sauvegarde réelle. Voici un exemple rapide.

def save(self, *args, **kwargs): 
    if not self.subject_init: 
     self.subject_init = self.subject_initials() 
    super(Subject, self).save(*args, **kwargs) 

Ceci est couvert dans Overriding Model Methods dans la documentation.

12

Je ne sais pas s'il y a une meilleure façon de le faire, mais vous pouvez use a signal handler pour the pre_save signal:.

from django.db.models.signals import pre_save 

def default_subject(sender, instance, using): 
    if not instance.subject_init: 
     instance.subject_init = instance.subject_initials() 

pre_save.connect(default_subject, sender=Subject) 
+1

Vous avez importé '' '' '' '' '' '' '' '' '' '' '' pre_save''' '' '' '' 'pre_save'''. – arkocal

+1

@arkocal: merci pour les heads-up, juste réparés. Vous pouvez proposer vous-même des modifications dans de tels cas, cela aide à corriger des choses comme ça :) –

0

En utilisant Django signals, cela peut être fait assez tôt, en recevant the post_init signal à partir du modèle.

from django.db import models 
import django.dispatch 

class LoremIpsum(models.Model): 
    name = models.CharField(
     "Name", 
     max_length=30, 
    ) 
    subject_initials = models.CharField(
     "Subject Initials", 
     max_length=5, 
    ) 

@django.dispatch.receiver(models.signals.post_init, sender=LoremIpsum) 
def set_default_loremipsum_initials(sender, instance, *args, **kwargs): 
    """ 
    Set the default value for `subject_initials` on the `instance`. 

    :param sender: The `LoremIpsum` class that sent the signal. 
    :param instance: The `LoremIpsum` instance that is being 
     initialised. 
    :return: None. 
    """ 
    if not instance.subject_initials: 
     instance.subject_initials = "".join(map(
       (lambda x: x[0] if x else ""), 
       instance.name.split(" "))) 

Le signal post_init est envoyé par la classe une fois qu'il a fait l'initialisation de l'instance. De cette façon, l'instance obtient une valeur pour name avant de tester si ses champs non nullables sont définis.

Questions connexes