모든 코드의 바탕은 sqlalchemy의 Docs기반입니다
(https://docs.sqlalchemy.org/en/13/orm/tutorial.html)
Insight
- print(‘python manage.py migrate 안녕…')
- SELECT "Hello World!";
- 파이썬 쉘 스크립팅을 통한 DB table 생성 및 접속
- 무엇인지 알았지만 어디서 쓰이는지 백엔드 서버입장에서 다시 보이게 되는 Oracle DB의 instance
Django에서 기존 DjanoORM을 안쓰고 SQLalchemy(ORM)로 쓰는 이유는?
1.실제 DB의 운영과 맞지 않습니다. (기본적인 CRUD 사용할 것은 괜찮음)
2.기존장고ORM은 N+1 쿼리문제시 for문으로 쿼리를 요청하면 쿼리가 매우 많아져 대용량 데이터 처리에 매우 비효율적 입니다.
(https://zetawiki.com/wiki/N%2B1_%EC%BF%BC%EB%A6%AC_%EB%AC%B8%EC%A0%9C) [쿼리는 비싸다....]
2-1. 기존ORM에서 select_related()라는 함수로 쿼리를 한 번만 불러와서 해결 시켜줄 수있지만
foreign_key, one-to-one 관계에서만 해결이 되는 것이고 (Inner Join문 을사용하는 한번의
쿼리)
many-to-one, many-to-many 는 prefetch_related()를 사용하는데 메인쿼리 이외 별도
쿼리들을 생성하여 실행시킨다.
2-2. 위와 같이 제한적으로 핸들링은 할수 있지만 sql.raw()나 다른 직접 sql을 쓰는 ORM을
직접 DB를 컨트롤 하기에는 효과적입니다.!
3.DjangoORM은 대용량 사용하기에 아직까진 부족한 면이 있습니다.(하지만 꾸준한 업데이트로 Djanog3.0버전 릴리즈 장고 짱!)
더 자세한 비교는 여기 (https://www.eversql.com/django-vs-sqlalchemy-which-python-orm-is-better/)
기존의 셋팅 환경
- 환경은 ubuntu 18.04에서 진행하였습니다.
- 가상환경은 conda를 활용하였습니다.
- 파이썬은 3.7버전이고
- mysql-server는 5.7버전을 사용 하였습니다.
(mysql-server 8.0버전과 그 이상은 거의 비슷하나
https://mysql.wisborg.dk/2019/03/03/using-sqlalchemy-with-mysql-8/
를 참조 하세요)
필요한 사전 지식
SQL(DDL,DML,DCL)
Cases by DB setting in Django models
Python OOP (__init__,__repr__ Magic method), Sequence
MysqlDB, Pool, Cache, Transaction, Session,Dialect.DBAPI(https://docs.sqlalchemy.org/en/13/core/engines.html)
필요한 라이브러리
Conda 내
- sqlalchemy(SQL-ORM)
- mysqlclient(DBAPI)
- Shapely(mysqlclient가 잘 설치 안될때)
사전 설치
1.conda에서 mysqlclient를 설치하기 전 터미널에서 세팅을 해줘야 합니다.
파이썬 라이브러리로서 sqlalchemy 공식 Docs에서는 mysqlclient PyMySQL을 권장합니다.
https://pypi.org/project/mysqlclient/
(macOS 및 py2버전 은 위의 링크에 있습니다.)
리눅스에 원하는 환경에 맞추어 python3-dev를
sudo apt-get install python3-dev # debian / Ubuntu
sudo yum install python3-devel # Red Hat / CentOS
- 그래도 안된다면
- sudo apt-get install python-dev default-libmysqlclient-dev # Debian / Ubuntu
- sudo yum install python-devel mysql-devel # Red Hat / CentOS
- brew install mysql-client # macOS (Homebrew)
기본 요건을 설치 후 conda에서 mysqlclient를 설치
pip install mysqlclient
#사전 설치 issue가 좀 있어서 에러가 나시면 아래의 링크의 댓글을 참조
부탁드립니다(https://elfinlas.github.io/2019/01/23/pip-mysql-error/)
2.conda에서 sqlalchemy 설치합니다.
pip install sqlalchemy
3.settings.py 에서의 DATABASES관련 주석처리 합니다.
따로 모듈 파일을 만들어서 사용하겠습니다.
추가로 경고 메시지 무시를 위해 아래와 같은 코드를 맨 밑에 넣어 줍니다.
##Stop Warning about '/'
APPEND_SLASH=False
전체 코드 보기
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
#데이터 베이스에 접속한다.
DATABASES = create_engine('mysql+mysqldb://<user>:<password>@<localhost>:<port>:<databaseName>', echo = True)
# orm과의 매핑을 명시하는 함수를 선언한다.
Base = declarative_base()
class Tada(Base):
__tablename__ = 'tada'
id = Column(Integer, primary_key=True)
name = Column(String(50))
fullname = Column(String(50))
password = Column(String(50))
def __init__(self, name, fullname, password):
self.name = name
self.fullname = fullname
self.password = password
def __repr__(self):
return "<Tada('%s', '%s', '%s')>" % (self.name, self.fullname, self.password)
if __name__ == '__main__':
# Database를 없으면 생성 또는 사용의 의미 django에서 create_or_update() (table) 같은것
Base.metadata.create_all(DATABASES)
# 세션을 만들어서 연결시킨다.
Session = sessionmaker()
Session.configure(bind=DATABASES)
session = Session()
# 위의 클래스,인스턴스 변수를 지킨 다음에
tada = Tada('ks','ks','1111')
# 세션에 추가를 한다.
session.add(tada)
session.commit()
세부 코드 보기
1. 데이터 베이스에 일단 접속을 합니다. <>에 필요하신 값을 대체 넣어주시면 됩니다.
(<>괄호는 넣지 않습니다)
<databaseName>는 미리 mysql에 존재하는 이름이여야 합니다.
#데이터 베이스에 접속한다.
DATABASES = create_engine('mysql+mysqldb://<user>:<password>@<localhost>:<port>:<databaseName>', echo = True)
2. ORM mapping 을 선언 해 Base를 선언합니다.
# orm과의 매핑을 명시하는 함수를 선언한다.
Base = declarative_base()
3. 클래스명을 선언합니다.
테이블 명은 __tablename__으로 지정합니다 (보통은 복수형으로 합니다)
클래스 변수에 Column을 명시하며 제약 조건도 넣습니다.
PK가 Integer일 경우 자동으로 DjangoORM의 AutoField 처럼 생성시 숫자를 증가시켜 줍니다.
__init__ 메서드로 실제의 값이 들어가서 저장될 변수를 선언합니다.
__repr__ 메서드로 컬럼의 값에 맞게 설정하여 <>로 return해줍니다.
<>로 묶는것은 쿼리셋으로 처리하기 위함이고
__str__은 비공식적인, 읽기 편한 매직매서드이므로 __repr__을 사용하셔야 합니다.
class Tada(Base):
__tablename__ = 'tada'
id = Column(Integer, primary_key=True)
name = Column(String(50))
fullname = Column(String(50))
password = Column(String(50))
def __init__(self, name, fullname, password):
self.name = name
self.fullname = fullname
self.password = password
def __repr__(self):
return "<Tada('%s', '%s', '%s')>" % (self.name, self.fullname, self.password)
4. 실제로 실행할 코드를 마지막에 명세합니다.
create_all()데이터 베이스의 테이블이 있는지 없는지 없으면 생성하고 있으면 추가하는 함수입니다.
세션은 사용자의 요청이라고 이해하시면 편합니다.
사용자 요청을 만들어 세션을 준비하고
원하는 row를 실어서
session.add()
session.commit()
합니다
if __name__ == '__main__':
# Database를 사용의 의미 django에서 create_or_update() (table) 같은것
Base.metadata.create_all(DATABASES)
# 세션을 만들어서 연결시킨다.
Session = sessionmaker()
Session.configure(bind=DATABASES)
session = Session()
# 위의 클래스,인스턴스 변수를 지킨 다음에
tada = Tada('ks','ks','1111')
# 세션에 추가를 한다.
session.add(tada)
session.commit()
*
mysql 5.7에서의 rank, rownum 함수는 따로 지원이 안되므로 아래의 링크를 참조 하길 바랍니다.
https://stackoverflow.com/questions/54239851/mysql-5-7-getting-the-row-number
mysql 5.7 rank, rownum
**
쿼리셋과 캐싱
쿼리셋은 DB에 Access 할 때 캐시 메모리를 포함하고 있습니다. 처음 쿼리셋이 연산될 때, 캐시가 비었기 때문에 Query가 발생합니다. 그 이후 동일한 쿼리셋을 사용할 경우 추가적인 Query는 발생하지 않고, 캐시에서 꺼내서 사용합니다.
(https://jay-ji.tistory.com/35)
***sqlalchemy cheatsheet
https://towardsdatascience.com/sqlalchemy-python-tutorial-79a577141a91
https://www.pythonsheets.com/notes/python-sqlalchemy.html
****reference
'Web > Web' 카테고리의 다른 글
장고에서 Unittest하기 (0) | 2019.11.12 |
---|