동일한 모델의 다른 필드를 기반으로하는 Django 모델 필드 기본값


92

주제 이름과 이니셜을 포함하고 싶은 모델이 있습니다 (데이터는 다소 익명화되고 이니셜로 추적 됨).

지금 내가 쓴

class Subject(models.Model):

    name = models.CharField("Name", max_length=30)
    def subject_initials(self):
        return ''.join(map(lambda x: '' if len(x)==0 else x[0],
                           self.name.split(' ')))
    # Next line is what I want to do (or something equivalent), but doesn't work with
    # NameError: name 'self' is not defined
    subject_init = models.CharField("Subject Initials", max_length=5, default=self.subject_initials)

마지막 줄에서 알 수 있듯이 이니셜을 실제로 데이터베이스에 필드 (이름과 무관)로 저장하는 것을 선호하지만 이름 필드를 기반으로하는 기본값으로 초기화됩니다. 그러나 django 모델에 '자신'이없는 것 같아 문제가 있습니다.

줄을로 변경하면 subject_init = models.CharField("Subject initials", max_length=2, default=subject_initials)syncdb를 수행 할 수 있지만 새 제목을 만들 수는 없습니다.

장고에서 이것이 가능합니까, 호출 가능한 함수가 다른 필드의 값을 기반으로 일부 필드에 기본값을 제공하도록 하시겠습니까?

(궁금한 점은 매장 이니셜을 따로 분리하고 싶은 이유는 드물게 이상한 성이 내가 추적하는 것과 다를 수 있기 때문입니다. "JO"가 아닌 "JM"을 수정하고 관리자 권한으로 수정하려고합니다.)

답변:


89

모델에는 확실히 "자아"가 있습니다! 모델 인스턴스에 의존하는 것으로 모델 클래스의 속성을 정의하려고하는 것입니다. 클래스와 그 속성을 정의하기 전에 인스턴스가 존재하지 않기 때문에 가능하지 않습니다.

원하는 효과를 얻으려면 모델 클래스의 save () 메서드를 재정의하십시오. 필요한 인스턴스를 원하는대로 변경 한 다음 슈퍼 클래스의 메서드를 호출하여 실제 저장을 수행합니다. 다음은 간단한 예입니다.

def save(self, *args, **kwargs):
    if not self.subject_init:
        self.subject_init = self.subject_initials()
    super(Subject, self).save(*args, **kwargs)

이것은 문서의 모델 메서드 재정의 에서 다룹니다 .


5
Python 3 에서는 참조 문서의 예제 에서처럼 (인수 super().save(*args, **kwargs)없이) 간단히 호출 할 수 있습니다 Subject, self.
Kurt Peek

1
안타깝게도 다른 항목을 기반으로 기본 모델 속성 값을 정의 할 수 없습니다. 이는 특히 관리 측 (예 : 자동 증가 필드의 경우)에서 유용합니다. 저장시 처리하기 때문입니다. 아니면 내가 오해하고 있습니까?
Vadorequest

18

이 일을 더 나은 방법이 있는지 모르겠지만, 당신은 할 수 신호 처리기를 사용 하기위한 신호 :pre_save

from django.db.models.signals import pre_save

def default_subject(sender, instance, using):
    if not instance.subject_init:
        instance.subject_init = instance.subject_initials()

pre_save.connect(default_subject, sender=Subject)

1
수입했습니다 post_save대신pre_save .
알리 Rasim Kocal

1
@arkocal : 머리를 올려 주셔서 감사합니다. 이러한 경우 직접 편집을 제안 할 수 있으며 다음과 같은 문제를 해결하는 데 도움이됩니다. :)
가비 Purcaru

1
이 구현에는 docs.djangoproject.com/en/2.0/topics/signals/…**kwargs 에 따라 모든 수신자 함수가 가져야 하는 인수 가 누락 된 것 같습니다. ?
Kurt Peek

문서에서는 사용자가 제어하는 ​​코드 경로에 대한 신호에 대해 권장 합니다 . overriding의 허용 된 대답은 save()아마도 overriding 으로 변경 __init__되었을 수 있으며 더 명시 적이므로 더 좋습니다.
Adam Johnson

7

Django 신호를 사용 하면 모델 에서 post_init신호 를 수신 하여 매우 일찍 수행 할 수 있습니다 .

from django.db import models
import django.dispatch

class LoremIpsum(models.Model):
    name = models.CharField(
        "Name",
        max_length=30,
    )
    subject_initials = models.CharField(
        "Subject Initials",
        max_length=5,
    )

@django.dispatch.receiver(models.signals.post_init, sender=LoremIpsum)
def set_default_loremipsum_initials(sender, instance, *args, **kwargs):
    """
    Set the default value for `subject_initials` on the `instance`.

    :param sender: The `LoremIpsum` class that sent the signal.
    :param instance: The `LoremIpsum` instance that is being
        initialised.
    :return: None.
    """
    if not instance.subject_initials:
        instance.subject_initials = "".join(map(
                (lambda x: x[0] if x else ""),
                instance.name.split(" ")))

post_init이 인스턴스에 초기화 완료되면 신호는 클래스에 의해 전송됩니다. 이러한 방식으로 인스턴스는 namenullable이 아닌 필드가 설정되었는지 여부를 테스트하기 전에 값을 가져옵니다 .


문서에서는 사용자가 제어하는 ​​코드 경로에 대한 신호에 대해 권장 합니다 . overriding의 허용 된 대답은 save()아마도 overriding 으로 변경 __init__되었을 수 있으며 더 명시 적이므로 더 좋습니다.
Adam Johnson

2

Gabi Purcaru 답변 의 대안 구현으로 데코레이터를pre_save 사용하여 신호에 연결할 수도 있습니다 .receiver

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


@receiver(pre_save, sender=Subject)
def default_subject(sender, instance, **kwargs):
    if not instance.subject_init:
        instance.subject_init = instance.subject_initials()

이 수신기 함수는 또한 **kwargs모든 신호 처리기가 https://docs.djangoproject.com/en/2.0/topics/signals/#receiver-functions 에 따라 가져와야 하는 와일드 카드 키워드 인수를 받습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.