๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Tech Stack/Flask

flask - back-end๋ฅผ MVCํŒจํ„ด์œผ๋กœ ๊ตฌํ˜„

https://dev.to/themeselection/the-best-python-web-frameworks-d2d

 

The Best Python Web Frameworks 2024๐Ÿคฉ

Want to kickstart your journey as a Python developer? Then you are in the right place. Here you’ll...

dev.to

 

MVC (๋ชจ๋ธ-๋ทฐ-์ปจํŠธ๋กค๋Ÿฌ) ๋Š” ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค, ๋ฐ์ดํ„ฐ ๋ฐ ๋…ผ๋ฆฌ ์ œ์–ด๋ฅผ ๊ตฌํ˜„ํ•˜๋Š”๋ฐ ๋„๋ฆฌ ์‚ฌ์šฉ๋˜๋Š” ์†Œํ”„ํŠธ์›จ์–ด ๋””์ž์ธ ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ์†Œํ”„ํŠธ์›จ์–ด์˜ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ ํ™”๋ฉด์„ ๊ตฌ๋ถ„ํ•˜๋Š”๋ฐ ์ค‘์ ์„ ๋‘๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ "๊ด€์‹ฌ์‚ฌ ๋ถ„๋ฆฌ" ๋Š” ๋”๋‚˜์€ ์—…๋ฌด์˜ ๋ถ„๋ฆฌ์™€ ํ–ฅ์ƒ๋œ ๊ด€๋ฆฌ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

 

 

 

 


์šฐ์„  ๋ฐฑ์—”๋“œ๋ฅผ ๊ตฌ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ๊ตฌ์กฐ

 

 


# pip๋ฅผ ํ†ตํ•ด ์ตœ์†Œํ•œ์˜ ๊ฐ€์ƒํ™˜๊ฒฝ์„ ์ƒ์„ฑํ•˜๋Š” ๋ช…๋ น์–ด
$ python -m venv myproject

 

๋‹ค์Œ์„ ํ†ตํ•ด myproject๋ผ๋Š” ๊ฐ€์ƒํ™˜๊ฒฝ์„ ๋งŒ๋“ ๋‹ค.

# myproject.cmd ์ƒ์„ฑ

@echo off
set FLASK_APP=test
set FLASK_DEBUG=true
C:\ITStudy\09_flask\fisa-flask\myproject\Scripts\activate.bat

๋‹ค์Œ์„ ํ†ตํ•ด ๋ฐ”๋กœ init์„ ์ฝ์„ ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ ๋‹ค.

๊ฐ„๋‹จํ•˜๊ฒŒ ๋ฐฑ์—”๋“œ ๊ตฌ์„ฑ

def create_app():
     app = Flask(__name__)

     # URL๊ณผ FLASK์ฝ”๋“œ๋ฅผ ๋งคํ•‘ํ•˜๋Š” Flask ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ
     # @app.route์ฒ˜๋Ÿผ ์• ๋…ธํ…Œ์ด์…˜์œผ๋กœ URL์„ ๋งคํ•‘ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋ผ์šฐํŒ… ํ•จ์ˆ˜๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค.
     @app.route('/')
     def hello():
          return f'Hello, {__name__}'

     @app.route('/jeongwoo')
     def hello_jeongwoo():
          return f'Hello, jeongwoo'
    
     return app

 

## test/__init__.py
def create_app(): # ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ํŒฉํ† ๋ฆฌ - ํ”Œ๋ผ์Šคํฌ ์„œ๋ฒ„๊ฐ€ ์‹คํ–‰๋  ๋•Œ ๊ฐ€์žฅ ์ตœ์ดˆ๋กœ ์‹คํ–‰๋˜๋Š” ์ƒ์„ฑ์ž
    test = Flask(__name__)

    # ORM
    test.config.from_object(config)
    db.init_app(test)
    migrate.init_app(test, db)

    # ๋ธ”๋ฃจํ”„๋ฆฐํŠธ
    from .views import main_views  # views ํด๋” ๋ฐ์˜ main_views.py ์ž„ํฌํŠธ
    test.register_blueprint(main_views.bp)
    
    return test

 

models.py ์ƒ์„ฑ

from test import db

class Question(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    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)

 

config.py

import os

BASE_DIR = os.path.dirname(__file__)

SQLALCHEMY_DATABASE_URI = 'sqlite:///{}'.format(os.path.join(BASE_DIR, 'test.db'))
SQLALCHEMY_TRACK_MODIFICATIONS = False

 

 

