LoginSignup
12
7

More than 5 years have passed since last update.

REST Framework で unique_together の エラーメッセージを変えたい

Last updated at Posted at 2018-12-17
  • この記事は Django Advent Calendar 2018 の 8日目(代打) と BeProud Advent Calendar 2018 と17日目の記事です。
  • REST Framework の ModelSerializer を使った時に unique_together のエラーメッセージを変えたかった。
  • 通常 Modelunique_together が設定されていたら、ModelSerializerはその設定従い validation してくれる
  • この時のエラーメッセージは Model のデフォルトのエラーメッセージとなる
  • But そのエラメッセージをもう少しわかりやすいものに変えたかったが割とめんどうだったという話

前提

  • Python 3.7
  • Django 2.1.4
  • djangorestframework 3.9.0

確認

  • まずデフォルトのエラーがどうなるか確認する。
  • 適当に unique_together を持つ Model と、それを使う ModelSerializer を用意する

# model ---

class Spam(models.Model):

   name = models.CharField('name', max_length=255)
   company_id = models.IntegerField('company id')

   class Meta:
        unique_together = ('name', 'company_id')

# serializer ---

class SpamSerializer(serializers.ModelSerializer):

    class Meta:
        model = Spam
        fields = '__all__'
  • unique_together のエラーを確認
>>> from spam.serializers import SpamSerializer
>>> s = SpamSerializer(data={'name': 'name1', 'company_id': 1})
>>> s.is_valid()
True
>>> s.save()
<Spam: Spam object>
>>> s = SpamSerializer(data={'name': 'name1', 'company_id': 1})
>>> s.is_valid()
False
>>> s.errors
{u'non_field_errors': [ErrorDetail(string=u'The fields name, company_id must make a unique set.', code=u'unique')]}
  • "The fields name, company_id must make a unique set." の部分を適当にカスタマイズしたい

どうするか?

1. Serializer 側に別途 UniqueTogetherValidator を設定する

UniqueTogetherValidator(message='Your custom message', fields=(field1, field2,))

-> エラーメッセージを変えたいだけなのに別途validation設定するのなんか微妙

2. Modelunique_error_messageメソッドoverride する

def unique_error_message(self, model_class, unique_check):
    error = super().unique_error_message(model_class, unique_check)
    # Intercept the unique_together error
    if len(unique_check) != 1:
        error.message = 'Your custom message'
    return error

-> これもなんか微妙にめんどい、カスタムなエラーメッセージは Serializer側 に寄せたいキムチがある

メッセージだけ書き換えるやつを作る

  • 結局 ModelSerializerunique_together があったら、どっかで UniqueTogetherValidator を使ってるんじゃろう?
  • そこのメッセージだけ変えるようなものを作ればええんやんかという都合の良い推測を立てる。
  • 調べて見た結果 get_unique_together_validators とかいうメソッドを持ってるやんかということがわかったので適当に override してメッセージを差し替えるようにする
  • https://github.com/encode/django-rest-framework/blob/master/rest_framework/serializers.py#L1488
class MyModelSerializer(ModelSerializer):

    def get_unique_together_validators(self):
        """ override ModelSerializer.get_unique_together_validators """

        validators = super().get_unique_together_validators()
        kwargs = self.get_extra_kwargs()

        my_errors = kwargs.get('my_errors', {})
        errors = my_errors.get('unique_together')
        if not my_errors or not errors:
            return validators

        for v in validators:
            if v.fields in errors:
                v.message = errors.get(v.fields)
        return validators


# overrideした Serializerをbaseにする
class SpamSerializer(MyModelSerializer):

    class Meta:
        model = Spam
        fields = '__all__'
        extra_kwargs = {
            'my_errors': { # <- 適当に自分で決めたエラメッセージを設定する
                'unique_together': {
                    ('name', 'company_id'): "その名前は既に使われてはりますなぁ(にちゃぁぁ"
                }
            }
        }

実行結果

>>> from spam.serializers import SpamSerializer
>>> s = SpamSerializer(data={'name': 'name1', 'company_id': 1})
>>> s.is_valid()
False
>>> s.errors
{'non_field_errors': [ErrorDetail(string='その名前は既に使われてはりますなぁ(にちゃぁぁ', code='unique')]}

これでやりたいことはとりあえず出来た。

まとめ

  • unique_together の エラーメッセージ変えるだけが結構めんどい。
  • メッセージ部分だけをoverride するような Serializer を用意して対処した。
  • But このやり方は Undocumented で将来的な restframework の変更に対して何も保証がないので注意が必要。 -> restframework の バージョンアップとともに壊れる可能性がある
  • 多分一番無難で安全なのは UniqueTogetherValidator をひとつづ設定していくのが良い。

以上おわり

12
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
7