기본 콘텐츠로 건너뛰기

[MySQL-Flask SqlAlchemy] Python 으로 ORM 완벽사용 (2)

[MySQL-Flask SqlAlchemy] Python 으로 ORM 완벽사용 (2)

건프의 소소한 개발이야기

건강한프로그래머 2016. 12. 1. 19:24

안녕하세요, 건프입니다.

앞에서 ORM 의 개념과, 클래스 지정 방법에 대해서 알아봤습니다.

이번에는 ORM 방법으로 Flask-Sqlalchemy 에서는 어떤식으로 DB 에서 Query 하는지를 알아보고, 실전에서 자주 사용하는 여러가지 기법들에 대해서도 간단하게 메모해보려고 합니다.

첫번째로 일반적으로 테이블에서 값을 가져오는 Query 인 Select 하는 방법을 먼저 알아봅니다.

def search_events_by_userid (user_id):

"""

user_id 를 기준으로 event 찾기, all 로 찾음

"""

return TravelEvent.query.filter_by( user_id =user_id).all()

def search_event_by_eventid (event_id):

"""

event_id 를 기준으로 event 를 찾기. 한개만 리턴

"""

return TravelEvent.query.filter_by( event_id =event_id).first()

이렇게 두개의 예제가 간단하게 확인할 수 있는 방법입니다.

search_events_by_userid 로 정의된 함수는 TravelEvent 라는 디비클래스를 이용해서 쿼리를 날립니다.

저기 있는 TravelEvent 라는 클래스는 ORM 완벽사용 (1) 에서 처럼 정의한 db 클래스입니다.

from datetime import datetime

class TravelEvent (db.Model):

__tablename__ = 'travel_event'

__table_args__ = { 'mysql_collate' : 'utf8_general_ci' }

event_id = db.Column(db.Integer , primary_key = True , unique = True , autoincrement = True ) # primary key for event table

user_id = db.Column(db.String( 30 )) # 어느 사용자가 이벤트를 오픈했는지

course_id = db.Column(db.Integer) # 어느 코스를 사용하는지, 해당 코스의 detail 정보를 모두 가져올 수 있다

title = db.Column(db.String( 30 )) # 해당 모집글의 제목정보

description = db.Column(db.String( 1000 )) # 사용자가 작성할 이벤트의 설명글

max_tourist = db.Column(db.Integer) # 최대 모집자 수

current_tourist = db.Column(db.Integer) # 현재 모집자 수

travel_start_time = db.Column(db.DateTime) # 여행 시작 날짜와 시간

travel_end_time = db.Column(db.DateTime) # 여행 끝 날짜와 시간

event_end_time = db.Column(db.DateTime) # 모집이 끝나는 시간, 모집글이 등록되는 순간부터 모집 시

status = db.Column(db.Integer) # 모집여부, 0: 모집 끝, 1:모집 진행중

hash_tag = db.Column(db.String( 200 )) # 검색을 위해서 이벤트가 지나가는 경로에 대한 해시태그

created = db.Column(db.DateTime)

def __init__ ( self , user_id , course_id , title , description , max_tourist , start_time , end_time , event_end_time , hash_tag):

self .user_id = user_id

self .course_id = course_id

self .title = title

self .description = description

self .max_tourist = max_tourist

self .current_tourist = 1 # 처음에는 무조건 본인 한명

self .travel_start_time = start_time

self .travel_end_time = end_time

self .event_end_time = event_end_time

self .status = 1 # 처음에 등록하면 무조건 모집 중 상태

self .hash_tag = hash_tag

self .created = datetime.now() # 만들면 현재 날짜를 등록

def as_dict ( self ):

return {x.name: getattr ( self , x.name) for x in self .__table__.columns}

별로 중요하진 않지만, 모호하게 설명하는 것을 안좋아해서 첨부합니다.

TravelEvent.query.filter_by( user_id =user_id).all()

TravelEvent.query 객체를 부른다음, filter_by() 함수에서 user_id 가 매개변수로 들어온 user_id 에 매칭이 되는 애들을 찾아서, all() : 모두 리턴해달라.

TravelEvent.query.filter_by( event_id =event_id).first()

first() : 첫번째에 매칭되는 인스턴스만 달라

어떤 식으로 사용되는지 아시겠나요?

filter_by 의 경우 sql 에서 where 문을 생성해주는 것이라고 생각하면 될 것 같습니다.

여러 조건을 한번에 query 하고 싶은 경우에는 어떻게 할까요?

예를들어 where a == 'a' and b == 'b' 요렇게 말이죠.

from sqlalchemy import and_

queries = CourseDetail.query.filter(and_(CourseDetail.content_id == item[ 'content_id' ] ,

CourseDetail.sequence_id == item[ 'sequence_id' ])).all()

이렇게 진행하시면 됩니다. filter() 함수 안에서 flask-sqlalchemy 를 설치하면 함께 설치되는 sqlalchemy 안에 있는 and_ 함수를 이용하는 것이죠.