## flask shell
>>> from test.models import Question
>>> from datetime import datetime
>>> q = Question(subject="์ œ๋ชฉ์ž…๋‹ˆ๋‹ค", content="๋‚ด์šฉ์ž…๋‹ˆ๋‹ค", create_date=datetime.now())
>>> q
<Question (transient 2955131385264)>
>>> from test import db                                                           
>>> db.session.add(q)         
>>> db.session.commit()
>>> q
<Question 1>
>>> q.id
1
>>> q.content
'๋‚ด์šฉ์ž…๋‹ˆ๋‹ค'
>>> q.subject
'์ œ๋ชฉ์ž…๋‹ˆ๋‹ค'
>>> Question.query.all()
[<Question 1>]
>>> Question.query.filter(Question.id==1).all()
[<Question 1>]
>>> q

 

ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ…

๋‹ค์‹œ ํ•˜๋‹ˆ๊นŒ ๋๋‹ค..? update๋ฅผ ๋‹ค์‹œ ํ•ด์ฃผ๋‹ˆ๊นŒ insert๊ฐ€ ๋จ

 

 

๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ™•์ธ ๊ฐ€๋Šฅ

 

db ์ถ”๊ฐ€ ๊ฐ€๋Šฅ

 

์ด๋Ÿฐ์‹์œผ๋กœ db์˜ ๋‚ด์šฉ์„ ์ฐพ์„ ์ˆ˜ ์žˆ์Œ

>>> q = Question.query.get(2)                        
>>> q
<Question 2>
>>> q.subject
'์ œ๋ชฉ์ž…๋‹ˆ๋‹ค2'
>>> q = Question.query.filter(Question.subject.like("%๋‚ ์”จ%")).get() 
Traceback (most recent call last):
  File "<console>", line 1, in <module>
TypeError: Query.get() missing 1 required positional argument: 'ident'
>>> q = Question.query.filter(Question.subject.like("%๋‚ ์”จ%")).all() 
>>> q
[<Question 4>]
>>> q[0]
<Question 4>
>>> q[0].subject
'๋‚ ์”จ๊ฐ€ ์ถ”์œ„์ž…๋‹ˆ๋‹ค'
>>> q[0].subject = "๋‚ ์”จ๊ฐ€ ๋งค์šฐ ์ถฅ์Šต๋‹ˆ๋‹ค"
>>> q[0].subject                         
'๋‚ ์”จ๊ฐ€ ๋งค์šฐ ์ถฅ์Šต๋‹ˆ๋‹ค'
>>> db.session.commit()
>>> q
[<Question 4>]
>>> db.session.delete(q)
Traceback (most recent call last):
  File "C:\ITStudy\09_flask\fisa-flask\myproject\lib\site-packages\sqlalchemy\orm\session.py", line 3478, in delete
    state = attributes.instance_state(instance)
AttributeError: 'list' object has no attribute '_sa_instance_state'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "C:\ITStudy\09_flask\fisa-flask\myproject\lib\site-packages\sqlalchemy\orm\scoping.py", line 672, in delete
    return self._proxied.delete(instance)
  File "C:\ITStudy\09_flask\fisa-flask\myproject\lib\site-packages\sqlalchemy\orm\session.py", line 3480, in delete
    raise exc.UnmappedInstanceError(instance) from err
sqlalchemy.orm.exc.UnmappedInstanceError: Class 'builtins.list' is not mapped     
>>> db.session.delete(q[0])
>>> db.session.commit()     

- ์—ฌ๋Ÿฌ๋ถ„์˜ ์ด๋ฆ„์„ question ํ…Œ์ด๋ธ”์˜ content ์†์„ฑ์— ๋„ฃ์–ด์„œ 1๊ฐœ ๊ธ€์„ ์ž‘์„ฑํ•˜์„ธ์š”
- filter๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ทธ ๊ธ€์„ ๊ฐ€์ ธ์™€๋ณด์„ธ์š”
- ๊ธ€์˜ content๋ฅผ '์‹ ์งฑ๊ตฌ'๋กœ ๋ณ€๊ฒฝํ•ด๋ณด์„ธ์š”
- ๊ทธ ๊ธ€์„ ์‚ญ์ œํ•ด์ฃผ์„ธ์š”

