반응형

 

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_listQuestion.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_404get으로 가져오는 방식과 동일한데 만약 값이 없을 때 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._listquestion, _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') 이런식으로 바꾸면 되겠죠?

 

 

반응형