[djnago] django 를 이용한 간단한 블로그앱 만들기 – 2

이전 포스트에서 django 설치, 관리자 페이지 및 hello world 페이지 까지 작성하였다. 본 포스트에서는 리스트 페이지, 뷰페이지, 글쓰기 및 수정 삭제 기능, 페이징, 검색기능을 구현하려고 한다.

목록

  1. 리스트 페이지 작성
  2. 뷰 페이지 작성
  3. 글쓰기 페이지 작성
  4. 수정페이지 작성
  5. 삭제기능 추가
  6. 템플렛 다듬기

1 . 리스트페이지 작성

view.py에서 사용할 모델을 import한다. 그리고 현재보다 미래의 글은 리스트에 출력되면 안되기 때문에 시간을 처리해주는 장고 내장함수(timezone)를 import 한다.

#view.py

from django.shortcuts import render
from django.http import HttpResponse
from django.utils import timezone
from .models import Post

# Create your views here.
def index(requests):
    posts = Post.objects.filter(published_date__lte = timezone.now())
    return render(requests, 'myblog/list.html', {'posts' : posts})

render함수에 requests, ‘템플릿경로’, ‘데이터 객체’를 전달하고 리턴해주면 된다.
localhost:8000/myblog 로 접속해보면 템플렛을 찾을수 없다고 에러가 발생할 것이다.
장고가 render 함수로 전달된 ‘myblog/list.html’ 부분을 찾게 해주기 위해서 해당 경로에 디렉토리 및 파일을 생성해준다.

myblog 디렉토리 구조
templates 폴더 안에 앱이름으로 폴더를 생성하는 이유는 django는 프로젝트내 에서 모든 templates 폴더를 한곳으로 모아 파일을 찾는데 같은 파일이름이 있을 수 있으면 문제가 발생하기 때문에 네임스페이를 처리해주기 위해서 관행적으로 생성해준다고 한다.

이제 list.html 파일을 열고 마크업을 작성한다.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>myblog</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
</head>
<body>
    <div>
        <table class="table">
            <thead>
                <tr>
                    <th>title</th>
                    <th>published_date</th>
                </tr>
            </thead>
            <tbody>
                {% for post in posts %}
                <tr>
                    <td>{{ post.title }}</td>
                    <td>{{ post.published_date | date:"Y-m-d" }}</td>
                </tr>
                {% endfor %}
            </tbody>
        </table>
    </div>
</body>
</html>

view.py 에서 전달받은 posts객체를 이용해서 리스트를 출력해준다.

2. 뷰페이지 작성

게시글의 제목을 클릭하면 뷰페이지로 이동하는 부분을 작성할 차례이다. 작업순서는 상관없지만 나름대로 규칙을 가지고 작성하는 것이 좋기 때문에 아래와 같은 순서로 진행했다.

1) urls.py 에 뷰페이지 url 패턴 등록
2) view.py 에 게시글을 얻는 함수 추가
3) list.html에 게시글의 제목에 링크를 걸어준다.
4) post.html (뷰페이지) 작성

#urls.py
from . import views
from django.urls import path, include

app_name='myblog'

urlpatterns = [
    path('', views.index, name="index"),
    path('post/<int:pk>/', views.post, name="post"),
]

새로등록한 패턴은 아래의 주소처럼 접근할 수 있게 된다. http://localhost:8000/myblog/post/게시글번호

이제 path(‘post//’, views.post, name=”post”)에서 전달한 2번째 인자인 view.post 부분을 view.py에 post라는 함수를 작성하겠다.
post 함수는 url에서 전달받은 pk를 테이블에서 조회해서 템플렛으로 렌더링하는 함수이다.

#view.py
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse
from django.utils import timezone
from .models import Post

# Create your views here.
def index(requests):
    posts = Post.objects.filter(published_date__lte = timezone.now())
    return render(requests, 'myblog/list.html', {'posts' : posts})

def post(requests, pk):
    post = get_object_or_404(Post, pk=pk)
    return render(requests, 'myblog/post.html', {'post' : post})

*get_object_or_404 : 테이블에서 pk를 찾을 수 없으면 404 에러를 일으킨다.

<!-- list.html -->
{% load static %}
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>myblog</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
    <link rel="stylesheet" href="{% static 'myblog/css/style.css' %}">
</head>
<body>
    <div>
        <table class="table">
            <thead>
                <tr>
                    <th>title</th>
                    <th>published_date</th>
                </tr>
            </thead>
            <tbody>
                {% for post in posts %}
                <tr>
                    <td>
                        <a href="{% url 'myblog:post' post.pk %}">{{ post.title }}</a>
                    </td>
                    <td>{{ post.published_date | date:"Y-m-d" }}</td>
                </tr>
                {% endfor %}
            </tbody>
        </table>
    </div>    
</body>
</html>

{% url ‘myblog:post’ post.pk %}
*’myblog:post’ : myblog앱의 urls.py의 패턴으로 등록된 ‘name=post’라는 url 패턴을 사용하겠다는 의미이다.

django는 각각의 앱이 모듈로 사용되어지기 때문에 네임스페이스 처리를 해주는 것이 좋다. 네임스페이스는 urls.py에서 app_name을 이용해서 선언해준다.

#urls.py
app_name='myblog' #myblog 네임스페이스 선언

urlpatterns = [
    path('', views.index, name="index"),
    path('post/<int:pk>/', views.post, name="post"),
]

post.html 작성
view.post 를 통해 context로 전달한 post를 이용해 간단하게 내용을 출력 할 수 있다.

{% load static %}
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>myblog</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
    <link rel="stylesheet" href="{% static 'myblog/css/style.css' %}">
</head>
<body>
    <table class="table">
        <tbody>
            <tr>
                <th>제목</th>
                <td>{{ post.title }}</td>
            </tr>
            <tr>    
                <th>발행일</th>
                <td>{{ post.published_date | date:"Y-m-d" }}</td>
            </tr>
            <tr>
                <th>내용</th>
                <td>{{ post.content }}</td>
            </tr>
        </tbody>
    </table>        
</body>
</html>
뷰페이지 완성!

3. 글쓰기 페이지 작성

1) urls.py 에 뷰페이지 url 패턴 등록

