Flask app: RESTful-blog

Flask app: RESTful-blog

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 %} {{post.title}} {{post.subtitle}} Posted by {{post.author}} on {{post.date}} {% endfor %}

2-2 포스트 제목,소제목을 눌렀을 때 해당 포스트 페이지로 보내주려면 a href {{ url_for('show\_post', index=post.id) }}을 이용해 링크로 연결해주어야한다.

보내줄 페이지는 show_post함수이고 파라미터로 포스트 아이디를 갖는다. index 파라미터 값으로 show_post에서 지정한 post를 받는다.

@app.route("/post/") 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 %} ~생략~ {{ ckeditor.load() }} {{ ckeditor.config(name='body') }} {{ 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/ 라우트에 GET request를 만든다.

Edit Post

2. edit_post(post_id)는 GET, POST method를 가진다. POST는 DB에서 수정할 포스트를 불러온다. 이 때 form에 불러온 포스트의 내용이 미리 담겨있어야 한다.

@app.route("/edit_post/", 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 %}

make-post.html" 템플릿을 공유하는 /new-post 라우트(왼쪽)와 /edit-post/ 라우트 (오른쪽)

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. 삭제 버튼 링크를 만든다.

2. 삭제할 포스트를 DB에서 get()해준뒤 .delete(), .commit() 을 해주고 get_all_posts 라우트로 redirect해준다.

@app.route("/delete/") 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"))

깃허브 링크

from http://dayleeand.tistory.com/48 by ccl(A) rewrite - 2021-07-07 17:00:29