티스토리 뷰
100 Days of Code - The Complete Python Pro Bootcamp for 2021
Day 67 - Advanced - Blog Capstone Project Part 3 - RESTful Routing
Goals: Building a RESTful Blog with Editing! CRUD로 글 수정 기능을 가진 블로그 만들기
set FLASK_APP=mian.py
set FLASK_ENV=development
(한번 지정해 놓으면 계속 사용가능)
서버 시작
flask run
서버 연결 끊기
ctrl + c
Requirement 1 - Be Able to GET Blog Post Items
1. 데이터베이스에서 모든 데이터를 가져오려면 db.session.query(BlogPost).all()을 사용한다. html에서 사용하기 위해 all_posts 파라미터에 DB에서 가져온 데이터를 값으로 넣어준다.
main.py
##CONFIGURE TABLE
class BlogPost(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(250), unique=True, nullable=False)
subtitle = db.Column(db.String(250), nullable=False)
date = db.Column(db.String(250), nullable=False)
body = db.Column(db.Text, nullable=False)
author = db.Column(db.String(250), nullable=False)
img_url = db.Column(db.String(250), nullable=False)
~생략~
@app.route('/')
def get_all_posts():
# Fetch objects from database
posts = db.session.query(BlogPost).all()
return render_template("index.html", all_posts=posts)
2-1. html에서 all_posts = posts 는 객체를 담은 리스트 형태이다. 따라서 반복을 줄이고자 for 문을 사용한다. {% for post in all_posts %}
index.html
{% for post in all_posts %}
<div class="post-preview">
<a href="{{ url_for('show_post', index=post.id) }}">
<h2 class="post-title">
{{post.title}}
</h2>
<h3 class="post-subtitle">
{{post.subtitle}}
</h3>
</a>
<p class="post-meta">Posted by
<a href="#">{{post.author}}</a>
on {{post.date}}</p>
</div>
<hr>
{% endfor %}
2-2 포스트 제목,소제목을 눌렀을 때 해당 포스트 페이지로 보내주려면 a href {{ url_for('show\_post', index=post.id) }}을 이용해 링크로 연결해주어야한다.
보내줄 페이지는 show_post함수이고 파라미터로 포스트 아이디를 갖는다. index 파라미터 값으로 show_post에서 지정한 post를 받는다.
@app.route("/post/<int:index>")
def show_post(index):
requested_post = BlogPost.query.get(index)
return render_template("post.html", post=requested_post)
Requirement 2 - Be Able to POST a New Blog Post
새로운 블로그 포스트 만들기
1. /new_post는 GET과 POST method를 가진다. (선)POST가 폼을 가져오면, (후)GET은 입력받은 데이터를 DB에 저장하고 '/' url로 이동한다.
2. #GET에서 if form.validate_on_submit(): 으로 입력받은 form이 form 클래스에서 지정한 validators를 통과했는지 알 수 있다. 만약 통과 됐다면 DB에 새로운 row를 만들기 위해 new_post = BlogPost(title=form.title.data ...) 으로 form에서 입력받은 데이터를 넣어준다. db.session.add(new_post) 와 commit()으로 DB에 저장한다.
3. HTML파일에서 Jinja2 템플릿을 바꿀 때마다 서버를 재시작해줘야 변화가 적용된다.
##WTForm
class CreatePostForm(FlaskForm):
title = StringField("Blog Post Title", validators=[DataRequired()])
subtitle = StringField("Subtitle", validators=[DataRequired()])
author = StringField("Your Name", validators=[DataRequired()])
img_url = StringField("Blog Image URL", validators=[DataRequired(), URL()])
body = CKEditorField("Blog Content", validators=[DataRequired()])
submit = SubmitField("Submit Post")
~생략~
@app.route("/new_post", methods=["GET", "POST"])
def new_post():
form = CreatePostForm()
# GET
if form.validate_on_submit():
new_post = BlogPost(
title=form.title.data,
subtitle=form.subtitle.data,
body=form.body.data,
img_url=form.img_url.data,
author=form.author.data,
date=date.today().strftime("%B %d, %Y")
)
db.session.add(new_post)
db.session.commit()
return redirect(url_for("get_all_posts"))
# POST
return render_template("make-post.html", form=form)
4. html에서 wtform을 사용하려면 import해줘야 한다. {% import "bootstrap/wtf.html" as wtf %}
{{ wtf.quick_form(form, novalidate=True) }} 에서 form 은 new_post()에서 받아온 값이다. form=form
5. CKeditor를 사용하려면 {{ ckeditor.load() }} 로 로드해주고, {{ ckeditor.config(name='body') }} config에서 ckeditor를 사용할 요소를 지정해준다. name='body'
CKeditor Useful Docs:
https://flask-ckeditor.readthedocs.io/en/latest/basic.html
https://pythonhosted.org/Flask-Bootstrap/forms.html
https://flask-wtf.readthedocs.io/en/stable/
make-post.html
{% import "bootstrap/wtf.html" as wtf %}
~생략~
<!--Load ckeditor-->
{{ ckeditor.load() }}
{{ ckeditor.config(name='body') }}
<!-- Add WTF quickform-->
{{ wtf.quick_form(form, novalidate=True) }}
Requirement 3 - Be Able to Edit Existing Blog Posts
포스트 수정하기
1. 포스트를 보여주는 post.html 의 마지막 부분에는 Edit Post버튼이 있다. 이 버튼을 누르면 포스트 수정화면으로 넘어간다. {{url_for('edit_post', post_id=post.id)}} 이 버튼은 /edit-post/<post_id> 라우트에 GET request를 만든다.
<a class="btn btn-primary float-right" href="{{url_for('edit_post', post_id=post.id)}}">Edit Post</a>
2. edit_post(post_id)는 GET, POST method를 가진다. POST는 DB에서 수정할 포스트를 불러온다. 이 때 form에 불러온 포스트의 내용이 미리 담겨있어야 한다.
@app.route("/edit_post/<post_id>", methods=["GET", "POST"])
def edit_post(post_id):
post_to_edit = BlogPost.query.get(post_id)
edit_form = CreatePostForm(
title=post_to_edit.title,
subtitle=post_to_edit.subtitle,
img_url=post_to_edit.img_url,
author=post_to_edit.author,
body=post_to_edit.body
)
#GET: save changes in edited post
if edit_form.validate_on_submit(): #request.method == "GET":
post_to_edit.title = edit_form.title.data
post_to_edit.subtitle = edit_form.subtitle.data
post_to_edit.body = edit_form.body.data
post_to_edit.img_url = edit_form.img_url.data
post_to_edit.author = edit_form.author.data
post_to_edit.date = post_to_edit.date
db.session.commit()
return redirect(url_for("show_post", index=post_id))
#POST: fetch post to edit
return render_template("make-post.html", form=edit_form, is_edit=True)
3. 수정 할 포스트를 query.get()으로 가져온다. form 객체(edit_form)를 만들고 불러온 포스트의 내용을 form에 넣어준다. (예시title=post_to_edit.title) 받아온 데이터를 form=edit_form 파라미터로 html 템플릿에 넘겨준다.
4. is_edit=True 파라미터와 jinja if문을 사용해 "make-post.html" 템플릿을 공유하는 /new-post 라우트(빈 form)와 /edit-post/ 라우트(불러온 포스트로 채워진 form)을 구분시켜준다.
{% import "bootstrap/wtf.html" as wtf %}
~생략~
{% if is_edit: %}
{{ wtf.quick_form(form, novalidate=True) }}
{% else: %}
{{ wtf.quick_form(form, novalidate=True) }}
{% endif %}
5.
#GET에서 if form.validate_on_submit(): 이 True이면 DB에 있던 기존 데이터를 업데이트 해준다.
앞의 new_post()와 다르게 edit_post()는 새로운 row를 저장하는 것이 아니라 업데이트 해주는 것이다. 따라서 db.session.add(new_post) 와 commit()으로 DB에 저장하지 않고, post_to_edit.title = edit_form.title.data 객체에 입력받은 새 값을 넣어준뒤 add()하지 않고 바로 db.session.commit() 한다.
#GET: save changes in edited post
if edit_form.validate_on_submit(): #request.method == "GET":
post_to_edit.title = edit_form.title.data
post_to_edit.subtitle = edit_form.subtitle.data
post_to_edit.body = edit_form.body.data
post_to_edit.img_url = edit_form.img_url.data
post_to_edit.author = edit_form.author.data
post_to_edit.date = post_to_edit.date
db.session.commit()
return redirect(url_for("show_post", index=post_id))
Requirement 4- Be Able DELETE Blog Posts
1. 삭제 버튼 링크를 만든다.
<a href="{{ url_for('delete_post', post_id=post.id) }}">✘</a>
2. 삭제할 포스트를 DB에서 get()해준뒤 .delete(), .commit() 을 해주고 get_all_posts 라우트로 redirect해준다.
@app.route("/delete/<post_id>")
def delete_post(post_id):
post_to_delete = db.session.query(BlogPost).get(post_id)
db.session.delete(post_to_delete)
db.session.commit()
return redirect(url_for("get_all_posts"))
day-lee/RESTful-blog
Contribute to day-lee/RESTful-blog development by creating an account on GitHub.
github.com
'Python' 카테고리의 다른 글
Flask app: cafe_api (0) | 2021.07.05 |
---|---|
Flight Deal (0) | 2021.06.21 |
TIL Python Basics Day 30 - Errors, Exceptions and JSON Data (0) | 2021.06.17 |
파이썬 복습: Computational thinking (1) | 2021.06.10 |
Udemy Python Pro Bootcamp by Angela Yu (0) | 2021.06.07 |