본문 바로가기

MLOps 개발자 양성과정/flask

[Day-76] 플라스크① 개발기초

https://wikidocs.net/book/4542

 

 

mkdir 

md

 

dir => 디렉토리 확인

ls(리눅스에서)

 

dir v* => v로 시작하는 디렉토리

dir /p => 페이지씩 보여줘

dir /s  => 서브디렉토리 

dir /s/p => 페이지

 

가상환경 

배치파일 만드는 방법

확장자명 .cmd

# 메모장
@echo off
cd c:/projects/myproject
c:/venvs/myproject/scripts/activate

 

일일이 가상환경 들어가기 귀찮아

환경변수 찾아가는 방법

window r

sysdm.cpl

고급에서 시스템 변수의 path 수정

 

플라스크라는 웹서버를 구동시키겠다

flask run

 


1장 플라스크 개발준비!

 
# 플라스크 클래스
from flask import Flask

#플라스크 객체 생성
app = Flask(__name__)

# 어노테이션? 플라스크 객체가 가지고 있는 route함수(경로를 지정해 줌)
@app.route('/')
def hello_pybo():
    return 'Hello, pybo!'

경로 두 가지야

- local 경로와

- url경로

 

=>route함수가 사용하는 경로는 url경로

 

플라스크 서버 실행하기

(myproject) c:\projects\myproject> flask run
Usage: flask run [OPTIONS]
Try 'flask run --help' for help.

Error: Could not locate a Flask application. Use the 'flask --app' option, 'FLASK_APP' environment variable, or a 'wsgi.py' or 'app.py' file in the current directory.

(myproject) c:\projects\myproject>set FLASK_APP=pybo

(myproject) c:\projects\myproject> flask run
 * Serving Flask app 'pybo'
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
# 플라스크 서버 중지하고 싶으면
Press CTRL+C to quit

# 디버그 모드 활성화
(myproject) c:\projects\myproject>set FLASK_DEBUG=true

(myproject) c:\projects\myproject>flask run
 * Serving Flask app 'pybo'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 249-664-470

route('/') 경로

C:\projects\myproject

myproject위치가 127.0.0.1

http://127.0.0.1:5000

 

Flask 구동되기 시작하면 app.py를 먼저 찾아

app.py가 main 파일 => 전체 프로젝트에 하나만 존재해야 함

app.py 없어? set으로 설정해둬

set FLSAK_APP=pybo

 


chap2 

2장 플라스크 개발 기초 공사!

 

데이터베이스를 처리하는 models.py 파일

ORM 파이썬 데이터베이스 도구인 SQLAlchemy를 사용

models.py파일 필요

서버로 전송된 폼을 처리하는 forms.py 파일

WTForms라는 라이브러리 

forms.py 파일

화면을 구성하는 views 디렉터리

꾸며주는 파이썬 파일들

CSS, 자바스크립트, 이미지 파일을 저장하는 static 디렉터리

HTML 파일을 저장하는 templates 디렉터리

실제 HTML파일 넣어놔

파이보 프로젝트를 설정하는 config.py 파일

파이보 프로젝트의 환경을 설정

 

2-02 플라스크 애플리케이션 팩토리

 

플라스크 app객체를 전역으로 사용하면 프로젝트 규모가 커질수록 문제가 발생할 확률 높아져(순환참조 오류)

-> application factory 사용하기

팩토리는 기본적으로 interface의 개념이 들어가 있어
코드 design pattern -> 기본적으로 Singleton 패턴

자바 interface??
=> 변수x 메소드x 생성자 x

상수는 선언o
메소드명은 사용할 수 있어 (그마저도 추상메소드)
메소드에 대한 재정의 해야 해

 

(myproject) c:\projects\myproject>mkdir pybo

(myproject) c:\projects\myproject>dir
 C 드라이브의 볼륨에는 이름이 없습니다.
 볼륨 일련 번호: FA4E-6478

 c:\projects\myproject 디렉터리

