IT world

[Django] 24.02.26 Django CRUD 실습 본문

모두의 연구소(오름캠프)/AI 모델 활용 백엔드 개발 과정

[Django] 24.02.26 Django CRUD 실습

엄킹 2024. 2. 26. 17:20

오늘은 Create, Read, Update, Delete에 대한 Django 실습을 진행했다.

즉 저장된 내역을 읽고, 새로운 데이터를 추가하고, 기존 데이터를 수정하고, 삭제하는 내용을 실습했다.

 

이전 내용을 이어서 admin 페이지에서 생성한 블로그 게시물을 사용하여 CRUD 기능을 구현할 것이다.

현재 BLOG > Posts 에는 3개의 게시물이 작성되어 있다.

[게시글 작성 내역]

 

우선 Models.py에 DB 내용을 가져오고 admin.py에 DB에 반영한 것을 admin 페이지에 반영했다.

(각 .py 역할이 정확한 내용인지 잘 모르겠다. 추후 수정 예정하겠다.)

# blog > models.py

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=100)
    contents = models.TextField()
    main_image = models.ImageField(upload_to="blog/%Y/%m/%d/", blank=True)

    created_at = models.DateTimeField(auto_now_add=True)  
    updated_at = models.DateTimeField(auto_now=True)  

    def __str__(self):
        return self.title
        
        
# blog > admin.py

from django.contrib import admin
from .models import Post

admin.site.register(Post)

 

여기서 각 페이지를 생성할 때 input 태그, textarea 태그를 사용한 것이 아닌 Model Form을 사용하여 페이지를 구성했다.

Model Form은 자동으로 Form을 생성해주는 기능으로 모델과 필드를 지정하면 모델폼이 자동으로 폼 필드를 생성한다. 

 

즉 모델에 정의된 필드를 바탕으로 form을 자동 생성하고 각 필드 유형에 맞는 폼 필드가 매핑되어 자동으로 생성된다. 또한 제출된 폼 데이터에 대한 유효성 검사를 자동으로 수행한다. Model Form을 사용하면 Form을 위한 HTTML 작성이 필요없고 재사용이 가능하다.

 

forms.py를 만들고 어떤 필드를 입력받을지 정하고 각 페이지에 form을 반영했다. (각 페이지 form 사용은 아래 예시에서 확인)

  • from django import forms: django에서 제공하는 forms 기능을 사용하기 위해 import
  • from .models import Post: Post 모델을 사용하기 위해 import
  • class PostForm(forms.ModelForm): PostForm이라는 이름의 모델폼 클래스 생성
  • model = Post: form에서 사용할 모델이 Post을 명시
  • fields: Post 모델에서 입력받고 싶은 필드를 리스트 형태로 작성("__all__" : 모든 필드 사용)
# forms.py

from django import forms
from .models import Post


class PostForm(forms.ModelForm):
    title = forms.CharField(max_length=10)
    contents = forms.CharField(widget=forms.Textarea)
 
 	class Meta:
        model = Post
        fields = ["title", "contents"]

 

아래 코드는 form 태그를 작성하는 HTML과 forms.py에 출력할 필드를 작성하고 {{form}}에 담아서 호출한 방법 차이입니다.

 

1. 입력 받을 필드들을 input 태그를 써서 만드는 방법(비추천)

<form method = "POST">
	    {% csrf_token %}
        <input type = "text" name = "title">
        <textarea name = "content">
        <button type="submit">저장</button>
</form>

 

2. 저장할 필드를 파이썬 파일에 작성하고 파일을 {{form}}에 담아서 불러오기만 하는 방법

<form method = "POST">
	    {% csrf_token %}
        {{ form }}
        <button type="submit">저장</button>
</form>

1. Read

우선 Read 작업, 즉 DB에 저장되어 있는 게시글의 내용을 출력할 것이다.

경로는 blog/, blog/detailNo 로 blog의 메인 페이지에서는 저장된 목록을 보여주며, 목록에 출력된 게시글 클릭 시 세부 페이지로 이동하여 해당 게시글의 정보를 출력할 것이다. 검색 기능을 추가하여 저장된 목록 출력 페이지에 해당 검색내용이 포함된 내용 전부를 출력할 수 있도록 작성하였다.

 

아래 코드는 blog/로 접속할 경우와 세부 내역으로 접속할 경우 처리 방식을 작성했다.

# blog > urls.py

from django.urls import path
from . import views

urlpatterns = [
    path("", views.blog_list, name="blog_list"),
    path("<int:pk>/", views.blog_details, name="blog_details"),
    path("create/", views.blog_create, name="blog_create"),
    path("update/<int:pk>/", views.blog_update, name="blog_update"),
    path("delete/<int:pk>/", views.blog_delete, name="blog_delete"),
]


# blog > views.py

from django.shortcuts import render
from .models import Post

