DB : Many to One(N:1)

김재훈·2024년 4월 3일

Django

목록 보기
11/15

Many to one relationships

N:1 or 1:N

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

Comment(N) - Article(1)

  • 0개 이상의 댓글은 1개의 게시글에 작성될 수 있다.
CommentArticle
idid
contenttitle
created_atcontent
updated_atcreated_at
Article에 대한 외래 키updated_at

댓글 모델 정의

  • ForeignKey 클래스의 인스턴스 이름은 참조하는 모델 클래스 이름의 단수형으로 작성하는 것을 권장
  • 외래 키는 ForeignKey 클래스를 작성하는 위치와 관계없이 테이블 필드의 마지막에 생성됨
    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)를 어떻게 처리할지를 정의하는 설정(데이터 무결성)
    • 'CASCADE' : 부모 객체(참조된 객체)가 삭제됐을 때 이를 참조하는 객체도 삭제
    • 기타 설정값 : https://docs.djangoproject.com/en/4.2/ref/models/fields/#arguments
    • OptionDetail
      CASCADECascade deletes. Django emulates the behavior of the SQL constraint ON DELETE CASCADE and also deletes the object containing the ForeignKey.
      PROTECTPrevent deletion of the referenced object by raising ProtectedError, a subclass of django.db.IntegrityError.
      RESTRICTPrevent deletion of the referenced object by raising RestrictedError (a subclass of django.db.IntegrityError). Unlike PROTECT, deletion of the referenced object is allowed if it also references a different object that is being deleted in the same operation, but via a CASCADE relationship.
      SET_NULLSet the ForeignKey null; this is only possible if null is True.
      SET_DEFAULTSet the ForeignKey to its default value; a default for the ForeignKey must be set.
      SET()Set the ForeignKey to the value passed to SET(), or if a callable is passed in, the result of calling it. In most cases, passing a callable will be necessary to avoid executing queries at the time your models.py is imported:
      DO_NOTHINGTake no action. If your database backend enforces referential integrity, this will cause an IntegrityError unless you manually add an SQL ON DELETE constraint to the database field.

역참조

  • N:1 관계에서 1에서 N을 참조하거나 조회하는 것

    • 1 -> N

      N은 외래 키를 가지고 있어 물리적으로 참조가 가능하지만 1은 N에 대한 참조 방법이 존재하지 않아 별도의 역참조 긴으이 필요

  • article.comment_set.all()

    • article : 모델 인스턴스
    • comment_set : related manager(역참조 이름)
    • all() : QuerySet API

      특정 게시글에 작성된 댓글 전체를 조회하는 명령

  • N:1 혹은 M:N 관계에서 역참조 시에 사용하는 매니저

    'objects' 매니저를 통해 QuerySet API를 사용했던 것처럼 related manager를 통해 QuerySt API를 사용할 수 있게 됨

댓글 구현

CREATE

  1. 사용자로부터 댓글 데이터를 입력받기 위한 CommentForm 정의
    articles/forms.py
    from .models import Article, Comment
    class CommentForm(forms.ModelForm):
    	class Meta:
        	model = Comment
            fields = '__all__'
  1. detail view 함수에서 CommentForm을 사용하여 detail 페이지에 렌더링
    articles/views.py
    from .forms import ArticleForm, CommentForm
    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)

DAY 2

Article(N) - User(1)

  • 0개 이상의 게시글은 1명의 회원에 의해 작성될 수 있다.

Article - User 모델 관계 설정

  • user 외래 키 정의
articles/models.py
from django.conf import settings
class Article(models.Model):
	user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

USER 모델을 참조하는 2가지 방법

  • django 프로젝트 '내부적인 구동 순서'와 '반환 값'에 따른 이유
  • 기억할 것은 User 모델은 직접 참조하지 않는다는 것'
get_user_modelsettings.AUTH_USER_MODEL
반환값User Object(객체)'accounts.User' (문자열)
사용 위치models.py가 아닌 다른 모든 위치models.py

게시글 CREATE

  • 게시글 작성시 작성자 정보가 함께 저장될 수 있도록 save의 commit 옵션 사용
articles/views.py
@login_required
def create(request)
	if request.method == 'POST':
    	form = ArticleForm(request.POST)
        if form.is_valid():
        	article = form.save(commit=False)
            article.user = request.user
            article.save()
            return redirect('articles:detail', article.pk)
     else:
     	...

게시글 READ

  • template에서 DTL로 읽어오기 (article.user)

게시글 UPDATE

  • 본인의 게시글만 수정할 수 있도록 하기
    • 게시글 수정 요청 사용자와 게시글 작성 사용자를 비교
articles/views.py
@login_required
def update(request, pk):
	article = Article.objects.get(pk=pk)
    if request.user == article.user:
    	if request.method == 'POST':
        	form = ArticleForm(request.POST, instance=article)
            if form.is_valid():
            	form.save()
                return redirect('articles:detail', article.pky)
        else:
        	form = ArticleForm(instance=article)
    else:
    	return redirect('articles:index'):
    ...

게시글 DELETE

  • 본인의 게시글만 삭제할 수 있도록 하기
    • 삭제를 요청하려는 사용자와 게시글을 작성한 사용자를 비교
artilces/views.py
@login_required
def delete(request, pk):
	article = Article.objects.get(pk=pk)
    if request.user == article.user:
    	article.delete()
    return redirect('articles:index')

Comment(N) - User(1)

  • 0개 이상의 댓글은 1명의 회원에 의해 작성될 수 있다.

0개의 댓글