티스토리 뷰
관계형 데이터 베이스를 배웠으니 django의 백엔드를 통해 저장하고 데이터를 호출하는 법도 배워야 할 차례다. N:1까지는 할만하니 한번 해보자! (django라서 다루기도 쉽다)
Many to one relationships (N:1)
: 한 테이블의 0개 이상의 레코드가 다른 테이블의 레코드 한개와 관련된 관계
=> 예를들어 게시글에 댓글을 생각해보자. 한 개의 게시글에는 여러개의 댓글을 달 수 있을 것이다. 이때, Comment는 N, Article은 1이다.
Comment | Article |
id content created_at updated_at |
id title content created_at updated_at |
현재 위처럼 테이블이 작성되어있다고 하면, Article에 댓글이 여러개 달리기 때문에 N:1인건 이제 모두 알것이다. 그럼 Fk는 어디에 작성되어야 할까?
기준이 Article이 되었다고 생각하면 아래와 같다
그럼 django에서 ForeignKey를 추가해보자.
- ForeignKey 클래스의 인스턴스 이름은 참조하는 모델 클래스의 소문자 + 단수형으로 작성하는 것을 권장한다
- ForeignKey 클래스를 작성하는 위치와 관계없이 테이블의 필드 마지막에 생성된다
- on_delete 는 외래 키가 참조하는 객체가 사라졌을 때, 외래 키를 가진 객체를 어떻게 처리할 지를 정의하는 설정으로 데이터의 무결성을 위해 작성해준다.
- CASCADE : 부모 객체(참조 된 객체)가 삭제 되었을 때 이를 참조하는 객체도 삭제한다.
- 필드명은 참조하는 대상 클래스 이름 + ' _ ' + '클래스 명(id)'
- 데이터 활용에 혼동이 없기 위해 models.py에서 클래스 이름의 소문자+단수형으로 작성하는 것을 권장
N : 1 데이터 생성 및 참조
이제 database에도 적용했으니 어떻게 데이터를 생성하면 되는지 확인해보자.
그럼 댓글도 동일하게 생성될까?
그리고 create를 사용하면 save() 없이 바로 저장할 수 있으나, 데이터 검증이 어렵기 때문에 참조가 생기는 부분부터는 사용을 피해야 한다.
여기서 궁금증이 생길 수 있다. comment.article = article을 보면 comment의 인스턴스 데이터로 객체 자체를 넣어버리는 것을 확인할 수 있다.
어떤게 맞는 표현일까? 사실 둘 다 맞는 표현이다.
앞의 방식의 경우 comment의 데이터에 article이라는 객체 자체를 넘긴 상황인데, ORM은 객체와 관형 데이터베이스간의 매핑을 자동으로 처리해주기 때문에 실제로 Article(model) 객체가 할당되고, ORM이 객체를 기반으로 필요한 데이터를 추출한다. 즉, 자동으로 처리해준다..
뒤의 방식의 경우 직접 데이터에 접근해 comment.article_id 값에 artticle의 pk(id) 데이터를 할당하는 방식이라 직접적이고 명시적이지만, 객체 지향적인 관점에서는 불편한 경우가 생긴다.
일반적으로 ORM을 사용할 경우에는 전자의 방식을 선호한다.
역참조(related manager)
: N:1 관계에서 1이 N을 참조하거나 조회하는 것
: 'objects' 매니저를 통해 QuerySet API를 사용했던 것처럼 related manager를 통해 QuerySet API를 사용할 수 있다
앞서 만든 데이터는 연결되어있는 통로(comment.article)을 통해 article의 데이터를 조회할 수 있었다. 반대로는 연결된 통로가 없는데 데이터 조회는 어떻게 할 수 있을까?
이때 사용하는 것이 역참조 매니저를 사용한다고 표현하는데, 사용 방법은 아래와 같다.
위를 해석하면 'article에 쓰인 댓글을 모두 조회하겠다'는 의미다. N:1 관계에서 생성되는 Related manager의 이름은 참조하는 '모델명_set' 규칙으로 이름이 정해진다.
그럼 어떤 데이터들을 출력해 볼 수 있을까?
이제 기초 지식이 완료되었으니, 댓글 창을 구현해보자.
CRUD 중 Create 과정
이제 어느정도 django의 흐름을 알고 있을테니, 추가하는 데이터만 보여줄 예정이다.
- 사용자로부터 댓글 데이터를 입력 받기 위한 CommentForm을 정의한다
- detail view 함수에서 detail 페이지에 랜더링할 CommentForm 전달
- 전달 받은 CommentForm을 detail.html에서 전개
- 유효성 검사인 csrf_token과 form data의 method를 POST로 설정
- 데이터 입력을 전달해줄 input (type="submit") 버튼 추가
위의 방법은 사용자 입장에서는 필요없는 과정이다. 이유는 이미 게시글에 들어와서 댓글을 작성하는데, 해당 게시글을 다시 찾아서 댓글달아야 하는점, 다른 게시글이 있다면 다른 페이지에서 다른 게시글에 댓글을 달 수 있는 아이러니한 상황이 놓이게 된다. 즉, 전달될 필요 없이 외래 키 필드 데이터는 사용자로부터 입력 받는 값이 아닌 view 함수 내에서 다른 방법으로 전달 받아 데이터가 저장되어야 하는 것임을 알 수 있다.
먼저 form에서 넘겨줄 필요없는 article Foreign Key부터 제외하자.
일단 급한불은 껐다. 이제 article의 게시글 번호를 불러와야할 방법을 찾아야 되는 것인데 어떻게 가져올 수 있을까? detail 페이지에 포함되는 데이터를 확인해보자.
왜 pk값이 넘어오고 있었나 생각하면 당연하다. 게시글의 detail 페이지를 들어가기 위해 게시글 번호를 넘겨주어야 이동이 가능하기 때문이다. 우리가 원하는 데이터는 게시글의 pk로 comment의 article.pk를 채워주어야 하니 정답이다. 이제 form이 제출되었을때 create되는 부분을 구현하면된다.
위의 required 속성은 폼 입력 필드가 제출될 때 반드시 필요한 값(id)이 있다는 뜻이다. 사용자가 이 필드를 비어있는 상태로 제출하려고 하면 브라우저가 제출을 막고 사용자에게 값을 입력하도록 요청한다.
우리는 Comment model에 aritcle을 참조하기 위한 데이터를 넘겨줘야하는 것을 기억한다! 그렇다면 article을 호출하는 것은 redirect뿐만 아니라, 어떤 게시글에 댓글이 달리는지 데이터를 넣어주어야 한다는 것을 잊지말자.
그러면 comments_form 안의 인스턴스를 필요로하는 article에 article.pk를 직접 할당할 수 있을까? 아쉽게도 불가능하다. save()하면서 comment로 새로 할당받는데, 이때 데이터 틀과 comments_form의 틀이 다르기 때문에 바로 저장할 순 없다. 대신 임시저장을 사용한다. 객체를 연결해줄 수 있는 데이터 형태로 바꾼 다음, 필요한 데이터를 채워넣는 방식이다.
CRUD 중 Read 과정
데이터를 조회하는 과정은 간단하다. 다만, 우리가 detail 함수에서 갖고있는 데이터는 article의 pk값이다. 그럼 어떻게 조회할 수 있을까? 아까 배운 역참조를 사용하면 가능하다! article 객체는 호출되어 있으니 해당하는 통로로 comments들을 모두 조회할 수 있을 것이다.
이로써 detail 페이지에는 comments 데이터도 전달되어있으니 사용할 수 있다. 모든 댓글을 반복 출력할 예정이니 for문을 통해 전개해주면 되겠다.
CRUD 중 Delete 과정
Update는 잠시 보류!
이젠 Delete는 빠르게 작성해보자. delete 호출받을 url이 필요하고, 행동할 view함수가 필요하며, 추가 template는 필요없고 detail.html에 삭제 버튼을 추가해주면 된다. 이제는 이렇게 파악할 수 있어야 한다.
근데, comment의 모델을 확인했을때, 어떤 데이터들이 필요할까? 일단, 댓글의 pk는 반드시 필요하다는 것을 알 것이다. 삭제하려면 댓글의 pk를 호출해서 delete()를 해주면 데이터베이스에서 삭제가 가능하니까 말이다. 그러면 게시글의 pk가 필요할까? 이때는 두가지 선택지가 있다. 삭제 되었을 때, detail 페이지를 유지하지 않고 메인 페이지로 이동하거나, 다른 경로로 이동한다면 굳이 게시글 번호는 필요하지 않을 것이다. 하지만 해당 페이지를 유지하려면 detail 페이지인데, 이때 detail 경로에는 게시글 pk가 필요하다.
우리가 사용하는 대부분 사이트, 클라이언트 입장에서는 댓글이 삭제되었을 때는 보통은 해당 페이지를 유지하는 경우가 많으니 후자를 선택하는 것이 자연스러울 것이다.
그렇다면 필요한 데이터는 article의 pk, 게시글의 pk임을 확인할 수 있다. 이를 토대로 url과 view함수를 정의해보자.
만약 댓글이 없는 경우에는 댓글의 데이터가 없기 때문에 반복문 for을 돌지 않기 때문에 아무것도 출력되지 않는다. 그러면 댓글이 없다는 텍스트를 출력해주는 것이 자연스러울텐데, 조건문 if를 사용해서 if, else를 하면 되지 않을까? 물론 가능하다. 하지만 조건문 내부에 반복문이 사용되면서 코드의 가독성이 떨어질 수 있다. 이때 사용할 수 있는 for의 empty 옵션이 존재한다. 이는 반복문을 실행하지 않는다(데이터가 비어있다면) 이라는 조건문이 붙어있는 옵션이라고 보면 된다.
+ 부록
댓글의 개수를 출력하는 방법
물론 view함수에서 파이썬 함수를 사용하여 context에 dictionary로 감싸서 억지로 전달하는 경우도 있겠지만, 이왕이면 html에서 데이터를 조작하는 필터를 사용하면 좋지 않을까?
- DTL filter - '| length' 사용
- QuerySet API - 'count()' 사용
이제 N:1 했는데 벌써 참조 역참조 헷갈리기 시작한다.. django라서 쉬운 편인건데 복습하면서도 파고들면 파고들수록 헷갈려지는 부분인 것 같으니 어느정도 암기와 이해력을 갖고 차근차근 이어가면 될 것 같다.
'일상코딩 > 노트' 카테고리의 다른 글
Django : REST API 2 (0) | 2024.04.12 |
---|---|
Django : REST API 1 (0) | 2024.04.11 |
DB : SQLite JOIN (0) | 2024.04.04 |
DB : SQLite (DDL, DML) 명령어 (0) | 2024.04.03 |
DB : SQLite (DQL) 명령어 (0) | 2024.04.02 |
- Total
- Today
- Yesterday
- Database
- 연산자
- 백준
- 재귀
- 카운팅정렬
- JavaScript
- Django
- baby-gin
- Python
- CRUD
- Serializer
- vue3
- restapi
- app
- 순열
- SQLite
- Sequence types
- ChatGPT
- SQL
- vue
- CodeTree
- Method
- basic syntax
- 중복순열
- 함수
- Authentication System
- dfs
- HTML
- views.py
- Python3
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |