- RDB에서 1:1 관계 혹은 M:N 관계가 아닌 한 테이블의 0개 이상의 레코드가 다른 테이블의 레코드 한 개와 관련된 경우
- 기준 테이블에 따라 (1:N)이라고도 한다.
- ex) 고객 1명이 여러 주문을 할 수 있다.
- 외래 키(외부 키)
- 관계형 데이터베이스에서 다른 테이블의 행을 식별할 수 있는 키
- 참조되는 테이블의 기본 키(Primary Key)
- 참조하는 테이블의 행 1개의 값은 참조되는 측 테이블의 행 값에 대응된다.
- 이로 인해 참조하는 테이블의 행에는 참조되는 테입르에 나타나지 않는 값을 포함 할 수 없다.
- 키를 사용하여 부모 테이블의 유일한 값을 참조(참조 무결성)
- 외래 키의 값이 반드시 부모 테이블의 기본 키일 필요는 없지만 유일한 값이어야 한다.
- 모델 관계 설정
- 댓글창 만들어보기
models.py
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)
def __str__(self):
return self.content
- ForeignKey arguments - on_delete
- 외래 키가 참조하는 객체가 사라졌을 때 외래 키를 가진 객체를 어떻게 처리할 지를 정의
- 데이터 무결성을 위해서 매우 중요한 설정
- on-delete 옵션 값
- CASCADE : 부모 객체(참조된 객체)가 삭제되었을 때 이를 참조하는 객체도 삭제
- PROTECT, SET_NULL, SET_DEFAULT
shell_plus
In [1]: comment = Comment()
In [2]: comment.content = 'first comment'
In [3]: article = Article.objects.create(title='title', content='content')
In [4]: Article.objects.all()
Out[4]: <QuerySet [<Article: 1번째글 - 첫번째>, <Article: 2번째글 - title>]>
In [5]: comment.article = article
In [6]: article.save()
이렇게 확인할 수도 있다.
In [7]: comment.article
Out[7]: <Article: 2번째글 - title>
In [8]: comment.article.content
Out[8]: 'content'
In [9]: comment.article.title
Out[9]: 'title'
다시만들어보자
In [10]: comment = Comment(content='second comment', article=article)
In [11]: comment.article_id
Out[11]: 2
In [12]: comment.save()
- Related manager
-
N:1, M:N 관계에서 사용 가능한 문맥
-
역참조할 때 사용 가능한 manager생성
- 모델 생성 시 objects라는 매니저를 통해 queryset api를 사용한 것처럼 related manager를 통해 queryset api를 사용할 수 있다.
-
역참조
- 나를 참조하는 테이블을 참조하는 것
- 본인을 외래 키로 참조 중인 다른 테이블에 접근하는 것
- article.comment_set.method()
- 역참조 시 사용하는 매니저 이름을 변결할 수 도 있다.
- 작성 후 migration 필요
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='comments')
- related_name 작성
-
In [15]: article = Article.objects.get(id=2)
In [16]: article.comment_set.all()
Out[16]: <QuerySet [<Comment: second comment>]>
In [17]: article.comment_set.get(id=1)
Out[17]: <Comment: second comment>
- views.py
def detail(request, pk):
article = Article.objects.get(pk=pk)
comment_form = CommentForm()
context = {
'article': article,
'comment_form' : comment_form,
}
return render(request, 'articles/detail.html', context)
- detail.html
{% extends 'base.html' %}
{% block content %}
<h1>DETAIL</h1>
<hr />
{% if article.image %}
<img src="{{article.image.url}}" />
{% endif %}
<div id="article-content">
<p>글 제목 : {{article.title}}</p>
<p>글 내용 : {{article.content}}</p>
<p>생성시각 : {{article.created_at}}</p>
<p>수정시각 : {{article.updated_at}}</p>
<hr>
<a href="{% url 'articles:update' article.pk %}">수정하기</a>
<form action="{% url 'articles:delete' article.pk %}" id="delete-form">
{% csrf_token %}
<input type="submit" value="삭제하기" id="delete-btn" />
</form><br>
<hr>
<a href="{% url 'articles:index' %}">목록보기</a>
<br>
<h5>Comments</h5>
<hr>
<form action="" method="POST">
{% csrf_token %}
{{comment_form}}
<input type="submit" value="작성">
</form>
</div>
{% endblock content %}
- forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
exclude = ('article',)
views.py
def detail(request, pk):
article = Article.objects.get(pk=pk)
comment_form = CommentForm()
comments = article.comment_set.all()
context = {
'article': article,
'comment_form' : comment_form,
'comments' : comments,
}
return render(request, 'articles/detail.html', context)
def comments_create(request, pk):
article = Article.objects.get(pk=pk)
comment_form = CommentForm(request.POST)
if comment_form.is_valid():
comment = comment_form.save(commit=False)
comment.article = article
comment_form.save()
return redirect('articles:detail', article.pk)
urls.py
from django.urls import path
from . import views
app_name = 'articles'
urlpatterns = [
path('', views.index, name='index'),
path('<int:pk>/', views.detail, name='detail'),
path('create/', views.create, name='create'),
path('<int:pk>/delete/', views.delete, name='delete'),
path('<int:pk>/update/', views.update, name='update'),
path('<int:pk>/comments/', views.comments_create, name='comments_create'),
]
detail.py
{% extends 'base.html' %}
{% block content %}
<h1>DETAIL</h1>
<hr />
{% if article.image %}
<img src="{{article.image.url}}" />
{% endif %}
<div id="article-content">
<p>글 제목 : {{article.title}}</p>
<p>글 내용 : {{article.content}}</p>
<p>생성시각 : {{article.created_at}}</p>
<p>수정시각 : {{article.updated_at}}</p>
<hr>
<a href="{% url 'articles:update' article.pk %}">수정하기</a>
<form action="{% url 'articles:delete' article.pk %}" id="delete-form">
{% csrf_token %}
<input type="submit" value="삭제하기" id="delete-btn" />
</form><br>
<hr>
<a href="{% url 'articles:index' %}">목록보기</a>
<br>
<h5>Comments</h5>
<hr>
<ul>
{% for comment in comments %}
<li>{{comment.content}}</li>
{% endfor %}
</ul>
<form action="{% url 'articles:comments_create' article.pk %}" method="POST">
{% csrf_token %}
{{comment_form}}
<input type="submit" value="작성">
</form>
</div>
{% endblock content %}
Comment 삭제하기
views.py
def comments_delete(request, article_pk, comment_pk):
comment = Comment.objects.get(pk=comment_pk)
comment.delete()
return redirect('articles:detail', article_pk)
urls.py
path('<int:article_pk>/comments/<int:comment_pk>/delete', views.comments_delete, name='comments_delete'),
detail.html
사이에 넣어주기
<ul>
{% for comment in comments %}
<li>{{comment.content}}</li>
<form action=" {% url 'articles:comment_delete' article.pk comment.pk %} " method="POST">
{% csrf_token %}
<input type="submit" value="DELETE">
</form>
{% endfor %}
</ul>
- 댓글이 없을 때 있을 때 몇개가 존재한다. 등을 필터를 통해 표현 가능하다.
<h5>Comments</h5>
{% if comments %}
<p><b> {{comments|length}}개의 댓글이 있습니다. </b></p>
{% endif %}
<hr>
<ul>
{% for comment in comments %}
<li>{{comment.content}}
<form action=" {% url 'articles:comments_delete' article.pk comment.pk %} " method="POST">
{% csrf_token %}
<input type="submit" value="DELETE">
</form>
</li>
{% empty %}
<p>댓글이 없어요.. :( </p>
{% endfor %}
</ul>
Article(N) - User(1)
0개 이상의 게시글은 1개의 회원에 의해 작성될 수 있다.
개인 게시글 회원 탈퇴시 삭제 및 확인
from django.conf import settings
class Article 과 class Comment에 추가할 내용
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)