urlpatterns = [
    path('', views.index, name="index"),
    path('post/<int:pk>/', views.post, name="post"),
    path('write/', views.write, name="write"),

2) form.py에 PostForm 클래스 생성

from django import forms
from .models import Post

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ('autor', 'title', 'content', 'created_date', 'published_date')

3) view.py 에 write함수 추가

def write(request):
    if request.method == 'POST':
        form = PostForm(request.POST)
        if form.is_valid():
            post = form.save(commit=False)
            post.author = request.user
            post.save()
            return HttpResponseRedirect(reverse('myblog:post', args=(post.pk,)))
    else :
        form = PostForm()
    return render(request, 'myblog/write.html', {'form' : form})

4) wirte.html에 form 작성

{% load static %}
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>myblog</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
    <link rel="stylesheet" href="{% static 'myblog/css/style.css' %}">
</head>
<body>
    <form action="" method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value="save">
    </form>
    <a href="{% url 'myblog:index' %}">목록</a>
</body>
</html>

4. 게시글 수정 페이지 작성

1) urls.py 에 수정페이지 url 패턴 등록

urlpatterns = [
    path('', views.index, name="index"),
    path('post/<int:pk>/', views.post, name="post"),
    path('write/', views.write, name="write"),
    path('write/<int:pk>/', views.modi, name="modi"),
]

2) view.py에 modi 함수추가

def modi(request, pk):
    post = get_object_or_404(Post, pk=pk)
    if request.method == 'POST':
        form = PostForm(request.POST, instance=post)
        if form.is_valid():
            post = form.save(commit=False)
            post.author = request.user
            post.save()
            return HttpResponseRedirect(reverse('myblog:post', args=(post.pk,)))
    else :
        form = PostForm(instance=post)
    return render(request, 'myblog/write.html', {'form' : form})

5. 삭제 기능 추가

1) 삭제 url 팬턴 추가

urlpatterns = [
    path('', views.index, name="index"),
    path('post/<int:pk>/', views.post, name="post"),
    path('write/', views.write, name="write"),
    path('write/<int:pk>/', views.modi, name="modi"),
    path('del/<int:pk>/', views.delete, name="delete"),
]

2. view.py delete 함수 추가

def delete(request, pk):
    post = get_object_or_404(Post, pk=pk)
    post.delete()
    return HttpResponseRedirect( reverse('myblog:index') )

6.템플렛 다듬기

템플릿 파일의 중복된 부분을 수정하기 위해서 공통으로 쓰여질 부분(base.html)을 만들어준다.

<!-- base.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>myblog</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
</head>
<body>
    {% block content %}
    {% endblock %}
</body>
</html>

list.html post.html, write.html을 수정해 준다.

<!--list.html -->
{% extends 'myblog/base.html' %}

{% block content %}
<div>
    <table class="table">
        <thead>
            <tr>
                <th>title</th>
                <th>published_date</th>
            </tr>
        </thead>
        <tbody>
            {% for post in posts %}
            <tr>
                <td>
                    <a href="{% url 'myblog:post' post.pk %}">{{ post.title }}</a>
                </td>
                <td>{{ post.published_date | date:"Y-m-d" }}</td>
            </tr>
            {% endfor %}
        </tbody>
    </table>
</div>
<a href="{% url 'myblog:write' %}">write</a>
{% endblock %}
<!-- write.html -->
{% extends 'myblog/base.html' %}
{% block content %}
<form action="" method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="save">
</form>
<a href="{% url 'myblog:index' %}">목록</a>
{% endblock %}
<!-- post.html -->
{% extends 'myblog/base.html' %}

{% block content %}
<table class="table">
    <tbody>
        <tr>
            <th>제목</th>
            <td>{{ post.title }}</td>
        </tr>
        <tr>    
            <th>발행일</th>
            <td>{{ post.published_date | date:"Y-m-d" }}</td>
        </tr>
        <tr>
            <th>내용</th>
            <td>{{ post.content }}</td>
        </tr>
    </tbody>
</table>
<a href="{% url 'myblog:index' %}">목록</a>
<a href="{% url 'myblog:modi' post.pk%}">수정</a>
<a href="{% url 'myblog:delete' post.pk%}">삭제</a>
{% endblock %}

답글 남기기