2023-04-11  오전 11:41    <DIR>          .
2023-04-11  오전 11:41    <DIR>          ..
2023-04-11  오전 10:34    <DIR>          .idea
2023-04-11  오전 11:41    <DIR>          pybo
2023-04-11  오전 11:40               265 pybo.py
2023-04-11  오전 10:28    <DIR>          venv
2023-04-11  오전 11:40    <DIR>          __pycache__
               1개 파일                 265 바이트
               6개 디렉터리  126,014,287,872 바이트 남음

# move 도스 명령어
# pybo.py 파일을 pybo폴더에 __init__.py파일로 이름을 바꿔서 이동시키자
(myproject) c:\projects\myproject> move pybo.py pybo/__init__.py
        1개 파일을 이동했습니다.

(myproject) c:\projects\myproject>dir
 C 드라이브의 볼륨에는 이름이 없습니다.
 볼륨 일련 번호: FA4E-6478

 c:\projects\myproject 디렉터리

2023-04-11  오전 11:42    <DIR>          .
2023-04-11  오전 11:42    <DIR>          ..
2023-04-11  오전 10:34    <DIR>          .idea
2023-04-11  오전 11:42    <DIR>          pybo
2023-04-11  오전 10:28    <DIR>          venv
2023-04-11  오전 11:40    <DIR>          __pycache__
               0개 파일                   0 바이트
               6개 디렉터리  126,014,140,416 바이트 남음

[중요]!! flask run 명령은 반드시 프로젝트 홈 디렉터리(C:/projects/myproject)에서 실행해야 함!!

다른 곳에서 하면 실행은 되지만 디렉토리를 못찾아 구동이 안됨!!

 

2-03 블루프린트로 라우팅 함수 관리하기

블루프린트  라우트 함수들을 체계적으로 관리해주는 모듈
from flask import Blueprint

bp = Blueprint('main', __name__, url_prefix='/')
# 블루프린트에 대한 구별 이름(별칭) , __name__(파이썬에서 제공해주는 이름 수정불가!!), url에 대한 기본경로
# __name__ 에 모듈명인 main_views가 전달(하나의 파일이 모듈이야)


@bp.route('/')
def hello_pybo():
    return 'Hello, Pybo!'

# 호출할 때 localhost:5000/

 

 

# 블루프린트 등록
# main_views.py 파일에 생성한 블루프린트 객체 bp를 app.register_blueprint(main_views.bp)로 등록

from flask import Flask

def create_app():
    app = Flask(__name__)

# 현재 내위치 .
# ~~/myproject/pybo/__init__.py/views/main_views.py
    from .views import main_views
    app.register_blueprint(main_views.bp) # main_views 모듈안에 있는 bp객체를 등록하겠다
    
    # 실행구조 .views를 찾아 main_views 모듈에 오류가 없어야 그래야지 등록 완료
    
    return app

 

Flask 서버라고 생각해

web --------- myproject

브라우저에서 로컬 위치 127.0.0.1 치면

5000번 포트를 사용하고 있는 flask로

기본 app을 pybo로 설정해둠

 

 

통신할 수 있는 포트번호 5000번

 

 

2-04 모델로 데이터 처리하기

 

데이터베이스를 사용하려면 SQL 쿼리(query)라는 구조화된 질의를 작성하고 실행하는 등의 복잡한 과정이 필요하다. 이때 ORM(object relational mapping)을 이용하면 파이썬 문법만으로도 데이터베이스를 다룰 수 있다. 즉, ORM을 이용하면 개발자가 쿼리를 직접 작성하지 않아도 데이터베이스의 데이터를 처리할 수 있다.

(자바쪽에서는jpa)

 

sql에서 record

data에서 sample

id auto increment

# 일반 쿼리
insert into question (subject, content) values ('안녕하세요', '가입 인사드립니다 ^^');
insert into question (subject, content) values ('질문 있습니다', 'ORM이 궁금합니다');

# ORM
question1 = Question(subject=’안녕하세요’, content='가입 인사드립니다 ^^')
db.session.add(question1)
question2 = Question(subject=’질문 있습니다’, content='ORM이 궁금합니다')
db.session.add(question2)
mysql
>>> auto_increments

oracle
>>> 시퀀스 다시 만들어 줘야 해..

