잇쯔미
무지 좋은 log
잇쯔미
  • 분류 전체보기 (67)
    • 웹 (33)
      • HTML, CSS (4)
      • Javascript (4)
      • Django (16)
      • Vue.js (6)
      • Web (2)
    • 서버 (3)
    • 알고리즘 (11)
    • 파이썬 (8)
    • 인공지능 (6)
    • 블록체인 (1)
    • Computer Science (3)
      • 개발상식 (3)
    • 컴퓨터일반 (2)
      • 전자계산기구조론 (1)
      • 데이터통신 (1)
    • 자바 (0)

최근 글

hELLO · Designed By 정상우.
잇쯔미

study blog

1:N 관계
웹/Django

1:N 관계

2022. 3. 9. 13:29

Foreign Key (외래 키)

RDBMS에서 한 테이블의 필드 중, 다른 테이블의 행을 식별할 수 있는 키

  • (주로) 참조되는 테이블의 Primary key(기본 키)를 가리킨다. (유일한 값이어야 함) *참조무결성
  • 참조하는 테이블의 행에는 참조되는 테이블에 없는 값이 나타날 수 없다.
  • 여러 행이 참조되는 테이블의 한 행을 동시에 참조할 수 있다.
  • 외래 키는 참조하는 테이블에 존재한다. 참조되는 테이블에 존재하지 않는다.

❓ 참조 무결성

데이터베이스 관계 모델에서 관련된 두 테이블의 일관성을 의미한다.
참조하는 테이블의 외래 키 필드의 값은, 참조되는 테이블의 기본 키 값으로 존재해야 한다.
"자식은 무조건 부모가 있다!"

 

모델 관계 형성

  • Comment 모델 클래스 예시
  class Article(models.Model):
      pass	# 부모(참조되는) 클래스
  
  class Comment(models.Model):
      article = models.ForeignKey(Article, on_delete=models.CASCADE) # ✔
      content = models.CharField(max_length=300)
      created_at = models.DateTimeField(auto_now_add=True)
      updated_at = models.DateTimeField(auto_now=True)
      
      def __str__(self):
          return self.content
  • Django에서 외래 키는 ForeignKey field를 사용해서 만들어진다.
  • 2개의 위치 인자가 반드시 필요하다:
    • 참조되는 model 클래스
    • on_delete 옵션
      • 참조되는 객체가 사라지면 참조하는 객체를 어떻게 처리할지를 정의한다. *데이터 무결성
      • (주로 on_delete=models.CASCADE로, CASCADE 옵션이 주를 이룬다.
      • CASCADE 옵션은, 참조되는 테이블이 삭제되는 참조하는 테이블도 연쇄적으로 삭제되도록 하는 것이다.)
  • 외래키 필드의 이름은 참조되는 클래스를 소문자로, 그리고 단수형으로 바꿔서 만든다. (국룰)
    • 지정된 이름에 '_id' 가 붙은 이름의 외래키 필드가 만들어진다.

✅ 참고

더보기

comment라는 Comment 클래스의 인스턴스 객체를 생성했을 때, arTicle을 Article 클래스의 인스턴스 객체라고 한다면 comment.article 에는 arTicle가 대응되고, comment.article_id에는 arTicle.pk이 대응된다.

comment.article에 객체를 넣으면 그 객체의 PK를 article_id 필드에 넣어준다. (실제로 생성되는 필드는 article_id 하나다.) comment.article_id는 말 그대로 컬럼을 가리키기 때문에 참조되는 모델의 기본키가 대응되는 것이다.

즉, comment.article_id는 참조되는 article의 pk 값을 가리키고, comment.article.pk와 같다. comment.article은 참조되는 객체를 가리킨다.

 

❓ 데이터 무결성

데이터의 정확성과 일관성을 유지하고 보증하는 것이다. DB와 RDBMS에서 중요한 개념이다.

❓ 복합 키

하나의 컬럼으로는 데이터의 구분이 불가능할 때, 여러 컬럼을 합쳐서 키로 사용하는 것

 

예시

0. 댓글 구현

클래스의 인스턴스 생성 ⇢ 값 넣고 ⇢ save()로 저장

comment = Comment()
comment.content = '내용'
# 여기서는 comment.save() 해도 저장되지 않는다. 외래 키 필드 값이 빠졌기 때문.

article = Article.objects.get(pk=1)
comment.article = article	# 객체를 comment.article에 할당하면 pk값을 추출하여 저장해줌.
comment.save()

# comment.article과 comment.article_id 비교
comment.article_id
>> 1
comment.article
>> <Article: title>

 

'참조'와 '역참조'

참조

더보기

1:N 관계에서 N이 1을 참조하는 것

즉, 참조하는 테이블이 참조되는 테이블을 참조하는 것.

Comment(N)에서 Article(1)을 참조하려면, comment.article.메서드()로 사용하면 된다.

 

역참조

더보기

1:N 관계에서 1이 N을 참조하는 것

참조되는 테이블이 역으로 참조하는 테이블을 참조하는 것.

Article(1)에서 Comment(N)을 참조하려면, article.comment_set.메서드()로 사용하면 된다.

  • 역참조의 경우는 related_name 이름의 필드 또는 '_set'이 붙은 필드가 역참조하는 모델에 있다고 생각하면 된다.

✔ 외래 키 클래스 이름, 즉 모델명에 '_set'이 붙은 manager가 생성된다. (∵ 1은 N에 대한 정보가 없으므로)

 

