될때까지

((WnB)) 2일차 : 모델링 작업 도중 만난 몰랐던 개념들 본문

프로젝트/wecode2차 : WnB

((WnB)) 2일차 : 모델링 작업 도중 만난 몰랐던 개념들

랖니 2022. 8. 3. 01:00
728x90

어제보다 복잡해진 우리의 ERD . 3명이서 작업하기에는 시간이 많이 남을 것 같다고 하셔서 호스트 기능과 리뷰를 추가했다. 모델링작업은 초기에 확장성까지 고려해서 만드는 게 좋다는 걸 1차에서 뼈저리게 느꼈기에..!!! 해당 ERD를 베이스로 모델링 작업을 진행했다. 이미 다 알고있는 개념이다 싶었는데 알고있는 게 아닌 '익숙'해진 것들이였다ㅜㅜ 그래서 진행하면서 몰랐던, 혹은 부족했던 개념들을 포스팅으로 정리해보고 넘어가자!!

 

auto_now=True & auto_now_add=True

  • updated_at = models.DateTimeField(auto_now = True)
    • auto_now=True는 현재 날짜
  • created_at = models.DateTimeField(auto_now_add = True)
    • auto_now_add=True는 최초 저장시 현재 날짜

 

DateField() & DateTimeField() 

  • DateField() : 날짜
  • DateTimeField() : 날짜와 시간

 

ForeignKey

  • models.ForeignKey('클래스이름') 
    • 아직 생성되지 않은 클래스를 가져올 때는 '모듈이름'을 가져와서 사용할 수 있다.
  • models.ForeignKey(Category)
    • 미리 정의되었다면 해당 모듈을 그대로 사용할 수 있다.
  • models.ForeignKey('users.User')
    • 다른 앱의 모델을 import하지 않고 사용할 수 있다.

 

db_table

roms_room은 어디서 나왔지?

rooms_room은 만든적없는 테이블인데 왜 어떻게 생성된거지??!!??!

코드를 다시 보니까 class Room을 정의할 때 Meta 클래스를 안썼다...  

class Meta :
	db_table = 'rooms'

class Meta를 쓰지 않아서 테이블명이 저런걸까 궁금해서 장고 공식문서를 찾아봤다.

yes!! db_table을 쓰지 않으면 앱이름_클래스이름 으로 생성된다.

 

ManyToManyField

class Room(TimeStampModel):
    name                 = models.CharField(max_length = 50)
    address              = models.CharField(max_length = 50)
    detail_address       = models.CharField(max_length = 50)
    price                = models.DecimalField(max_digits = 10, decimal_places = 2)
    description          = models.TextField()
    latitude             = models.DecimalField(max_digits=18, decimal_places=10)
    longitude            = models.DecimalField(max_digits=18, decimal_places=10)
    maximum_occupancy    = models.IntegerField()
    bedroom              = models.IntegerField()
    bathroom             = models.IntegerField()
    bed                  = models.IntegerField()
    host                 = models.ForeignKey('hosts.Host', on_delete = models.CASCADE)
    category             = models.ForeignKey('Category', on_delete = models.CASCADE)
    room_type            = models.ForeignKey('RoomType', on_delete = models.CASCADE)
    facilities           = models.ManyToManyField('Facility', through = 'RoomFacility')

    class Meta:
        db_table         = 'rooms'

 

- ManyToManyField를 사용한 facilites는 rooms 테이블에 생성되지 않는다!!

sql에서 전체 컬럼 확인하는 명령어 

SHOW FULL COLUMNS FROM TABLE_NAME;

- 다대다의 경우 컬럼 이름을 '복수형'으로 작성해야한다.

- through : Django는 다대다 관계를 관리하기 위해 자동으로 중간 테이블을 생성한다. 내가 만든 테이블로 관리하고 싶을 때 through옵션을 사용하여 원하는 중간 테이블을 지정할 수 있다. 보통 다대다 관계말고 추가 데이터를 테이블에 넣고 싶을 때 사용한다.

(a와 b가 외래키로 연결이 되어 있는데 c라는 컬럼값을 CharField로 입력받고 싶은 경우? 장고가 생성한 중간 테이블은 새로운 컬럼을 생성할 수 없다.)

- through_field : 하나의 테이블에 외래키로 똑같은 테이블이 2개이상 연결된 경우, 장고는  어느 키를 사용해야하는 지 알 수 없다. 이럴 때 외래키를 명시적으로 지정하기 위해 through_fields를 꼭 사용해야한다. 

class RoomFacility(models.Model):
    name                 = models.CharField(max_length = 50)
    rooms_room_facility  = models.ManyToManyField(
        'Room', through = 'RoomsRoomFacility', through_fields = ('room_facility', 'room')  
    ) 

    class Meta:
        db_table    = 'room_facilities' 

class RoomsRoomFacility(models.Model):
    room            = models.ForeignKey('Room', on_delete = models.CASCADE)
    room_facility   = models.ForeignKey('RoomFacility', on_delete = models.CASCADE)

    class Meta:
        db_table    = 'rooms_room_facilities'

처음에 작성했던 다대다관계는 위와 같았다.

위처럼 작성한 이유는 1차 프로젝트에서 저렇게 작성했기 때문에... 찾아보니까 우리는 하나의 테이블에 2개 이상의 외래키도 없다. 그래서 위의 코드와 지저분했던 테이블 이름은 아래와 같이 수정이 된다.

class Room(models.Model):
	...
    facilities = models.ManyToManyField('Facility', through='RoomFacility')
    
    class Meta:
        db_table    = 'rooms' 

class Facility(models.Model):
    name            = models.CharField(max_length = 50)
    
    class Meta:
        db_table    = 'facilities' 
        
class RoomFacility(models.Model):
    room            = models.ForeignKey('Room', on_delete = models.CASCADE)
    facility   	    = models.ForeignKey('Facility', on_delete = models.CASCADE)
    
    class Meta:
        db_table    = 'rooms_facilities'

하나의 방에는 여러가지의 편의시설이 존재한다. 그리고 하나의 편의시설은 여러가지 방에 존재한다. 이 둘은 다대다의 관계기 때문에 중간테이블이 필요하다. 장고가 만든 테이블을 사용해도 되지만, 우리가 만든 중간테이블을 사용하기 위해(안전성 때문에) through속성을 사용했다. 그리고 Facility에 대한 정보를 Room에 종속시킨 이유는, 객체를 가져올 때 Room.objects.get(id=1).facilities ~ 처럼 Room에서 facilities를 찾아가야하기 때문이다. 

다대다의 경우 어느쪽에 해당 필드를 추가해야하나 어려웠는데 어디서 타고 들어가야하나 생각해본 뒤 구현해보자! 

 

latitude & longitude 

    latitude             = models.CharField(max_length = 50)
    longitude            = models.CharField(max_length = 50)

위도와 경도같은 경우 소수점으로 다뤄진다. deicimal로 처리해야 훗날 계산을 할 때 편리하다.

 

DB에 migration 변경 사항들이 반영되지 않는 이유(원인 파악 불가..)

모델링 리뷰를 받고 models.py 내부 컬럼 이름과 같은 정보들을 수정했다. 위에서 정리한 것처럼 db_table을 쓰지않았기에 해당 내용을 적고 table이름은 'rooms'로 적었다. 그럼 DB상에서도 rooms_room이 rooms로 변경될거라 생각했는데 안되는 상황.. 왤까?? migration 파일도 삭제하지않았는데 업데이트되야하는 것 아닌가?

하지만 반영이 되질않았다.. 제대로 메뉴얼대로 진행했는데 왜 변경사항이 적용이 안되냐구요 부들부들!!!! 지금은 AWS RDS를 같이 사용하고 있어서 현재 models.py의 상태가 제대로 반영이 됐다.

 

 

git rebase

같은 팀원인 도연님 컴퓨터에서 앱들을 생성하고, 모델링까지 작업한 뒤 rebase 명령어를 사용하여 올렸다. 그 후에 멘토님 리뷰를 받았고 해당 사항 적용 뒤 새로 add & commit한 뒤 git rebase -i main 명령어를 사용했다. 했더니 아래와 같은 에러메세지를 만났다.

 '''https://github.com/wecode-bootcamp-korea/35-2nd-WnB-backend.git'에
 푸시하는데 실패했습니다. 힌트: 현재 브랜치의 끝이 리모트 브랜치보다 뒤에 있으므로 업데이트가 거부되었습니다."

이 에러의 이유는 rebase한 뒤에 커밋이 달라졌기 때문이다. 리모트에 올라가있는 커밋과 내가 작업하고 있는 파일의 커밋 상태가 다르기 때문이다. 이럴 경우 'git push origin --force' 명령어를 사용해서 push하는 방법이 있다. force하기 싫다면 remote branch를 지워버리고 다시 push를 해야한다.

728x90