티스토리 뷰

Python

[웹 앱프로그래밍] Python Flask SQLAlchemy ORM

I like simple code 2020. 5. 21. 02:04
728x90

이전 글에서 sqlite를 이용해서 간단하게 db 사용법에 대해 알아봤습니다. 데이터베이스 시스템이 변경되는 상황이 발생하게 되면 쿼리나 소스를 변경해야 합니다. 유지보수가 상당히 힘들어집니다.

그리고 개발자 입장에서 SQL이 어렵게 느껴질 수도 있습니다. 프로그래밍으로 객체를 만들어 데이터베이스를 관리를 할 수 있는 방법이 있으면 개발에만 집중을 할 수 있겠죠.

그래서 ORM(Object Relaition Mapping), ORM(Object Relaition Model)이라고 하는 개념이 나왔습니다.

ORM 용어를 굳이 풀이를 하면 Object(객체)는 객체 지향 프로그래밍(OOP)에서 그 객체를 뜻하고 Relaition(관계)는 관계형 데이터베이스 관리 시스템(RDBMS)에서의 그 관계를 나타 냅니다. 즉 객체와 관계형 데이터 베이스를 연결(Mapping)을 하는 개념이라고 생각할 수 있습니다. 

 

 

다시 파이썬으로 돌아와 파이썬 객체(Class)에 데이터 모델(Model)을 정의해서 파이썬에서 만든 데이터 모델 객체와 데이터베이스와 연결을 시켜 줍니다. 이 ORM을 사용하게 되면 SQL을 잘 모르더라도 데이터 모델 객체를 이용해서 데이터를 저장하거나 검색 등을 할 수 있게 됩니다. 하지만 프로젝트가 커지면 모델 자체를 설계하고 작성하기도 쉽지는 않습니다. 물론 한 번만 제대로 만들면 재사용이 가능하고 유지보수도 용이하기 때문에 사용하는 것을 추천합니다.

파이썬에서 ORM으로 사용되는 SQLAlchemy가 있습니다. Flask에서 Flask-SQLAlchemy 패키지도 있습니다.

 

pip install sqlalchemy

이제 sqlalchemy 사용 가능하게 됩니다. sqlite를 비교하면서sqlalchemy 사용해보도록 하죠.

sqlite에서 sqlite3을 import 해서 사용한 거처럼 sqlalchemy을 import 시켜 사용하면 됩니다.

import sqlalchemy

먼저 ORM으로 만들기 때문에 데이터 모델을 만들어야 합니다. 이 데이터 모델이 테이블이 되는데 이 데이터 모델 객체와 데이터베이스 테이블을 연결해주는 Declarative라는 것을 사용해야 합니다. 클래스를 만들 파이썬 파일을 하나 만들어 아래와 같이 코드를 작성합니다. TableClass.py라는 이름으로 만들었습니다.

from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

저 Base를 우리가 만드는 데이터 모델 클래스 에로 상속시킵니다. 

student 테이블을 이미 있기 때문에 students라는 테이블을 생성을 해보죠. 안에 칼럼의 값은 학번, 이름까지는동일 한데 sqlalchemy에서

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, String

Base = declarative_base()
class Students(Base):
    __tablename__ = 'students'
    num = Column(String(50), primary_key=True)
    name = Column(String(50))
    def __init__(self, num, name):
        self.num = num
        self.name = name
    def __repr__(self):
       return "<Students('%s', '%s')>" % (self.num, self.name)

__tablename__ 에 선언된 이름이 테이블명이 되고 이 테이블은 Column()에 String(문자열)로 만든 num, name 2개의 칼럼을 가집니다.  num을 primary_key=True 속성을 주고 설정을 한 것을 볼 수 있습니다. 일반적으로는 id를 하나 만들어 자동으로 증가되는 숫자의 값을 사용합니다.

id = Column(Integer, primary_key=True)

여기서 String은 SQL에서 varchar와 같은데 길이를 정할 수 있습니다. 일반적인 DB시스템은 가변 길이 문자열을 선언할 때 길이를 정하는데 이전 글에서 우리는 sqlite로 만들 때 varchar(50) 이런 형식으로 사용했습니다.

마찬가로 String(50) 이렇게 길이를 지정을 해야 할 수 있습니다. 길이를 꼭 지정해야 하는 RDBMS 같은 경우 길이를 지정하지 않으면 오류가 날 수 있습니다.

파이썬에서 클래스를 만들 때 __init__ 과 __repr__ 을 만들어 사용하는데 여기서는 __repr__만 만들어 사용했습니다.

이제 데이터 모델 클래스를 우리 app.py 코드에 가서 생성해서 사용해 봅니다.

 

