简体   繁体   English

如何在 Django 中自定义验证消息

[英]How to customize validation messages in Django

I would like to know how to customize the Validation message when using UniqueConstraint to create unique constraints on multiple columns (including foreign keys) of Model in Django.我想知道在使用 UniqueConstraint 在 Django 的 Model 的多个列(包括外键)上创建唯一约束时如何自定义验证消息。

In Django, I am creating a system for parents to apply for school lunches for their children.在 Django 中,我正在创建一个系统,供家长为孩子申请学校午餐。 An account exists for each parent, and one or more children are associated with one parent.每个父母都有一个帐户,并且一个或多个孩子与一位父母相关联。 When parents log in to the school lunch application screen, select their child and date, and press the school lunch application button to complete the application.当家长登录学校午餐申请界面,select他们的孩子和日期,然后按学校午餐申请按钮完成申请。 The validation of this application form did not go well.此申请表的验证没有 go 很好。

We use UniqueConstraints for multiple columns (child, date) in the Lunch model to avoid duplicate lunch requests for the same child on the same date.我们对午餐 model 中的多个列(子项、日期)使用 UniqueConstraints 以避免同一子项在同一日期重复午餐请求。 By default, the error message is confusing, so I tried to change the validation message, but it didn't work.默认情况下,错误消息令人困惑,因此我尝试更改验证消息,但没有成功。 Specifically, I created check_duplicate as a class method in the Lunch model and called it from the clean method of the form (ModelForm).具体来说,我在午餐 model 中将 check_duplicate 创建为 class 方法,并从表单 (ModelForm) 的 clean 方法中调用它。 At this time, both the validation message that I created and the default message are displayed on the actual screen.此时,我创建的验证消息和默认消息都显示在实际屏幕上。

・Message I created (child has already applied for yyyy/mm/dd school lunch.)・我创建的消息(孩子已经申请了 yyyy/mm/dd 学校午餐。)

・Default message (Lunch with this child and date already exists.)・默认消息(与该孩子共进午餐和日期已经存在。)

I want the validation message displayed on the screen below to be only the message I created.我希望下面屏幕上显示的验证消息只是我创建的消息。

source code源代码

# models.py
from django.db import models
from django.utils.timezone import now
from django.contrib.auth import get_user_model
User = get_user_model()

class Child(models.Model):
    name = models.CharField(max_length=128)
    user = models.ForeignKey(User, on_delete=models.CASCADE)

class Lunch(models.Model):
    child = models.ForeignKey(Child, on_delete=models.CASCADE)
    date = models.DateField(default=now)

    class Meta:
       constraints = [
           models.UniqueConstraint(fields=['child', 'date'],
           name='unique_lunch_application',
           )
       ]

    @classmethod
    def check_duplicate(cls, child, date):
        return cls.objects.filter(child=child, date=date).exists()
# forms.py
from django import forms
from .models import Child, Lunch
from django.core.exceptions import ValidationError
from django.utils.translation import gettext as _
from django.contrib.auth import get_user_model
User = get_user_model()

class LunchForm(forms.ModelForm):
    class Meta:
        model = Lunch
        fields = ('child', 'date',)
        
    def __init__(self, user, *args, **kwargs):
        super(LunchForm, self).__init__(*args, **kwargs)
        self.fields['child'].queryset = Child.objects.filter(user=user)
    
    def clean(self):
        cleaned_data = super().clean()
        child = cleaned_data.get('child')
        date = cleaned_data.get('date')
        if Lunch.check_duplicate(child=child, date=date):
            _validation_messsage = f'{child} has already applied for {date} school lunch.'
            raise ValidationError(
                _(_validation_messsage), code="duplicated_lunch_application"
                )
        return cleaned_data
# views.py
from django.shortcuts import render, redirect
from .models import Child, Lunch
from .forms import LunchForm
from django.contrib.auth import get_user_model
User = get_user_model()
def lunch(request):
    if request.method == 'POST':
        form = LunchForm(request.user, request.POST, request.FILES)
        if form.is_valid():
            form.save()
            return redirect('another view name')
    else:                
        form = LunchForm(user=request.user)
    return render(request, 'members/lunch.html', {'form': form})  # Urls are defined in urls.py.
<!-- HTML(DjangoTemplates) -->
<form action="" method="POST">
    {{ form.non_field_errors }}
    {% for field in form %}
        <div class="field">
            {{ field.label_tag }}
            {{ field }}
            {% if field.help_text %}
                <span class="helptext">{{ field.help_text }}</span>
            {% endif %}
            {{ field.errors }}
        </div>
    {% endfor %}
    <div class="field">
        <button type="submit">apply lunch</button>
    </div>
    {% csrf_token %}
</form>

tried试过了

I found that the following implementation using Form instead of ModelForm works fine with other parts as is.我发现以下使用 Form 而不是 ModelForm 的实现可以与其他部分正常工作。 (default validation message is not displayed) However, this time, the child column has to work as a foreign key, so it has not been resolved. (不显示默认验证消息)但是,这一次,子列必须作为外键工作,因此尚未解决。 It seems necessary to solve it in another way while using ModelForm, or to incorporate foreign keys in Form.似乎有必要在使用 ModelForm 时以另一种方式解决它,或者在 Form 中合并外键。

# forms.py
from django import forms
from .models import Child, Lunch
from django.core.exceptions import ValidationError
from django.utils.translation import gettext as _
from django.contrib.auth import get_user_model
User = get_user_model()

class LunchForm(forms.Form):
    child = forms.CharField(max_length=16)  # ←Actually, this part is a foreign key, so I can't write it like this.
    date = forms.DateField()
    
    def __init__(self, user, *args, **kwargs):
        super(LunchForm, self).__init__(*args, **kwargs)
        self.fields['child'].queryset = Child.objects.filter(user=user)
    
    def clean(self):
        cleaned_data = super().clean()
        child = cleaned_data.get('child')
        date = cleaned_data.get('date')
        if Lunch.check_duplicate(child=child, date=date):
            _validation_messsage = f'{child} has already applied for {date} school lunch.'
            raise ValidationError(
                _(_validation_messsage), code="duplicated_lunch_application"
                )
        return cleaned_data

version information版本信息

python 3.9.13 Django 4.1 python 3.9.13 Django 4.1

Use forms.validationerror instead of plain validationerror使用 forms.validationerror 而不是普通的validationerror

like this...像这样...

raise forms.ValidationError(f"{child} has already applied for {date} school lunch.")

i think it's should be work...我认为这应该是工作...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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