Web/Django

django_08_model_relationship

5_ssssseung 2021. 3. 29. 21:35

08_django_model_relationship

https://docs.djangoproject.com/en/3.1/ref/models/fields/#module-django.db.models.fields

User - Article

# articles/models.py

from django.conf import settings


class Article(models.Model)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    ...
$ python manage.py makemigrations
# 첫번째 상황(null 값이 허용되지 않는 user_id 가 아무 값도 없이 article 에 추가되려 하기 때문)
$ python manage.py makemigrations
You are trying to add a non-nullable field 'user' 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: # 1 입력하고 enter

# 두번째 상황(그럼 기존 article 의 user_id 로 어떤 데이터를 넣을건지 물어봄)
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
>>> # 1 입력하고 enter (그럼 현재 작성된 모든 글은 1번 user가 작성한 것으로 됨)
$ python manage.py migrate

  • 게시글을 작성하려 하면 user 를 선택 해야하는 불필요한 field 가 노출된다.

  • 제목과 내용만 입력하도록 필드를 설정해야한다.

    # articles/forms.py
    
    class ArticleForm(forms.ModelForm):
        
        class Meta:
            model = Article
            fields = ('title', 'content',)
    
    • 글을 작성해보면 create 시에 유저 정보가 저장되지 않기 때문에 다음과 같은 에러가 발생한다.

      • NOT NULL constraint failed: articles_article.user_id

CREATE

  • request.user 라는 현재 요청의 유저 객체를 추가로 저장한다.

    # articles/views.py
    
    @login_required
    @require_http_methods(['GET', 'POST'])
    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)
    

READ

  • 게시글을 작성한 user가 누구인지 보기 위해 index.html 수정

    <!-- articles/index.html -->
    
    {% extends 'base.html' %}
    
    {% block content %}
      ...
      {% for article in articles %}
        <p><b>작성자 : {{ article.user }}</b></p>
        <p>글 번호 : {{ article.pk }}</p>
        <p>글 제목 : {{ article.title }}</p>
        <p>글 내용 : {{ article.content }}</p>
        <a href="{% url 'articles:detail' article.pk %}">DETAIL</a>
        <hr>
      {% endfor %}
    {% endblock %}
    

UPDATE

  • 사용자가 자신의 글만 수정 할 수 있도록 수정

    # articles/views.py
    
    
    @login_required
    @require_http_methods(['GET', 'POST'])
    def update(request, pk):
        article = get_object_or_404(Article, 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.pk)
            else:
                form = ArticleForm(instance=article)
        else:
            return redirect('articles:index')
        context = {
            'form': form,
        }
        return render(request, 'articles/update.html', context)
    

DELETE

  • 사용자가 자신의 글만 삭제 할 수 있도록 수정
# articles/views.py

@require_POST
def delete(request, pk):
    article = get_object_or_404(Article, pk=pk)
    if request.user.is_authenticated:
        if request.user == article.user:
            article.delete()
            return redirect('articles:index')
    return redirect('articles:detail', article.pk)
  • 해당 게시글의 작성가 아니라면, 수정/삭제 버튼을 출력하지 않도록 수정
<!-- articles/detail.html -->

{% extends 'base.html' %}
{% block content %}
  ...
  {% if request.user == article.user %}
    <a href="{% url 'articles:update' article.pk %}" class="btn btn-primary">[UPDATE]</a>
    <form action="{% url 'articles:delete' article.pk %}" method="POST">
      {% csrf_token %}
      <button class="btn btn-danger">DELETE</button>
    </form>
  {% endif %}
...



User - Comment

# articles/models.py

class Comment(models.Model):
    article = models.ForeignKey(Article, on_delete=models.CASCADE)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    ...
$ python manage.py makemigrations

# 첫번째 상황(null 값이 허용되지 않는 user_id 가 아무 값도 없이 comment 에 추가되려 하기 때문)
You are trying to add a non-nullable field 'user' to comment 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: # 1 입력하고 enter

# 두번째 상황 (그럼 기존 comment 의 user_id 로 뭘 넣을건지 물어봄)
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
>>> # 1 입력하고 enter (모든 댓글의 작성자를 1번 user로 하게 됨)

Migrations for 'articles':
  articles\migrations\0003_comment_user.py
    - Add field user to comment
$ python manage.py migrate

CREATE

  • 해당 view 함수를 요청한 유저의 정보를 넣고나서 저장한다. (로그인 사용자만 작성하도록)

    # articles/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)
                comment.article = article
                comment.user = request.user
                comment.save()
                return redirect('articles:detail', article.pk)
            ...
    

READ

  • 비로그인 유저는 댓글 작성 form 을 볼 수 없도록 한다.

    <!-- articles/detail.html -->
    
    {% extends 'base.html' %}
    {% block content %}
      ...
    <hr>
      {% if request.user.is_authenticated %}
        <form action="{% url 'articles:comments_create' article.pk %}" method="POST">
          {% csrf_token %}
          {{ comment_form }}
          <input type="submit">
        </form>
      {% else %}
        <a href="{% url 'accounts:login' %}">[댓글을 작성하려면 로그인하세요.]</a>
      {% endif %}
    {% endblock  %}
    
  # articles/forms.py
  
  class CommentForm(forms.ModelForm):
  
      class Meta:
          model = Comment
          exclude = ('article', 'user',)
  • 댓글 작성자 출력

    <!-- articles/detail.html -->
    
    {% extends 'base.html' %}
    
    {% block content %}
      ...
      <h4>댓글 목록</h4>
      ...
      <ul>
        {% for comment in comments %}
          <li>
            {{ comment.user }} - {{ 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>
      <hr>
      ...
    {% endblock %}
    

DELETE

  • 본인이 작성한 댓글만 삭제할 수 있도록 수정한다.

    # articles/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)
            if request.user == comment.user:
                comment.delete()
        return redirect('articles:detail', article_pk)
    
    <!-- articles/detail.html -->
    
    {% extends 'articles/base.html' %}
    {% block content %}
      ...
      <h4>댓글 목록</h4>
      ...
      <ul>
        {% for comment in comments %}
          <li>
            {{ comment.user }} - {{ comment.content }}
            {% if request.user == comment.user %}
              <form action="{% url 'articles:comments_delete' article.pk comment.pk %}" method="POST" class="d-inline">
                {% csrf_token %}
                <input type="submit" value="DELETE">
              </form>
            {% endif %}
          </li>
        {% empty %}
          <p>아직 댓글이 없네요...</p>
        {% endfor %}
      </ul>
      <hr>
      ...
    {% endblock %}
    
    • 본인 댓글에 대한 삭제 버튼을 볼 수 있도록 수정한다.

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

REST API  (0) 2021.05.08
django_09_model_relationship  (0) 2021.04.01
django_07_custom_authentication  (0) 2021.03.25
django_06_model_relationship  (0) 2021.03.24
django_05_auth  (0) 2021.03.23