>>> db.session.delete(q[0]) 
>>> db.session.commit()     
>>> a = Answer(question_id=1, content="1๋ฒˆ ๊ธ€์— ๋Œ€ํ•œ ๋‹ต๋ณ€์ž…๋‹ˆ๋‹ค", create_date=datetime.now())
>>> a
<Answer (transient 2955131376432)>
>>> db.session.add(a)   
>>> db.session.commit()  
>>> a
<Answer 1>
>>> a.question_id
1
>>> a.question   
<Question 1>
>>> a.question.content
'๋‚ด์šฉ์ž…๋‹ˆ๋‹ค'
>>> a.question.subject
'์ œ๋ชฉ์ž…๋‹ˆ๋‹ค'
>>> q = a.question    
>>> db.session.delete(q[0]) 
>>> db.session.commit()     
>>> a = Answer(question_id=1, content="1๋ฒˆ ๊ธ€์— ๋Œ€ํ•œ ๋‹ต๋ณ€์ž…๋‹ˆ๋‹ค", create_date=datetime.now())
>>> a
<Answer (transient 2955131376432)>
>>> db.session.add(a)   
>>> db.session.commit()  
>>> a
<Answer 1>
>>> a.question_id
1
>>> a.question   
<Question 1>
>>> a.question.content
'๋‚ด์šฉ์ž…๋‹ˆ๋‹ค'
>>> a.question.subject
'์ œ๋ชฉ์ž…๋‹ˆ๋‹ค'
>>> q = a.question    
>>> q.answer_set                                                                  
[<Answer 1>]
>>> a = Answer(question_id=1, content="๋‘๋ฒˆ์งธ 1๋ฒˆ ๊ธ€์— ๋Œ€ํ•œ ๋‹ต๋ณ€์ž…๋‹ˆ๋‹ค", create_date=datetime.now())
>>> a
<Answer (transient 2955131818416)>
>>> db.session.add(a)
>>> db.session.commit()  
>>> q.answer_set
[<Answer 1>, <Answer 2>]
>>> q.answer_set[1].content
'๋‘๋ฒˆ์งธ 1๋ฒˆ ๊ธ€์— ๋Œ€ํ•œ ๋‹ต๋ณ€์ž…๋‹ˆ๋‹ค'

 

๋‹ค์Œ๊ณผ ๊ฐ™์ด Answer๋„ ์ž…๋ ฅ์ด ๊ฐ€๋Šฅ

>>> a = Answer(question_id=1, content="1๋ฒˆ ๊ธ€์— ๋Œ€ํ•œ ๋‹ต๋ณ€์ž…๋‹ˆ๋‹ค", create_date=datetime.now())
>>> a
<Answer (transient 2955131376432)>
>>> db.session.add(a)   
>>> db.session.commit()  
>>> a
<Answer 1>
>>> a.question_id
1
>>> a.question   
<Question 1>
>>> a.question.content
'๋‚ด์šฉ์ž…๋‹ˆ๋‹ค'
>>> a.question.subject
'์ œ๋ชฉ์ž…๋‹ˆ๋‹ค'
>>> q = a.question    
>>> q.answer_set                                                                  
[<Answer 1>]
>>> a = Answer(question_id=1, content="๋‘๋ฒˆ์งธ 1๋ฒˆ ๊ธ€์— ๋Œ€ํ•œ ๋‹ต๋ณ€์ž…๋‹ˆ๋‹ค", create_date=datetime.now())
>>> a
<Answer (transient 2955131818416)>
>>> db.session.add(a)
>>> db.session.commit()  
>>> q.answer_set
[<Answer 1>, <Answer 2>]
>>> q.answer_set[1].content
'๋‘๋ฒˆ์งธ 1๋ฒˆ ๊ธ€์— ๋Œ€ํ•œ ๋‹ต๋ณ€์ž…๋‹ˆ๋‹ค'

 

๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

https://github.com/DINOQOS/fisa-flask 

 

GitHub - DINOQOS/fisa-flask

Contribute to DINOQOS/fisa-flask development by creating an account on GitHub.

github.com

 

 

 

 

 



ํ•˜๋ฉด์„œ ํ•„์š”ํ–ˆ๋˜ ๋ช…๋ น์–ด ์ •๋ฆฌ

