web-dev-qa-db-fra.com

Pourquoi le model.save () de Django n'appelle-t-il pas full_clean ()?

Je suis juste curieux de savoir si quelqu'un sait s'il y a une bonne raison pour laquelle l'orm de Django n'appelle pas 'full_clean' sur un modèle à moins qu'il ne soit enregistré dans le cadre d'un formulaire de modèle.

Notez que full_clean () ne sera pas appelé automatiquement lorsque vous appelez la méthode save () de votre modèle. Vous devrez l'appeler manuellement lorsque vous souhaitez exécuter la validation de modèle en une étape pour vos propres modèles créés manuellement. Doc complet et propre de Django

(REMARQUE: devis mis à jour pour Django 1.6 ... précédent Django comportaient également une mise en garde concernant ModelForms).)

Y a-t-il de bonnes raisons pour lesquelles les gens ne voudraient pas ce comportement? Je pense que si vous preniez le temps d'ajouter une validation à un modèle, vous voudriez que la validation s'exécute chaque fois que le modèle est enregistré.

Je sais comment tout faire fonctionner correctement, je cherche juste une explication.

138
Aaron

AFAIK, cela est dû à la rétrocompatibilité. Il existe également des problèmes avec ModelForms avec des champs exclus, des modèles avec des valeurs par défaut, des signaux pre_save (), etc.

Sources susceptibles de vous intéresser:

54
lqc

En raison de la compatibilité, le nettoyage automatique lors de l'enregistrement n'est pas activé dans le noyau Django.

Si nous démarrons un nouveau projet et voulons que la méthode par défaut save sur Model puisse nettoyer automatiquement, nous pouvons utiliser le signal suivant pour faire le nettoyage avant que chaque modèle ne soit enregistré.

from Django.dispatch import receiver
from Django.db.models.signals import pre_save, post_save

@receiver(pre_save)
def pre_save_handler(sender, instance, *args, **kwargs):
    instance.full_clean()
27
Alfred Huang

La façon la plus simple d'appeler le full_clean la méthode consiste simplement à remplacer la méthode save dans votre model:

def save(self, *args, **kwargs):
    self.full_clean()
    return super(YourModel, self).save(*args, **kwargs)
11
M.Void

Au lieu d'insérer un morceau de code qui déclare un récepteur, nous pouvons utiliser une application comme INSTALLED_APPS section dans settings.py

INSTALLED_APPS = [
    # ...
    'Django_fullclean',
    # your apps here,
]

Avant cela, vous devrez peut-être installer Django-fullclean utilisant PyPI:

pip install Django-fullclean
2
Alfred Huang

Si vous avez un modèle dont vous voulez vous assurer qu'il a au moins une relation FK, et que vous ne voulez pas utiliser null=False Car cela nécessite de définir un FK par défaut (qui serait des données inutiles), la meilleure façon J'ai proposé d'ajouter des méthodes personnalisées .clean() et .save(). .clean() déclenche l'erreur de validation et .save() appelle le nettoyage. De cette façon, l'intégrité est appliquée à la fois à partir des formulaires et des autres codes appelants, de la ligne de commande et des tests. Sans cela, il n'y a (AFAICT) aucun moyen d'écrire un test qui garantit qu'un modèle a une relation FK avec un autre modèle spécifiquement choisi (pas par défaut).

class Payer(models.Model):

    name = models.CharField(blank=True, max_length=100)
    # Nullable, but will enforce FK in clean/save:
    payer_group = models.ForeignKey(PayerGroup, null=True, blank=True,)

    def clean(self):
        # Ensure every Payer is in a PayerGroup (but only via forms)
        if not self.payer_group:
            raise ValidationError(
                {'payer_group': 'Each Payer must belong to a PayerGroup.'})

    def save(self, *args, **kwargs):
        self.full_clean()
        return super().save(*args, **kwargs)

    def __str__(self):
        return self.name
1
shacker