파이썬 웹 서비스 만들기

7. 컨텐츠 포스팅 및 관리기능 - 7.1. 포스팅

영바이트 2020. 10. 30. 23:13

이전 포스팅을 통해 '사용자 등록(계정 생성) - 로그인 - 사용자 페이지(계정 정보 수정)'에 대해 알아보고 실제 코드도 작성해 보았다. 로그인 과정이 추가되긴 했지만, 기본적인 동작은 사용자로부터 정보 받아오기, 받아온 정보를 서버(의 DB)에 저장하기로 요약할 수 있다.

 

이번 포스팅부터는 웹 서비스 이용자가 글을 남길 수 있는 기능을 알아볼 것이다. 글쓰기 기능을 구성하는 핵심 요소를 먼저 설명하면 역시 '사용자로부터 정보 받아오기'와 '받아온 정보를 서버에 저장하기'라고 할 수 있다.

 

① 사용자로부터 정보 받아오기

 

        - 글(포스팅)을 표현하는 모델 만들기 = 모델

        - 글 입력 페이지 만들기= 폼, 뷰

 

② 받아온 정보를 서버에 저장하기

 

        - 입력받은 정보를 정리하여 DB에 저장하기 = 로직

 

할 일의 순서를 정했으니 프로젝트를 생성하고 실제 코딩을 하면서 더 자세히 살펴보자.

 


 

7.1.1. 프로젝트 생성

 

아래 그림과 같이 새 프로젝트 디렉토리(05_posting으로 이름하였다)를 만들고 이전 프로젝트의 내용을 복사해서 가져오자.

 

새 프로젝트 생성 및 내용 가져오기

 

 

7.1.2. 모델/폼 정의

 

포스팅(=글) 입력을 받기 위해서는 서버의 데이터베이스(DB)에 저장될 데이터 모델과 이 데이터 모델을 채우기 위해 사용자에게 입력받을 폼이 필요하다. 먼저 포스팅을 구성하는 요소들을 정리해보자. 아래와 같은 요소들로 구성할 수 있을 것이다.

 

- 글 제목

- 작성자

- 작성 시간

- 글 본문

 

포스팅 관련한 코드들을 모아놓을 디렉토리를 post라는 이름으로 만들고 데이터 모델을 정의하자.

 

포스팅(=글) 데이터 모델 정의

 

▶ appmain/post/models.py

#### 글이 작성된 시간을 얻기위해 datetime 패키지를 가져온다.
from datetime import datetime
#### DB에 저장될 모델이기 때문에 SQLAlchemy 객체인 db를 가져온다.
from appmain import db

#### DB에 저장될 모델이기 때문에 SQLAlchemy 객체인 db의 Model 클래스를 상속받아 정의한다.
class Post(db.Model):

    #### id = DB에 저장할 때 부여되는 고유 번호
    id = db.Column(db.Integer, primary_key=True)
    
    #### 제목
    title = db.Column(db.String(100), nullable=False)
    
    #### 작성자
    author = db.Column(db.String(30), nullable=False)
    
    #### 작성 시간. 기본값은 현재 시간(datetime.utcnow)이다.
    datePosted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
    
    #### 글
    content = db.Column(db.Text, nullable=False)

 

웹 서버는 사용자로부터 정보를 입력받아 데이터 모델을 채우게된다. 입력받을 데이터인 '폼(form)'도 정의하자.

 

포스팅 입력내용 정의

 

▶ appmain/post/forms.py

from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, TextAreaField
from wtforms.validators import DataRequired, Length, Email, EqualTo, ValidationError

class PostForm(FlaskForm):

    #### 제목 - 문자열(String) 객체
    title = StringField('Title', validators=[DataRequired(), Length(min=1, max=90)])
    
    #### 글 - 글 상자(TextArea) 객체
    content = TextAreaField('Content', validators=[DataRequired(), Length(min=1, max=200)])
    
    #### 제출 버튼
    submit = SubmitField('Post')

 

폼을 보면 작성자와 작성 시간은 별도로 정의하지 않았다. 그 이유는 작성자는 로그인한 바로 그 사용자의 정보가 되기 때문이고, 작성 시간은 글이 DB에 저장되는 시간을 시스템에서 얻어와서 쓰면 되기 때문이다.

 


 

