[SQLAlchemy] Python ORM

원래 학부때 DB와 담쌓고 살았었는데, 살다보니 그게 안되더라
C, C++, Java, Python을 넘나드며 DB에 연결하기 다반사다.
하지만 가장 큰 문제는 매번 DB 클래스와 쿼리를 직접 만들어줘야했던것
무척 귀찮고 힘든 작업이다.

그런와중 현업자 친구들이 ORM을 알려줬다.
ORM은 Object-Relation Mapping의 줄임말로, 말 그대로 객체와 테이블을 매핑해준다는 것이다.
뭐.. 듣기만 해도 최고다.
쓰는 법도, 왜 편리한지도 묻지도 따지지도 않고 바로 해봤다.

SQLAlchemy가 널리 쓰이는 것 같다.
자바쪽에서는 hibernate나 mybatis가 많이 쓰인다고 얘기는 들어봤지만
실제로 ORM을 직접 돌려보는건 이번이 처음이다.

순서는 이렇다

  • 1. SQLAlchemy 설치
  • 2-a. 드라이버(Engine) 로드
  • 2-b. 세션(Session) 로드
  • 2-c. 매핑 클래스 정의
  • 3. 쿼리(Execute)
  • 4. 커밋(Commit)

2를 세부항목으로 나누어놓은건 순서가 상관없기 때문이다.
Session이 Engine에 종속적이긴 하지만, 그렇다고 반드시 Engine을 정의한 후 Session을 정의해야 하는건 아니다.
다시말해 Session은 동적으로 Engine에 연결된다.

기존의 pymysql이나 c에서의 libmysql 등 conventional sql scheme과 다른 점은 아무래도
2-c. 클래스 정의4. 커밋이다.
기존에는 ORM이 없기 때문에 DB 테이블과 매핑을 위해 클래스를 직접 구현했어야 했다.
그러나 ORM이 있는 지금은 클래스 정의만으로 테이블과 자동 매핑이 된다.
또한 커밋의 개념이 있다.
모든 CRUD 작업을 pending하고있다가 한꺼번에 모아 commit할 수 있다.
말 그대로 Transaction이며 Batch Process이다.
물론 undo도 가능하다.

이제 코드를 봐보자

우선 SQLAlchemy를 설치한다.

pip3 install sqlalchemy

나는 mariadb를 사용하기 때문에 mysql 모듈도 설치해줘야한다.
(Python에서의 mysql connector)

pip3 install mysqlclient

끝나면 이제 사용하면 된다.

Python에서 사용하기 위해서는 먼저 드라이버를 로드해줘야 한다.

from sqlalchemy import create_engine
engine = create_engine('mysql+mysqldb://<username>:<password>@<host>:<port>/<dbname>')

로드가 잘됐다면 아무거나 실행해보자
실행이 안된다면 당연히 로드가 안된것이다.
참고로 execute query의 결과는 fetchone나 fetchall로 가져올 수 있다.
차이는 이름 그대로이다.

print(engine.execute("desc test").fetchone()) # test table의 scheme을 출력
>>> ('a', 'int(11)', 'YES', '', None, '')

끝나면 이제 세션을 연결해보자

from sqlalchemy.orm import sessionmaker
Session = sessionmaker(engine) # Engine에 종속적인 Session을 정의하고
session = Session() # Session 객체를 만든다

그리고 ORM을 위한 클래스를 만들어보자
참고로 DB 스키마는 다음과 같다.

$ mysql -u <username> -p -e "use <dbname>; desc <tablename>"
+-------+-----------+------+-----+-------------------+-------+
| Field | Type      | Null | Key | Default           | Extra |
+-------+-----------+------+-----+-------------------+-------+
| a     | int(11)   | YES  |     | NULL              |       |
| b     | int(11)   | YES  |     | NULL              |       |
| time  | timestamp | NO   | PRI | CURRENT_TIMESTAMP |       |
+-------+-----------+------+-----+-------------------+-------+

매핑 클래스를 정의하기 전에, 실제 DB 테이블과 연결될 수 있도록 base 클래스를 생성해주어야 한다.

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

이제 Base 클래스를 상속받는 매핑 클래스를 만들어주면 DB와 연동되는 매핑 클래스로 변모한다.

>>> import datetime
>>> from sqlalchemy import Column, Integer, String, DateTime
>>> class User(Base):
...     __tablename__ = '<tablename>'
...     a = Column(Integer)
...     b = Column(Integer)
...     time = Column(DateTime, default=datetime.datetime.utcnow, primary_key=True)
...     def __init__(self, a, b):
...             self.a = a
...             self.b = b
... 

물론 프로그램에서 time에는 데이터가 안들어가겠지만, 이걸 DB에 insert하면 default로 지정된 현재시간이 자동으로 삽입된다.

이제 남은건 CRUD뿐이다.

user = User(1,2)
session.add(user)
session.commit()

아!
놀랍게도 쉽다!
레코드를 만들고 세션에 추가한 후 커밋한다.
이제 귀찮게 insert into 뭐시기를 입력하지 않아도 된다.
아주 좋다.

그 외에도 이런 저런 기능들이 많지만 일단 여기까지만 정리한다.
참 편하다.
다만 언어별로 공통된 ORM이 없다는 점, 그리고 ORM 자체가 무거울수밖에 없다는 점 등이 단점이긴 하다.
그래도 쿼리 직접 안쓰는것만으로도 프로그래머를 고통의 굴레에서 벗어나게 한다.
이제 남은건 불타는 코딩뿐이야

Hits: 202

댓글 남기기