Django REST Framework: validation unique together sur les sérialiseurs

avoir des problèmes avec un serializer.is_valid() retour True si l'instance de serializer échoue un unique_together contrainte du côté du modèle.

Est-il un moyen pour moi de préciser dans le sérialiseur pour appliquer un unique_together contrainte?

17
demandé sur Drewness 2014-03-25 01:34:36

6 réponses

malheureusement, la réponse D'Andreas n'est pas tout à fait complète, car elle ne fonctionnera pas dans le cas d'une mise à jour.

au lieu de cela, vous voudriez quelque chose comme:

def validate(self, attrs):
    field1 = attrs.get('field1', self.object.field1)
    field2 = attrs.get('field2', self.object.field2)

    try:
        obj = Model.objects.get(field1=field1, field2=field2)
    except StateWithholdingForm.DoesNotExist:
        return attrs
    if self.object and obj.id == self.object.id:
        return attrs
    else:
        raise serializers.ValidationError('field1 with field2 already exists')

cela fonctionnera pour PUT, PATCH, et POST.

6
répondu JeffS 2014-09-24 22:46:56

la classe ModelSerializer a cette fonctionnalité intégrée, au moins en djangorestframework>=3.0.0 toutefois, si vous utilisez un serializer ce qui n'inclut pas des champs qui sont touchés par votre unique_togethercontraindre, alors vous aurez un IntegrityError en sauvegardant une instance qui la viole. Par exemple, en utilisant le modèle suivant:

class Foo(models.Model):
    class Meta:
        unique_together = ('foo_a', 'foo_b')

    a = models.TextField(blank=True)
    b = models.TextField(blank=True)
    foo_a = models.IntegerField()
    foo_b = models.IntegerField(default=2)

et la suite sérialiseur et ViewSet:

class FooSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Foo
        fields = ('a', 'b', 'foo_a')

class FooViewSet(viewsets.ModelViewSet):
    queryset = models.Foo.objects.all()
    serializer_class = FooSerializer


routes = routers.DefaultRouter()
routes.register(r'foo', FooViewSet)

si vous essayez de sauvegarder deux instances avec le même foo_a et foo_b set, vous aurez une IntegrityError. Cependant, si nous modifions le serializer comme ceci:

class FooSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Foo
        fields = ('a', 'b', 'foo_a', 'foo_b')

vous obtiendrez alors un bon HTTP 400 BAD REQUEST code d'état, et le message descriptif JSON correspondant dans le corps de réponse:

HTTP 400 BAD REQUEST
Content-Type: application/json
Vary: Accept
Allow: GET, POST, HEAD, OPTIONS

{
    "non_field_errors": [
        "The fields foo_a, foo_b must make a unique set."
    ]
}

j'espère que ce résultat utile pour vous, même si c'est un peu ancien-posté question ;-)

11
répondu seorc 2015-03-02 17:42:34

j'en avais besoin pour remplacer le message par défaut. Résolu par .

from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers


class SomeSerializer(serializers.ModelSerializer):
  """
  Demostrating How to Override DRF UniqueTogetherValidator Message
  """

    class Meta:
        model = Some
        validators = [
            serializers.UniqueTogetherValidator(
                queryset=model.objects.all(),
                fields=('field1', 'field2'),
                message=_("Some custom message.")
            )
        ]

Similarly you can specify fields

7
répondu vaibhav jain 2016-07-02 12:49:41

Oui, vous pouvez le faire dans le .validate() méthode du sérialiseur.

def validate(self, attrs):
    try:
        Model.objects.get(field1=attrs['field1'], field2=attrs['field2'])
    except Model.DoesNotExist:
        pass
    else:
        raise serializers.ValidationError('field1 with field2 already exists')

    return attrs

la contrainte unique que vous définissez dans votre modèle est pour créer des contraintes de base de données, pas pour valider.

2
répondu Andreas 2014-09-21 11:37:23

Eu le même problème et à partir de cette réponse https://stackoverflow.com/a/26027788/6473175 j'ai été en mesure de le faire fonctionner, mais a dû utiliser self.instance au lieu de self.object.

def validate(self, data):
    field1 = data.get('field1',None)
    field2 = data.get('field2',None)

    try:
        obj = self.Meta.model.objects.get(field1=field1, field2=field2)
    except self.Meta.model.DoesNotExist:
        return data
    if self.instance and obj.id == self.instance.id:
        return data
    else:
        raise serializers.ValidationError('custom error message')
1
répondu StackEdd 2017-10-02 10:24:09

Eh bien c'est un peu stupide et il est très peu probable que quelqu'un d'autre que moi fasse cette erreur, mais j'avais mis le même modèle dans deux cours de serialiseurs, en conséquence j'ai eu ce problème

j'Espère que mon erreur aider quelqu'un!

0
répondu winter 2017-04-01 22:19:29