7.1.3. 뷰(페이지) 만들기

 

모델/폼을 정의했으므로 다음 단계인 뷰(=페이지)를 만들자. 아래와 같이 페이지 템플릿 디렉토리 아래에 작성할 것이다.

 

포스팅 입력 페이지

 

▶ appmain/templates/create_post.html

{% extends "layout.html" %}
{% block content %}
<form method="POST" action="">
    {{form.hidden_tag()}} {# Transferring CSRF token = secret key #}
    
    <!-- 제목 입력 부분 -->
    <div>
        {{form.title.label()}}
        {{form.title()}}
        {%if form.title.errors%}
            {%for error in form.title.errors%}
            <span>{{error}}</span>
            {%endfor%}
        {%endif%}
    </div>
    
    <!-- 글 본문 입력 부분 -->
    <div>
        {{form.content.label()}}
        {{form.content()}}
        {%if form.content.errors%}
            {%for error in form.content.errors%}
            <span>{{error}}</span>
            {%endfor%}
        {%endif%}
    </div>
    
    <!-- 제출 버튼 -->
    <div>
        {{form.submit()}}
    </div>
</form>
{% endblock content %}

 

여기까지 '모델/폼 - 뷰' 작성을 마쳤고 이제 로직(logic)을 작성할 차례다.

 


 

7.1.4. 입력 정보 처리 로직 작성

 

아래와 같이 post 디렉토리 아래에 routes.py 파일을 만들고(사용자가 특정 URL에 접속했을 때 플라스크의 route( ) 함수를 실행시키게 되므로 이름을 routes로 지정하였다) 아래와 같이 입력받은 내용을 DB에 저장하는 로직을 만들자.

 

포스팅 저장 로직

 

▶ appmain/post/routes.py

from flask import render_template, url_for, redirect, request, abort, Blueprint
from flask_login import current_user, login_required
from appmain import db
from appmain.post.forms import PostForm
from appmain.post.models import Post

#### post라는 이름의 Blueprint 객체를 만들고 이 Blueprint 객체의 route 기능을 활용한다.
#### 프로젝트를 구성하는 하위 패키지들의 Blueprint 객체들은 프로젝트 메인 패키지인 appmain 패키지에서
#### 플라스크 객체에 통합(등록)될 것이다.
post = Blueprint('post', __name__)

@post.route("/post/new", methods=['GET', 'POST'])
@login_required
def newPost():
    form = PostForm()
    
    #### 사용자가 '제출' 버튼을 누르면 아래 if 블록 내용이 실행된다.
    if form.validate_on_submit():
    
        #### Post 객체를 만들고 제목, 글 내용, 작성자, 작성 시간을 설정한다.
        content = Post(title = form.title.data, content = form.content.data, author = current_user.username)
        
        #### DB에 저장한다.
        db.session.add(content)
        
        #### 변경(저장) 사항을 반영한다.
        db.session.commit()
        
        print('A new post is created: ', form.title.data)
        return redirect(url_for('main.home'))
        
    #### 사용자가 '새 포스팅' URL(/post/new)로 접근하면 글 입력 페이지를 보여준다.
    return render_template('create_post.html', title='New Post', form=form)

 

post 디렉토리를 패키지로 만들고 필요한 초기화를 하기 위해 __init__.py 파일을 작성한다.

 

post 패키지 초기화(init) 파일

 

post 패키지에는 특별히 초기화해야할 내용이 없기 때문에 내용은 비워놓는다. 하지만 post 디렉토리가 파이썬 패키지 디렉토리임을 선언하기 위해 파일은 반드시 존재해야 한다.

 

▶ appmain/post/__init__.py

 
  
   

 


 

7.1.5. 플라스크 서비스에 post 패키지 통합

 

사용자로부터 글 입력을 받고 서버의 DB에 저장하는 기능을 가진 post 패키지를 플라스크 서비스에 통합시키자. 통합 과정에서 할 일들은 아래와 같다.

 

① post 패키지를 route 로직(Blueprint 객체) 등록

② 글 입력 페이지(포스팅 페이지) 링크 작성

③ 포스팅된 내용 보여주기

 

하나씩 만들어보자.

 

① post 패키지 route 로직(Blueprint 객체) 등록

 

 

post 패키지 기능의 통합

 

▶ appmain/__init__.py

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import pymysql
from flask_bcrypt import Bcrypt
from flask_login import LoginManager

app = Flask(__name__)

app.config['SECRET_KEY'] = 'd2eb5380841f5fdc4b70bb3e8be4a614'
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://flaskweb:your_password@localhost:3306/flaskdb'

db = SQLAlchemy(app)

loginManager = LoginManager(app)
#loginManager.login_view = 'login'

bcrypt = Bcrypt(app)

from appmain.user.routes import user

#### 새로 추가된 부분
#### post 패키지에서 post라고 명명된 blueprint 객체를 가져온다.
from appmain.post.routes import post

from appmain.routes import main

db.create_all()

app.register_blueprint(user)

#### 새로 추가된 부분
#### post blueprint 객체를 플라스크 앱에 등록한다.
app.register_blueprint(post)

app.register_blueprint(main)

 

 

② 글 입력 페이지(포스팅 페이지) 링크 작성

 

글쓰기 페이지 링크 작성

 

▶ appmain/templates/layout.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <link rel="stylesheet" type="text/css" href="{{url_for('static', filename='css/main.css')}}">
        {%if title%}
            <title>{{title}}</title>
        {%else%}
            <title>Flask Exercise</title>
        {%endif%}
    </head>
    <body>
        <a href="{{url_for('main.home')}}">Home</a>
        <a href="{{url_for('main.about')}}">About</a>
        <div>
            {%if current_user.is_authenticated%}
            <a href="{{url_for('user.logout')}}">Logout</a>
            <a href="{{url_for('user.account')}}">Account</a>
            {%else%}
            <a href="{{url_for('user.login')}}">Login</a>
            <a href="{{url_for('user.register')}}">Register</a>
            {%endif%}
        </div>

        <!-- 이 부분이 추가된 부분입니다 -->
        <div>
            {%if current_user.is_authenticated%}
            <a href="{{url_for('post.newPost')}}">New Post</a>
            {%endif%}
        </div>
        <!-- -->

        <div>
            {%block content%}{%endblock content%}
        </div>
    </body>
</html>

 

 

③ 포스팅된 내용 보여주기

 

포스팅된 내용을 DB에서 읽어와 웹 서비스의 메인 페이지에서 보여주도록 하였다. 웹 서비스의 메인 페이지 관리 로직이 기술되어 있는 appmain 패키지의 routes.py 파일을 수정한다.

 

포스팅된 글 보여주기

 

▶ appmain/routes.py

from flask import render_template, Blueprint

#### DB의 포스팅 테이블의 SQLAlchemy 모델을 가져온다.
from appmain.post.models import Post

main = Blueprint('main', __name__)

@main.route("/")
@main.route("/home")
def home():

    ##### DB에서 Post 테이블의 전체 내용을 읽어온다.
    posts = Post.query.all()
    
    ##### 읽어온 내용을 메인 페이지에서 보여준다.
    return render_template("home.html", posts=posts)

@main.route("/about")
def about():
        return render_template("about.html", title='About')

 


 

전체 프로젝트 내용을 저장하고 프로젝트 실행 스크립트(run.py)를 실행시키자. 그리고 웹 브라우저를 열고 웹 서비스(http://127.0.0.1:5000)로 접속한다.

 

로그인하고 나면 아래와 같이 새로운 글쓰기 링크가 생긴 것을 볼 수 있다.

 

홈 화면 글쓰기 링크 - New Post

 

 

링크를 클릭해서 글쓰기 페이지로 접근하면 아래와 같이 포스팅 페이지를 볼 수 있다.

 

포스팅 페이지

 

제목과 글을 입력하고 Post 버튼을 클릭하면 홈 화면으로 이동하고 작성한 포스팅이 게시되어 있는 것을 볼 수 있다.

 

 

홈 화면에 게시된 포스팅

 

웹 서비스 DB의 내용을 윈도우 명령 프롬프트를 통해 살펴보면 설정한 테이블인 'post'에 입력한 포스팅이 저장되어 있는 것을 확인할 수 있다.

 

웹 서비스 DB에 저장되어 있는 포스팅 정보