장고에서 FileFieldImageField는 실제 파일이 아닌 파일이 저장될 상대경로 문자열을 저장합니다. 이 때, 파일이 주어지지 않았을 경우를 대비해 default속성에 지정한 값은 동적으로 해당 경로를 찾는것이 아니라 해당 값을 필드에 문자열로 저장합니다.

ImageField를 사용할 때, 파일이 없는 경우 장고 프로젝트의 정적파일을 서빙하는 법에 대해 알아봅니다.

ImageFieldFile 재정의

참고문서

FieldFileImageFieldFile은 각각 모델 인스턴스의 FileFieldImageField속성에 접근 시 파일에 액세스 하기 위한 프록시 역할을 합니다. 지금은 템플릿에서 필드의 url속성에 대해 접근하기 때문에 해당 속성을 재정의 해줍니다.

fields.py

DEFAULT_IMAGE_PATH = 'django_fields/no_image.png'


class DefaultStaticImageFieldFile(ImageFieldFile):
    @property
    def url(self):
        try:
            # 파일이 존재한다면  부모의 url속성을 리턴합니다.
            return super().url
        except ValueError:
            # 파일이 없다면 ValueError가 발생합니다.
            from django.contrib.staticfiles.storage import staticfiles_storage
            from django.contrib.staticfiles import finders
            # staticfiles app의 finders를 사용해 FieldFile에 연결된 field의 
            # static_image_path값에 해당하는 파일이 있는지 검사합니다.
            if finders.find(self.field.static_image_path):
                # 존재할경우 staticfiles_storage의 url메서드를 사용해
                # 정적파일에 액세스 할 수 있는 URL을 반환합니다.
                return staticfiles_storage.url(self.field.static_image_path)
            # static_image_path값에 해당하는 경로에 파일이 없다면
            # 'django_fields/no_image.png'경로의 파일을 사용합니다.
            return staticfiles_storage.url(DEFAULT_IMAGE_PATH)

ImageField 재정의

그리고 위에서 정의한 DefaultStaticImageFieldFile을 클래스 속성 attr_class로 사용하는 ImageField클래스를 상속을 이용해 정의해줍니다.

fields.py

class DefaultStaticImageField(ImageField):
    # field에 접근 시 프록시로 사용할 필드파일 클래스를 지정합니다.
    attr_class = DefaultStaticImageFieldFile

    def __init__(self, *args, **kwargs):
        # 필드의 속성중 'default_image_path'키로 주어진 값을 가져와 인스턴스의 static_image_path값으로 할당합니다.
        # 이 때, 속성이 지정되어 있지 않으면 settings모듈의 'DEFAULT_IMAGE_PATH'속성의 값을 가져오며, 
        # 이것도 정의되어 있지 않다면 현재 모듈의 'DEFAULT_IMAGE_PATH'상수의 값을 사용합니다.
        self.static_image_path = kwargs.pop(
            'default_image_path',
            getattr(settings, 'DEFAULT_IMAGE_PATH', DEFAULT_IMAGE_PATH))
        # 추가 정의된 키워드인수의 값을 pop()으로 제거한 뒤, 부모의 초기화 메서드를 실행합니다.
        super().__init__(*args, **kwargs)

모듈의 최상단에 있는 DEFAULT_IMAGE_PATH속성의 값이 정적파일에 포함될 수 있도록 프로젝트의 STATICFILES_DIRS에 포함되는 경로에 django_fields/no_image.png파일을 추가해주어야 합니다.

사용법

위와 같이 커스텀 필드를 만든 후, 아래와 같이 사용합니다.

class AnyModel(models.Model):
    # 필드의 속성에 사용할 정적 경로를 입력해주는 경우
    image1 = DefaultStaticImageField(blank=True, default_image_path='images/no_image.png')
    
    # settings.py에 정의된 DEFAULT_IMAGE_PATH 또는 fields모듈 최상단의 값을 사용
    image2 = DefaultStaticImageField(blank=True)

이 코드는 재사용하기 위해 PyPI - django-default-imagefieldGitHub - Django-Default-ImageField 에 업로드 되어 있습니다.

Terminal

pip install django-default-imagefield

위 명령어로 패키지 설치 후 from django_fields import DefaultStaticImageField문으로 필드를 불러와 사용할 수 있으며, 자세한 사용법은 README에 작성되어 있습니다.