📝 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( ) 안에 작성하는 것과 비슷해서 생략 ( 사용법만 필요할 때 숙지 )
'python' 카테고리의 다른 글
[Flask] (7) REST API (0) | 2021.01.28 |
---|---|
[Flask] (5) RDB - Flask Connection & 게시판 예제 (0) | 2021.01.25 |
[Flask] (4) 인증 & 로그인구현 & 로깅 (0) | 2021.01.25 |
[Flask] (3) 렌더링 템플릿 & Jinja2 & 간단한 게시판 (0) | 2021.01.24 |
[Flask] (2) REST & HTTP Method & API/End Point (0) | 2021.01.22 |