# pip๋ฅผ ํ†ตํ•ด ์ตœ์†Œํ•œ์˜ ๊ฐ€์ƒํ™˜๊ฒฝ์„ ์ƒ์„ฑํ•˜๋Š” ๋ช…๋ น์–ด
$ python -m venv myproject
$ cd myproject\Scripts
C:\itstudy\06_flask\flak01\myproject\Scripts> activate
$ (myproject) C:\itstudy\06_flask\flak01\myproject\Scripts>

# ํŒŒ์ด์ฌ ๋‚ด์žฅ ๋ชจ๋“ˆ๋กœ ์›น ์„œ๋ฒ„ ์‹คํ–‰ํ•˜๊ธฐ
$ python -m http.server 8080

$ deactivate


$ python -m pip install --upgrade pip
$ pip install flask


### test.py
from flask import Flask

# app.py์ธ ๊ณณ์„ ์ž…๊ตฌ๋กœ ์ฐพ์•„์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค
# ๋˜๋Š” FLASK_APP์ด๋ผ๋Š” ํ™˜๊ฒฝ๋ณ€์ˆ˜์˜ ์ด๋ฆ„์„ ํŒŒ์ผ๋ช…์œผ๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค
# set FLASK_APP=test 
# wsgi.py์— ์ง์ ‘ ํ‚ค=๋ฐธ๋ฅ˜๋กœ ์—ฌ๋Ÿฌ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋“ค์„ ๊ธฐ์ž…ํ•ฉ๋‹ˆ๋‹ค.
app = Flask(__name__)

@app.route("/")
def hello():
    return f'Hello {__name__}'

# localhost:5000/bye ๋กœ ์ ‘์†ํ•˜๋ฉด bye ๋งŒ ์ถœ๋ ฅ๋˜๋„๋ก ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”


@echo off
set FLASK_APP=test
set FLASK_DEBUG=true
C:\ITStudy\09_flask\fisa-flask\myproject\Scripts\activate.bat


## ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํŒฉํ† ๋ฆฌ
$ mkdir test
$ move test.py test/__init__.py  # ๋งฅ์ด๋‚˜ ๋ฆฌ๋ˆ…์Šค์—์„œ๋Š” move ๋Œ€์‹  mv ๋ช…๋ น์–ด ์‚ฌ์šฉ
$ flask run



from flask import Flask

def create_app():
     app = Flask(__name__)

     # URL๊ณผ FLASK์ฝ”๋“œ๋ฅผ ๋งคํ•‘ํ•˜๋Š” Flask ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ
     # @app.route์ฒ˜๋Ÿผ ์• ๋…ธํ…Œ์ด์…˜์œผ๋กœ URL์„ ๋งคํ•‘ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋ผ์šฐํŒ… ํ•จ์ˆ˜๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค.
     @app.route('/')
     def hello():
          return f'Hello, {__name__}'

     @app.route('/yeonji')
     def hello_yeonji():
          return f'Hello, yeonji'
    
     return app


mkdir views
cd views


# projects/myproject/test/views/main_views.py

from flask import Blueprint

bp = Blueprint('main', __name__, url_prefix='/')


@bp.route('/')
     def hello():
          return f'Hello, {__name__}'
          
## __init__.py์— ์ถ”๊ฐ€

    from .views import main_views  # views ํด๋” ๋ฐ์˜ main_views.py ์ž„ํฌํŠธ
    test.register_blueprint(main_views.bp)
    
## views/main_views.py
from flask import Blueprint

bp = Blueprint('main', __name__, url_prefix="/main")

@bp.route("/")
def hello():
    return f'main์—์„œ ์ž‘์„ฑํ•œ Hello {__name__}'
    
    
## __init__.py
from flask import Flask

# app.py์ธ ๊ณณ์„ ์ž…๊ตฌ๋กœ ์ฐพ์•„์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค
# ๋˜๋Š” FLASK_APP์ด๋ผ๋Š” ํ™˜๊ฒฝ๋ณ€์ˆ˜์˜ ์ด๋ฆ„์„ ํŒŒ์ผ๋ช…์œผ๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค
# set FLASK_APP=test 
# wsgi.py์— ์ง์ ‘ ํ‚ค=๋ฐธ๋ฅ˜๋กœ ์—ฌ๋Ÿฌ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋“ค์„ ๊ธฐ์ž…ํ•ฉ๋‹ˆ๋‹ค.

