简体   繁体   中英

Django Rest Framework how to set error_messages on models.CharField

Hi!

summarize the problem

I have a Django Rest Framework backend, on which I want to do a simple thing: change default validation message :

'max_length': _('Ensure this field has no more than {max_length} characters.'),

To a custom one like

'max_length': 'I am happy to see you {max_length}.'

methods that failed are on the bottom of this post

minimal example:

You can pull a minimal example from git repo , and run tests by calling this file .

from django.contrib.auth.models import (
AbstractBaseUser,
PermissionsMixin,
)
from django.db import models
from rest_framework import serializers


class User(AbstractBaseUser, PermissionsMixin):
    name = models.CharField("Name", max_length=42, null=True, 
        error_messages={'max_length':"I am happy to see you {max_length}."})

class UserSerializer(serializers.ModelSerializer):

    class Meta:
        model = User
        fields = ["name"]
        extra_kwargs = {
            "name": {"required": True, "allow_blank": False, "allow_null": False},
    }

class TestUserSerializer:
    def test_name() -> None:
        data = {
            "name": "A" * 43,
            }
        serializer = UserSerializer(data=data)
        assert serializer.is_valid() is False
        assert str(serializer.errors["name"][0]) == "I am happy to see you 42."

error messages seem to be ignored.

What failed:

  • ( main ) error_messages as a CharField argument (as shown in example)
  • ( experiment_1 ) based on this issue , I tried setting up a validatior argument for CharField, like: validators=[MaxLengthValidator(42, message="I am happy to see you {max_length}.")]
  • (experiment 2A 2B ) based on a very similar stack question I tried adding a custom CharField class. Maybe I didnt understand their solution, because inheriting from fields.CharField didnt allow me to set stuff into init method (I use model.CharField , they use field.Charfield . It didnt work either way.
  • Based on this issue I started wondering if this is even doable.
  • ( experiment 3 ) Writing a custom validation method def validate_name(self, value): inside UserSerializer . It is also ignored.

Feel free to git am this. Explanation in the commit message.

Also fixed the display of the value and added a test to make sure the override works at the model level.

From b5e0b90d8aea9348ef0de7624ad1460c06a2c44e Mon Sep 17 00:00:00 2001
From: Melvyn <melvyn@stackexchange.site>
Date: Sat, 20 Mar 2021 00:30:29 +0100
Subject: [PATCH] fix(main): Add error_messages to serializer

Apparently the ModelSerializer doesn't copy the error_messages attribute
from the model field, so we have to do that by hand.
---
 stack_example/tests/test_user.py  | 9 +++++++++
 stack_example/user/models.py      | 2 +-
 stack_example/user/serializers.py | 5 ++---
 3 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/stack_example/tests/test_user.py b/stack_example/tests/test_user.py
index 5aeba74..8493dc1 100644
--- a/stack_example/tests/test_user.py
+++ b/stack_example/tests/test_user.py
@@ -1,5 +1,14 @@
 import pytest
 from user.serializers import UserSerializer
+from user.models import User
+from django.core.exceptions import ValidationError
+
+def test_model():
+    user = User(name="A" * 43)
+    try:
+        user.clean_fields()
+    except ValidationError as e:
+        assert e.message_dict['name'][0] == "I am happy to see you 42."
 
 class TestUserSerializer():
     def test_name(self) -> None:
diff --git a/stack_example/user/models.py b/stack_example/user/models.py
index 2d03e7a..7d92470 100644
--- a/stack_example/user/models.py
+++ b/stack_example/user/models.py
@@ -8,4 +8,4 @@ from django.contrib.auth.models import (
 
 class User(AbstractBaseUser, PermissionsMixin):
     name = models.CharField("Name", max_length=42, null=True, 
-        error_messages={'max_length':"I am happy to see you {max_length}."})
+        error_messages={'max_length':"I am happy to see you %(limit_value)s."})
diff --git a/stack_example/user/serializers.py b/stack_example/user/serializers.py
index ddd86ce..704f844 100644
--- a/stack_example/user/serializers.py
+++ b/stack_example/user/serializers.py
@@ -2,10 +2,9 @@ from rest_framework import serializers
 from user.models import User 
 
 class UserSerializer(serializers.ModelSerializer):
-
     class Meta:
         model = User
         fields = ["name"]
         extra_kwargs = {
-            "name": {"required": True, "allow_blank": False, "allow_null": False},
-    }
+            "name": {"required": True, "allow_blank": False, "allow_null": False, "error_messages": User._meta.get_field('name').error_messages},
+        }
-- 
2.20.1 (Apple Git-117)

Solution can be browsed on github

What I saw from your attempts was that you inherited CharField correctly but applied it to the model field when the right thing to do would be to use it in the serializer. That work for me:

from rest_framework import serializers
from rest_framework.fields import CharField
from django.utils.translation import gettext_lazy as _
from user.models import User


class CustomCharField(CharField):
    default_error_messages = {
        'max_length': _('I am happy to see you {max_length}.'),
    }


class UserSerializer(serializers.ModelSerializer):
    name = CustomCharField(required=True, allow_blank=False, allow_null=False, max_length=42)

    class Meta:
        model = User
        fields = ["name"]
        # extra_kwargs = {
        #     "name": {"required": True, "allow_blank": False, "allow_null": False},
        # }

This solution can be browsed on guthub

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM