-
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