728x90
728x90
파이썬에서 ORM(Object Relational Mapping) 라이브러리 사용해보기 (SQLAlchemy)
들어가며
- 파이썬에서 SQLAlchemy ORM(Object Relational Mapping) 라이브러리를 사용해보자.
ORM(Object Relational Mapping)
개념
- 데이터베이스를 사용하려면 SQL 쿼리(Query)라는 구조화된 질의를 작성하고 실행하는 등의 복잡한 과정이 필요하다.
- 이때 ORM(Object Relational Mapping)을 이용하면 파이썬 문법만으로도 데이터베이스를 다룰 수 있다.
- 즉, ORM을 이용하면 개발자가 쿼리를 직접 작성하지 않아도 데이터베이스의 데이터를 처리할 수 있다.
- ORM은 데이터베이스에 데이터를 저장하는 테이블을 파이썬 클래스로 만들어 관리하는 기술로 이해해도 좋다.
- 예를 들어 다음과 같이 @QUESTION@ 테이블이 있다고 해보자.
@QUESTION@ 테이블
id | subject | content |
1 | 오늘의 날씨 | 오늘의 날씨는 어때요? |
2 | 안녕하세요? | 만나서 반가워요. |
... | ... | ... |
- 위의 테이블을 SQL(Structured Query Language)을 이용하여 구성하려면 아래와 같은 코드를 작성해야 한다.
INSERT INTO QUESTION(subject, content) VALUES("오늘의 날씨", "오늘의 날씨는 어때요?");
INSERT INTO QUESTION(subject, content) VALUES("안녕하세요?", "만나서 반가워요.");
- 하지만, ORM(Object Relational Mapping)을 이용하면 아래와 같은 파이썬 코드로 작성할 수 있다.
question1 = Question(subject="오늘의 날씨", content="오늘의 날씨는 어때요?")
db.session.add(question1)
question2 = Question(subject="안녕하세요?", content="만나서 반가워요.")
db.session.add(question2)
- 코드에서 @Question@은 파이썬 클래스이며, 이처럼 데이터를 관리하는 데 사용하는 ORM 클래스를 모델(Model)이라고 한다.
- 모델을 사용하면 내부에서 SQL 쿼리를 자동으로 생성해 주므로 직접 작성하지 않아도 된다.
ORM을 이용한 새 데이터 삽입 예는 코드 자체만 놓고 보면 양이 많아 보이지만 별도의 SQL 문법을 배우지 않아도 된다는 장점이 있어 훨씬 좋다.
장점
- ORM을 이용하면 데이터베이스 종류에 상관 없이 일관된 코드를 유지할 수 있어서 프로그램을 유지·보수하기가 편리하다.
- 또한 내부에서 안전한 SQL 쿼리를 자동으로 생성해 주므로 개발자가 달라도 통일된 쿼리를 작성할 수 있고 오류 발생률도 줄일 수 있다.
설치하기
- 파이썬 ORM 라이브러리 중 가장 많이 사용하는 @SQLAlchemy@를 설치해보자.
> pip install sqlalchemy
사용 방법
- 예제 코드와 함께 SQLAlchemy 사용 방법을 알아보자.
모듈 불러오기
from sqlalchemy import create_engine, Column, Integer, String, Sequence
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
- 불러온 각 함수와 클래스에 대한 설명은 아래와 같다.
모듈 | 설명 |
@create_engine@ 함수 | - SQLAlchemy에서 데이터베이스와의 연결을 설정하는 역할을 한다. - 연결 문자열을 받아 해당 데이터베이스에 연결하는 엔진(Engine) 객체를 생성한다. - 예) @create_engine('sqlite:///:memory:', echo=True)@ (SQLite를 메모리에 연결하는 엔진을 생성하고, @echo=True@는 생성되는 SQL을 터미널에 출력하도록 설정한다.) |
@declarative_base@ 함수 | - 클래스를 정의할 때 사용되는 기본 클래스를 생성한다. - 이 기본 클래스를 상속받은 클래스는 데이터베이스의 테이블을 나타내게 된다. - @Base = declarative_base()@와 같이 사용한다. |
@Column@ 클래스 | - 데이터베이스의 테이블에서 각 열(Column)을 나타낸다. - 예) @id = Column(Integer, Sequence('user_id_seq'), primary_key=True)@ (정수형의 @id@ 열을 정의하고, 이 열을 기본 키(Primary Key)로 설정한다.) |
@Sequence@ 클래스 | - 일련번호(Sequence)를 생성하는데 사용된다. - 주로 기본 키를 자동으로 생성할 때 활용된다. |
@sessionmaker@ 클래스 | - 데이터베이스와의 세션을 생성하는 역할을 한다. - 세션은 트랜잭션과 관련된 작업을 처리하며, 데이터베이스와의 상호 작용을 담당한다. - 예) @Session = sessionmaker(bind=engine)@ (엔진에 바인딩된 세션을 생성한다.) |
@Session@ 객체 | - 실제로 데이터베이스와의 세션을 나타내는 인스턴스 - @session = Session()@과 같이 생성하여 사용한다. |
데이터베이스 연결하기
- 파이썬에 기본으로 내장되어 있는 @SQLite@에 연결해본다.
- 연결 문자열은 사용하는 데이터베이스에 따라 다르다.
- @echo@ 옵션을 @True@로 지정하여 생성되는 SQL을 터미널에 출력해보도록 한다.
engine = create_engine('sqlite:///:memory:', echo=True)
모델 정의하기
- 모델을 정의하여 파이썬 클래스를 테이블로 나타낸다.
Base = declarative_base()
클래스(테이블) 생성하기 (CREATE)
- 다음과 같이 @User@ 클래스를 선언한 후, 테이블 이름(@__tablename__@)을 @users@로 설정한다.
- 그리고 각 컬럼(@id@, @name@, @age@)을 추가한다.
- @id@를 기본 키(Primary Key) 및 정수형(@Integer@)으로 설정하였다.
- 각 컬럼(@Column@)의 인자에 해당 컬럼의 자료형을 넣어준다.
class User(Base):
__tablename__ = 'users'
id = Column(Integer, Sequence('user_id_seq'), primary_key=True)
name = Column(String(50))
age = Column(Integer)
- @Column@ 클래스의 매개 변수는 아래에서 확인할 수 있다.
더보기
- @Column@ 클래스의 매개 변수는 다음과 같다.
class Column(
__name_pos: str | type[TypeEngine[str]] | TypeEngine[str] | SchemaEventTarget | None = None,
__type_pos: type[TypeEngine[str]] | TypeEngine[str] | SchemaEventTarget | None = None,
*args: SchemaEventTarget,
name: str | None = None,
type_: type[TypeEngine[str]] | TypeEngine[str] | None = None,
autoincrement: _AutoIncrementType = "auto",
default: Any | None = None,
doc: str | None = None,
key: str | None = None,
index: bool | None = None,
unique: bool | None = None,
info: _InfoType | None = None,
nullable: bool | Literal[SchemaConst.NULL_UNSPECIFIED] | None = SchemaConst.NULL_UNSPECIFIED,
onupdate: Any | None = None,
primary_key: bool = False,
server_default: _ServerDefaultArgument | None = None,
server_onupdate: FetchedValue | None = None,
quote: bool | None = None,
system: bool = False,
comment: str | None = None,
insert_sentinel: bool = False,
_omit_from_statements: bool = False,
_proxies: Any | None = None,
**dialect_kwargs: Any
)
테이블 생성하기
- 아래의 명령을 실행하여 테이블(Table)을 생성한다.
Base.metadata.create_all(engine)
세션 생성하기
- 아래의 명령을 실행하여 세션(Session)을 생성한다.
Session = sessionmaker(bind=engine)
session = Session()
데이터 추가하기 (INSERT)
- 위에서 생성한 클래스(테이블)의 인자에 값을 넣어 새로운 클래스 객체를 생성한 후, @add@ 명령으로 테이블에 추가한다.
- 그리고 커밋(@commit@)을 해준다.
new_user = User(name='dev-astra', age=777) # 새로운 클래스 객체 생성
session.add(new_user)
session.commit()
데이터 조회 해보기 (READ)
- @query(User).all()@로 모든 데이터들을 불러온 후, 데이터들을 한 줄씩 불러와 출력한다.
users = session.query(User).all()
for user in users:
print(user.id, user.name, user.age)
데이터 업데이트(수정) 해보기 (UPDATE)
- @filter_by@ 함수를 이용하여 조건에 맞는(@name='dev-astra'@) 데이터를 불러온 후, 나이(@age@)를 @123@으로 바꿔본다.
- 데이터를 업데이트를 하면 반드시 커밋(@commit@)을 해줘야 업데이트된 내용이 반영된다.
# 데이터 업데이트
user_to_update = session.query(User).filter_by(name='dev-astra').first()
if user_to_update:
user_to_update.age = 123
session.commit()
# 업데이트 후 데이터 조회
updated_users = session.query(User).all()
for user in updated_users:
print(user.id, user.name, user.age)
데이터 삭제 해보기 (DELETE)
- @filter_by@ 함수를 이용하여 조건에 맞는(@name='dev-astra'@) 데이터를 불러온 후, @delete@ 함수를 이용하여 해당 데이터를 삭제해본다.
- 데이터를 삭제한 후 반드시 커밋(@commit@)을 해줘야 삭제된 내용이 반영된다.
# 데이터 삭제
user_to_delete = session.query(User).filter_by(name='dev-astra').first()
if user_to_delete:
session.delete(user_to_delete)
session.commit()
# 삭제 후 데이터 조회
remaining_users = session.query(User).all()
for user in remaining_users:
print(user.id, user.name, user.age)
세션 닫기
- 모든 작업을 완료했으면 반드시 세션을 닫아준다.
session.close()
전체 코드
from sqlalchemy import create_engine, Column, Integer, String, Sequence
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# 데이터베이스에 연결 (연결 문자열은 사용하는 DBMS에 따라 다름)
engine = create_engine('sqlite:///:memory:', echo=True)
# 모델 정의
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, Sequence('user_id_seq'), primary_key=True)
name = Column(String(50))
age = Column(Integer)
# 테이블 생성
Base.metadata.create_all(engine)
# 세션 생성
Session = sessionmaker(bind=engine)
session = Session()
# 데이터 추가
new_user = User(name='dev-astra', age=777)
session.add(new_user)
session.commit()
# 데이터 조회
users = session.query(User).all()
for user in users:
print(user.id, user.name, user.age)
# 데이터 업데이트
user_to_update = session.query(User).filter_by(name='dev-astra').first()
if user_to_update:
user_to_update.age = 123
session.commit()
# 업데이트 후 데이터 조회
updated_users = session.query(User).all()
for user in updated_users:
print(user.id, user.name, user.age)
# 데이터 삭제
user_to_delete = session.query(User).filter_by(name='dev-astra').first()
if user_to_delete:
session.delete(user_to_delete)
session.commit()
# 삭제 후 데이터 조회
remaining_users = session.query(User).all()
for user in remaining_users:
print(user.id, user.name, user.age)
# 세션 닫기
session.close()
c:\databaseEx1.py:9: MovedIn20Warning: The ``declarative_base()`` function is now available as sqlalchemy.orm.declarative_base(). (deprecated since: 2.0) (Background on SQLAlchemy 2.0 at: https://sqlalche.me/e/b8d9)
Base = declarative_base()
2023-11-14 11:47:10,818 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2023-11-14 11:47:10,819 INFO sqlalchemy.engine.Engine PRAGMA main.table_info("users")
2023-11-14 11:47:10,819 INFO sqlalchemy.engine.Engine [raw sql] ()
2023-11-14 11:47:10,820 INFO sqlalchemy.engine.Engine PRAGMA temp.table_info("users")
2023-11-14 11:47:10,820 INFO sqlalchemy.engine.Engine [raw sql] ()
2023-11-14 11:47:10,821 INFO sqlalchemy.engine.Engine
CREATE TABLE users (
id INTEGER NOT NULL,
name VARCHAR(50),
age INTEGER,
PRIMARY KEY (id)
)
2023-11-14 11:47:10,821 INFO sqlalchemy.engine.Engine [no key 0.00067s] ()
2023-11-14 11:47:10,822 INFO sqlalchemy.engine.Engine COMMIT
2023-11-14 11:47:10,823 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2023-11-14 11:47:10,824 INFO sqlalchemy.engine.Engine INSERT INTO users (name, age) VALUES (?, ?)
2023-11-14 11:47:10,825 INFO sqlalchemy.engine.Engine [generated in 0.00053s] ('dev-astra', 777)
2023-11-14 11:47:10,825 INFO sqlalchemy.engine.Engine COMMIT
2023-11-14 11:47:10,826 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2023-11-14 11:47:10,828 INFO sqlalchemy.engine.Engine SELECT users.id AS users_id, users.name AS users_name, users.age AS users_age
FROM users
2023-11-14 11:47:10,828 INFO sqlalchemy.engine.Engine [generated in 0.00031s] ()
1 dev-astra 777
2023-11-14 11:47:10,830 INFO sqlalchemy.engine.Engine SELECT users.id AS users_id, users.name AS users_name, users.age AS users_age
FROM users
WHERE users.name = ?
LIMIT ? OFFSET ?
2023-11-14 11:47:10,830 INFO sqlalchemy.engine.Engine [generated in 0.00036s] ('dev-astra', 1, 0)
2023-11-14 11:47:10,832 INFO sqlalchemy.engine.Engine UPDATE users SET age=? WHERE users.id = ?
2023-11-14 11:47:10,832 INFO sqlalchemy.engine.Engine [generated in 0.00044s] (123, 1)
2023-11-14 11:47:10,833 INFO sqlalchemy.engine.Engine COMMIT
2023-11-14 11:47:10,834 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2023-11-14 11:47:10,834 INFO sqlalchemy.engine.Engine SELECT users.id AS users_id, users.name AS users_name, users.age AS users_age
FROM users
2023-11-14 11:47:10,835 INFO sqlalchemy.engine.Engine [cached since 0.006951s ago] ()
1 dev-astra 123
2023-11-14 11:47:10,836 INFO sqlalchemy.engine.Engine SELECT users.id AS users_id, users.name AS users_name, users.age AS users_age
FROM users
WHERE users.name = ?
LIMIT ? OFFSET ?
2023-11-14 11:47:10,836 INFO sqlalchemy.engine.Engine [cached since 0.006343s ago] ('dev-astra', 1, 0)
2023-11-14 11:47:10,837 INFO sqlalchemy.engine.Engine DELETE FROM users WHERE users.id = ?
2023-11-14 11:47:10,837 INFO sqlalchemy.engine.Engine [generated in 0.00031s] (1,)
2023-11-14 11:47:10,837 INFO sqlalchemy.engine.Engine COMMIT
2023-11-14 11:47:10,838 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2023-11-14 11:47:10,838 INFO sqlalchemy.engine.Engine SELECT users.id AS users_id, users.name AS users_name, users.age AS users_age
FROM users
2023-11-14 11:47:10,838 INFO sqlalchemy.engine.Engine [cached since 0.01119s ago] ()
2023-11-14 11:47:10,838 INFO sqlalchemy.engine.Engine ROLLBACK
참고 사이트
728x90
728x90
'Programming > Python' 카테고리의 다른 글
[Python] try-except 문 사용할 때 에러 발생 시, 전체 에러 정보 표시 방법 (2) | 2024.09.07 |
---|---|
[Python] 데이터 전처리 할 때 결측값 제거 방법 (Pandas) (1) | 2024.06.21 |
[Python] or 연산자와 | 연산자의 차이 (0) | 2024.05.29 |
[Python] self (0) | 2023.11.29 |
[Python] HTML 코드를 이미지로 변환하는 방법 (Html2Image) (0) | 2023.11.06 |
[Python] 파이썬을 이용하여 텔레그램(Telegram) 메시지 보내는 방법 (0) | 2023.11.06 |
[Python] 파이썬을 이용하여 이메일 보내는 방법 (smtplib, email) (0) | 2023.11.02 |
[Python] 파이썬에서 / 연산자와 // 연산자의 차이점 (0) | 2023.10.09 |