Backend/Flask

Flask 기초 I - SQL Alchemy

yxemsy 2022. 2. 16. 00:55

1) SQL Alchemy와 ORM

 

  • ORM

데이터베이스에 객체를 통해 접근하는 방법을 ORM (Object Relational Mapping, 객체 관계 매핑)이라고 한다.

ORM은 SQL 질의어 없이 DB를 다룰 수 있도록 도와준다.

 

아래와 같은 테이블이 있다고 해보자. 테이블 명은 user다.

name age
hayan 25
eunsol 26
user 테이블에 대한 아래 두 코드는 같은 행위를 수행하는 코드이다.

* SQL쿼리문: INSERT INTO user (name, age) VALUES('hayan', 25);

* ORM: member = Member()
        member.name ='hayan'
        member.age = 25
        db.session.add(member)
        db.session.commit()

ORM으로 표현하는 것이 더 복잡해보인다..

 

그렇지만 DB에 대한 큰 고민 없이 DB를 코드로 다룰 수 있다는 장점,

테이블 구조가 변경될 때 ORM 모델만 수정하면 된다는 장점,

코드로 작성하기 때문에 쿼리를 직관적으로 이해할 수 있다는 장점이 있다. 고 한다. 난 잘 모르겟지만 ㅎ

 


  • SQL Alchemy

파이썬 ORM 라이브러리로, 파이썬 코드에서 DB와 연결하기 위해 사용가능한 라이브러리다.

이번 포스팅의 아래쪽에서 더 자세히 다룰 것이다.

 


2) DB와 Model

member1 = Member(name='hayan', age='25')
db.session.add(member1)

위 코드에서 Member는 파이썬 클래스이며, DB의 Member 테이블과 매핑하여 사용한다.

DB의 테이블과 매핑되는 클래스는 모델 이라고 한다.

 

  • ORM Model
from db_connect import db

class Member(db.Model):
    __tablename__ = 'user'   # 테이블 이름 명시
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(20), nullable=False)
    age = db.Column(db.Integer, nullable=False)

id, name, age는 DB 테이블 컬럼을 명시해준다.

해당하는 DB를 다룰 때, Member 클래스로 접근한다.

 


 

3) Query - SQL Alchemy

  • 간단한 CRUD

아래와 같은 Model이 있을 때

class Member(db.Model):
    __tablename__ = 'user'   # 테이블 이름 명시
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(20), nullable=False)
    age = db.Column(db.Integer, nullable=False)

 

(1) Create

member = Member()
member.id = 1
member.name = 'hayan'
member.age = 25
db.session.add(member)
db.session.commit()

Member 객체를 생성하여 . 을 이용해 컬럼에 접근하여 값을 추가할 수 있다.

db.session.add(member)로 추가해주고, commit()을 하여 저장을 완료한다.

 

 

(2) Read

member = db.session.query(Member)
.filter(Member.name == 'hayan').all()

데이터를 가져올 때 기본적으로 db.session.query(Member)를 사용한다.

근데 이건 Member.query 처럼 더 간단하게 쓸 수도 있다.

.filter 함수를 사용하여 name이 hayan인 데이터를 받아오는데, all()은 데이터를 리스트로 반환한다.

 

 

(3) Update

db.session.query(Member)
.filter(Member.name == 'hayan').all()
member.name = 'kong'
db.session.commit()

name이 hayan인 데이터의 name을 kong으로 바꿔준다!

 

 

(4) Delete

me = db.session.query(Member) 
.filter(Member.name == 'hayan').first()
db.session.delete(me)
db.session.commit()

name이 hayan인 데이터를 지워주는데, first()는 하나의 값만을 가져온다.

db.session.delete()를 사용하여 지워준다.

 


  • 다양한 Query 사용법

(1) equal

@app.route('/')
def list():
    member_list = Member.query.filter(Member.name == 'hayan')
    return " ".join(i.name for i in member_list)

 

equal은 == 를 사용한다. name이 hayan인 데이터들을 불러온다.

 

 

(2) not equal

@app.route('/')
def list():
    member_list = Member.query.filter(Member.name != 'hayan')
    return " ".join(i.name for i in member_list)

not equal은 !=를 사용한다.  name이 hayan이 아닌 데이터들을 불러온다.

 

 

(3) like

@app.route('/')
def list():
    member_list = Member.query.filter(Member.name.like('hayan'))
    return " ".join(i.name for i in member_list)

like는 해당 문자열이 포함된 모든 데이터를 불러온다. hayan 123, hayanee 등등

'%hayan%' 을 쓴다면 hayan 앞,뒤에 어떤 문자열이 있든 hayan이 포함된 데이터들을 가져온다.

 

 

(4) in

@app.route('/')
def list():
    member_list = Member.query.filter(Member.name.in_(['hayan', 'kong']))
    return " ".join(i.name for i in member_list)

in을 사용하려면 Member.name.in_ 이렇게 언더바도 같이 써줘야한다.

name이 hayan, kong인 데이터들을 불러온다.

 

 

(5) not in

@app.route('/')
def list():
    member_list = Member.query.filter(~Member.name.in_(['hayan', 'kong']))
    return " ".join(i.name for i in member_list)

not in을 쓸 때는 Member.name.in_ 앞에 ~ 를 붙여줘야한다.

~Member.name.in_  

name이 hayan, kong인 데이터는 불러오지 않는다.

 

 

(6) is null

@app.route('/')
def list():
    member_list = Member.query.filter(Member.name == None)
    return " ".join(i.name for i in member_list)

일반 SQL문에서 is null이라고 쓰는 것을 우리는 None을 써서 알아낼 수 있다. name컬럼이 null인 데이터를 불러온다.

 

 

(7) is not 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은 !=None이다. name컬럼이 비어있지 않은 데이터를 가져온다.

 

 

(8) and

from sqlalchemy import and_
@app.route('/')
def list():
    member_list = Member.query.filter(and_(Member.name == 'hayan',
    					Member.age == 25))
    return " ".join(i.name for i in member_list)

~이면서 ~인 값을 가져온다. and_ 를 먼저 import 해주어야한다!

and_( 조건 ) 으로 사용한다. name이 hayan이면서 age가 25인 데이터를 가져온다.

 

 

(9) or

from sqlalchemy import or_
@app.route('/')
def list():
    member_list = Member.query.filter(or_(Member.name == 'hayan',
    					Member.age == 25))
    return " ".join(i.name for i in member_list)

마찬가지로 or_ 을 import 해주어야한다. hayan이거나 25살인 데이터를 가져온다.

 

 

(10) order by

@app.route('/')
def list():
    member_list = Member.query.order_by(Member.age.desc())
    return " ".join(i.name for i in member_list)

데이터를 정렬할 때는 filter 함수는 사용하지 않고 order_by를 사용한다.

기본값은 asc() 오름차순이며, desc()는 내림차순이다. age를 내림차순으로 반환한다.

 

 

(11) 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)

Query문 실행 결과에서 limit 크기만큼을 반환한다.

즉 위의 코드는 age를 내림차순 한 것에서 5개의 데이터를 반환한다.

 

 

(12) 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()).off_set(off_set)
    return " ".join(i.name for i in member_list)

Query문 실행 결과에서 offset 크기만큼 앞에서부터 생략하고 반환한다.

즉 위의 코드는 age를 내림차순한 것에서 앞의 5개를 생략하고 반환한다.

 

 

(13) count

@app.route('/')
def list():
    member_list = Member.query.order_by(Member.age.desc()).count()
    return str(member_list)

Query문 실행 결과로 반환된 tuple수를 반환한다.

age를 내림차순하고 그 튜플들의 개수를 세준다.