- 6주차 배운점 목차
[이번주 배운점]
<이미지 파일의 삭제>
우리가 만든 팀프로젝트에서는 트윗 게시글의 내용을 삭제하거나 수정하면,
media폴더 내 저장된 사진 파일이 삭제된다.
사진 업로드에서 더욱 완성도를 올린것이다.
사진 파일이 계속 불필요하게 늘어나는 걸 막는다.
삭제의 경우는 동작이 쉽다.
삭제가 동작 될때, 지칭하는 인스턴스가 사진을 가졌는지 판단하고,
사진을 가졌다면 파일을 삭제하면 된다.
class TweetModel(models.Model):
(중략)
def delete(self):
if self.image:
os.remove(self.image.path)
super().delete()
이렇게 메소드를 작성해 삭제 함수를 조정했다.
삭제하는 명령어는 위와 같다.
os.remove(삭제할 파일의 경로)로 삭제한다.
image필드에 path속성을 호출하면 경로를 정확히 제공한다.
참고로 사진이 없는데 삭제 동작을 하면 에러가 난다.
그래서 사진의 유무를 체크하는 건 필요하다.
(조건문을 try, except으로 대체할수도 있겠다.)
<수정하며 이미지파일 지울때 경우의 수>
트윗을 수정할때도 이미지 파일을 지울 필요가 있다.
만약 사진이 수정되어서, 이전 사진 파일이 필요하지 않게될 경우가 그렇다.
수정의 경우를 생각해보면 조금 복잡하다.
여러 경우를 따져서 그중에 사진 삭제가 필요한 순간을 골라야 하기 때문이다.
수정의 경우의 수는 아래 5개이다.
X → X
X → O
A → B ★
O → X ★
O → O
사진 데이터가 지워져야 하는 경우는 ★표시를 한 두개 경우다.
왼쪽이 인스턴스의 기존 값, 오른쪽이 새 값이다.
X는 사진이 없는 걸 말하고, O는 있는 것이며,
A에서 B로 바뀌는 경우도 있다.
※ 이렇게 5가지 경우가 나오는 것은,
사진이 비어있어도 된다는 blank=True옵션이 있기때문이다.
팀원분이 작성했던 내용은 22일차 배운점에 적어두었다.
(https://barryjung.tistory.com/43)
나는 프로젝트 복기를 하면서 이부분을,
원래 하고자 하셨던대로, 클래스 메소드로 작성하는데 성공했다.
첫번째 쟁점은 메소드로 작성할 경우,
create과 update를 구분하기 어렵다는 데에 있었다.
생성과 수정 모두 save라는 함수도 데이터를 저장시키기 때문이다.
이 부분은 아래와 같이 해결할수 있었다.
class TweetModel(models.Model):
(중략)
def save(self):
if self.id is not None:
oldimage = self.__class__.objects.get(id=self.id).image
if oldimage and oldimage != self.image:
os.remove(oldimage.path)
super().save()
save가 동작될 때, 인스턴스의 첫 생성인지,
기존의 인스턴스가 있는지를 판단하면 될 것 같았다.
인터넷에 찾아보니 아주 스마트한 방법을 찾을수 있었다.
위처럼 self.id를 검사하도록 작성해주면 된다.
첫 생성일 경우 self.id는 None인채로 인스턴스가 넘어온다.
수정의 경우는 넘어오는 인스턴스는 self.id가 생성되있으니,
self.id로 생성인지 수정인지 구분 가능한 것이다.
앞으로도 요긴하게 써먹을 좋은 작성법 인것 같다.
그럼 그 아래에서 동작을 보겠다.
아래 부분을 저렇게 작성하기까지 꽤 공이 들어갔다.
우선 수정의 경우의 수에 대해 분석했다.
공책에 알고리즘 연결도를 그려가며 고민해본건 부트캠프 시작하고 처음이였다.
최종적으로 내가 고민한 로직은 이렇다.
첫입력인지 판단 ▷ Y. 패스
▽ N
기존 이미지가 있는지 판단 ▷ N. 패스
▽ Y
기존 이미지와 새 이미지가 다른지 판단 ▷ N. 패스
▽ Y
사진 파일 삭제
위의 경우의 수 나열을 보면 딱 이렇게 판단하는게 순서가 맞다.
기존 이미지가 있어야 하며, 새 이미지가 다르거나 없는 변동이 있어야 한다.
그러기 위해서는 불러온 새 인스턴스말고, (여기에는 새 이미지가 있다)
기존 이미지를 가져올 방법이 필요했다.
인터넷으로 알아본 결과 위 방법을 알수 있었다.
oldimage = self.__class__.objects.get(id=self.id).image
__class__로 기존의 인스턴스를 가져올 수 있었다.
.image로 이미지 값만 가져왔다.
그 아래에서 한번에,
oldimage가 존재하는 지와 새 이미지와 다른지 비교한 다음,
맞다면 이미지를 삭제하도록 작성해줬다.
잘 동작하는 것까지 확인할수 있었다.
(이걸 작성해보며 느낀건데, 사진이 없을 경우 os.remove를 하면 에러가 난다.
이걸 막기위해 try, except를 쓰는 건 좋지만 그럴 경우 놓치는 부분이 생길 수 있다.)
실제로, 작성 도중 시행착오가 있었다.
나는 입력 인스턴스에 사진이 있다면 수정사항이 있는 것으로 판단할 수 있다고 생각했다.
하지만 실제 수정 폼의 동작은 그렇지 않다.
인스턴스가 작성된 수정폼은 사진을 바꾸지 않아도 기존 사진을 전송한다.
그리고 사진을 없애는 경우 빈 사진 필드를 전송한다.
그러니 사진의 입력이 있냐 없냐로는 구분이 안된다.
위처럼 기존 이미지 필드와 새 이미지가 다른지 비교하는게 알맞다.
결론, 모델 내 메소드를 이용해 수정 기능을 작성할수 있었다.
self.id와 self.__class__등 유용한 작성법을 배웠다.
<is not은 !=과 다르다>
내 복습 프로젝트 마지막 커밋은 follow기능의 오류 수정이다.
< if request.user is not target_user: #변경전
> if request.user != target_user: #변경후
팔로우 기능 안에 있는
현재 유저와 타겟 유저가 다른지 판단하는 조건문이다.
자기 자신을 팔로우 하면, 팔로우 되지 않아야 하기에 필요하다.
그런데 위 조건문의 경우 계속 참이다.
즉, 수정 전에는 내가 나를 팔로우가 가능했다.
그래서 아래처럼 바꾸니 제대로 의도대로 동작했다.
프로젝트 전체적으로 user의 검사 동작은 !=로 작성했는데,
저부분만 왜 그랬는지 자연스럽게 is not 으로 작성했던것이 문제를 일으킨것이다.
인터넷에 알아보니 !=과 is not은 동작이 다르다.
!=는 두 개의 값이 다른 지를 비교한다.
is not은 두 개의 객체가 다른 지를 비교한다.
두개의 값은 같을수 있지만,
당연하게 두개 객체는 항상 다르다.
대게의 경우 값을 비교하게 되니 보통 !=이 적합하다.
그럼 is not이 알맞은 때는 언제일까?
복습 프로젝트 안에서 is not을 찾아봤더니 한군데가 있다.
바로 위에서 다뤘던 save()메소드에,
self.id is not None이다.
이때는 왜 is not None이 적합했을까 고민해봤다.
self.id라는 객체가 아직 존재 하지 않을 경우를 검사하기 위해서이다.
좀더 정확히 이해하기 위해 더 알아봤고 이렇게 이해할수 있었다.
self.id가 아직 생성되지 않았다면 조건문을 만족한다.
반면, self.id가 생성되어 있으나 값이 none이거나 Falsy한 값인 경우는 조건문을 만족하지 않는다.
즉, is not은 두 객체끼리 비교하는 비교연산이고,
None이라는 파이썬 객체는 객체가 없는 상태를 나타내는 특별한 역할을 가진 객체이다.
그래서 self.id라는 객체가 None 상태여야 조건문을 만족하도록 만들수 있는것이다.
결론, is not과 !=는 완전 다르다.
그러니 혼동하면 안되고, 각각 정확히 알맞은 위치에 사용되어야 한다!!
마지막으로,
내 복습프로젝트의 깃허브 주소이다.
https://github.com/barryjung/bootsCampStrap_Self
버전 1.0.0으로 태그도 붙여줬다.
다만 여기서 AWS배포까지 한번 도전해보고 싶다.