하루에는

[Django]장고 모델폼 ModelForm을 사용한 CRUD 구현 본문

Python, Django

[Django]장고 모델폼 ModelForm을 사용한 CRUD 구현

에는 2019. 12. 10. 14:55

Overviews

  • Model Form 사용
  • post 앱의 CRUD 구현

지금 post앱의 새 글 쓰기는 admin 페이지에서만 가능합니다. 웹에서도 CRUD가 가능하게 해보겠습니다.

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

<form method='POST'>
	{% csrf_token %}
	<input type='text' name='title'/>
	<textarea name='content'/>
	<input type='submit' value='등록'/>
</form>

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

<form method='POST'>
	{% csrf_token %}
	{{form}}
	<input type='submit' value='등록'/>
</form>

Model Form

  • 모델에서 정의한 필드대로 form을 자동으로 만들어 줌
  • app에 forms.py를 만들고, 어떤 필드를 입력받을지 정한다.
  • 사용자로부터 데이터를 입력받아서 저장할 때는 대부분 모델폼을 사용
  • 모델폼의 장점
    1. 폼을 위한 HTML을 작성할 필요 없음
    2. 데이터 유효성 자동으로 검사
    3. 올바르지 않은 데이터를 입력했을 경우 에러메시지와 함께 사용자에게 알려줌
    4. 재사용 가능

Create

post 앱에 forms.py를 만듭니다.

그리고 내용을 작성합니다.

# post/forms.py

from django import forms
from .models import Post

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['title','content']
  • from django import forms : 장고에서 제공하는 forms 기능을 사용하기 위해 임포트
  • from .models import Post : Post 모델을 사용하기 위해 임포트
  • class PostForm(forms.ModelForm) : PostForm이라는 이름의 모델폼 클래스 생성
  • model = Post : form에서 사용할 모델이 Post임을 명시
  • fields : Post 모델에서 입력받고 싶은 필드를 리스트 형태로 작성. 'all' 이라고 작성하면 전체 필드를 추가함.

url 설정

글 작성을 위해서는 두 가지 path가 필요합니다.

  1. 글 작성 화면, 즉 form을 띄울 화면(new)

  2. 실제 create를 진행하는 메서드(postcreate)

from django.urls import path
from . import views

urlpatterns = [
    path('list/', views.postlist, name='postlist'),
    path('show/<int:post_id>', views.show, name='show'),
    path('new/', views.new, name='new'), # 추가
    path('postcreate/', views.postcreate, name='postcreate'), # 추가
]

View 설정

post의 views.py에 new와 postcreate를 추가합니다.

from django.shortcuts import render, get_object_or_404, redirect # 추가
from .models import Post
from .forms import PostForm # 추가
from datetime import datetime # 추가

# Create your views here.
def postlist(request):
    ... 

def show(request, post_id):
    ...

def new(request):
    return render(request, 'new.html')

def postcreate(request):
    if request.method == 'POST': # POST 방식으로 요청이 들어왔을 때
        form = PostForm(request.POST) # 입력된 내용들을 form이라는 변수에 저장
        if form.is_valid(): # form이 유효하다면(models.py에서 정의한 필드에 적합하다면)
            post = form.save(commit=False) # form 데이터를 가져온다.
						post.created_at = datetime.now()
            post.save() # form 데이터를 DB에 저장한다.
            return redirect('postlist')
        else:
            return redirect('postlist')
    else: # GET 방식으로 요청이 들어왔을 때
        form = PostForm()
        return render(request, 'new.html', {'form': form})
  • redirect, PostForm 임포트
  • postcreate
    1. 요청이 POST로 들어왔을 경우 : 입력받은 내용의 유효성을 검사하고 저장
    2. 요청이 GET으로 들어왔을 경우 : PostForm을 form이라는 변수에 담아서 new.html을 렌더링

Template 설정

post/templates에 new.html을 만들고 내용을 작성합니다.

<h3>글 쓰기</h3>
<form method='POST'>
    {% csrf_token %}
    <table>
        {{form.as_table}}
    </table>
    <input type="submit" value="등록">
</form>
  • {{form.as_table}} : form을 table 형식으로 출력. 그냥 {{form}}으로 써도 무방함. {{form.as_p}}는 p태그 형식으로 출력함

postlist.html에 글 작성하기 링크를 추가하고 서버를 돌려봤습니다.

 

DateTimeField에 auto_now_add=True를 추가하면 객체가 생성된 시각이 자동으로 저장됩니다.

 

그러면 아래와 같이 현재 시각을 따로 저장해주는 과정을 생략할 수 있습니다.

from django.shortcuts import render, get_object_or_404, redirect # 추가
from .models import Post
from .forms import PostForm # 추가
from datetime import datetime # 추가

# Create your views here.
def postlist(request):
    ... 

def show(request, post_id):
    ...

def new(request):
    return render(request, 'new.html')

