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 fieldspre_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.