web-dev-qa-db-fra.com

Messages d'erreur personnalisés dans le sérialiseur Django Rest Framework

Le scénario est assez simple:

J'ai un modèle avec certains champs obligatoires. Supposons que l'un d'eux est une TextField qui ne peut pas être blank. J'ai aussi une ModelSerializer (Django Rest Framework) qui représente ce modèle.

Lorsqu'une chaîne vide est utilisée pour définir ce champ via le sérialiseur, l'erreur renvoyée provient du modèle lui-même (This field can't be blank).

Je voudrais remplacer les messages d'erreur uniquement au niveau du sérialiseur, sans qu'il soit nécessaire de re-spécifier explicitement chaque champ du sérialiseur (ce qui, selon moi, va à l'encontre du principe DRY), ce qui oblige à écrire une méthode validate_ pour chaque champ et lever ma propre ValidationError ou devoir modifier les messages d'erreur du niveau Model (car parfois le contexte du message d'erreur est important pour mon cas d'utilisation et le message d'erreur doit être donné en conséquence).

En d'autres termes, existe-t-il un moyen de remplacer les messages d'erreur au niveau du sérialiseur aussi facilement que pour un ModelForm:

class MyModelForm(ModelForm):
    class Meta:
        model = MyModel
        error_messages = {"field1": {"required": _("For some reason this is a custom error message overriding the model's default")}}
22
Gabriel Amram

EDIT: Je vois que cette question suscite encore quelques critiques, il est donc important de noter qu'il existe une autre approche, beaucoup plus nette que la réponse originale que j'ai postée ici.

Vous pouvez simplement utiliser l'attribut extra_kwargs de la classe Meta du sérialiseur, comme suit:

class UserSerializer(ModelSerializer):

    class Meta:
        model = User
        extra_kwargs = {"username": {"error_messages": {"required": "Give yourself a username"}}}

Réponse originale:

En utilisant la réponse de @mariodev, j'ai créé une nouvelle classe dans mon projet:

from rest_framework.serializers import ModelSerializer, ModelSerializerOptions

class CustomErrorMessagesModelSerializerOptions(ModelSerializerOptions):
    """
    Meta class options for CustomErrorMessagesModelSerializerOptions
    """
    def __init__(self, meta):
        super(CustomErrorMessagesModelSerializerOptions, self).__init__(meta)
        self.error_messages = getattr(meta, 'error_messages', {})

class CustomErrorMessagesModelSerializer(ModelSerializer):
    _options_class = CustomErrorMessagesModelSerializerOptions

    def __init__(self, *args, **kwargs):
        super(CustomErrorMessagesModelSerializer, self).__init__(*args, **kwargs)

        # Run through all error messages provided in the Meta class and update
        for field_name, err_dict in self.opts.error_messages.iteritems():
            self.fields[field_name].error_messages.update(err_dict)

La première offre la possibilité d’ajouter un nouvel attribut de classe Meta au sérialiseur, comme pour ModelForm. Le second hérite de ModelSerializer et utilise la technique de @ mariodev pour mettre à jour les messages d'erreur.

Tout est laissé à faire, c'est juste hériter, et faire quelque chose comme ça:

class UserSerializer(CustomErrorMessagesModelSerializer):
    class Meta:
        model = User
        error_messages = {"username": {"required": "Give yourself a username"}}
23
Gabriel Amram

Dans votre sérialiseur:

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User

    def __init__(self, *args, **kwargs):
        super(UserSerializer, self).__init__(*args, **kwargs)

        self.fields['username'].error_messages['required'] = u'My custom required msg'

Veuillez noter que certains messages d'erreur consistent en des espaces réservés %s tels que:

'invalid': _("'%s' value must be either True or False."),

pour BooleanField.

Vous devez donc parcourir la partie default_error_messages dans chaque type de champ dans le fields.py du DRF pour pouvoir l'utiliser correctement. 

17
mariodev

unique semblait être ignoré de error_messages, alors j'ai dû adopter une approche différente.

email = serializers.EmailField(validators=[
    UniqueValidator(
        queryset=models.Client.objects.all(),
        message="My custom error",
    )]
)

C'est plus simple (mais moins souple, moins réutilisable) que celui de @ gabriel-amram, mais beaucoup moins hacky que celui de @ mariodev.

10
WhyNotHugo

Une autre approche pour UniqueValidator (à utiliser avec ModelSerializer):

def __init__(self, *args, **kwargs):
    super(UserSerializer, self).__init__(*args, **kwargs)
    # Find UniqueValidator and set custom message
    for validator in self.fields['email'].validators:
        if isinstance(validator, validators.UniqueValidator):
            validator.message = _('This email already exist on this site')
4
LennyLip

J'ai essayé de créer un simple Serializer plutôt qu'un ModelSerializer. Probablement à cause de cela la réponse acceptée avec extra_kwargs par Gabriel Amram n'a pas fonctionné pour moi. Une autre réponse de @mariodev a fonctionné, mais je recherchais une solution plus élégante et en trouvai une. Il s'avère que la classe Field accepte error_messages en tant que paramètre, qui est un dictionnaire qui remplace les messages d'erreur par défaut. Voici la référence aux documents . C'est le même format que décrit dans les réponses acceptées. Voici un exemple:

from rest_framework import serializers

class MySerializer(serializers.Serializer):
    client_id = serializers.IntegerField(required=True, error_messages={'required': 'Custom error message'})
0
Tim Osadchiy

DRF3.0 attend de nous que nous définissions explicitement les validateurs des champs si nous souhaitons remplacer les validateurs de modèle par défaut. Cela peut être fait en passant extra_kwargs et en définissant explicitement les validateurs pour n'importe quel champ vous semblez nécessaire. Vous pouvez également spécifier votre propre validateur personnalisé qui pourra être réutilisé pour différents champs ou même pour d'autres sérialiseurs.

http://www.Django-rest-framework.org/api-guide/serializers/#validation

http://www.Django-rest-framework.org/api-guide/validators/#validation-in-rest-framework

# my_app/validators.py
def validate_required(value):
    # whatever validation logic you need
    if value == '' or value is None:
        raise serializers.ValidationError('This field is required.')

# my_app/serializers.py
class MyModelSerializer(serializers.ModelSerializer):

    class Meta:
        model = MyModel
        extra_kwargs = {"field1": {"validators": [validators.validate_required,]}}
0
Kashif Siddiqui

Juste une note depuis que je joue à ça depuis un moment, si vous utilisez quelque chose comme un URLField qui ajoute juste un URLValidator, il ne semble pas utiliser le error_messages, alors j'ai fait quelque chose de similaire à la réponse de @ Hugo:

class Meta:
    extra_kwargs = {"url_field": {"validators": [validators.URLValidator(message="My error message")]}}
0
Devin