일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- Binary Search
- X윈도우
- BFS
- preprocessing
- External Scheme
- Operator
- Polymolphism
- Entity
- Reference Type
- 셀레니움
- 리눅스 마스터 1급
- Class
- spring
- X.org
- 백준
- Unity
- Mac
- descriptive statistics
- 자바
- Physical Scheme
- 리눅스
- Inheritance
- dbms
- selenium
- literal
- systemd
- python
- Java
- OOP
- Entity Set
- Today
- Total
Byeol Lo
Flask - 7. Tutorial Blog Blueprint 본문
이제 배웠던 것을 토대로 blog의 blueprint를 구현하자.
The Blueprint
# flaskr/blog.py
from flask import (
Blueprint
)
bp = Blueprint('blog', __name__)
어플리케이션을 시작할때 __init__()에서 청사진을 등록하자.
# flaskr/__init__.py
def create_app():
app = ..
# omitted
from . import blog
app.register_blueprint(blog.bp)
app.add_url_rule('/', endpoint='index')
return app
app_url_rule이 생소할 수 있는데 /로 들어온 것은 index의 endpoint로 여기겠다는 의미이다.
Index
인덱스는 모든 포스트에 보여지는 숫자들이다. SQL의 JOIN을 통해 현재 접속한 유저의 게시글들만 볼 수 있도록 할 수 있다.
# flaskr/blog.py
@bp.route('/')
def index():
db = get_db()
posts = db.execute(
'SELECT p.id, title, body, created, author_id, username '
' FROM post p JOIN user u ON p.author_id = u.id'
' ORDER BY created DESC'
).fetchall()
return render_template('blog/index.html', posts=posts)
가지고 있는 모든 포스트들을 띄워주는 코드이다. db를 가져와서 JOIN을 사용해 등록되어 있는 author들만 보여지도록 한다.
<!-- flaskr/templates/blog/index.html -->
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Posts{% endblock %}</h1>
{% if g.user %}
<a class="action" href="{{ url_for('blog.create') }}">New</a>
{% endif %}
{% endblock %}
{% block content %}
{% for post in posts %}
<article class="post">
<header>
<div>
<h1>{{ post['title'] }}</h1>
<div class="about">by {{ post['username'] }} on {{ post['created'].strftime('%Y-%m-%d') }}</div>
</div>
{% if g.user['id'] == post['author_id'] %}
<a class="action" href="{{ url_for('blog.update', id=post['id']) }}">Edit</a>
{% endif %}
</header>
<p class="body">{{ post['body'] }}</p>
</article>
{% if not loop.last %}
<hr>
{% endif %}
{% endfor %}
{% endblock %}
위는 로그인을 했을때는 create가 뜨도록 하며, 위의 view function에서 가져온 posts들을 순회하며 글 목록을 띄워준다. 또한 거기서 자신이 쓴 글이라면 편집을 할 수 있도록 a태그를 설정해준다.
Create
create 뷰는 register 뷰와 비슷한데, POST 메소드로 받고, 데이터베이스에 저장시켜주면 된다.
@bp.route('/create', methods=('GET', 'POST'))
@login_required
def create():
if request.method == 'POST':
title = request.form['title']
body = request.form['body']
error = None
if not title:
error = 'Title is required.'
if error is not None:
flash(error)
else:
db = get_db()
db.execute(
'INSERT INTO post (title, body, author_id)'
' VALUES (?, ?, ?)',
(title, body, g.user['id'])
)
db.commit()
return redirect(url_for('blog.index'))
return render_template('blog/create.html')
뷰를 만들었다면 템플릿을 만들자
<!-- flaskr/templates/blog/create.html -->
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}New Post{% endblock %}</h1>
{% endblock %}
{% block content %}
<form method="post">
<label for="title">Title</label>
<input name="title" id="title" value="{{ request.form['title'] }}" required>
<label for="body">Body</label>
<textarea name="body" id="body">{{ request.form['body'] }}</textarea>
<input type="submit" value="Save">
</form>
{% endblock %}
Update
update는 포스트를 id로 가져와서 작성자가 현재 로그인한 사용자와 일치하는지 확인하는 작업이 필요하고, POST 메소드로 하는 이유는 대량의 데이터를 url 만으로 하기엔 부족하다. 그리고 누군가가 이를 통해 악의적으로 글을 수정할 수 있게 하면 안되기 때문이다. 우선 get_post 함수를 정의하여 post를 반환하는 함수를 만들자.
# flaskr/blog.py
def get_post(id, check_author=True):
post = get_db().execute(
'SELECT p.id, title, body, created, author_id, username'
' FROM post p JOIN user u ON p.author_id = u.id'
' WHERE p.id = ?',
(id, )
).fetchone()
if post is None:
abort(404, f"Post id {id} doesn't exist.")
if check_author and post['author_id'] != g.user['id']:
abort(403)
return post
abort는 special exception으로 HTTP status code를 설정해주고, 에러 메시지를 보여주는 함수이다. 나중에 직접 테스트해보자. 또한 여기서 중요한 코딩하는 방식이 check_author을 인자로 가져가서 코드 스니펫에 명시를 해줌으로써 다른 사용자가 해당 함수를 사용할 때 이 인자를 통해 간편하게 수정할 수 있다는 것이다
# flaskr/blog.py
@bp.route('/<int:id>/update', methods=('GET', 'POST'))
@login_required
def update(id):
post = get_post(id)
if request.method == 'POST':
title = request.form['title']
body = request.form['body']
error = None
if not title:
error = 'Title is required'
if error is not None:
flash(error)
else:
db = get_db()
db.execute(
'UPDATE post SET title = ?, body = ?'
' WHERE id = ?',
(title, body, id)
)
db.commit()
return redirect(url_for('blog.index'))
return render_template('blog/update.html', post=post)
<!-- flaskr/templates/blog/update.html -->
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Edit "{{ post['title'] }}"{% endblock %}</h1>
{% endblock %}
{% block content %}
<form method="post">
<label for="title">Title</label>
<input name="title" id="title"
value="{{ request.form['title'] or post['title'] }}" required>
<label for="body">Body</label>
<textarea name="body" id="body">{{ request.form['body'] or post['body'] }}</textarea>
<input type="submit" value="Save">
</form>
<hr>
<form action="{{ url_for('blog.delete', id=post['id']) }}" method="post">
<input class="danger" type="submit" value="Delete" onclick="return confirm('Are you sure?');">
</form>
{% endblock %}
Delete
삭제는 템플릿이 필요 없다. 따라서 그냥 버튼만 있어주면 된다. 기능 구현 후에 view에 바인딩 시켜주자.
# flaskr/blog.py
@bp.route('/<int:id>/delete', methods=('POST',))
@login_required
def delete(id):
get_post(id)
db = get_db()
db.execute('DELETE FROM post WHERE id = ?', (id, ))
return redirect(url_for('blog.index'))
데이터베이스 다루기 CRUD
CRUD는 create, read, update, delete의 약자로 데이터베이스에서 데이터베이스를 다루는 기본적인 네 비즈니스 프로세스이다. 따라서 해당 기능을 구현한 다음에 view function으로 바인딩만 시켜준다면 게시판이 완성된다. 여기서 read를 제외한 모든 프로세스, 동작은 POST 형태로 다뤄야 한다.
'BackEnd > Flask' 카테고리의 다른 글
Flask - 9. Deploy to Production (0) | 2024.02.25 |
---|---|
Flask - 8. Tutorial Make the Project Installable and Test Coverage (0) | 2024.02.25 |
Flask - 6. Tutorial Templates (0) | 2024.02.14 |
Flask - 5. Tutorial Blueprint and View (1) | 2024.02.14 |
Flask - 4. Define and Access the Database (1) | 2024.02.12 |