sqlite에서는 conn = sqlite3.connect('db 파일명') 이렇게 연결해서 사용했었습니다. sqlalchemy에서는

engine = sqlalchemy.create_engine('sqlite:///db파일명')

이렇게 사용합니다. create_engine() 함수에 echo=True 파라미터를 추가하면 SQL 코드 log를 볼 수 있습니다.

sqlite:///db파일명 여기서 db 파일명은 전체 경로를 써줘야 합니다. 만약 윈도우에서 작업을 하고 있다면 C:\\dev\\db\\mydb.db  이렇게 써줘야 합니다. 

지금은 sqlite로 연결해서 저런 식으로 사용 하지만 다른 DB 시스템을 사용할 땐 바꿔주면 됩니다.

테이블 생성은 create table 쿼리를 실행(excute) 시켜 만들었는데 ORM에서는 파이썬에서 데이터 모델 객체를 생성해서 사용합니다. 물론 engine.excute()를 사용하면쿼리도 쓸 수 있지만 ORM으로 만드는 것이 목표이기 때문에 데이터 모델 객체를 사용해서 테이블을 만들고 데이터를 넣어 보도록 합니다.

아래 코드로 테이블을 생성해줍니다.

Base.metadata.create_all(engine)

engine으로 연결된 DB에 테이블을 생성하게 됩니다. (sqlite:///여기에서 만들어진 DB파일)

필자는 저 코드를 TableClass.py에 함수로 만들어 두었습니다.

def create_tb(engine):
    Base.metadata.create_all(engine)

그리고 app.py 에서 호출해서 사용합니다.

이제 데이터를 테이블에 넣기 위한 작업을 해야 하는데 excute(쿼리)를 안 쓰고객체에 바인딩된 쿼리를 위해 Session 객체를 사용해야 합니다. 

session.add(), session.rollback(), session.commit(), session.close()

위와 같이 Session 객체를 사용해서 정상이면 commit 오류면 rollback을 할 수 있어트랜잭션을 단일 작업 단위로 관리하기 편합니다.

Session을

session.commit() 이 수행이 되어야 테이블에 데이터가 저장이 됩니다. 에러가 발생하면 session.rollback()을 수행하고 데이터가 저장되지 않게 코드를 작성했습니다.

sqlite excute를 사용하면

execute("CREATE TABLE student (num varchar(50), name varchar(50))")

execute("INSERT INTO student VALUES ('20201234', '파이썬')")

기존 코드에 부분 추가 또는 수정을 합니다.

import TableClass
import sqlalchemy
from sqlalchemy.orm import sessionmaker

engine = sqlalchemy.create_engine('sqlite:///C:\\dev\\db\\orm.db')
Session = sessionmaker(bind=engine)
# DB table 생성
TableClass.create_tb(engine)

@app.route('/method', methods=['GET', 'POST'])
def method():
    if request.method == 'GET':
        return 'GET 으로 전송이다.'
    else:
        num = request.form["num"]
        name = request.form["name"]
        
        # dbdb.insert_data(num, name)
        
        session = Session()
        st_data = TableClass.Students(num, name)
        try:
            session.add(st_data)
            session.commit()
        except Exception as e:
            session.rollback ()
            abort (500, 'Error. 데이터 저장 실패')
        return 'POST 이다. 학번은: {} 이름은: {}'.format(num, name)

 

데이터가 들어갔는지 확인을 해 봅니다. 이전에 만든 getinfo()를 수정해서 SQL 쿼리 대신 데이터를 가져오도록 하죠.

session.query(모델명)
session.query(모델명.컬럼)

이런 형식으로 쓰게 됩니다. 전체 데이터는. all() 검색은 filter() 또는 filter_by()를를 사용합니다.

수정된 getinfo() 코드입니다.

@app.route('/getinfo')
def getinfo():
#    info = dbdb.select_all()
    session = Session()
    info  = session.query(TableClass.Students.num, TableClass.Students.name).all()
    return render_template("info.html", data=info)

Mysql 데이터 베이스 서버로 변경해서 사용해봅니다. Mysql 서버로 연결하기 위해 데이터 베이스 정보가 필요합니다. host 주소, username, password, db 명의 정보를 알아야 합니다. 우리는 pythonanywhere에서 이 정보들을 다뤘습니다. 

Mysql을 연결시키기 위해서는 이렇게 사용합니다.

'mysql+mysqlconnector://<user>:<password>@<host>:<port>/<db>...'

뒤에 ... 은 설정값을 추가할 수 있습니다. 아래와 같은 설정 등

한글 설정을 위해 ?charset=utf8 

engine = sqlalchemy.create_engine('mysql+mysqlconnector://디비유저명:패스워드@Mysql디비주소:3306/mydb?charset=utf8')

 

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함