program story

Django의 모델 저장 방법에서 유효성 검사 오류 발생

inputbox 2021. 1. 8. 08:11
반응형

Django의 모델 저장 방법에서 유효성 검사 오류 발생


모델의 저장 방법에서 유효성 검사 오류를 올바르게 제기하고 사용자에게 명확한 메시지를 다시 보내는 방법을 잘 모르겠습니다.

기본적으로 "if"의 각 부분이 어떻게 끝나야하는지, 오류를 발생시키고 싶은 부분과 실제로 저장하는 부분을 알고 싶습니다.

def save(self, *args, **kwargs):
    if not good_enough_to_be_saved:
        raise ValidationError
    else:
        super(Model, self).save(*args, **kwargs)

그런 다음 예를 들어 값이 고유하지 않은 경우 Django가 자동으로 반환하는 것과 같이 사용자에게 정확히 무엇이 잘못되었는지를 알려주는 유효성 검사 오류를 보내려면 어떻게해야하는지 알고 싶습니다. (ModelForm)을 사용하고 모델의 모든 것을 조정합니다.


대부분의 Django 뷰 (예 : Django 관리자)는 save 메소드에서 유효성 검사 오류를 처리 할 수 ​​없으므로 사용자는 500 오류를 받게됩니다.

모델 양식 또는 모델에서 유효성 검사를 수행하고 ValidationError거기에서 제기 해야합니다. 그런 다음 save()모델 양식 데이터가 '저장하기에 충분'한 경우에만 호출 하십시오.


Bastian, 내 코드 템플릿에 대해 설명합니다. 도움이 되었기를 바랍니다.

django 1.2 부터 model에서 유효성 검사 코드를 작성할 수 있습니다 . modelforms로 작업 할 때 form 유효성 검사시 instance.full_clean ()이 호출됩니다.

각 모델에서 clean()메서드를 사용자 지정 함수로 덮어 씁니다 (이 메서드는 modelform 유효성 검사시 full_clean ()에서 자동으로 호출 됨).

from django.db import models

class Issue(models.Model):
    ....
    def clean(self): 
        rules.Issue_clean(self)  #<-- custom function invocation

from issues import rules
rules.connect()

그런 다음 rules.py파일에 비즈니스 규칙을 작성합니다. 또한 pre_save()잘못된 상태로 모델을 저장하지 않도록 사용자 지정 함수에 연결 합니다.

issues.models에서 import Issue

def connect():    
    from django.db.models.signals import post_save, pre_save, pre_delete
    #issues 
    pre_save.connect(Issue_pre_save, sender = Incidencia ) 
    post_save.connect(Issue_post_save, sender = Incidencia )
    pre_delete.connect(Issue_pre_delete, sender= Incidencia) 

def Incidencia_clean( instance ):    #<-- custom function 
    import datetime as dt    
    errors = {}

    #dia i hora sempre informats     
    if not instance.dia_incidencia:   #<-- business rules
        errors.setdefault('dia_incidencia',[]).append(u'Data missing: ...')

    #dia i hora sempre informats     
    if not  instance.franja_incidencia: 
        errors.setdefault('franja_incidencia',[]).append(u'Falten Dades: ...')

    #Només es poden posar incidències més ennlà de 7 dies 
    if instance.dia_incidencia < ( dt.date.today() + dt.timedelta( days = -7) ): 
        errors.setdefault('dia_incidencia 1',[]).append(u'''blah blah error desc)''')

    #No incidències al futur. 
    if instance.getDate() > datetime.now(): 
        errors.setdefault('dia_incidencia 2',[]).append(u'''Encara no pots ....''') 
    ... 

    if len( errors ) > 0: 
        raise ValidationError(errors)  #<-- raising errors

def Issue_pre_save(sender, instance, **kwargs): 
    instance.clean()     #<-- custom function invocation

그런 다음 modelform은 모델의 clean 메서드를 호출하고 내 custon 함수가 올바른 상태를 확인하거나 모델 형식에서 처리하는 오류를 발생시킵니다.

