[Django/DB] 댓글 기능 구현하기 (2) : Article-User (N:1) 관계 설정

문지은·2023년 5월 7일
0

Django + Database

목록 보기
2/12
post-thumbnail

댓글 기능 구현을 위해 필요한 Article-User 관계 설정하는 법에 대해 알아보자.

N:1 (Article-User)

Referencing the User model

Django에서 User 모델을 참조하는 방법

  1. settings.AUTH_USER_MODEL
    • 반환 값 : ‘accounts.User’ (문자열)
    • User 모델에 대한 외래 키 또는 M:N 관계를 정의 할 때 사용
    • models.py의 모델 필드에서 User 모델을 참조할 때 사용
  2. get_user_model()
    • 반환 값 : User Object (객체)
    • 현재 활성화(active)된 User 모델을 반환
    • 커스터마이징한 User 모델이 있을 경우는 Custom User 모델, 그렇지 않으면 User를 반환
    • models.py가 아닌 다른 모든 곳에서 유저 모델을 참조할 때 사용

모델 관계 설정

Article과 User간 모델 관계 설정

  • 0개 이상의 게시글은 1개의 회원에 의해 작성될 수 있음
  • Article(N) - User(1) 관계 설정

  • Article 모델과 User 모델을 참조하는 외래 키 작성
# articles/models.py

from django.conf import settings

class Article(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
        ...

Migration 진행

  1. migrations 파일 생성하기
$ python manage.py makemigrations

첫번째 화면

You are trying to add a non-nullable field 'users' to article without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
    1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
    2) Quit, and let me add a default in models.py
Select an option:
  • 기본적으로 모든 컬럼은 NOT NULL 제약조건이 있기 때문에 데이터가 없이는 새로 추가되는 외래 키 필드 user_id가 생성되지 않음
  • 그래서 기본 값을 어떻게 작성할 것인지 선택해야 함
  • 1을 입력하고 Enter 진행 (다음 화면에서 직접 기본 값 입력)

두번째 화면

Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
>>>
  • article의 user_id에 어떤 데이터를 넣을 것인지 직접 입력해야 함
  • 마찬가지로 1 입력하고 Enter 진행
  • 그러면 기존에 작성된 게시글이 있다면 모두 1번 회원이 작성한 것으로 처리됨
  1. migrations 파일 생성 후 migrate 진행
$ python manage.py migrate
  1. article 테이블 스키마 변경 및 확인

Django에서 User 모델을 참조하는 방법

  • models.py에서는 settings.AUTH_USER_MODEL
  • 다른 모든 곳에서는 get_user_model()

CREATE

  • 인증된 회원의 게시글 작성 구현하기
  • 작성하기 전 로그인을 먼저 진행한 상태로 진행하자

ArticleForm

  • ArticleForm 출력을 확인해보면 create 템플릿에서 불필요한 필드(user)가 출력됨
  • 이전에 CommentForm에서 외래 키 필드 article이 출력되는 상황과 동일한 상황
  • user 필드에 작성해야 하는 user 객체는 view 함수의 request 객체를 활용해야 함

ArticleForm의 출력 필드 수정

# articles/forms.py

class ArticleForm(forms.ModelForm):
    class Meta:
        model = Article
        fields = ('title', 'content',)
  • 확인 후 게시글 작성하기

  • 외래 키 데이터 누락으로 인한 에러 발생

    • NOT NULL constraint failed: articles_article.users_id
    • 게시글 작성 시 외래 키에 저장되어야 할 작성자 정보가 누락되었기 때문
  • 게시글 작성시 작성자 정보가 함께 저장될 수 있도록 save의 commit 옵션 활용

# articles/views.py

def create(request):
    if request.method == 'POST':
        form = ArticleForm(request.POST, request.FILES)
        if form.is_valid():
            article = form.save(commit=False)
            article.user = request.user
            article.save()
            return redirect('articles:detail', article.pk)
    else:
        form = ArticleForm()

    context = {'form': form}
    return render(request, 'articles/create.html', context)
  • 수정 후 게시글 잘 작성되는지 확인하기

DELETE

게시글 삭제 시 작성자 확인

  • 이제 게시글에는 작성자 정보가 함께 들어있기 때문에 현재 삭제를 요청하려는 사람과 게시글을 작성한 사람을 비교하여 본인의 게시글만 삭제할 수 있도록 함
# articles/views.py

def delete(request, pk):
    article = Article.objects.get(pk=pk)
    if request.user.is_authenticated:
        if request.user == article.user:
            article.delete()
            return redirect('articles:index')
    return redirect('articles:index')

UPDATE

게시글 수정 시 작성자 확인

  • 수정도 마찬가지로 수정을 요청하려는 사람과 게시글을 작성한 사람을 비교하여 본인의 게시글만 수정할 수 있도록 함
def update(request, pk):
    article = Article.objects.get(pk=pk)
    if request.user == article.user:
        if request.method == 'POST':
            form = ArticleForm(request.POST, request.FILES, instance=article)
            if form.is_valid():
                form.save()
                return redirect('articles:detail', pk=article.pk)
        else:
            form = ArticleForm(instance=article)
    else:
        return redirect('articles:index')
    context = {'form': form, 'article': article}
    return render(request, 'articles/update.html', context)
  • 추가로 해당 게시글의 작성자가 아니라면, 수정/삭제 버튼을 출력하지 않도록 함
<!-- articles/detail.html -->

{% if request.user == article.user %}
	<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>
{% endif %}
  • 다른 계정으로 접속해보기

READ

게시글 작성자 출력

  • index 템플릿과 detail 템플릿에서 각 게시글의 작성자 출력
<!-- articles/detail.html -->

<p><b>작성자 : {{ article.user }}</b></p>
<p>글 제목 : {{article.title}}</p>
<p>글 내용 : {{article.content}}</p>
<p>생성시각 : {{article.created_at}}</p>
<p>수정시각 : {{article.updated_at}}</p>
<!-- articles/index.html -->

{% extends 'base.html' %} 

{% block content %}
  <h1>INDEX</h1>
  <a href="{% url 'articles:create' %}">작성하기</a>
  <hr />

  {% for article in articles %}
    <p>
      <p><b>작성자 : {{ article.user }}</b></p>
      [{{article.id}}] <a href="{% url 'articles:detail' article.pk %}" id="article-title">{{article.title}}</a>
    </p>
    <hr />
  {% endfor %} 

{% endblock content %}
  • 출력 확인하기

📍 프로젝트 전체 코드 확인하기

profile
코드로 꿈을 펼치는 개발자의 이야기, 노력과 열정이 가득한 곳 🌈

0개의 댓글