CourseDetail 로 정의되어 있는 Table 에서 content_id 와 sequence_id 가 정확히 일치되는 모든 인스턴스를 찾아서 리턴해주는 역할을 하게 됩니다.

정렬은 어떻게할까요?

일반적으로 정렬은 sql 문에서 order by 로 작업합니다.

new_course_id = CourseMeta.query.order_by(CourseMeta.created.desc()).first().course_id

이렇게 하시면 동작합니다.

CourseMeta 테이블에서 created 칼럼을 내림차순으로 정렬한 뒤에,

첫번째 인스턴스의

course_id 를 반환하는 예제입니다.

offset 과 limit 도 지정할 수 있을까요?

네 가능합니다.

def search_course_reviews (course_id , off_set= 0 , limit_num= 20 ):

if limit_num is None :

limit_num = 20

return CourseReview.query.filter_by( course_id =course_id).order_by(CourseReview.created.desc()).offset(off_set).limit(limit_num)

이 함수는 CourseReview 라는 테이블에서 course_id 로 필터하고, created 를 내림차순으로 정렬한뒤, offset 을 줘서 앞에서부터 스킵할 인스턴스 단위를 전달합니다. limit 는 한번에 가져올 인스턴스의 최대량을 의미하게 되죠.

이러한 방법은 sql 에서도 limit, offset 기능을 사용하는 것으로 예상하고, MongoDB 에서는 skip () 이라는 함수로 제공되는 것 같더군요?

클라이언트에서 버튼을 누르면 페이지나 내용물을 추가로 로딩하는 기능을 구현할때 유용합니다.

그렇다면 어떻게 인스턴스를 추가(Insert) 할까요?

from app import db

from app.models.event import TravelEvent

def add_new_event (new_event_object , course_id):

new_event = TravelEvent(new_event_object[ 'user_id' ] ,

course_id ,

new_event_object[ 'title' ] ,

new_event_object[ 'description' ] ,

new_event_object[ 'max_tourist' ] ,

new_event_object[ 'start_time' ] ,

new_event_object[ 'end_time' ] ,

new_event_object[ 'event_end_time' ] ,

new_event_object[ 'hash_tag' ])

db.session.add(new_event)

db.session.commit()

이 예제는 TravelEvent 테이블에 새로운 인스턴스를 추가하는 예제입니다.

TravelEvent 테이블을 클래스로 매핑했고(ORM) 클래스의 인스턴스를 만들때 생성자에 넣어야 할 것들을 정의했습니다.

그렇게 new_event 를 정의했고,

db.session.add() 함수에 해당 인스턴스를 넘긴뒤에

db.session.commit() 명령을 수행하면 됩니다.

db 객체는 ORM 완벽사용(1) 에서 봤듯이 app/__init__.py 안에 있던, 그 DB 객체를 가져온 것입니다.

없애는 것은 (delete) insert 와 워낙 닮았기 때문에 따로 언급하지 않겠습니다.

이미 넣었던 인스턴스의 정보를 어떻게 업데이트(Update) 하나요?

def user_join_event (user_id , event_id):

"""

유저가 이벤트에 참여하는 행동을 합니다

"""

new_user_bag = UserBag(user_id , 1 , event_id)

db.session.add(new_user_bag)

db.session.commit()

print (user_id , " 의 UserBag 에 " , event_id , " 를 추가했습니다" )

event = search_event_by_eventid(event_id)

current_tourist = event.current_tourist

if current_tourist + 1 <= event.max_tourist:

event.current_tourist=(event.current_tourist+ 1 )

db.session.commit()

return True

else :

# 이미 모집인원이 꽉차버림..

return False

다른 코드는 보지말고 우리가 원하는 update 하는 코드만 살펴보겠습니다.

event = search_event_by_eventid(event_id)

이렇게 해서 event_id 로 쿼리한 결과 인스턴스를 받아냅니다.

current_tourist = event.current_tourist

이렇게 하면, event 객체 안에 있는 현재 모집된 여행자수의 값을 받을 수 있습니다.

if current_tourist + 1 <= event.max_tourist:

따라서 이렇게 if 분기 연산을 진행할 수 있습니다.

event.current_tourist=(event.current_tourist+1)

db.session.commit()

event.current_tourist 맴버변수의 값을 새로운 값으로 업데이트 해주고

db.session.commit() 을 날려주면 업데이트가 완료됩니다!

사용하면 사용할 수록 ORM 의 뼈대가 되는 db.Model 의 역할이 너무나도 대단하는 생각이 듭니다. ㅎㅎ

이렇게 Flask-sqlalchemy 를 이용해 ORM 방법으로 디비를 손쉽게 관리하는 방법에 대해서 메모해보았습니다.

예전에 PHP 로 디비작업을 할때는 어떻게든 sql query 문을 객체지향적으로 짜보려고, 쿼리문을 만드는 make 함수를 두고, 테이블 이름과 칼럼명과 값을 넘겨가면서 객체지향적으로 짜보려는 노력을 했었었는데

Python 에서는 위와 같이 이미 정형화된 패턴이 있기 때문에 정말 편한것 같습니다! 갓파이썬

