python

[Flask] (6) ORM & SQLAlchemy

Meng's Computer 2021. 1. 28. 01:48

๐Ÿ“ 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 ํ…Œ์ด๋ธ”์ด ์žˆ๊ณ , ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฝ์ž…ํ•˜๋ ค๊ณ  ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์ž.

[ ์ถœ์ฒ˜ : elice ]

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์ด ์•„๋‹ˆ๋ผ, ํ”„๋กœ๊ทธ๋ž˜๋ฐ์–ธ์–ด ์ ์œผ๋กœ ์‚ฌ์šฉํ•ด์„œ, ์‚ฌ์šฉ๋ฐฉ๋ฒ•์ด ์กฐ๊ธˆ์”ฉ์€ ๋‹ค๋ฅธ ๊ฒƒ ๊ฐ™๋‹ค.

[ ์ถœ์ฒ˜ : elice ]

๐Ÿ’ก ์‚ฌ์šฉ ์˜ˆ์‹œ

  • 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 %}

[ ์ถœ์ฒ˜ : elice ]

 

๐Ÿ“ 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 %}

[ ์ถœ์ฒ˜ : elice ]

 

๐Ÿ“ 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>

[ ์ถœ์ฒ˜ : elice ]

  • in, not in, is null, is not null, and, or, order by, limit, offset ๋˜ํ•œ ์œ„์— Member๊ฐ์ฒด.query.filter( ) ์•ˆ์— ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ๊ณผ ๋น„์Šทํ•ด์„œ ์ƒ๋žต ( ์‚ฌ์šฉ๋ฒ•๋งŒ ํ•„์š”ํ•  ๋•Œ ์ˆ™์ง€ )

 

728x90
๋ฐ˜์‘ํ˜•