django의 model.save ()가 full_clean ()을 호출하지 않는 이유는 무엇입니까?


150

django의 orm이 모델 양식의 일부로 저장되지 않는 한 모델에서 'full_clean'을 호출하지 않는 이유가 있는지 아는 사람이 있는지 궁금합니다.

모델의 save () 메소드를 호출 할 때 full_clean ()이 자동으로 호출되지 않습니다. 직접 만든 모델에 대해 1 단계 모델 유효성 검사를 실행하려면 수동으로 호출해야합니다. django의 완전 깨끗한 의사

(참고 : Django 1.6에 대한 견적이 업데이트되었습니다 ... 이전 장고 문서는 ModelForms에 대해서도주의를 기울였습니다.)

사람들이이 행동을 원하지 않는 좋은 이유가 있습니까? 모델에 유효성 검사를 추가하는 데 시간이 걸린다면 모델이 저장 될 때마다 유효성 검사가 실행되기를 원할 것입니다.

모든 것이 올바르게 작동하는 방법을 알고 있으며 설명을 찾고 있습니다.


11
이 질문에 대해 대단히 감사합니다. 벽에 머리를 두드리는 것을 훨씬 더 많이 막았습니다. 다른 사람들을 도울 믹스 인을 만들었습니다. 요점을 확인하십시오 : gist.github.com/glarrain/5448253
— glarrain

그리고 마지막으로 신호를 사용하여 pre_save후크 를 잡고 full_clean모든 잡힌 모델을 처리합니다.
— Alfred Huang

답변:


59

AFAIK는 이전 버전과의 호환성 때문입니다. 필드가 제외 된 ModelForms, 기본값이있는 모델, pre_save () 신호 등에도 문제가 있습니다.

여러분이 모욕 할만한 출처 :


3
두 번째 참조에서 가장 유용한 발췌문 (IMHO) : "실제로 유용하고 견고 할 수있을 정도로 간단하고 모든 경우를 처리 할 수있는"자동 "검증 옵션 개발은 가능하다면 훨씬 더 중요합니다. 따라서 현재 Django에는 그러한 것이 없으며 1.2에는 없습니다 .1.3에 대해 효과가 있다고 생각되면 가장 좋은 방법은 "단순하고 강력한 코드를 유지하는 방법에 대한 설명과 함께 샘플 코드를 포함하여 제안"
— Josh

30

호환성을 고려하여 django 커널에서 저장시 자동 정리 기능을 사용할 수 없습니다.

새 프로젝트를 시작하고 saveModel 의 기본 방법이 자동으로 정리되도록하려면 모든 모델을 저장하기 전에 다음 신호를 사용하여 정리할 수 있습니다.

from django.dispatch import receiver
from django.db.models.signals import pre_save, post_save

@receiver(pre_save)
def pre_save_handler(sender, instance, *args, **kwargs):
    instance.full_clean()

2
full_clean을 먼저 호출 한 다음 super ()를 호출하기 위해 일부 BaseModel (다른 모든 클래스가 상속 할)의 save 메소드를 대체하는 것보다 이것이 더 나은 (또는 더 나쁜) 이유는 무엇입니까?
— J__

7
1) ModelForm의 full_clean ()이 두 번 호출되는 경우 : 폼과 신호에 의해 2)이 방법에 대한 두 가지 문제가 있습니다 .2) 폼이 일부 필드를 제외하면 여전히 신호에 의해 유효성이 검사됩니다.
— mehmet

1
@mehmet 그래서 당신은이를 추가 할 수있을 수 있습니다 if send == somemodel, then exclude some fields에pre_save_handler
— 인 Simin 지에

