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?
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.
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_together
contraindre, 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 ;-)
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
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.
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')
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!