TIL - Flask

김영훈·2021년 5월 23일
0

ETC

목록 보기
14/34

# flask의 특징

  • flask는 django와 더불어 python을 기반으로 작동하는 웹 프레임워크다. 필자의 경우 웹 개발 공부를 django로 시작했기 때문에, 이번에 flask를 사용해보는 과정에서 자연스레 django와의 차이점이 궁금해졌다.

  • 첫 번째 특징은 가볍고 단순하다는 점이다.

    • flask는 URL 라우팅, Template, Cookie, Debugger 및 개발 서버 등 기본 기능만을 제공하는 Micro Framework다.
    • flask는 기본으로 제공되는 모듈에 새로운 모듈을 더해 확장이 가능한 구조다. 이는 웹 개발에 필요한 대부분의 기능을 갖춘 django와 대조적이다. 그렇기 때문에 데이터베이스 연결, 보안, 인증 등의 기능을 모두 개발자가 직접 처리해야 한다.
  • ORM 기능이 제공되지 않는다.

    • 그러므로 개발자 직접 sql문을 사용거나 패키지를 설치하는 방식을 선택해야 한다.
  • 종합하면 단순한 REST API 서버를 만들기에는 Flask가 django보다 효율적이라고 할 수 있다.

    Micro Framework??
    웹 개발에 필요한 최소한의 기능만을 갖춘 프레임워크. ORM 기능, 유효성 검사 기능, 인증·인가 기능 등을 제공하지 않는 것이 일반적이며, 주로 application의 API를 설계하는 데 사용된다.

# flask 사용법

  • 설치 및 불러오기

    • pip install flask
    • from flask import Flask, jsonify, request
      • jsonify: 데이터를 json형태로 보낼 때 사용되는 모듈
        • 한글이 제대로 출력되지 않는 경우: app.config['JSON_AS_ASCII'] = False 추가
      • request: client의 요청에 대한 정보를 확인할 수 있는 모듈
        • 요청 구분할 때: if request.method == 'GET':
        • 파라미터 value 접근할 때name = request.args.get('key', "None")
  • Flask 객체 생성 및 할당

    • app = Flask(__name__)
  • 객체를 이용해 라우팅 경로 설정

    • @app.route('/port 번호 뒤에 이어질 url 경로')
  • 라우팅 경로에 대한 실행 함수 작성

    • @app.route("/"
      def hello():
      	result = 'Hello world!'
      	return result
  • 실행할 서버 IP와 포트 입력

    • if __name__ == "__main__":
          app.run(debug=True, host='localhost', port=8000)
  • 서버 열기

    • terminal에서 해당 파일이 존재하는 경로로 이동
    • python 파일명.py

# 내가 작성한 code

  • app.py
from datetime import datetime

from flask import Flask, jsonify, request
from flask.signals import appcontext_tearing_down

from directquery        import DirectQuery

app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False

@app.route('/subway')
def get(): 
    if request.method == 'GET':
        name = request.args.get('station', None)
        direct_query = DirectQuery()

        if not name:
            return jsonify({"message" : "NO DATA"})
            
        data_list = direct_query.execute_query(

            f"SELECT sub_lines.name AS line, stations.*, names.name AS name, station_time_info.*, names.name as name2 FROM \
            subway_lines AS sub_lines, subway_stations AS stations, station_names AS names, \
            station_time_information AS station_time_info WHERE sub_lines.id = stations.subway_line_id AND \
            stations.station_id = station_time_info.station_id AND names.station_id = stations.station_id \
            AND (names.name LIKE '%{name}%' OR sub_lines.name LIKE '%{name}%' OR stations.station_code = \
            '{name}');"

        )
        if not data_list:
            return jsonify({"message" : "NO DATA"})
        
        result = [{
            '호선'        : data[0],
            '역 이름(한글)' : data[6],
            '역 외부 코드'  : data[4],
            '환승역 여부'   : 'Y' if data[5] == 1 else 'N',
            '역 시간 정보'  : [{
                '상/하행선'     : '상행선' if data[8] == True else '하행선',
                '요일 구분'     : '평일' if data[9] == 1 else '토요일' if data[9] == 2 else '휴일',
                '첫차 출발 시간' : (data[11] + datetime.min).time().strftime('%H시 %M분'),
                '첫차 도착역명'  : direct_query.execute_query(f"SELECT names.name FROM station_names AS names JOIN station_time_information AS time_info ON names.station_id = '{data[12]}' LIMIT 2;")[1][0] if data[12] != '0000' else None,
                '막차 출발 시간' : (data[13] + datetime.min).time().strftime('%H시 %M분'),
                '막차 도착역명'  : direct_query.execute_query(f"SELECT names.name FROM station_names AS names JOIN station_time_information AS time_info ON names.station_id = '{data[14]}' LIMIT 2;")[1][0] if data[14] != '0000' else None,
            }]} for data in data_list]    

        return jsonify({'result': result})

if __name__ == "__main__":
    app.run(debug=True, host='localhost', port=8000)
  • directquery.py
import pymysql
import db_config

class DirectQuery():    
    def execute_query(self, sql):
        conn = pymysql.connect(
            host     ='localhost', user=db_config.DATABASES['USER'],
            password =db_config.DATABASES['PASSWORD'], db=db_config.DATABASES['NAME'], 
            charset  ='utf8mb4', port=3306
        )
        try:
            with conn.cursor() as cursor:
                cursor.execute(sql)
                data   = [x for x in cursor.fetchall()] if cursor else None
                
            conn.commit()
            conn.close()
        except:
            data = 'sql error'
            
        return data 

# review

  • directquery.py에서 db 데이터를 가져올 때, 튜플 형태가 아닌 딕셔너리 형태로 가져오려면 pymysql.connect() 내부에 cursorclass=cursors.DictCursor를 추가해주면 된다.

         conn = pymysql.connect(
             host     ='localhost', user=db_config.DATABASES['USER'],
             password =db_config.DATABASES['PASSWORD'], db=db_config.DATABASES['NAME'], 
             charset  ='utf8mb4', port=3306, cursorclass=cursors.DictCursor
         )
    
  • SQL 쿼리문을 통해 db 데이터를 불러오는 횟수가 증가할수록 서버에 부담을 주게 된다. 그러므로 하나의 API에서 SQL쿼리문 사용을 한 번으로 줄이는 것이 바람직하다.

  • if __name__ == "__main__":이 사용될 경우, 상대경로가 아닌 절대경로를 사용하여 외부 모듈을 import해야 한다.

  • Datetime module

    • from datetime import datetime로 모듈을 import하면 datetime을 두 번 입력할 필요 없이 메소드 실행 가능 ex)datetime.now()

    • Mysql의 필드 타입인 time은 datetime.timedelta() 형식으로 입력된 시간을 return한다.

      • c.f) Mysql의 time 필드에 데이터를 입력할 때는 int 형태로 넣어주면 된다. Ex) 52433 —> 5시24분33초
    • datetime.timedelta() 형식을 일반적인 시간으로 표시하려면 다음과 같은 명령어를 사용하자

      • (datetime.timedelta 객체 + datetime.min).time().strftime()
profile
Difference & Repetition

0개의 댓글