4
이 접근법을 사용하거나 사용하는 사람들의 경우 :이 접근법은 Django에서 공식적으로 지원하지 않으며 가까운 장래에는 지원되지 않을 것임을 명심하십시오 (Django 버그 추적기의 의견 : code.djangoproject.com/ticket/ 29655 # comment : 3 )이므로 모든 모델에 대한 유효성 검사를 활성화하면 인증 중지 작동 ( code.djangoproject.com/ticket/29655 ) 과 같은 일부 결함이 발생할 수 있습니다. 그러한 문제를 직접 해결해야합니다. 그러나 더 나은 접근 방식은 없습니다.
— Evgeny A.

2
Django 2.2.3부터는 기본 인증 시스템에 문제가 발생합니다. 당신은 얻을 것이다 ValidationError: Session with this Session key already exists. 이를 피하려면 sender in list_of_model_classes신호가 Django의 기본 인증 모델을 무시하지 않도록 if 문을 추가해야합니다 . list_of_model_classes그러나 정의 하십시오
— Addison Klinke

15

full_clean메소드 를 호출하는 가장 간단한 방법은 다음에서 메소드를 대체 save하는 것입니다 model.

def save(self, *args, **kwargs):
    self.full_clean()
    return super(YourModel, self).save(*args, **kwargs)

왜 이것이 신호를 사용하는 것보다 낫습니까?
— J__

6
1) ModelForm의 full_clean ()이 두 번 호출되는 경우 : 양식과 저장에 의해 2)이 방법에 대한 두 가지 문제가 있습니다.
— mehmet

3

수신자를 선언하는 코드를 삽입하는 대신 앱을 INSTALLED_APPS섹션으로 사용할 수 있습니다.settings.py

INSTALLED_APPS = [
    # ...
    'django_fullclean',
    # your apps here,
]

그 전에 django-fullcleanPyPI 를 사용 하여 설치해야 할 수도 있습니다 .

pip install django-fullclean

13
이 줄을 직접 쓰는 대신 pip install4 줄의 코드가있는 응용 프로그램 ( 소스 코드 확인)이 필요한 이유는 무엇 입니까?
— David D.

내가 직접 시도하지 않은 또 다른 라이브러리 : github.com/danielgatis/django-smart-save
— Flimm

2

FK 관계가 하나 이상 있는지 확인하려는 모델이 null=False있고 기본 FK (가비지 데이터)를 설정해야 하기 때문에 사용하지 않으려는 경우 가장 좋은 방법은 사용자 정의 .clean()및 .save()메소드 를 추가 합니다. .clean()유효성 검사 오류가 발생 .save()하고 정리를 호출합니다. 이런 식으로 무결성은 양식과 다른 호출 코드, 명령 줄 및 테스트에서 모두 시행됩니다. 이것이 없으면 (AFAICT) 테스트를 작성하는 방법이 없으므로 모델이 특별히 선택된 (기본이 아닌) 다른 모델과 FK 관계를 갖도록합니다.

class Payer(models.Model):

    name = models.CharField(blank=True, max_length=100)
    # Nullable, but will enforce FK in clean/save:
    payer_group = models.ForeignKey(PayerGroup, null=True, blank=True,)

    def clean(self):
        # Ensure every Payer is in a PayerGroup (but only via forms)
        if not self.payer_group:
            raise ValidationError(
                {'payer_group': 'Each Payer must belong to a PayerGroup.'})

    def save(self, *args, **kwargs):
        self.full_clean()
        return super().save(*args, **kwargs)

    def __str__(self):
        return self.name

1

@Alfred Huang의 답변에 댓글을 달았습니다. 현재 모듈 (models.py)에서 클래스 목록을 정의하고 pre_save 후크에서이를 확인하여 pre_save 후크를 앱에 고정시킬 수 있습니다.

CUSTOM_CLASSES = [obj for name, obj in
        inspect.getmembers(sys.modules[__name__])
        if inspect.isclass(obj)]

@receiver(pre_save)
def pre_save_handler(sender, instance, **kwargs):
    if type(instance) in CUSTOM_CLASSES:
        instance.full_clean()
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.