실전적으로 도움이 되기 위해서 제가 진행했었던 프로젝트를 예제로 따와서 전체적인 맥락은 볼 수 없어 불편하시겠지만

ORM 을 사용하는 방법을 익히는데는 큰 무리가 없을 것이라 생각합니다.

질문은 언제나 환영합니다.

도움이 되었길 바랍니다.

고맙습니다 :)

from http://ljs93kr.tistory.com/60 by ccl(A) rewrite - 2020-03-06 13:54:20

댓글

이 블로그의 인기 게시물

Coupang CS Systems 채용 정보: 쿠팡 운용 관리 시스템을 구축 하고...

Coupang CS Systems 채용 정보: 쿠팡 운용 관리 시스템을 구축 하고... Global Operation Technology는 상품을 고객에게 지연 없이 전달 될 수 있도록 하는 조직입니다. 1997년, 초창기 아마존에 입사한다고 상상해보세요. 그 당시 누구도 e-commerce 산업이, 아마존이라는 회사가 지금처럼 성장하리라고는 생각하지 못했을 것입니다. 하지만, 그 당시 아마존을 선택한 사람들은 e-commerce 산업을 개척했고, 아마존을 세계적인 회사로 성장시켰습니다. 2016년 '아시아의 아마존'으로 성장하고 있는 쿠팡, 당신에게 매력적인 선택이 아닐까요? Global Operation Technology: eCommerce에서 주문을 한 뒤 벌어지는 상황에 대해서 호기심을 가져보신 적이 있나요? Global Operation Technology는 상품을 고객에게 지연 없이 전달 될 수 있도록 하는 조직입니다. 매일 최첨단 소프트웨어 기술을 이용해 고객의 주문을 받고 상품을 어느 창고에서 출고 시킬지, 포장을 하나의 박스 또는 여러 개로 나눌 것인지, 어떤 배송 루트를 선택하고 어떻게 고객에게 배송 상태를 보여줄지 결정하는 시스템과 서비스를 개발 합니다. What Global Operations Technology does: CS and C-Returns System 적극적 고객서비스를 바탕으로 고객의 목소리를 통해 끊임없이 고객 에게 서비스를 제공하고 Andon 메커니즘을 통해 고객의 목소리를 회사 전체와 공유합니다. 그리고 고객 문제 해결과 구매 이후 벌어질 수 있는 고객 문제를 사전에 예방하기 위한 시스템 개발을 통해 미래의 상황을 예측 합니다. Tranportation System TSP (Traveling Salesman Problem) 와 같은 CS 최적화 관리 문제를 다룹니다.배송 물품의 실시간 추적, 3P 하드웨어와 소프트웨어를 통합, 각 배송 루트에 할당되는 물량 예측하고 T...

[ubuntu] FLASK_APP

[ubuntu] FLASK_APP Development/Debugging 🐞 FLASK_ENV=development FLASK_APP = app.py flask run zsh: command not found: FLASK_APP ✔️ FLASK_ENV=development FLASK_APP=app.py flask run 띄어쓰기를 해서 저런 오류를 출력할수도 있구나 😲 참고 : 108p에서 FLASK가 FKAS로 오타나있다. from http://hee-stories.tistory.com/18 by ccl(A) rewrite - 2020-03-24 17:20:11

[GCP] Argo로 Workflow 만들기

[GCP] Argo로 Workflow 만들기 사실 Production 레벨로 가지 않으면, ML개발에 Workflow를 사용할 일은 많지 않다. 대부분 샘플데이터로 전처리 한후 그 데이터를 공유해서 각자 모델을 개발하게 되는데, Production Level에서는 계속 새로운 데이터가 발생하기 때문에 데이터 수집부터 배포까지 하나의 파이프라인으로 관리해야할 필요성이 생긴다. Argo는 컨테이너 기반으르 파이프라인을 구성해주는 도구로 Kubeflow에서도 Workflow Orchestration은 Argo를 사용한다. Kubeflow Pipeline Overview Argo 설치 curl -sSL -o /usr/local/bin/argo https://github.com/argoproj/argo/releases/download/v2.2.1/argo-linux-amd64 chmod +x /usr/local/bin/argo Argo를 위와 같이 다운로드 받고, Controller와 UI를 kubectl을 통해 설치한다. GCP에서 kubectl의 설치는 아래를 따르면 된다. 터미널에서 Kubectl 사용하기 kubectl create ns argo kubectl apply -n argo -f https://raw.githubusercontent.com/argoproj/argo/v2.2.1/manifests/install.yaml Argo를 통해 간단한 'Hello World'예제를 실행해보자. 사용법은 아래와 같이 간단하다. submit은 지정된 yaml 파일을 workflow 만드는데 사용한다는 것이고 watch 파라미터는 외부의 yaml을 가져올 때 사용한다. argo submit --watch https://raw.githubusercontent.com/argoproj/argo/master/examples/hello-world.yaml 'argo list' 명령으로 실행되고 있는 argo wor...