양식에 오류를 표시하려면 다음을 양식 서식 파일에 포함해야합니다.

{% if form.non_field_errors %}
      {% for error in form.non_field_errors %}
        {{error}}
      {% endfor %}
{% endif %}  

그 이유는 모델 유효성 검사 오류가 non_field_errors 오류 사전 항목에 바인딩 되었기 때문입니다.

양식에서 모델을 저장하거나 삭제할 때 오류가 발생할 수 있음을 기억해야합니다.

try:
    issue.delete()
except ValidationError, e:
    import itertools
    errors = list( itertools.chain( *e.message_dict.values() ) )

또한 모델 양식이없는 양식 사전에 오류를 추가 할 수 있습니다.

    try:
        #provoco els errors per mostrar-los igualment al formulari.
        issue.clean()
    except ValidationError, e:
        form._errors = {}
        for _, v in e.message_dict.items():
            form._errors.setdefault(NON_FIELD_ERRORS, []).extend(  v  )

이 코드는 save () 메서드에서 실행되지 않는다는 점을 기억하십시오. full_clean ()은 모델의 save () 메서드를 호출 할 때 자동으로 호출되지 않으며 ModelForm 유효성 검사의 결과로도 호출되지 않습니다. 그런 다음 modelforms없는 양식 사전에 오류를 추가 할 수 있습니다 .

    try:
        #provoco els errors per mostrar-los igualment al formulari.
        issue.clean()
    except ValidationError, e:
        form._errors = {}
        for _, v in e.message_dict.items():
            form._errors.setdefault(NON_FIELD_ERRORS, []).extend(  v  )

ValidationError도 가져와야합니다.

from django.core.exceptions import ValidationError

I think this is more clear way to do that for Django 1.2+

In forms it will be raised as non_field_error, in other cases, like DRF you have to check this case manual, because it will be 500 error.

class BaseModelExt(models.Model):
is_cleaned = False

def clean(self):
    # check validation rules here

    self.is_cleaned = True

def save(self, *args, **kwargs):
    if not self.is_cleaned:
        self.clean()

    super().save(*args, **kwargs)

def clean(self):
    raise ValidationError("Validation Error")

def save(self, *args, **kwargs):
    if some condition:
        #do something here
    else:
        self.full_clean()
    super(ClassName, self).save(*args, **kwargs)

Edit: This answer assumes that you have a scenario that does not allow you to edit the currently implemented User class, because you are not starting a project from scratch, the current implementation does not already use a custom User class, and you instead have to figure out how to accomplish this task by modifying Django's built in User model behavior.

You can just stick a clean method to your model most of the time, but you don't have that option necessarily with the built in auth.User model. This solution will allow you to create a clean method for the auth.User model in such a way that ValidationErrors will propagate to forms where the clean method is called (including admin forms).

The below example raises an error if someone attempts to create or edit an auth.User instance to have the same email address as an existing auth.User instance. Disclaimer, if you are exposing a registration form to new users, you do not want your validation error to call out usernames as mine does below.

from django.contrib.auth.models import User
from django.forms import ValidationError as FormValidationError

def clean_user_email(self):
    instance = self
    super(User, self).clean()
    if instance.email:
        if User.objects.filter(id=instance.id, email=instance.email).exists():
            pass  # email was not modified
        elif User.objects.filter(email=instance.email).exists():
            other_users = [*User.objects.filter(email=instance.email).values_list('username', flat=True)]
            raise FormValidationError(f'At least one other user already has this email address: '
                                      f'{", ".join(other_users)}'
                                      , code='invalid')

# assign the above function to the User.clean method
User.add_to_class("clean", clean_user_email)

I have this at the bottom of my_app.models but I am sure it would work as long as you stick it somewhere that is loaded before the form in question.

ReferenceURL : https://stackoverflow.com/questions/8771029/raise-a-validation-error-in-a-models-save-method-in-django

반응형