Python Flask 로 CRUD 만들어 보기

SQLAlchemy 사용해서 1시간만 해보기

Flask와 SQLAlchemy를 사용해서 간단한 텍스트를 CRUD(Create, Read, Update, Delete)하는 기능을 만들려고 합니다.

프로젝트를 생성합니다.

> pip install virtualenv
> virtualenv flasknote
> cd flasknote
> source bin/activate

개발에 필요한 모듈을 설치합니다. Flask-SQLAlchemy 는 Flask를 위한 SQLAlchemy 확장입니다.

(flasknote) > pip install flask Flask-SQLAlchemy mysqlclient Flask-Migrate

macOS 에서 ‘No module named MySQLdb’ 에러가 발생하면 MySQL 연결 드라이버를 설치합니다.

> brew install mysql-connector-c

PyCharm 으로 flasknote 디렉토리에 프로젝트를 생성하고 작업 디렉토리와 파일을 생성합니다.

Flask App 초기화 파일을 작성합니다.

> vi app/__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
# config.py 설정파일
app.config.from_object('config')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.secret_key = 'flasknotewithsqlalchemy'
db = SQLAlchemy(app)

from app import views, models

DB 연결 설정 파일을 작성합니다.

> vi config.py
SQLALCHEMY_DATABASE_URI = 'mysql://<mysqluser>:<password>@<mysqlhostname>/<databasename>?charset=utf8'

Model 을 작성합니다.

> vi app/models.py
from app import db

class Note(db.Model):
__tablename__ = "note"
__table_args__ = {'mysql_collate': 'utf8_general_ci'}
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(128))
content = db.Column(db.Text)

def __init__(self, title, content):
self.title = title
self.content = content

DB Migration 을 작성합니다.

> vi db.py
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
from config import SQLALCHEMY_DATABASE_URI
from app import app, db

migrate = Migrate(app, db)

manager = Manager(app)
manager.add_command('db', MigrateCommand)

if __name__ == '__main__':
manager.run()

정의한 SQLAlchemy 모델과 테이블을 매핑하고 DB 테이블을 생성합니다.

(flasknote) > python db.py db init
(flasknote) > python db.py db migrate
(flasknote) > python db.py db upgrade

MySQL Workbench 로 접속해서 ‘note’ 테이블의 생성을 확인합니다. 필드들이 utf8 인코딩으로 생성되어 있는지 확인합니다.

테이블을 만들었으니 CRUD 에 필요한 각 화면과 기능을 작성합니다.

Create

노트 등록 화면을 작성합니다.

> vi app/templates/add.html
<!DOCTYPE html>
<html class="no-js" lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Flask Note</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/uikit/2.27.2/css/uikit.min.css" />
</head>
<body class="uk-width-1-3 uk-container-center uk-block">

<form action="" method="post" class="uk-form uk-form-stacked">
<div class="uk-form-row">
<label class="uk-form-label" for="title">제목</label>
<div class="uk-form-controls">
<input type="text" name="title" id="title" value="" class="uk-form-width-large" /></div>
</div>
<div class="uk-form-row">
<label class="uk-form-label" for="content">내용</label>
<textarea type="text" name="content" id="content" class="uk-form-width-large"></textarea>
</div>
<div class="uk-form-row">
<button class="uk-button uk-button-success" type="submit">등록</button>
</div>
</form>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/uikit/2.27.2/js/uikit.min.js"></script>

</body>
</html>

노트 등록 ‘/add’ 요청 처리를 작성합니다.

> vi app/views.py
from flask import render_template, request, flash, redirect, url_for
from app import app, db
from app.models import Note

# 노트 생성
@app.route('/add', methods=['POST', 'GET'])
def add():
if request.method == 'POST':
note = Note(request.form['title'], request.form['content'])
db.session.add(note)
db.session.commit()
return redirect(url_for('index'))

return render_template('add.html')

Read

노트 조회 화면을 작성합니다.

> vi app/templates/index.html
<!DOCTYPE html>
<html class="no-js" lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Flask Note</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/uikit/2.27.2/css/uikit.min.css" />
</head>
<body class="uk-width-1-3 uk-container-center uk-block">
<p><a href="{{ url_for('add') }}">새 노트 등록하기</a></p>
<ul class="uk-list uk-list-striped">
{% for item in note %}
<li>
<article class="uk-article">
<h3>{{ item.title }}</h3>
<p>{{ item.content }}</p>
<p><a href="{{ url_for('edit', id=item.id) }}">수정</a>&nbsp;&nbsp;<a href="{{ url_for('delete', id=item.id) }}">삭제</a></p>
</article>
<hr/>
</li>
{% endfor %}
</ul>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/uikit/2.27.2/js/uikit.min.js"></script>

</body>
</html>

노트 조회 ‘/’ 요청 처리를 작성합니다.

> vi app/views.py
...
# 노트 조회
@app.route('/')
def index():
note = Note.query.all()
return render_template('index.html', note=note)

Update, Delete

노트 조회 화면에서 노트 수정 ‘/edit’ 과 노트 삭제 ‘/delete’ 에 대한 링크를 작성합니다.

> vi app/templates/index.html
...
<article class="uk-article">
<h3>{{ item.title }}</h3>
<p>{{ item.content }}</p>
<p><a href="{{ url_for('edit', id=item.id) }}">수정</a>&nbsp;&nbsp;<a href="{{ url_for('delete', id=item.id) }}">삭제</a></p>
</article>
...

노트 수정 ‘/edit’, 노트 삭제 ‘/delete’에 대한 요청 처리를 작성합니다.

> vi app/views.py
...
# 노트 수정
@app.route('/edit/<id>', methods=['POST', 'GET'])
def edit(id):
note = Note.query.get(id)
if request.method == 'POST':
note.title = request.form['title']
note.content = request.form['content']
db.session.commit()
return redirect(url_for('index'))
return render_template('edit.html', note=note)

# 노트 삭제
@app.route('/delete/<id>', methods=['POST', 'GET'])
def delete(id):
note = Note.query.get(id)
db.session.delete(note)
db.session.commit()

return redirect(url_for('index'))

App 실행 코드를 작성합니다.

> vi run.py
from app import app

app.debug = True
app.run(host='127.0.0.1')

구동 후 결과를 확인합니다.

(flasknote) > python run.py

이제 산책하러 가야겠습니다.

참조