from sqlalchemy import Column, Integer, String
from pydantic import BaseModel
from db import Base
from db import ENGINE
class UserTable(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(50), nullable=False)
age = Column(Integer)
class User(BaseModel):
id: int
name: str
age: int
<main.py>
from fastapi import FastAPI
from typing import List
from starlette.middleware.cors import CORSMiddleware
from db import session
from model import UserTable, User
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/users")
async def read_users():
users = session.query(UserTable).all()
return users
@app.get("/users/{user_id}")
async def read_user(user_id: int):
user = session.query(UserTable).filter(UserTable.id == user_id).first()
return user
@app.post("/user")
async def create_users(name: str, age: int):
user = UserTable()
user.name = name
user.age = age
session.add(user)
session.commit()
return f"{name} created..."
@app.put("/users")
async def update_user(users: List[User]):
for i in users:
user = session.query(UserTable).filter(UserTable.id == i.id).first()
user.name = i.name
user.age = i.age
session.commit()
# users[0].name
return f"{users[0].name} updated..."
@app.delete("/user")
async def delete_users(user_id: int):
user = session.query(UserTable).filter(UserTable.id == user_id).delete()
session.commit()
return read_users
화면 구성에 대해서는 최대한 자제하고 로직 구성에만 전념하겠습니다. 이에 대한 라우팅은 아래와 같이 구성합니다.
@app.route('/register', methods=['GET','POST'])
def register():
if request.method =='GET':
return render_template("register.html")
else:
userid = request.form.get('userid')
username = request.form.get('username')
password = request.form.get('password')
re_password = request.form.get('re_password')
if not (userid and username and password and re_password):
return "모두 입력해주세요"
elif password != re_password:
return "비밀번호를 확인해주세요"
else:
user = User()
user.password = password
user.userid = userid
user.username = username
db.session.add(user)
db.session.commit()
return "회원가입 완료"
return redirect('/')
최초 /register에 접속할 때에는 GET 방식으로 단순 html 파일을 보여주고, html 내 form 양식에 의해
<form method="POST" action="/register"> 로 불러질 때에는 POST 방식에 의해 수행이 됩니다. POST 방식에서는 아이디, 이름, 비번, 비번확인을 받아들여서, 하나라도 빠진게 있으면 체크하고, 비번과 비번확인이 다르면 체크하며, 모두 통과하면 users 테이블에 입력합니다. 입력이 완료되면 '/'으로 리다이렉트 됩니다.
4.login.html
로그인 페이지를 아래와 같이 구성합니다. 조금 뒤에 나오겠지만, 'userid'라는 session정보가 있을 경우 이미 로그인 했음을 알려주고, 그렇지 않을 경우 로그인 폼을 보여줍니다.
# login 페이지 접속(GET) 처리와, "action=/login" 처리(POST)처리 모두 정의
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method=='GET':
return render_template('login.html')
else:
userid = request.form['userid']
password = request.form['password']
try:
data = User.query.filter_by(userid=userid, password=password).first() # ID/PW 조회Query 실행
if data is not None: # 쿼리 데이터가 존재하면
session['userid'] = userid # userid를 session에 저장한다.
return redirect('/')
else:
return 'Dont Login' # 쿼리 데이터가 없으면 출력
except:
return "dont login" # 예외 상황 발생 시 출력
@app.route('/logout', methods=['GET'])
def logout():
session.pop('userid', None)
return redirect('/')
로그인 폼을 보여줄 'GET' 방식과, 로그인 처리(사용자 확인)를 위한 'POST'방식을 모두 정의합니다. GET이면 login.html을 곧바로 보여주고, POST방식일 경우 login.html 내 폼에서 userid와 password 부분을 가져와서 database 조회를 합니다. 조회가 성공하면 session['userid']에 폼으로부터 전달받은 userid값을 저장하고 '/'으로 이동합니다. 데이터가 없거나, 예외가 발생하면 그에 따른 처리를 각각 해 줍니다.
아울러 '/logout'에 대해서도 함께 정의해줍니다. 이 때에는 GET만 정의하면 됩니다.
5. __init__.py 수정
순서가 뒤죽박죽이 된 것 같네요. 위에서 세션을 사용한 코드를 이미 작성했는데.....한가지, Flask에서는 세션을 사용하려면 어플리케이션에 정의된 시크릿키(SECRET_KEY)가 필요합니다. 이 키로 서명된 쿠키가 사용된다고 합니다. 꼭 잊지말고 넣어주도록 합시다.
그밖에 기존에 사용자 인증과 관련없는 라우팅은 지면관계상 지우도록 하겠습니다.
@app.route('/home')도 지워버리고, 대신 home.html을 '/' 에서 라우팅하도록 하였습니다. 단 session['userid']가 있을 경우와 없을 경우를 나누어 처리하도록 하였습니다.
from flask import Flask, render_template, request, redirect, session, url_for
from flask_sqlalchemy import SQLAlchemy
from helloflask.model.user_model import Member, User
app = Flask(__name__)
app.secret_key="123123123"
# database 설정파일
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+pymysql://root:1234@localhost:3306/testdb"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db = SQLAlchemy(app)
@app.route('/')
def home():
#로그인 세션정보('userid')가 있을 경우
if not session.get('userid'):
return render_template('home.html')
#로그인 세션정보가 없을 경우
else:
userid = session.get('userid')
return render_template('home.html', userid=userid)
# @app.route('/home') 삭제
# @app.route('/one') 삭제
# @app.route('/all') 삭제
# @app.route('/search', methods=['POST','GET']) 삭제
# register 페이지 접속(GET) 처리와, "action=/register" 처리(POST) 모두 정의
@app.route('/register', methods=['GET','POST'])
def register():
if request.method =='GET':
return render_template("register.html")
else:
userid = request.form.get('userid')
username = request.form.get('username')
password = request.form.get('password')
re_password = request.form.get('re_password')
if not (userid and username and password and re_password):
return "모두 입력해주세요"
elif password != re_password:
return "비밀번호를 확인해주세요"
else:
user = User()
user.password = password
user.userid = userid
user.username = username
db.session.add(user)
db.session.commit()
return "회원가입 완료"
return redirect('/')
# login 페이지 접속(GET)처리와, "action=/login" 처리(POST)모두 정의
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method=='GET':
return render_template('login.html')
else:
userid = request.form['userid']
password = request.form['password']
try:
data = User.query.filter_by(userid=userid, password=password).first() # ID/PW 조회Query 실행
if data is not None: # 쿼리 데이터가 존재하면
session['userid'] = userid # userid를 session에 저장한다.
return redirect('/')
else:
return 'Dont Login' # 쿼리 데이터가 없으면 출력
except:
return "dont login" # 예외 상황 발생 시 출력
@app.route('/logout', methods=['GET'])
def logout():
session.pop('userid', None)
return redirect('/')
6. home.html 수정
userid 세션이 존재할 경우 logout 버튼이 나오도록 처리하고, userid 세션이 없을 경우에는 등록하기와 로그인 버튼이 나타나도록 처리했습니다.
form 으로부터 전달된 이름('nm')을 받아서 temp 변수에 저장하고, query.filter_by로 DB에서 조회한 후, 결과를 members로 저장합니다. 그리고 members의 갯수를 count에 저장합니다. render_template함수를 통해 db.html로 연결이 되는데, 이때 members와 count를 함께 넘겨줍니다.
from flask import Flask, render_template, request
from flask_sqlalchemy import SQLAlchemy
from helloflask.model import Members
from datetime import datetime
app = Flask(__name__)
# database 설정파일
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+pymysql://root:1234@localhost:3306/testdb"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db = SQLAlchemy(app)
@app.route("/")
def home():
member = Members.query.first()
return 'Hello {0}, {1}, {2}, {3}, {4}'\
.format(member.name, member.email, member.phone, member.start.isoformat(), member.end.isoformat())
#return render_template('home.html')
@app.route('/all')
def select_all():
members = Members.query.all()
return render_template('db.html', members=members)
@app.route('/search', methods=['POST','GET'])
def calculate():
if request.method=='POST':
#temp = request.args.get('nm')
temp = request.form['nm']
members = Members.query.filter_by(name=temp)
count=members.count()
return render_template('db.html', members=members, cnt=count)
2.db.html
form 입력은 "POST" 형태로 이루어지고, 처리를 한 action을 명시해줍니다. 이후 "/search"에서 처리한 결과는 members 변수에 담아 전달됩니다. members 변수의 각 attribute를 출력하는 부분을 작성합니다.
이번 강좌에서는 ORM 방식으로 Database와 연동하는 방법에 대해 포스팅하겠습니다. ORM은 Object-Relational Mapping 이라고 하여, Database 객체를 객체지향 프로그래밍 언어로 변환하는 기법입니다. (자세한 내용은 다른 포스팅들을 참조하시기 바랍니다.) 그 중에서도 Flask에서 ORM을 구현할 수 있게 해주는 라이브러리가 Flask-SQLAlchemy 입니다. 이 Flask-SQLAlchemy를 이용하여 MySql과 연동해보도록 하겠습니다.
1. Database 자료 생성
아래와 같이 id, name, email, phone, start(datetime), end(datetime)을 필드로 하는 자료를 생성해놓습니다.
2. 라이브러리 설치
먼저 말했듯이 이번 강좌의 메인인 flask_sqlalchemy 라이브러리를 설치합니다. 그리고 mysql과 연동하기 위한 pymysql라이브러리도 함께 설치해줍니다.
pip install flask_sqlalchemy
pip install pymysql
3. 앱 구성
이번 helloflask앱은 아래와 같은 구성으로 만들려고 합니다. DB연동은 __init__.py에서 구현하고 db.html에서 결과를 표시합니다.
3. model.py
연결된 Database의 table을 받아들일 class를 정의합니다.
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
db = SQLAlchemy()
class Members(db.Model):
""" table name : members
table info
- id : index id
- name
- start: start datetime
- end: end datetime """
__tablename__ = 'members'
id = db.Column(db.Integer, primary_key=True, nullable=False, autoincrement=True)
name = db.Column(db.String(20, 'utf8mb4_unicode_ci'))
email = db.Column(db.String(50, 'utf8mb4_unicode_ci'))
phone = db.Column(db.String(20, 'utf8mb4_unicode_ci'))
start = db.Column(db.DateTime, default=datetime.utcnow())
end = db.Column(db.DateTime, default=datetime.utcnow())
def __init__(self, name, email, phone, start, end):
self.name = name
self.email = email
self.phone = phone
self.start = start
self.end = end
4. __init__.py
예제 구성을 단순화하기 위해 database설정부분을 __init__.py 파일 안에 포함시켰습니다. 본인의 Database/ID/Password 등에 맞게 설정을 하시면 됩니다. one만 출력할 때에는 string을 return시켰으며, all을 출력할 때에는 db.html에 출력하도록 구성하였습니다.
from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
from helloflask.model.user_model import Members
app = Flask(__name__)
# database 설정파일
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+pymysql://root:1234@localhost:3306/testdb"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db = SQLAlchemy(app)
@app.route('/home')
def home():
return render_template('home.html')
@app.route("/one")
def home():
member = Members.query.first()
return 'Hello {0}, {1}, {2}, {3}, {4}'\
.format(member.name, member.email, member.phone, member.start.isoformat(), member.end.isoformat())
#return render_template('home.html')
@app.route('/all')
def select_all():
members = Members.query.all()
return render_template('db.html', members=members)