[DB] Many to one relationships 1

Jingi·2024년 4월 3일

Web

목록 보기
17/40
post-thumbnail

Realtionships

  • 한 테이블의 0개 이상의 레코드가 다른 테이블의 레코드 한 개와 관련된 관계

Comment - Article

  • 0개 이상의 댓글은 1개의 게시글에 작성될 수 있다.

Comment(N) - Article(1)

  • 0개 이상의 댓글은 1개의 게시글에 작성될 수 있다.

ForeignKey()

  • N:1 관계 설정 모델 필드

댓글 모델 정의

  • ForiegnKey 클래스의 인스턴스 이름은 참조하는 모델 클래스 이름의 단수형으로 작성하는 것을 권장
  • 외래 키는 ForiegnKey 클래스를 작성하는 위치와 관계없이 테이블 필드 마지막에 생성됨
class Comment(models.Model):
    article = models.ForeignKey(Article, on_delete=models.CASCADE)
    content = models.CharField(max_length=200)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

ForeignKey(to, on_delete)

  • to : 참조하는 모델 class 이름
  • on_delete : 외래 키가 참조하는 객체(1)가 사라졌을 때, 외래 키를 가진 객체(N)를 어떻게 처리할 지를 정의하는 설정(데이터 무결성)
  • on_delete의 'CASCADE' : 부모 객체(참조 된 객체)가 삭제 됐을 때, 이를 참조하는 객체도 삭제

댓글 생성 연습

  • shell_plus 실행 및 게시글 작성
# Case 1
comment = Comment()
comment.content = 'first comment'
article = Article.objects.get(pk=1)
comment.article = article
comment.save()

# Case 2
comment = Comment(content='second comment', article=article)
comment.save()

역참조

  • N:1 관계에서 1에서 N을 참조하거나 조회하는 것
  • N은 외래 키를 가지고 있어 물리적으로 참조가 가능하지만 1은 N에 대한 참조방법이 존재하지 않아 별도의 역참조 기능이 필요
  • article.comment_set.all()
  • 모델.역참조 이름. QeurySet API
  • 특정 게시글에 작성된 댓글 전체를 조회하는 명령
  • N:1 혹은 M:N 관계에서 역참조 시에 사용하는 매니저
  • 'objects' 매니저를 통해 QeurySet API를 사용했던 것처럼 related manager를 통해 QuerySet APU를 사용할 수 있게 됨
  • shell_plus 연습
article = Article.objects.get(pk=1)
comments = article.comment_set.all()
for comment in comments:
    print(comment.content)

댓글 구현

CREATE

# articles/ forms.py
from django import forms
from .models import Article, Comment

class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = '__all__'
# articles/views.py
def detail(request, pk):
    article = Article.objects.get(pk=pk)
    # 특정 게시글에 작성된 모든 댓글 조회 (Article -> Comment, 역참조) 
    # DB의 모든 댓글을 조회(특정 게시글에 작성된 모든 댓글 조회가 아님)
    # Comment.objects.all()
    
    # 사용자로부터 댓글 데이터 입력을 받기 위한 form
    comment_form = CommentForm()
    context = {
        'article': article,
        'comment_form': comment_form,
    }
    return render(request, 'articles/detail.html', context)

def comments_create(request, pk):
    # 게시글 조회 (어떤 게시글에 작성되어야하는지 알아야하기 때문)
    article = Article.objects.get(pk=pk)
    comments = article.comment_set.all()
    # 사용자 입력 데이터를 받아서 Comment 저장 (+유효성 검사)
    comment_form = CommentForm(request.POST)
    if comment_form.is_valid():
        comment = comment_form.save(commit=False)
        comment.article = article
        comment.save()
        return redirect('articles:detail', article.pk)
    context = {
        'comment_form': comment_form,
        'article': article,
        'comments': comments,
    }
    return render(request, 'articles/detail.html', context)
  <form action="{% url "articles:comments_create" article.pk %}" method="POST">
    {% csrf_token %}
    {{ comment_form }}
    <input type="submit" value="댓글 작성">
  </form>
# articles/urls.py
urlpatterns = [
    path('<int:pk>/comments/', views.comments_create, name='comments_create'),
]

PS. NO Reverse Match 오류 발생시 -> 현재 URL 태그 확인


READ

# articles/views.py
def detail(request, pk):
    article = Article.objects.get(pk=pk)
    # 특정 게시글에 작성된 모든 댓글 조회 (Article -> Comment, 역참조) 
    comments = article.comment_set.all()
    # DB의 모든 댓글을 조회(특정 게시글에 작성된 모든 댓글 조회가 아님)
    # Comment.objects.all()
    
    # 사용자로부터 댓글 데이터 입력을 받기 위한 form
    comment_form = CommentForm()
    context = {
        'article': article,
        'comments' : comments,
        'comment_form': comment_form,
    }
    return render(request, 'articles/detail.html', context)

Delete

# articles/urls.py
urlpatterns = [
    path('<int:article_pk>/comments/<int:comment_pk>/delete/', views.comments_delete, name='comments_delete'),
]
# articles/views.py
def comments_delete(request, article_pk, comment_pk):
    # 어떤 댓글을 삭제하는지 조회
    comment = Comment.objects.get(pk=comment_pk)
    # 아래 코드처럼 작성가능
    # 단, 이렇게 작성할경우 url에서 article_pk가 제거되고 url 구성을 변경해야 함
    # 지금까지의 url 전체 구성 및 통일성을 유지하기 위해 아래코드 방식을 선택하지 않음
    # article_pk = comment.article.pk
    comment.delete()
    return redirect('articles:detail', article_pk)
    {% for comment in comments %}
      <li>
        {{ comment.content }}
        <form action="{% url "articles:comments_delete" article.pk comment.pk %}" method="POST" style="display: inline;">
          {% csrf_token %}
          <input type="submit" value="삭제">
        </form>
      </li>
    {% endfor %}
profile
데이터 분석에서 백엔드까지...

0개의 댓글