※ 'related_name' 옵션

역참조 시에, 참조하는 모델의 외래 키 필드에서 'related_name' 옵션을 지정함으로써, 외래 키 이름에 '_set' 이 붙은 이름 외에 새롭게 지정한 이름으로 매니저를 사용할 수 있다. (이때, 변경되기 전 _set이 붙은 이름은 더 이상 사용할 수 없게 된다.)

 

1. 댓글 생성

  1. 댓글 폼 작성: 외래 키 필드는 사용자 입력을 받지 않도록 하기
   # forms.py
   from .models import Comment
   
   class CommentForm(forms.ModelForm):
       
       class Meta:
           model = Comment
           exclude = ('article',)
  1. url 작성
   # urls.py
   app_name = 'articles'
   urlpatterns = [
   	path('<int:pk>/comments/', views.comments_create, name='comments_create'),
   ]
  1. detail 페이지에서 폼 보이기
   # views.py
   from .forms import CommentForm
   
   def detail(request, pk):
       article = get_object_or_404(Article, pk=pk)
       comment_form = CommentForm()	# CommentForm() 객체 전달하기
       context = {
           'article': article,
           'comment_form': comment_form,
       }
       return render(request, 'article/detail.html', context)
<!-- detail.html -->
...
<form action="{% url 'articles:comments_create' article.pk %}" method="POST">
  {% csrf_token %}
  {{ comment_form }}
  <input type="submit">
</form>
  1. 생성 view 작성
   # views.py
   
   @require_POST
   def comments_create(request, pk):
       if request.user.is_authenticated:	# 인증된 사용자만 허용
           article = get_object_or_404(Article, pk=pk)
           comment_form = CommentForm(request.POST)
           if comment_form.is_valid():
               comment = comment_form.save(commit=False)   # 인스턴스 생성, 저장은 X
               comment.article = article # 사용자 입력을 받지 않으므로, article 넣어주기
               comment.save()  # 저장
           return redirect('articles:detail', article.pk)
       return redirect('accounts:login')	# 인증되지 않았다면 로그인 페이지로 이동

✔ save() 메서드의 commit 인자

  • 기본값: True
  • False로 설정하면, 데이터베이스에 저장하지 않고, 객체를 리턴해준다.
  • 저장하기 전에 객체에 대한 사용자 지정 처리를 수행할 때 유용하게 사용된다.

 

2. 댓글 조회

article에서 comment를 역참조한다.

  1. article에서 comment를 모두 가져와서 context로 전달
   # views.py
   from .models import Article, Comment
   
   def detail(request, pk):
       article = get_object_or_404(Article, pk=pk)
       comment_form = CommentForm()
       comments = article.comment_set.all()	# 모든 댓글 가져오기
       context = {
           'article': article,
           'comment_form': comment_form,
           'comments': comments,
       }
       return render(request, 'article/detail.html', context)
  1. detail 페이지에서 출력
   <!-- detail.html -->
   ...
   <h4>댓글 목록</h4>
   <ul>  
     {% for comment in comments %}
     <li>
       {{ comment.content }}
     </li>  
     {% endfor %}
   </ul>

 

3. 댓글 삭제

  1. urls.py
    1. 댓글을 삭제한 후, 댓글이 있던 게시글 페이지에 머물도록 하려면 comment의 pk 외에도 article의 pk가 요구된다. 따라서, variable routing을 통해 별도의 pk를 하나 더 들고 오도록 한다.
   # urls.py
   app_name = 'articles'
   urlpatterns = [
   	path(
   	    '<int:article_pk>/comments/<int:comment_pk>/delete/',
            views.comments_delete,
            name='comments_delete' 
        ),
   ]
  1. delete 함수 만들기
   # views.py
   
   @require_POST
   def comments_delete(request, article_pk, comment_pk):
   	if request.user.is_authenticated:	# 인증된 사용자만 허용
    		comment = get_object_or_404(Comment, pk=comment_pk)
            	comment.delete()
        return redirect('articles:detail', article_pk)
  1. detail.html에 삭제 버튼 만들기
   <!-- detail.html -->
   ...
   <h4>댓글 목록</h4>
   {% if comments %}
     <!-- 댓글 개수 -->
     <p><b>{{ comments|length }}개의 댓글이 있습니다.</b></p>
   {% endif %}
   <ul>
     {% for comment in comments %}
       <li>
         {{ comment.content }}
         <form action="{% url 'articles:comments_delete' article.pk comment.pk %}" method="POST" class="d-inline">
           {% csrf_token %}          
           <input type="submit" value="DELETE">
         </form>      
       </li>    
     {% empty %}		<!-- 댓글이 없는 경우, 대체 컨텐츠 출력 -->      
       <p>댓글이 없어요.</p>    
     {% endfor %}  
   </ul>
저작자표시 (새창열림)

'웹 > Django' 카테고리의 다른 글

Authentication (1)  (0) 2022.03.09
Authentication (2)  (0) 2022.03.09
Customizing Authentication  (0) 2022.03.09
N:M 관계  (0) 2022.03.09
REST API  (0) 2022.03.09
    '웹/Django' 카테고리의 다른 글
    • Authentication (1)
    • Authentication (2)
    • Customizing Authentication
    • N:M 관계
    잇쯔미
    잇쯔미

    티스토리툴바