ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Django FileField upload_to Custom 야매 적용기
    개발 일기 2021. 7. 12. 13:06

    서론

    필자의 개인 프로젝트 간 사용했던 내용을 저장 및 정리 용도로 쓰는 페이지입니다.

    찾으시는 내용이 없을 수 있습니다.

    Crawler -> Crwaler 오타가 있습니다.

     

    FileField upload_to Custom

    먼저 filefield의 upload_to 페이지를 보자.

    기본적으로 upload_to는 아래와 같이 model 딴에서 간단하게 사용이 가능하다.

    class MyModel(models.Model):
        # file will be uploaded to MEDIA_ROOT/uploads
        upload = models.FileField(upload_to='uploads/')
        # or...
        # file will be saved to MEDIA_ROOT/uploads/2015/01/30
        upload = models.FileField(upload_to='uploads/%Y/%m/%d/')

     

     

    그러나 필자의 상황으로는 이렇다.

    각 회사는 Crawler Model 생성 시 Crwaler Model의 왜래키를 가지고 나머지는 빈 값으로 자동 생성된다.

    각 회사의 사진들을 하나의 모델에서 받기 위해 한 개의 Cralwer Photos에 각 회사의 외래키를 때려 박았다.

    각 회사의 사진을 저장하기 위해서는 다르게 적용되는 upload path가 필요하다.

     

     

    이해를 돕기 위한 Model 코드 더보기(Custom이 완성된 코드입니다)

    더보기
    class CrwalingModel(TimeModel):
        enter_name = models.CharField(max_length=50)
    
        def __str__(self):
            return self.enter_name
    
        class Meta:
            ordering = ['created_at']
    
    
    class CrwalingBaseModel(models.Model):
        enter = models.OneToOneField(CrwalingModel, on_delete=models.CASCADE)
        company_code = models.CharField(max_length=1000, blank=True)
        location = models.CharField(max_length=50, blank=True)
        url = models.SlugField(max_length=1000, blank=True)
        upload_to_path = models.CharField(max_length=2000, blank=True)
    
    
        def __str__(self):
            return self.enter.enter_name
    
        class Meta:
            abstract = True
    
    
    @receiver(post_save, sender=CrwalingModel)
    def create_Post(sender, instance, created, **kwargs):
        if created:
            if instance not in ['SaraminInfo', 'JobKoreaInfo', 'JobPlanetInfo', 'KreditJobInfo']:
                SaraminInfo.objects.create(enter=instance)
                JobKoreaInfo.objects.create(enter=instance)
                JobPlanetInfo.objects.create(enter=instance)
                KreditJobInfo.objects.create(enter=instance)
    
    
    class SaraminInfo(CrwalingBaseModel):
        # name = models.CharField(max_length=7, default='saramin')
        pass
    
    
    class JobKoreaInfo(CrwalingBaseModel):
        # name = models.CharField(max_length=8, default='jobkorea')
        pass
    
    
    class JobPlanetInfo(CrwalingBaseModel):
        # name = models.CharField(max_length=9, default='jobplanet')
        pass
    
    
    class KreditJobInfo(CrwalingBaseModel):
        # name = models.CharField(max_length=9, default='kreditjob')
        pass
    
    # 경로: 회사이름 -> 크롤링회사 -> 코드 -> 날짜
    # 이미지이름: 회사이름_회사코드_기타내용.png
    def crwaling_photo_path(instance, filename):
        if instance.jobkorea_info:
            return '{0}/{1}'.format(instance.jobkorea_info.upload_to_path, filename)
        elif instance.saramin_info:
            return '{0}/{1}'.format(instance.saramin_info.upload_to_path, filename)
        elif instance.jobplanet_info:
            return '{0}/{1}'.format(instance.jobplanet_info.upload_to_path, filename)
        elif instance.kreditjob_info:
            return '{0}/{1}'.format(instance.kreditjob_info.upload_to_path, filename)
    
    class CrwalingPhotos(TimeModel):
        photo = models.ImageField(upload_to=crwaling_photo_path, max_length=1000)
        saramin_info = models.ForeignKey(SaraminInfo, on_delete=models.RESTRICT, blank=True, null=True)
        jobkorea_info = models.ForeignKey(JobKoreaInfo, on_delete=models.RESTRICT, blank=True, null=True)
        jobplanet_info = models.ForeignKey(JobPlanetInfo, on_delete=models.RESTRICT, blank=True, null=True)
        kreditjob_info = models.ForeignKey(KreditJobInfo, on_delete=models.RESTRICT, blank=True, null=True)

     

     

    그럼 과연 django에는 upload_to를 어떻게 커스텀해서 적용할까?

     

    이하 django filefield 예시

    def user_directory_path(instance, filename):
        # file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
        return 'user_{0}/{1}'.format(instance.user.id, filename)

    위와 같이 instance와 filename가 넘어오는데 instance는 model에서 생성된 instance 내용물들을 말한다

    그래서 그 내용물의 필드들, 왜래키의 필드들에 접근할 수 있다.

     

     

    그러면 살짝 바꿔서 테스트해보자

    def user_directory_path(instance, filename):
        # file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
        return 'user_{0}/%Y/%m/%d/{1}'.format(instance.user.id, filename)

    이 코드의 결과로는 날짜가 들어가지 않고 %Y/%m/%d가 문자열로 들어간다.

    그러므로 time 라이브러리로 직접 넣어줘야 날짜가 들어간다.

     

     

     

    time을 따로 적용해야 하는 걸 알았으니 직접 적용해보자

    필자는 path를 따로 만들어 db에 저장 후, db path를 이미지에 적용하도록 했다.

    또한 Crawler Model 생성 시 각 회사가 자동 생성되므로 그 회사에 해당하는 코드를 넣었다.

    path = os.path.join(BASE_DIR) + '/media/search_job/' + company_name + '/jobkorea/' + time.strftime(
                "/%Y/%m/%d/")
    jobkorea = JobKoreaInfo.objects.get(enter=self.enter)
    jobkorea.company_code = company_code
    jobkorea.upload_to_path = path
    jobkorea.save()
    CrwalingPhotos.objects.create(jobkorea_info=jobkorea, photo=image)

     

     

    회사에 대한 upload_path를 생성 후, CrawlerPhotos의 Custom Upload Path를 만들어보자

    def crwaling_photo_path(instance, filename):
        if instance.jobkorea_info:
            return '{0}/{1}'.format(instance.jobkorea_info.upload_to_path, filename)
        elif instance.saramin_info:
            return '{0}/{1}'.format(instance.saramin_info.upload_to_path, filename)
        elif instance.jobplanet_info:
            return '{0}/{1}'.format(instance.jobplanet_info.upload_to_path, filename)
        elif instance.kreditjob_info:
            return '{0}/{1}'.format(instance.kreditjob_info.upload_to_path, filename)
    
    class CrwalingPhotos(TimeModel):
        photo = models.ImageField(upload_to=crwaling_photo_path, max_length=1000)
        saramin_info = models.ForeignKey(SaraminInfo, on_delete=models.RESTRICT, blank=True, null=True)
        jobkorea_info = models.ForeignKey(JobKoreaInfo, on_delete=models.RESTRICT, blank=True, null=True)
        jobplanet_info = models.ForeignKey(JobPlanetInfo, on_delete=models.RESTRICT, blank=True, null=True)
        kreditjob_info = models.ForeignKey(KreditJobInfo, on_delete=models.RESTRICT, blank=True, null=True)

    한 번의 Crawlerphoto당 한 개의 회사, 한 개의 photo만 들어갈 것이므로(나머지 회사는 null)

    crawling_photo_path에 조건문으로 instance.~~~_info가 생성되었다면 그 데이터에서 저장된 path를 불러와

    upload_path와 filename을 적용하도록 만들었다.(참고로 filename은 db 저장하는 곳에서 적용한다.)

     

     

    결론

    필자의 코드를 바탕으로 설명하느라 좀 길어졌는데

    간단하게 줄이자면 upload_to에는 생성된 instance와 filename이 넘어오는데

    필자의 경우는 path를 직접 db에 넣어서 그 경로를 불러와서 file 저장 path로 쓰는 방식을 사용했다.

     

    다른 경우가 있나 잠깐 찾아봤지만 다른 방법이 안보이길래 이런 야매 방식으로 업로드 경로를 지정했다.

     

     

    '개발 일기' 카테고리의 다른 글

    Docker-compose에서 localhost 사용하기  (0) 2021.08.02
    Windows 포트 잠김 해결법  (0) 2021.08.02
    selenium Tips  (0) 2021.07.08
    Django restframework ModelViewSet Delete, Update  (0) 2021.06.07
    알고리즘 풀다가 어이없던 일  (0) 2021.05.31

    댓글

Designed by Tistory.