def postcreate(request):
    if request.method == 'POST': # POST 방식으로 요청이 들어왔을 때
        form = PostForm(request.POST) # 입력된 내용들을 form이라는 변수에 저장
        if form.is_valid(): # form이 유효하다면(models.py에서 정의한 필드에 적합하다면)
            post = form.save(commit=False) # form 데이터를 가져온다.
						post.created_at = datetime.now()
            post.save() # form 데이터를 DB에 저장한다.
            return redirect('postlist')
        else:
            return redirect('postlist')
    else: # GET 방식으로 요청이 들어왔을 때
        form = PostForm()
        return render(request, 'new.html', {'form': form})

Update

모델을 조금 수정하고 시작하겠습니다.

from django.shortcuts import render, get_object_or_404, redirect # 추가
from .models import Post
from .forms import PostForm # 추가
from datetime import datetime # 추가

# Create your views here.
def postlist(request):
    ... 

def show(request, post_id):
    ...

def new(request):
    return render(request, 'new.html')

def postcreate(request):
    if request.method == 'POST': # POST 방식으로 요청이 들어왔을 때
        form = PostForm(request.POST) # 입력된 내용들을 form이라는 변수에 저장
        if form.is_valid(): # form이 유효하다면(models.py에서 정의한 필드에 적합하다면)
            post = form.save(commit=False) # form 데이터를 가져온다.
						post.created_at = datetime.now()
            post.save() # form 데이터를 DB에 저장한다.
            return redirect('postlist')
        else:
            return redirect('postlist')
    else: # GET 방식으로 요청이 들어왔을 때
        form = PostForm()
        return render(request, 'new.html', {'form': form})

DateTimeField에 auto_now=True를 추가하면 객체가 수정된 시각이 자동으로 저장됩니다.

새 글 쓰기와 마찬가지로 forms.py를 사용해서 수정 화면을 만들겠습니다.

url 설정

글 쓰기와 마찬가지로 두 가지 path가 필요합니다.

  1. 글 수정 화면, 즉 form을 띄울 화면(edit)

  2. 실제 update를 진행하는 메서드(postupdate)

from django.urls import path
from . import views

urlpatterns = [
    path('list/', views.postlist, name='postlist'),
    path('show/<int:post_id>', views.show, name='show'),
    path('new/', views.new, name='new'),
    path('postcreate/', views.postcreate, name='postcreate'),
    path('edit/', views.edit, name='edit'), # 추가
    path('postupdate/<int:post_id>', views.postupdate, name='postupdate'), # 추가
]
  • postupdate path는 어떤 post 객체를 수정할 지도 알아야 하기 때문에 path-converter가 필요합니다.

View 설정

post/views에 아래 코드를 추가합니다. 기존에 있던 코드들은 길어져서 생략했습니다.

def edit(request):
    return render(request, 'edit.html')

def postupdate(request, post_id):
    post = get_object_or_404(Post, pk=post_id)
    if request.method == 'POST':
        form = PostForm(request.POST, instance=post)
        if form.is_valid():
            post = form.save(commit=False)
            post.save()
            return redirect('show', post_id=post.pk)
        else:
            return redirect('postlist')
    else:
        form = PostForm(instance=post)
        return render(request, 'edit.html', {'form': form})
  • post = get_object_or_404(Post, pk=post_id) : path-converter로 받은 post_id로 수정하고자 하는 post 객체를 get
  1. POST로 요청이 들어왔을 경우
    • form = PostForm(request.POST, instance=post) : PostForm의 인스턴스는 post임을 표시
    • update 완료 후 해당 글의 상세 페이지로 리다이렉트
  2. GET으로 요청이 들어왔을 경우
    • form = PostForm(instance=post) : post에 기존에 입력되어 있던 내용을 form에 담아서 edit.html을 렌더링

DateTimeField에 auto_now_add=True를 추가하면 객체가 생성된 시각이 자동으로 저장됩니다.

Template 설정

post/templates에 edit.html을 추가하고 내용을 작성합니다. new.html와 거의 동일합니다.

<h3>글 수정</h3>
<form method='POST'>
    {% csrf_token %}
    <table>
        {{form.as_table}}
    </table>
    <input type="submit" value="수정">
</form>

마지막으로 상세 페이지에 글 수정하기 링크를 추가하고 서버를 돌려봅니다.

Delete

delete는 path 하나만 있으면 됩니다.

from django.urls import path
from . import views

urlpatterns = [
    path('list/', views.postlist, name='postlist'),
    path('show/<int:post_id>', views.show, name='show'),
    path('new/', views.new, name='new'),
    path('postcreate/', views.postcreate, name='postcreate'),
    path('edit/', views.edit, name='edit'),
    path('postupdate/<int:post_id>/', views.postupdate, name='postupdate'),
    path('postdelete/<int:post_id>/', views.postdelete, name='postdelete'), # 추가
]
  • 어떤 post 객체를 삭제할지 지정해야 하기 때문에 역시 path-converter가 필요합니다.

View 설정

post/views.py에 코드를 추가합니다.

def postdelete(request, post_id):
    post = get_object_or_404(Post, pk=post_id)
    post.delete() # Post DB에서 post 객체 삭제
    return redirect('postlist')
  • path-converter로 보낸 post_id를 get
  • 해당 post 객체를 삭제하고 postlist로 리다이렉트한다

Template 설정

삭제하기 코드를 임의로 만들었습니다.

Comments