>>> 호환이 어려워 ORM 많이 사용


ORM을 이용하면 데이터베이스 종류에 상관 없이 일관된 코드를 유지할 수 있어서 프로그램을 유지·보수하기가 편리하다. 또한 내부에서 안전한 SQL 쿼리를 자동으로 생성해 주므로 개발자가 달라도 통일된 쿼리를 작성할 수 있고 오류 발생률도 줄일 수 있다.

 

모델 만들기

MVC

(Model View Controller)

from pybo import db


# Quetion이 이제 테이블명 db.Model을 상속받아
class Question(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    # 기본키로 설정한 db.Integer 데이터 타입 속성 값 자동으로 1씩 증가
    subject = db.Column(db.String(200), nullable=False)
    content = db.Column(db.Text(), nullable=False)
    create_date = db.Column(db.DateTime(), nullable=False)


class Answer(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    question_id = db.Column(db.Integer, db.Foreignkey('question.id', ondelete='CASCADE')) #질문이 삭제되면 같이 삭제
    question = db.relationship('Question', backref=db.backref('answer_set')) # 답변을 통해서 질문을 꺼내올 수도 있도록 역참조
    content = db.Column(db.Text(), nullable=False)
    create_date = db.Column(db.DateTime(), nullable=False)
위에서 "쿼리를 이용하여 질문 데이터를 삭제할 경우 답변도 함께 삭제된다"고 했는데 이는 정확히 말하면 데이터베이스 도구에서 쿼리를 이용하여 삭제하는 경우에 해당한다. 만약 
a_question.delete()
 처럼 파이썬 코드로 질문 데이터를 삭제하면 해당 질문과 연결된 답변 데이터는 삭제되지 않고 답변 데이터의 question_id 컬럼만 빈값으로 업데이트된다. 만약 파이썬 코드로 질문 데이터를 삭제할 때 연결된 답변 모두를 삭제하기 바란다면 다음처럼 
db.backref
 설정에 
cascade='all, delete-orphan'
를 추가해야 한다.
question = db.relationship('Question', backref=db.backref('answer_set', cascade='all, delete-orphan'))​

 

 

(myproject) c:\projects\myproject> flask shell
Python 3.11.3 (tags/v3.11.3:f3909b8, Apr  4 2023, 23:49:59) [MSC v.1934 64 bit (AMD64)] on win32
App: pybo
Instance: C:\projects\myproject\instance
# 플라스크 쉘 꺽세 세 개로 바뀜
>>> from pybo.models import Question, Answer
>>> from datetime import datetime
>>> q = Question(subject='pybo가 무엇인가요?', content='pybo에 대해서 알고 싶습니다.', create_date=datetime.now())
>>> from pybo import db
>>> db.session.add(q) # db 내부에 있는 session을 이용해서 만들어진 객체 추가
>>> db.session.commit() # 커밋 적용
>>>

>>> ctrl + z 플라스크 셀 종료

cookie 웹 브라우저쪽에 저장되는 임시저장소

session 서버 내부에 임시저장소

 

session.commit까지 해야지 저장됨

Flask / SQLite

 

수행한 작업을 취소하려면 커밋 이전에 진행해야 한다. 이때 작업을 취소하고 싶으면 db.session.rollback()으로 되돌리기(롤백)를 실행하면 된다.

>>> Question.query.filter(Question.id==1).all()
[<Question 1>]
>>>

# SQL에서 filter 같은 역할
select * from 
where id == 1

# get 함수
>>> Question.query.get(1)
<Question 1>

# like 함수
>>> Question.query.filter(Question.subject.like('%플라스크%')).all()
[<Question 2>]
# 답변을 통해서 질문 얻어내기
>>> a.question
<Question 2>

# 질문을 통해서 답변 얻어내기
>>> q.answer_set
[<Answer 1>]
>>>

파이썬 코드

# html 내부에 파이썬 코드가 있으면 결과값을 실행시켜서 브라우저로 쏴주기 위해 render_template

flask server {% %} 

<!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>

객체를 출력하려면

{{ 객체 }}

{{ 객체.속성 }}