Как настроить сообщения валидации в Django

Я хотел бы узнать, как настроить сообщение о проверке при использовании UniqueConstraint для создания уникальных ограничений на несколько столбцов (включая внешние ключи) модели в Django.

На Django я создаю систему для родителей, чтобы они могли подавать заявки на школьные обеды для своих детей. Учетная запись существует для каждого родителя, и один или несколько детей связаны с одним родителем. Когда родители заходят на экран заявки на школьный обед, они выбирают своего ребенка и дату и нажимают кнопку заявки на школьный обед, чтобы заполнить заявку. Проверка этой формы заявления не прошла успешно.

Мы используем UniqueConstraints для нескольких столбцов (ребенок, дата) в модели Lunch, чтобы избежать дублирования запросов на обед для одного и того же ребенка в одну и ту же дату. По умолчанию сообщение об ошибке является запутанным, поэтому я попытался изменить сообщение о проверке, но это не сработало. В частности, я создал check_duplicate как метод класса в модели Lunch и вызвал его из метода clean формы (ModelForm). В настоящее время на реальном экране отображается как созданное мной сообщение о проверке, так и сообщение по умолчанию.

・Созданное мной сообщение (ребенок уже подал заявление на получение школьного обеда yyyy/mm/dd.)

・ Сообщение по умолчанию (Обед с этим ребенком и датой уже существует.)

Я хочу, чтобы сообщение о проверке, отображаемое на экране ниже, было только тем сообщением, которое я создал.

исходный код

# 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>

пробовал

Я обнаружил, что следующая реализация с использованием Form вместо ModelForm работает нормально с другими частями как есть. (сообщение о валидации по умолчанию не отображается) Однако, на этот раз дочерний столбец должен работать как внешний ключ, поэтому эта проблема не решена. Похоже, что необходимо решить ее другим способом, используя 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

информация о версии

python 3.9.13 Django 4.1

Используйте forms.validationerror вместо обычного validationerror

вот так...

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

Я думаю, что это должна быть работа...

Вернуться на верх