def create_app(): # ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ํŒฉํ† ๋ฆฌ - ํ”Œ๋ผ์Šคํฌ ์„œ๋ฒ„๊ฐ€ ์‹คํ–‰๋  ๋•Œ ๊ฐ€์žฅ ์ตœ์ดˆ๋กœ ์‹คํ–‰๋˜๋Š” ์ƒ์„ฑ์ž
    test = Flask(__name__)

    from .views import main_views  # views ํด๋” ๋ฐ์˜ main_views.py ์ž„ํฌํŠธ
    test.register_blueprint(main_views.bp)
    
    return test
    
    
## test/views/main_views.py
from flask import Blueprint

bp = Blueprint('main', __name__, url_prefix="/")

@bp.route("/")
def hello():
    return f'main์—์„œ ์ž‘์„ฑํ•œ Hello {__name__}'

@bp.route("/bye")
def bye():
    return f'BYE'
  
  
https://github.com/YeonjiKim0316/fisa-ai-flask


from flask import Flask
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy

# app.py์ธ ๊ณณ์„ ์ž…๊ตฌ๋กœ ์ฐพ์•„์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค
# ๋˜๋Š” FLASK_APP์ด๋ผ๋Š” ํ™˜๊ฒฝ๋ณ€์ˆ˜์˜ ์ด๋ฆ„์„ ํŒŒ์ผ๋ช…์œผ๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค
# set FLASK_APP=test 
# wsgi.py์— ์ง์ ‘ ํ‚ค=๋ฐธ๋ฅ˜๋กœ ์—ฌ๋Ÿฌ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋“ค์„ ๊ธฐ์ž…ํ•ฉ๋‹ˆ๋‹ค.

import config

db = SQLAlchemy()
migrate = Migrate()


## test/__init__.py
def create_app(): # ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ํŒฉํ† ๋ฆฌ - ํ”Œ๋ผ์Šคํฌ ์„œ๋ฒ„๊ฐ€ ์‹คํ–‰๋  ๋•Œ ๊ฐ€์žฅ ์ตœ์ดˆ๋กœ ์‹คํ–‰๋˜๋Š” ์ƒ์„ฑ์ž
    test = Flask(__name__)

    # ORM
    test.config.from_object(config)
    db.init_app(test)
    migrate.init_app(test, db)

    # ๋ธ”๋ฃจํ”„๋ฆฐํŠธ
    from .views import main_views  # views ํด๋” ๋ฐ์˜ main_views.py ์ž„ํฌํŠธ
    test.register_blueprint(main_views.bp)
    
    return test
    
    
  # 
from test import db

class Question(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    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)


## flask shell
>>> from test.models import Question
>>> from datetime import datetime
>>> q = Question(subject="์ œ๋ชฉ์ž…๋‹ˆ๋‹ค", content="๋‚ด์šฉ์ž…๋‹ˆ๋‹ค", create_date=datetime.now())
>>> q
<Question (transient 2955131385264)>
>>> from test import db                                                           
>>> db.session.add(q)         
>>> db.session.commit()
>>> q
<Question 1>
>>> q.id
1
>>> q.content
'๋‚ด์šฉ์ž…๋‹ˆ๋‹ค'
>>> q.subject
'์ œ๋ชฉ์ž…๋‹ˆ๋‹ค'
>>> Question.query.all()
[<Question 1>]
>>> Question.query.filter(Question.id==1).all()
[<Question 1>]
>>> q
<Question (transient 2955131821776)>
>>> db.session.add(q)   
>>> db.session.commit() 
>>> Question.query.all()
[<Question 1>, <Question 2>]
>>> q = Question(subject="์ œ๋ชฉ์ž…๋‹ˆ๋‹ค", content="๋‚ด์šฉ์ž…๋‹ˆ๋‹ค", create_date=datetime.now())
>>> q1 = Question(subject="๋‚ ์”จ๊ฐ€ ์ถ”์œ„์ž…๋‹ˆ๋‹ค", content="๋‚ด์šฉ์ž…๋‹ˆ๋‹ค", create_date=datetime.now())
>>> q
<Question (transient 2955131387856)>
>>> q1
<Question (transient 2955131822592)>
>>> db.session.add(q)    
>>> db.session.add(q1) 
>>> db.session.commit()  
>>> Question.query.filter(Question.id==2).all() 
[<Question 2>]
>>> Question.query.filter(Question.id==2).all()
[<Question 2>]
>>> q = Question.query.filter(Question.id==2).all() 
>>> q.subject
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'list' object has no attribute 'subject'
>>> q.content
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'list' object has no attribute 'content'
>>> q = Question.query.filter(Question.id==2).get() 
Traceback (most recent call last):
  File "<console>", line 1, in <module>
