[Flask] (6) ORM & SQLAlchemy
๐ ORM
ORM์ด๋, ๊ฐ์ฒด ๊ด๊ณ ๋งคํ(Object Relational Mapping)์ผ๋ก, ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ด์ ํ ์ด๋ธ๋ค์ ๊ฐ์ฒดํํด์, ๊ฐ DBMS์ ๋ํด์ CRUD ๋ฑ์ ๊ณตํต๋ ์ ๊ทผ ๊ธฐ๋ฒ์ผ๋ก ์ฌ์ฉํ ์ ์๋ค.
์ผ๋ฐ์ ์ผ๋ก ํ๋์ ๊ฐ์ฒด์ ๋ณด๋ฅผ ๋ค๋ฃจ๊ธฐ ์ํด์ ์ฌ๋ฌ SQL ์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํ๊ฒ ๋๊ณ , ํ๋ก๊ทธ๋จ์ด ์ปค์ง์๋ก ์์ฑํด์ผ ํ๋ SQL ์ฟผ๋ฆฌ๊ฐ ๋ง์์ ธ ๋ณต์กํด์ง๋ค.
๋ฐ๋ณต๋๋ ์ฟผ๋ฆฌ๋ฅผ ๊ฐ์ฒด ๋จ์๋ก ์์ฑํ์ฌ, ์ด๋ฅผ ํด๊ฒฐํ๊ณ ์ ํ๊ณ , ์ด๋ฐ ์์ ์ ๋์์ฃผ๋ ๊ฒ์ ๋ฐ๋ก ORM์ด๋ผ๊ณ ํ๋ค.
ORM์ ์ฌ์ฉํ๊ฒ ๋๋ฉด ๋ฐ๋ก SQL๋ฌธ์ ์์ฑํ ํ์์์ด ๊ฐ์ฒด๋ฅผ ํตํด ๊ฐ์ ์ ์ผ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์กฐ์ํ ์ ์๋ค.
๐ ๋ํ์ ์ธ python ORM = DjangoORM ๊ณผ SQLAlchemy ๋ฑ์ด ์๋ค.
๐ก ์ฅ์
- ๊ฐ๋ฐ ์์ฐ์ฑ์ ์ฆ๊ฐ์ํจ๋ค. ํ๋ก๊ทธ๋๋จธ๋ DBMS์ ๋ํ ํฐ ๊ณ ๋ฏผ์์ด, ORM์ ๋ํ ์ดํด๋ง์ผ๋ก
๋๋ถ๋ถ์ CRUD๋ฅผ ๋ค๋ฃฐ ์ ์๋ค. - ์ ์ง๋ณด์์ฑ์ด ์ข๋ค. ๋ฐ์ดํฐ ๊ตฌ์กฐ ๋ณ๊ฒฝ์, ๊ฐ์ฒด์ ๋ํ ๋ณ๊ฒฝ๋ง ์ด๋ฃจ์ด์ง๋ฉด ๋๋ค.
- ์ฝ๋ ๊ฐ๋ ์ฑ์ด ์ข๋ค. ๊ฐ์ฒด๋ฅผ ํตํด์ ๋๋ถ๋ถ์ ๋ฐ์ดํฐ๋ฅผ ์ ๊ทผ ๋ฐ ์์ ์ ์งํํ๋ค.
๐ก ๋จ์
- ํธ์ถ ๋ฐฉ์์ ๋ฐ๋ผ ์ฑ๋ฅ์ด ์ฒ์ฐจ๋ง๋ณ์ด๋ค.
- ๋ณต์กํ ์ฟผ๋ฆฌ ์์ฑ์, ORM ์ฌ์ฉ์ ๋ํ ๋์ด๋๊ฐ ์ฆ๊ฐํ๋ค.
- DBMS(MySQL) ๊ณ ์ ์ ๊ธฐ๋ฅ์ ๋ชจ๋ ์ฌ์ฉํ์ง ๋ชปํ๋ค.
๐ SQL ์ฟผ๋ฆฌ์ ORM์ ์ฐจ์ด์
๋ค์๊ณผ ๊ฐ์ DB ํ ์ด๋ธ์ด ์๊ณ , ๋ฐ์ดํฐ๋ฅผ ์ฝ์ ํ๋ ค๊ณ ํ๋ค๊ณ ๊ฐ์ ํ์.
INSERT INTO ์๋ฆฌ์ค (name, age) VALUES (‘์ฌ์’, ‘18’);
member = Member(name=‘์ฌ์’, age=‘18’)
db.session.add(member)
์์๊ฐ SQL, ์๋๊ฐ ORM ๋ฐฉ์์ด๋ค.
๐ SQLAlchemy
ํ์ด์ฌ ORM ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉ๋๋ SQLAlchemy๋ ํ์ด์ฌ ์ฝ๋์์ Flask๋ฅผ DB์ ์ฐ๊ฒฐํ๊ธฐ ์ํด ์ฌ์ฉ๋๋ค. SQLAlchemy๋ ORM์ด๊ณ , DB ํ ์ด๋ธ์ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์ "ํด๋์ค"๋ก ํํํ๊ฒ ํด์ฃผ๊ณ , ํ ์ด๋ธ์ CRUD ๋ฑ์ ๋๋๋ค.
๐ก SQLAlchemy ๋ ์ ์ฌ์ฉํ๋ ?
- SQLAlchemy๋ฅผ ์ฌ์ฉํ๋ฉด, SQL์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ง ์๊ณ , ํ๋ก๊ทธ๋จ์ด ์ธ์ด๋ก ๊ฐ์ฒด๊ฐ์ ๊ด๊ณ๋ฅผ ํํํ ์ ์๋ค.
- SQLAlchemy๋ก Model์ ์ ์ํ๊ณ , ์ ์ํ ๋ชจ๋ธ์ ํ ์ด๋ธ๊ณผ Mapping ํ ์ ์๋ ์ฌ๋ฌ๊ฐ์ง ๋ฐฉ๋ฒ์ ์ฝ๊ฒ ๊ตฌํํ ์ ์๋ค.
- ์ด๋, DB์ ์ข ๋ฅ์ ์๊ด ์์ด ์ผ๊ด๋ ์ฝ๋๋ฅผ ์ ์งํ ์ ์์ด ํ๋ก๊ทธ๋จ ์ ์ง ๋ฐ ๋ณด์๊ฐ ํธ๋ฆฌํ๊ณ SQL ์ฟผ๋ฆฌ๋ฅผ ๋ด๋ถ์์ ์๋์ผ๋ก ์์ฑํ๊ธฐ ๋๋ฌธ์ ์ค๋ฅ ๋ฐ์๋ฅ ์ด ์ค์ด๋๋ ์ฅ์ ์ด ์๋ค.
๐ Model ๊ตฌํ
Member๋ ํ์ด์ฌ ํด๋์ค์ด๋ค. ํด๋์ค๋ DB์ ํ ์ด๋ธ๊ณผ Mappingํ์ฌ ์ฌ์ฉ๋๋ฉฐ DB์ ํ ์ด๋ธ๊ณผ ๋งคํ๋๋ ์ด ํด๋์ค๋ฅผ ๋ชจ๋ธ์ด๋ผ๊ณ ํ๋ค.
SQLAlchemy์ ๋ด์ฅ๋์ด ์๋ Mode ํด๋์ค๋ฅผ ์์ํด์, ์ด๋ฅผ ๊ตฌํํ ์ ์์ผ๋ฉฐ, ์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Member(db.Model):
name = db.Column(db.String(20), primary_key = True)
age = db.Column(db.Integer, nullable=False)
๐ก ์ฟผ๋ฆฌ์ ์ข ๋ฅ
์ผ๋ฐ์ ์ธ SQL ์ ์ฌ์ฉ๋ฒ๊ณผ ๋น์ทํ๋ค.
๋ค๋ง, ์์ ํ SQL์ด ์๋๋ผ, ํ๋ก๊ทธ๋๋ฐ์ธ์ด ์ ์ผ๋ก ์ฌ์ฉํด์, ์ฌ์ฉ๋ฐฉ๋ฒ์ด ์กฐ๊ธ์ฉ์ ๋ค๋ฅธ ๊ฒ ๊ฐ๋ค.
๐ก ์ฌ์ฉ ์์
- equal ( == )
@app.route('/')
def list():
member_list = Member.query.filter(Member.name == 'Elice')
return " ".join(i.name for i in member_list)
- not equal ( != )
@app.route('/')
def list():
member_list = Member.query.filter(Member.name != 'Elice')
return " ".join(i.name for i in member_list)
- like ( like( ) )
@app.route('/')
def list():
member_list = Member.query.filter(Member.name.like('Elice'))
return " ".join(i.name for i in member_list)
- in
@app.route('/')
def list():
member_list = Member.query.filter(Member.name.in_(['Elice', 'Dodo']))
return " ".join(i.name for i in member_list)
- not in
@app.route('/')
def list():
member_list = Member.query.filter(~Member.name.in_(['Elice', 'Dodo']))
return " ".join(i.name for i in member_list)
- is null
@app.route('/')
def list():
member_list = Member.query.filter(Member.name == None)
return " ".join(i.name for i in member_list)
- is not null
@app.route('/')
def list():
member_list = Member.query.filter(Member.name != None)
return " ".join(i.name for i in member_list)
- and
@app.route('/')
def list():
member_list = Member.query.filter((Member.name == 'Elice') & (Member.age == '15'))
return " ".join(i.name for i in member_list)
- or
@app.route('/')
def list():
member_list = Member.query.filter ((Member.name == 'Elice') | (Member.age == '15'))
return " ".join(i.name for i in member_list)
- order by
@app.route('/')
def list():
member_list = Member.query.order_by(Member.age.desc())
return " ".join(i.name for i in member_list)
- limit
@app.route('/')
def list(limit_num = 5):
if limit_num is None:
limit_num = 5
member_list = Member.query.order_by(Member.age.desc()).limit(limit_num)
return " ".join(i.name for i in member_list)
- offset
@app.route('/')
def list(off_set = 5):
if off_set is None:
off_set = 5
member_list = Member.query.order_by(Member.age.desc()).offset(off_set)
return " ".join(i.name for i in member_list)
- count
@app.route('/')
def list():
member_list = Member.query.order_by(Member.age.desc()).count()
return str(member_list)
๐ Model ์์ฑ ์์
- [ ์ฐธ๊ณ ] ์ ์ฅ๋ DB๋ฅผ ํ์ธํ ๋๋ ๋ ๊ฐ์ง ๋ฐฉ๋ฒ์ผ๋ก ํ์ธํ ์ ์๋ค.
- Member๊ฐ์ฒด.query.all( ) = ๋ฆฌ์คํธํ์ผ๋ก ๋ฐํ
- Member๊ฐ์ฒด.query.first( ) = ๋ชจ๋ธ ํ๋์ ๊ฐ์ฒด๋ก ๋ฐํํ๊ธฐ ๋๋ฌธ์ ์ ์
# models.py
from main import db # main.py์์ ์์ฑํ SQLAlchemy ๊ฐ์ฒด์ธ db import
class Member(db.Model): # db์ Model ํด๋์ค ์์, Member ๋ชจ๋ธ ์์ฑ
# Member ๊ฐ์ฒด์ Atrribute ์ค์
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), nullable=False)
age = db.Column(db.Integer, nullable=False)
# Member ๊ฐ์ฒด ์์ฑ์
def __init__(self,name,age):
self.name = name
self.age = age
# main.py
from flask import Flask, request, render_template, redirect, url_for
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
import config
db = SQLAlchemy()
migrate = Migrate()
app = Flask(__name__)
app.config.from_object(config)
db.init_app(app)
migrate.init_app(app, db)
from models import Member # main.py์์ Member ํด๋์ค๋ฅผ import
@app.route('/')
def _list():
name = ['Elice', 'Dodo', 'Checher', 'Queen']
age = 15
# ์ฃผ์ด์ง name ๋ฆฌ์คํธ๋ฅผ ์ฌ์ฉํ๊ณ , ๋์ด์ ํจ๊ป, DB์ ์ถ๊ฐ
for data in name:
member = Member(data,age)
age += 1
db.session.add(member)
db.session.commit() # DB๋ฅผ commit()
member_list = Member.query.all() # member_list์ Member์ ๋ชจ๋ ํํ ์ ์ฅ
if(type(member_list)!=type([])):
member_list=[member_list]
return render_template('member_list.html', member_list=member_list)
if __name__ == "__main__":
app.run()
# config.py
import os
BASE_DIR = os.path.dirname(__file__)
SQLALCHEMY_DATABASE_URI = 'sqlite:///{}'.format(os.path.join(BASE_DIR, 'main.db'))
SQLALCHEMY_TRACK_MODIFICATIONS = False
<!-- ./templates/member_list.html -->
<!DOCTYPE html>
{% if member_list %}
<ul>
{% for member in member_list %}
<li><p>{{ member.name }} {{ member.age }}</p></li>
{% endfor %}
</ul>
{% else %}
<p>๋ฉค๋ฒ๊ฐ ์์ต๋๋ค.</p>
{% endif %}
๐ Model ์ฌ์ฉํด์ ์๋ฃ์ถ๊ฐ ์์
# models.py
from main import db
class Member(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20), nullable=False)
age = db.Column(db.Integer, nullable=False)
def __init__(self, name, age):
self.name = name
self.age = age
# main.py
from flask import Flask, request, render_template, redirect, url_for
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
import config
db = SQLAlchemy()
migrate = Migrate()
app = Flask(__name__)
app.config.from_object(config)
db.init_app(app)
migrate.init_app(app, db)
from models import Member
@app.route('/list')
def _list():
member_list = Member.query.all()
return render_template('member_list.html', member_list=member_list)
@app.route("/", methods=["GET","POST"])
def _add():
if request.method == 'POST':
name = request.form['name']
try:
age = int(request.form["age"])
except:
return "๋์ด๋ ์ซ์๋ง ์
๋ ฅํ์ธ์."
member = Member(name, age)
db.session.add(member)
db.session.commit()
return redirect(url_for("_list"))
else:
return render_template('add.html')
if __name__ == "__main__":
app.run()
# config.py
import os
BASE_DIR = os.path.dirname(__file__)
SQLALCHEMY_DATABASE_URI = 'sqlite:///{}'.format(os.path.join(BASE_DIR, 'main.db'))
SQLALCHEMY_TRACK_MODIFICATIONS = False
<!-- ./templates/add.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>HTML for python flask</title>
</head>
<body>
<form action = "/" method="POST">
<p>name : <input type="text" id = "name" name = "name"></p>
<p>age : <input type="text" id = "age" name = "age"></p>
<p>์ด๋ฆ๊ณผ ๋์ด๋ฅผ ์
๋ ฅํ๊ณ ์ ์ถ๋ฒํผ์ ๋๋ฅด์ธ์. <br><input type = "submit" value = "์ ์ถ"/> </p>
</form>
</body>
</html>
<!-- ./templates/member_list.html -->
<!DOCTYPE html>
{% if member_list %}
<ul>
{% for member in member_list %}
<li><p>{{ member.name }} {{ member.age }}</p></li>
{% endfor %}
</ul>
{% else %}
<p>๋ฉค๋ฒ๊ฐ ์์ต๋๋ค.</p>
{% endif %}
๐ Query ์ฌ์ฉ๋ฒ ์์ ( ๋ช ๊ฐ์ง๋ง )
- equal, not equal, like
# main.py ์ ์ถ๊ฐ
# config.py , models.py ๋ ๋์ผ
@app.route('/search', methods=['GET', 'POST'])
def _search():
if request.method == 'POST':
key = request.form['keyword']
con = request.form['condition']
if(con=='1'):
member_list = Member.query.filter(Member.age == int(key))
elif(con=='2'):
member_list = Member.query.filter(Member.age != int(key))
elif(con=='3'):
member_list = Member.query.filter(Member.age.like(int(key)))
return render_template('member_list.html', member_list=member_list)
else:
return render_template('search.html')
<!-- ./templates/search.html ์ถ๊ฐ -->
<!-- add.html , member_list.html ์ model ์์ฑ ์์ ์ ๋์ผ -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>HTML for python flask</title>
</head>
<body>
<form action = "/search" method="POST">
<p><input type="text" id = "keyword" name = "keyword"></p>
<p><input type="radio" name = "condition" value ="1" checked="checked">equal <input type="radio" name = "condition" value ="2">not equal <input type="radio" name = "condition" value ="3">like</p>
<input type = "submit" value = "๊ฒ์"/>
</form>
</body>
</html>
- in, not in, is null, is not null, and, or, order by, limit, offset ๋ํ ์์ Member๊ฐ์ฒด.query.filter( ) ์์ ์์ฑํ๋ ๊ฒ๊ณผ ๋น์ทํด์ ์๋ต ( ์ฌ์ฉ๋ฒ๋ง ํ์ํ ๋ ์์ง )