from flask import Blueprint, render_template
from pybo.models import Question
bp = Blueprint('main', __name__, url_prefix='/')
@bp.route('/hello')
def hello_pybo():
return 'Hello, Pybo!'
@bp.route('/')
def index():
question_list = Question.query.order_by(Question.create_date.desc())
return render_template('question/question_list.html', question_list=question_list)
main_views.py 파일을 수정 해보겠습니다.
질문 목록은 Question.query.order_by로 얻을 수 있습니다. (order_by는 조회 결과를 정렬해줍니다.)
Question.create_date.desc() 코드는 작성일시 기준 역순으로 정렬하라는 의미입니다.
즉, 가장 최신 내용이 첫번째로 나오게 되고 가장 오래된 게 뒤에 나오게 되는 것이죠
하루전 글
3일전 글 .... 이런식이여야지
3일전 글
하루전 글... 이렇진 않잖아요?
question_list에 대해선 좀 이따 이야기하겠습니다.
render_template 함수는 템플릿 파일을 화면에 그려줍니다.
사용할 question/quesiton_list.html 템플릿 파일을 작성해야 합니다.
이 파일은 template라는 디렉터리에 저장하면 별 다른 설정 하지 않아도
template 디렉터리를 템플릿 디렉터리로 인식합니다.
cmd로 디렉터리를 만들어줬고 파이참에서 html파일을 그 안에 만들어 줬습니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% if question_list %}
<ul>
{% for question in question_list %}
<li>
<a href="/detail/{{question.id}}/">{{question.subject}}</a>
</li>
{% endfor %}
</ul>
{% else %}
<p> 질문이 없습니다. </p>
{% endif %}
</body>
</html>
{% %} 으로 둘러싸인 문장을 탬플릿 태그라고 합니다. JSP같은 느낌입니다. 이 태그가 파이썬 코드와 연결 됩니다.
{% if question_list %}
question_list 가 있는지 확인 합니다. 있으면 True값을 반환해서 참값인 부분에 들어가겠죠?
{% for question in question_list %}
for 문 입니다. question_list값이 하나씩 question에 들어가게 되는 것이죠
{% else %}
말 그대로 else문 입니다.
{% endif %}
if문이 끝나는 곳을 알려줍니다.
@bp.route('/')
def index():
question_list = Question.query.order_by(Question.create_date.desc())
return render_template('question/question_list.html', question_list=question_list)
여기에서 그럼 question_list는 뭐냐고 물으신다면 바로 여기입니다. question_list = question_list
question_list는 넘겨 빈 값을 의미하고 question_list는 Question.query로 조회한 질문 DB 내용입니다.
자주 사용하는 템플릿 태그에 대해서 알아보도록 하겠습니다.
1. if, elif, else문
{% if 조건문1 %}
<p> 조건문1에 해당하면 실행 </p>
{% elif 조건문2 %}
<p> 조건문2에 해당하면 실행 </p>
{% else %}
<p> 모두 해당하지 않으면 실행 </p>
{% endif %}
그냥 보시면 공부하셨으니까 알 거라고 생각합니다. 꼭 endif로 닫아줘야 합니다.
2. 반복문 태그
{% for item in list %}
<p> 순서 : {{ loop.index }} </p>
<p> {{ item }} </p>
{% endfor %}
반복문도 공부하셨으면 구조가 아주 유사해서 잘 아실 겁니다.
loop.index는 반복 순서를 나타냅니다. 1부터 1씩 증가
loop.index()는 반복 순서를 나타냅니다. 0부터 1씩 증가
이 외에도 여러 가지가 있지만 여기까지만 하겠습니다.
3. 객체 태그
{{ 객체 }}
{{ 객체.속성 }}
이렇게 객체나 객체 속성을 출력하거나 그 안에서 계산도 가능합니다.
변수는 어떤가요? 이러는데 파이썬은 객체로만 이루어진 프로그래밍 언어라서 변수 취급하는 것도 다 가능합니다.
나머지는 하면서 더 알아보도록 하겠습니다.
<a href="/detail/{{question.id}}/">{{question.subject}}</a>
이제 여기에 연결될 부분을 구현하겠습니다.
먼저 라우트 함수를 구현해야 하죠
from flask import Blueprint, render_template
from pybo.models import Question
bp = Blueprint('main', __name__, url_prefix='/')
@bp.route('/hello')
def hello_pybo():
return 'Hello, Pybo!'
@bp.route('/')
def index():
question_list = Question.query.order_by(Question.create_date.desc())
return render_template('question/question_list.html', question_list=question_list)
@bp.route('/detail/<int:question_id>/')
def detail(question_id) :
question = Question.query.get_or_404(question_id)
return render_template('question/question_detail.html', question=question)
main_views.py를 수정해줍시다.
@bp.route('/detail/<int:question_id>/')
<자료형 : 테이블 속성명>
db에서 가져올 속성 이름과 자료형을 같이 적어줘야 합니다.
Question.query.get_or_404는 get으로 가져오는 방식과 동일한데 만약 값이 없을 때 get은 빈 값을 넣어주지만
get_or_404의 경우는 없으면 404 에러 페이지를 띄웁니다.
get으로 빈 페이지를 보여주는 것보단 404 에러페이지를 띄우는게 더 좋겠죠?
render_template을 이용해 templates 폴더에 있는 question_detail.html파일을 가져옵니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1> {{ question.subject }} </h1>
<div>
{{ question.content }}
</div>
</body>
</html>
question_detail.html파일을 코딩하겠습니다.
1번은 저번에 삭제해서 404가 나옵니다.
이제 블루프린트로 파일을 분리해서 관리하겠습니다.
question_views.py 파일을 새로 만들고 질문 목록 조회와 질문 상세 조회 기능을 이동하겠습니다.
from flask import Blueprint, render_template
from pybo.models import Question
bp = Blueprint('question', __name__, url_prefix='/question')
@bp.route('/list')
def _list():
question_list = Question.query.order_by(Question.create_date.desc())
return render_template('question/question_list.html', question_list=question_list)
@bp.route('/detail/<int:question_id>')
def detail(question_id):
question = Question.query.get_or_404(question_id)
return render_template('question/question_detail.html', question=question)
question_views.py 입니다.
from flask import Flask
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
import config
db = SQLAlchemy()
migrate = Migrate()
def create_app():
app = Flask(__name__)
app.config.from_object(config) # config.py에 작성 내용을 환경 변수로 부르기 위한 작업
# ORM
db.init_app(app) # 우리가 적은 환경으로 DB 초기화
migrate.init_app(app,db) # DB와 우리가 적은 환경을 다른 환경으로 옮김
from . import models
from .views import main_views, question_views # .view에서 .은 현재위치의 views 폴더로부터 import한다는 말
app.register_blueprint(main_views.bp) # 블루프린트 객체 bp 등록
app.register_blueprint(question_views.bp) # 블루프린트 객체 bp 등록
return app
__init__.py입니다.
from flask import Blueprint, render_template, url_for
from werkzeug.utils import redirect
bp = Blueprint('main', __name__, url_prefix='/')
@bp.route('/hello')
def hello_pybo():
return 'Hello, Pybo!'
@bp.route('/')
def index():
return redirect(url_for('question._list'))
main_views.py입니다. 중복된 거를 빼고 index()를 수정했습니다.
question._list에 해당하는 URL로 redirect할 수 있게 수정했습니다. 입력받은 URL로 리다이렉트 해줍니다.
url_for에 있는 question._list는 question, _list 순서로 해석되어 함수명을 찾아줍니다.
question은 등록된 블루 프린트 이름을 의미하고
(bp = Blueprint('question', __name__, url_prefix='/question') 이 부분입니다.
_list는 블루 프린트에 등록된 함수명이라고 생각하면 됩니다.
@bp.route('/list')
def _list():
question_list = Question.query.order_by(Question.create_date.desc())
return render_template('question/question_list.html', question_list=question_list)
이 부분입니다.
@bp.route('/list/')이므로 bp의 접두어(url_prefix)인 /question/과 /list(라우트값)가 더해진
/question/list/ URL을 반환합니다.
즉, url_for('블루프린트 이름 . 등록된 함수명) 입니다.
그러면 저 부분은 render_template가 있기 때문에 저 html로 연결해줍니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% if question_list %}
<ul>
{% for question in question_list %}
<li>
<a href="{{ url_for('question.detail', question_id=question.id)}}">{{question.subject}}</a>
</li>
{% endfor %}
</ul>
{% else %}
<p> 질문이 없습니다. </p>
{% endif %}
</body>
</html>
하드코딩된 question_list.html에 부분도 수정하겠습니다
<a href="{{ url_for('question.detail', question_id=question.id)}}">{{question.subject}}</a>
이 부분을 수정했습니다. url_for를 템플릿태그로 쓰려면 {{ }} 안에다 적으시면 됩니다.
여기에서도 보면 question 블루프린트 이름에 detail이라는 함수로 연결해주는 것이죠
그리고 question_id값을 같이 보내줍니다.
여기에서 question은 question_list으로부터 담긴 부분입니다.
@bp.route('/list')
def _list():
question_list = Question.query.order_by(Question.create_date.desc())
return render_template('question/question_list.html', question_list=question_list)
question_list는 여기로부터 question_list=question_list로 위에 question_list.html로 보낸 것입니다.
이제 그걸 다시 url_for로 /question/detail/<int:question_id>로 보내는 것입니다.
question_id값도 같이 보냈으니 정확한 url값이 나오게 되겠죠?
url_for를 사용하는 장점은 유지보수가 쉬워집니다.
예를 들어 보겠습니다. (밑에 예시는 위에 내용하고 관계 없습니다.)
<a href="/question/detail/{{ question.id }}">{{ question.subject }}</a> 이렇게 하드 코딩이 되어 있으면
다음 과 같은 URL 구성방식 자체가 변경되면 이런 코드는 쉽게 대응하기 힘듭니다.
localhost:5000/detail/question/2 (기존)
localhost:5000/detail/2/question (바꿀 것)
이렇게 href가 여러개로 연결 되어 있는 부분이 있으면 하나씩 href 부분을 바꿔야합니다.
하지만 url_for를 이용하는 경우는 bp.route부분을 바꾸면 다른 부분은 바꿔줄 필요가 없습니다.
(bp = Blueprint('question', __name__, url_prefix='/detail')
@bp.route('/question/<int:question_id>') 이거를
@bp.route('/<int:question_id>/question') 이런식으로 바꾸면 되겠죠?
'플라스크 (추후 수정)' 카테고리의 다른 글
Flask CSS, 부트스트랩 적용해보기 (0) | 2021.08.11 |
---|---|
Flask 답변 등록, |length (0) | 2021.08.11 |
Flask 플라스크 셸을 이용해 DB 내용 추가 제거 수정해보기 (0) | 2021.08.10 |
Flask BluePrint, 라우트 함수 (0) | 2021.08.09 |
Flask 애플리케이션 팩토리 (0) | 2021.08.09 |