TypeError: Query.get() missing 1 required positional argument: 'ident'
>>> q = Question.query.get(2)                        
>>> q
<Question 2>
>>> q.subject
'์ œ๋ชฉ์ž…๋‹ˆ๋‹ค2'
>>> q = Question.query.filter(Question.subject.like("%๋‚ ์”จ%")).get() 
Traceback (most recent call last):
  File "<console>", line 1, in <module>
TypeError: Query.get() missing 1 required positional argument: 'ident'
>>> q = Question.query.filter(Question.subject.like("%๋‚ ์”จ%")).all() 
>>> q
[<Question 4>]
>>> q[0]
<Question 4>
>>> q[0].subject
'๋‚ ์”จ๊ฐ€ ์ถ”์œ„์ž…๋‹ˆ๋‹ค'
>>> q[0].subject = "๋‚ ์”จ๊ฐ€ ๋งค์šฐ ์ถฅ์Šต๋‹ˆ๋‹ค"
>>> q[0].subject                         
'๋‚ ์”จ๊ฐ€ ๋งค์šฐ ์ถฅ์Šต๋‹ˆ๋‹ค'
>>> db.session.commit()
>>> q
[<Question 4>]
>>> db.session.delete(q)
Traceback (most recent call last):
  File "C:\ITStudy\09_flask\fisa-flask\myproject\lib\site-packages\sqlalchemy\orm\session.py", line 3478, in delete
    state = attributes.instance_state(instance)
AttributeError: 'list' object has no attribute '_sa_instance_state'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "C:\ITStudy\09_flask\fisa-flask\myproject\lib\site-packages\sqlalchemy\orm\scoping.py", line 672, in delete
    return self._proxied.delete(instance)
  File "C:\ITStudy\09_flask\fisa-flask\myproject\lib\site-packages\sqlalchemy\orm\session.py", line 3480, in delete
    raise exc.UnmappedInstanceError(instance) from err
sqlalchemy.orm.exc.UnmappedInstanceError: Class 'builtins.list' is not mapped     
>>> db.session.delete(q[0])
>>> db.session.commit()     

- ์—ฌ๋Ÿฌ๋ถ„์˜ ์ด๋ฆ„์„ question ํ…Œ์ด๋ธ”์˜ content ์†์„ฑ์— ๋„ฃ์–ด์„œ 1๊ฐœ ๊ธ€์„ ์ž‘์„ฑํ•˜์„ธ์š”
- filter๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ทธ ๊ธ€์„ ๊ฐ€์ ธ์™€๋ณด์„ธ์š”
- ๊ธ€์˜ content๋ฅผ '์‹ ์งฑ๊ตฌ'๋กœ ๋ณ€๊ฒฝํ•ด๋ณด์„ธ์š”
- ๊ทธ ๊ธ€์„ ์‚ญ์ œํ•ด์ฃผ์„ธ์š”

>>> db.session.delete(q[0]) 
>>> db.session.commit()     
>>> a = Answer(question_id=1, content="1๋ฒˆ ๊ธ€์— ๋Œ€ํ•œ ๋‹ต๋ณ€์ž…๋‹ˆ๋‹ค", create_date=datetime.now())
>>> a
<Answer (transient 2955131376432)>
>>> db.session.add(a)   
>>> db.session.commit()  
>>> a
<Answer 1>
>>> a.question_id
1
>>> a.question   
<Question 1>
>>> a.question.content
'๋‚ด์šฉ์ž…๋‹ˆ๋‹ค'
>>> a.question.subject
'์ œ๋ชฉ์ž…๋‹ˆ๋‹ค'
>>> q = a.question    
>>> db.session.delete(q[0]) 
>>> db.session.commit()     
>>> a = Answer(question_id=1, content="1๋ฒˆ ๊ธ€์— ๋Œ€ํ•œ ๋‹ต๋ณ€์ž…๋‹ˆ๋‹ค", create_date=datetime.now())
>>> a
<Answer (transient 2955131376432)>
>>> db.session.add(a)   
>>> db.session.commit()  
>>> a
<Answer 1>
>>> a.question_id
1
>>> a.question   
<Question 1>
>>> a.question.content
'๋‚ด์šฉ์ž…๋‹ˆ๋‹ค'
>>> a.question.subject
'์ œ๋ชฉ์ž…๋‹ˆ๋‹ค'
>>> q = a.question    
>>> q.answer_set                                                                  
[<Answer 1>]
>>> a = Answer(question_id=1, content="๋‘๋ฒˆ์งธ 1๋ฒˆ ๊ธ€์— ๋Œ€ํ•œ ๋‹ต๋ณ€์ž…๋‹ˆ๋‹ค", create_date=datetime.now())
>>> a
<Answer (transient 2955131818416)>
>>> db.session.add(a)
>>> db.session.commit()  
>>> q.answer_set
[<Answer 1>, <Answer 2>]
>>> q.answer_set[1].content
'๋‘๋ฒˆ์งธ 1๋ฒˆ ๊ธ€์— ๋Œ€ํ•œ ๋‹ต๋ณ€์ž…๋‹ˆ๋‹ค'