# db 내용 호출
def blog_list(request):
    if request.GET.get("q"):
        db = Post.objects.filter(
            Q(title__contains=request.GET.get("q")) 
            | Q(contents__contains=request.GET.get("q")) 
        ).distinct()
    else:
        db = Post.objects.all()
    context = {"db": db}
    return render(request, "blog/blog_list.html", context)

def blog_details(request, pk):
    db = Post.objects.get(pk=pk)
    context = {"db": db}
    return render(request, "blog/blog_details.html", context)

 

출력 페이지는 아래와 같이 작성하였고, title에 링크를 설정하여 클릭 시 세부 정보 페이지로 이동하도록 설정했다.

"{% url 'blog_details' post.id %} : blog_details/id값 의 경로"

# blog/blog_list.html

<h1>게시판</h1>
<form action="" method="get">
    <input type="text" name="q" type="search">
    <button type="submit">검색</button>
</form>
<ul>
    {% for post in db %}
    <li>
        <a href="{% url 'blog_details' post.id %}">{{ post.title }}</a>
        <p>{{ post.contents }}</p>
    </li>
    {% endfor %}
</ul>



# blog/blog_details.html

<h1>게시판</h1>

<p>{{db.title}}</p>
<p>{{db.contents}}</p>
<p>{{db.created_at}}</p>
<p>{{db.updated_at}}</p>
<p>{{db.id}}</p>
{% if db.main_image %}
<img src="{{ db.main_image.url }}" alt="">
{% endif %}
<a href="{% url 'blog_list' %}">뒤로가기</a>

 


blog 메인 페이지


세부 페이지

검색 시 출력 메인 페이지

 

2. Create

Create 단계에서는 새로운 데이터를 생성하고, 이미지 저장에 대한 기능을 구현했으며, blog/create/ 경로 접속 시 새로운 데이터를 입력할  수 있다.

해당 경로로 접속 했을 때 제목과 내용, 이미지를 입력하고 저장할 수 있게 구현했다. 또한 이미지 저장 기능을 위해서 views.py > blog_create 함수에서 request.FILES 를 추가하였고 blog_create.html 에서 form에 enctype="multipart/form-data" 를 추가해줘야한다.

# blog > views.py 

from django.shortcuts import redirect

# ... 생략 ...

def blog_create(request):
    if request.method == "GET":
        form = PostForm()
        context = {"form": form}
        return render(request, "blog/blog_create.html", context)
    elif request.method == "POST":
        form = PostForm(request.POST, request.FILES) # request.FILES 추가
        if form.is_valid():
            post = form.save()
            # detail로 가야한다!
            # return redirect('blog_details', pk=post.pk)
            return redirect("blog_list")
        else:
            context = {"form": form}
            return render(request, "blog/blog_create.html", context)
# blog > blog_create.html

<form action="{% url 'blog_create'%}" method="post" enctype="multipart/form-data">
    {# 해킹 공격 방어를 위한 토큰입니다. #}
    {% csrf_token %}

    {{ form }}

    <button type="submit">저장</button>
</form>

 

[새로운 데이터 생성 페이지]
[저장 내역 확인]

3. Delete

저장된 각 세부 내역 페이지에서 삭제가 가능하도록 기능을 구현했고, 해당 페이지의 id값을 확인하고 DB에 해당 pk 값의 내용을 삭제했다.

# blog > views.py

from django.shortcuts import render, redirect, get_object_or_404
from django.db.models import Q
from .models import Post
from .forms import PostForm

# ... 생략 ...

def blog_delete(request, pk):
    # post = Post.objects.get(pk=pk)
    post = get_object_or_404(Post, pk=pk)
    print(post)
    if request.method == "POST":
        post.delete()
    return redirect("blog_list")
# blog > blog_details.html
# 삭제하기 버튼 추가

# ..... 생략 ......

<!-- 삭제하기 버튼 -->
<form action="{% url 'blog_delete' db.id %}" method="post"> <!-- blog_delete/id -->
    {% csrf_token %}
    <button type="submit">삭제하기</button>
</form>

[삭제 버튼 클릭 시 해당 내용 삭제]

4. Update

저장된 각 세부 내역 페이지에서 수정하기가 가능하도록 기능을 구현했다. 

# blog > views.py

def blog_update(request, pk):
    post = get_object_or_404(Post, pk=pk)
    if request.method == "POST":
        form = PostForm(request.POST, request.FILES, instance=post) 
        if form.is_valid():
            form.save()
            return redirect("blog_details", pk=post.pk)
    else: # request method가 Get일 경우 
        form = PostForm(instance=post)
        context = {"form": form, "pk": pk}
        return render(request, "blog/blog_update.html", context)
# blog > blog_details.html
# 수정하기 버튼 추가

# ..... 생략 ......

<!-- 수정하기 버튼 -->
<a href="{% url 'blog_update' db.id %}">수정하기</a>

세부 내역 페이지(수정하기)

수정 내용 작성 페이지
수정 내용 반영 확인

 

Comments