{# <% %> = {% %}
<%= %> = {{ }}
<%-- --%> = {# ... #} 
<jsp include: > = {% include 'header.html' %} #}


{% for item in list %}
    <p>์ˆœ์„œ: {{ loop.index }} </p>
    <p>{{ item }}</p>
{% endfor %}

https://docs.sqlalchemy.org/en/13/orm/query.html
https://sqlitebrowser.org/dl/


## templates/post_list.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

    {% for item in question_list %}
    <p>์ˆœ์„œ: {{ loop.index }} </p>
    <p>์ œ๋ชฉ: {{ item.subject }}</p>
    <p>๋‚ด์šฉ: {{ item.content }}</p>
    <p>๊ฒŒ์‹œ์ผ: {{ item.create_date }}</p>
    {% endfor %}


</body>
</html>


{% if ์กฐ๊ฑด๋ฌธ1 %}
    <p>์กฐ๊ฑด๋ฌธ1์— ํ•ด๋‹นํ•˜๋ฉด ์‹คํ–‰</p>
{% elif ์กฐ๊ฑด๋ฌธ2 %}
    <p>์กฐ๊ฑด๋ฌธ2์— ํ•ด๋‹นํ•˜๋ฉด ์‹คํ–‰</p>
{% else %}
    <p>์กฐ๊ฑด๋ฌธ1, 2 ๋ชจ๋‘ ํ•ด๋‹นํ•˜์ง€ ์•Š์œผ๋ฉด ์‹คํ–‰</p>
{% endif %}


## ์ƒ์„ธ ๊ฒŒ์‹œํŒ ๊ธ€ ์กฐํšŒ๋ฅผ ์œ„ํ•œ MVC
# templates/question_detail.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=\, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    ์ œ๋ชฉ
    ๋‚ด์šฉ
    ๊ฒŒ์‹œ์ผ
</body>
</html>


# views/board_views.py
from flask import Blueprint, render_template
from ..models import Question, Answer

# ์šฐ๋ฆฌ๊ฐ€ ๋ถ€๋ฅผ ์ด๋ฆ„, flask ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์ฐพ์„ ์ด๋ฆ„, ๋ผ์šฐํŒ…์ฃผ์†Œ
board = Blueprint('board', __name__, url_prefix="/board")

@board.route("/post")
def post_list():
    question_list = Question.query.all()
    return render_template("question_list.html", question_list=question_list)

# board/detail/1 2 3 4  -> question_detail.html๋กœ ๊ฐ ๊ธ€์˜ ์‹ค์ œ ์„ธ๋ถ€๋‚ด์šฉ์„ ์ „๋‹ฌํ•˜๊ณ  ์‹ถ์–ด์š”
@board.route("/detail/<int:question_id>") # 
def post_detail(question_id):

    return f"{question_id}"
    
    
## board_views.py    
from flask import Blueprint, render_template
from ..models import Question, Answer

# ์šฐ๋ฆฌ๊ฐ€ ๋ถ€๋ฅผ ์ด๋ฆ„, flask ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์ฐพ์„ ์ด๋ฆ„, ๋ผ์šฐํŒ…์ฃผ์†Œ
board = Blueprint('board', __name__, url_prefix="/board")

@board.route("/post")
def post_list():
    question_list = Question.query.all()
    return render_template("question_list.html", question_list=question_list)

# board/detail/1 2 3 4  -> question_detail.html๋กœ ๊ฐ ๊ธ€์˜ ์‹ค์ œ ์„ธ๋ถ€๋‚ด์šฉ์„ ์ „๋‹ฌํ•˜๊ณ  ์‹ถ์–ด์š”
@board.route("/detail/<int:question_id>") # question_id ๋ณ€์ˆ˜๋กœ ๋ฐ›์€ ๊ฐ’์„ 
def post_detail(question_id): # ํ•จ์ˆ˜์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ „๋‹ฌ
    # question = Question.query.get(question_id) # ๋ชจ๋ธ์—์„œ ํŠน์ • ๋ฒˆํ˜ธ(id)๋ฅผ ํ†ตํ•ด ๊ฐ’์„ ์กฐํšŒ 
    question = Question.query.get_or_404(question_id)
    return render_template("question_detail.html", ques = question)


# views/main_views.py
from flask import Blueprint
from ..models import Question

# ์šฐ๋ฆฌ๊ฐ€ ๋ถ€๋ฅผ ์ด๋ฆ„, flask ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์ฐพ์„ ์ด๋ฆ„, ๋ผ์šฐํŒ…์ฃผ์†Œ
bp = Blueprint('main', __name__, url_prefix="/")

# ์ฒซ๋ฒˆ์งธ blueprint๋ถ€ํ„ฐ ์ฐพ๊ธฐ ๋•Œ๋ฌธ์— board๋ฅผ ์“ธ ์ˆ˜ ์—†๊ฒŒ ๋ฉ๋‹ˆ๋‹ค 
# @bp.route("/", defaults={"var":'', "var2":""}) # ์—ฌ๋Ÿฌ๊ฐœ์˜ route ์–ด๋…ธํ…Œ์ด์…˜์„ ํ•˜๋‚˜์˜ ๋ฉ”์„œ๋“œ์— ์–น์–ด์„œ ์“ธ ์ˆ˜๋„ ์žˆ๋‹ค
# @bp.route("/<var>/<var2>")  #๋Œ€๋ถ€๋ถ„ uri๋Š” str๋กœ ๋ฐ›๊ธฐ ๋•Œ๋ฌธ์— str์€ ์ƒ๋žต # localhost:5000/yeonji  -> hello yeonji๊ฐ€ ์ถœ๋ ฅ๋˜๋„๋ก 
@bp.route("/") 
def hello(var, var2):

    return f'main์—์„œ ์ž‘์„ฑํ•œ Hello {var} {var2}'

@bp.route("/bye")
def bye():
    return f'BYE'
    
    
## templates/post_detail.html
        {% for ans in ques.answer_set %}
        <hr>
            {{ ans.id }} <br>
            {{ ans.content }} <br>
            {{ ans.create_date }} <br>
        {% endfor %}
        
## templates/post_list.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

    {% for item in question_list %}
    <p>์ˆœ์„œ: {{ loop.index0 }} </p>
    <p>์ œ๋ชฉ: <a href="/board/detail/{{ item.id }}"> {{ item.subject }}</a></p>
    <p>๊ฒŒ์‹œ์ผ: {{ item.create_date }}</p>
    {% endfor %}

    {% if False %}
        <p>์กฐ๊ฑด๋ฌธ1์— ํ•ด๋‹นํ•˜๋ฉด ์‹คํ–‰</p>
    {% elif True %}
        <p>์กฐ๊ฑด๋ฌธ2์— ํ•ด๋‹นํ•˜๋ฉด ์‹คํ–‰</p>
    {% else %}
        <p>์กฐ๊ฑด๋ฌธ1, 2 ๋ชจ๋‘ ํ•ด๋‹นํ•˜์ง€ ์•Š์œผ๋ฉด ์‹คํ–‰</p>
    {% endif %}


</body>
</html>

https://github.com/YeonjiKim0316/fisa-flask

pip freeze > requirements.txt   
git add .
git commit -m "flask day 1"
git push origin main

// woorifisa ์—๋‹ค๊ฐ€ ๊ฐ™์€ ์ž‘์—…์„ ํ•ด ๋ณด์‹ญ์‹œ์˜ค

'Tech Stack > Flask' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

flask - Pagination  (0) 2024.02.15
Flask 2 - MVC(MTV)ํŒจํ„ด์œผ๋กœ ์งˆ๋ฌธ๋‹ต๋ณ€ ๊ฒŒ์‹œํŒ ๋งŒ๋“ค๊ธฐ  (1) 2024.02.14