Flask RESTful API - POST /stores/create (매장등록)

2019. 7. 2. 22:50Python/Flask

반응형

POST /stores/create

매장명, 지번주소 또는 도로명 주소정보를 알고 있는경우 Kakao 주소검색 API를 이용하여서 매장의 위치 정보를 저장한다.

Resource URL

http://localhost:5000/stores/create

Request 

{
"store_name":"스타벅스 김포장기커낼점",
"abbr_address":"",
"road_address":"경기 김포시 김포한강2로 41",
"phone":"031-986-3530"
}

Response

{
"message":"스타벅스 김포장기커낼점 매장 정보가 등록 되었습니다."
}

소스코드 작성


소스코드 작성은 다음과 같이 작성한다.

  • Kakao API 연동을 위한 인터페이스 파일 작성 - C:\istarbucks_api\code\inf\kakao_api.py
  • 모델파일작성 - C:\istarbucks_api\code\models\store.py
  • 사용자로부터 전달 받은 URL 처리 및 모델 연동을 위한 resource 파일 작성 - C:\istarbucks_api\code\resources\store.py
  • app.py 수정 - C:\istarbucks_api\code\app.py


kakao_api.py

Kakao API Local REST API 연동을  통하여 주소에 해당하는 장소정보 검색, 검색어에 해당하는 장소 목록을 조회한다.

from urllib.parse import urlparse
from bs4 import BeautifulSoup

from selenium import webdriver
from selenium.webdriver.support.select import Select
from selenium.webdriver.common.keys import Keys

import requests

import json

def find_place(x,y,radius,keyword,page):
    """
    검색어에 해당하는 장소 목록 조회

    param x: 경도(longitude)
    param y: 위도(latitued)
    param radius: 반경
    param keyword: 장소명
    param page: 페이지
    :return: result: 검색어에 해당하는 장소 목록
    """
    if page is None:
        page = 1

    key = "발급받은인증키"

    # API URL
    url = "https://dapi.kakao.com/v2/local/search/keyword.json?y={}&x={}&radius={}&query={}&page={}".format(y,x,radius,keyword,page)

    # API 호출
    result = requests.get(urlparse(url).geturl(),headers={"Authorization":"KakaoAK "+ key})

    return result.json()

def find_address(keyword):
    """
    검색어에 해당하는 주소 정보 조회

    param keyword: 주소
    :return: result: 지번주소,도로명주소 정보
    """
    key = "발급받은인증키"

    # API URL
    url = "https://dapi.kakao.com/v2/local/search/address.json?query={}".format(keyword)

    # API 호출
    result = requests.get(urlparse(url).geturl(),headers={"Authorization":"KakaoAK "+ key})

    return result.json()



store.py - Model


앞에서 생성한 store table의 맵핑되는 모델 파일을 작성한다. 뒤에 나오는 예제에서는 메소드만 추가해 나갈 것이다.

from db_init import db
from geoalchemy2.types import Geometry

class StoreModel(db.Model):
    # 물리 테이블명
    __tablename__ = "store"

    # 컬럼 맵핑
    store_id = db.Column(db.Integer, primary_key=True)
    store_name = db.Column(db.String(50))
    abbr_address = db.Column(db.String(100))
    road_address = db.Column(db.String(50))
    phone = db.Column(db.String(13))
    store_url = db.Column(db.String(100))
    grade = db.Column(db.Float)
    lat = db.Column(db.Float)
    lon = db.Column(db.Float)
    geo = db.Column(Geometry(geometry_type='POINT'))

    def __init__(self, store_id, store_name, abbr_address, road_address, phone, store_url, grade, lat, lon, geo):
        self.store_id = store_id
        self.store_name = store_name
        self.abbr_address = abbr_address
        self.road_address = road_address
        self.phone = phone
        self.store_url = store_url
        self.grade = grade
        self.lat = lat
        self.lon = lon
        self.geo = geo

    def save(store):
        """
        매장 정보 저장/수정

        :param store: 매장 정보 객체
        """
        db.session.merge(store)
        db.session.commit()

    @classmethod
    def convert_geom(self,point):
        """
        반경검색을 위해서 좌표계를 2097로 변환하기 위한 함수

        param point: PONT('경도 위도')
        """
        q = 'SELECT ST_Transform(ST_GeomFromText(\'{}\', 4326), 2097)'.format(point)
        geom = db.session.execute(q).first()

        return geom[0]

    def __str__(self):
        return "[" + str(self.__class__) + "]: " + str(self.__dict__)



store.py - Resource


from flask_restful import Resource, reqparse
from flask import jsonify

from flask_marshmallow import Marshmallow

from models.store import StoreModel
from inf.kakao_api import *

from models.store import StoreModel

"""
JSON 처리를 위한 Mashmallow 메타 설정
"""
ma = Marshmallow()

"""
단일 매장 목록 조회 시 사용할 메타
"""
class StoreSchema(ma.Schema):
    class Meta:
        # Fields to expose
        fields = ('store_id', 'store_name','abbr_address','road_address','phone','store_url','grade','lat','lon')

"""
매장 목록 조회 시 사용할 메타
"""
class StoresSchema(ma.Schema):
    class Meta:
        # Fields to expose
        fields = ('store_id', 'store_name','abbr_address','road_address','phone','store_url','grade','lat','lon','distance')

"""
POST /stores/create - 매장 등록
"""
class StoreRegister(Resource):

    # 입력 파라미터 설정
    parser = reqparse.RequestParser()

    parser.add_argument('store_name',
        type=str,
        required=True,
        help="This field cannot be blank."
    )
    parser.add_argument('abbr_address',
        type=str,
        required=True,
        help="This field cannot be blank."
    )
    parser.add_argument('road_address',
        type=str,
        required=True,
        help="This field cannot be blank."
    )
    parser.add_argument('phone',
        type=str,
        required=True,
        help="This field cannot be blank."
    )

    def post(self):
        # Request Parameter를 dictionary 형태로 저장
        data = StoreRegister.parser.parse_args()

        # 매장명
        store_name = data['store_name']

        # 지번주소
        abbr_address = data['abbr_address']

        # 도로명주소
        road_address = data['road_address']

        # 전화번호
        phone = data['phone']

        # Kakao API를 검색을 통해 위치 정보를 입수한다.
        response = find_address(road_address)

        # API 응답값중에 조회 된 데이터 수를 반환해주는데 정상적으로 조회 되는 경우는 1이상이므로
        # total_count 값을 체크하여서 저장 여부를 판단한다.
        result_cnt = response['meta']['total_count']

        if result_cnt == 0:
            return {'message':'주소 정보가 올바르지 않습니다. 다시한번 확인 하여 주십시오.'},404
        else:
            # 기본정보를 입력 받았더라도 API 검색결과 데이터로 다시 설정 하여준다.

            # documents element
            data = response['documents'][0]

            # 지번주소
            abbr_address = data['address']['address_name']

            # 도로명주소
            road_address = data['road_address']['address_name']

            # 위도
            x = data['x']

            # 경도
            y = data['y']

            # Geometry 정보 설정
            point = 'POINT({} {})'.format(x, y)

            # point 정보를 바로 저장 할 시 우리나라 좌표계와 맞지 않으므로 좌표계를 변환 처리 한다.
            geo = StoreModel.convert_geom(point)

            # 매장 정보 저장
            StoreModel.save(StoreModel(None,store_name,abbr_address,road_address,phone,None,None,x,y,geo))

            return {'message':'{} 매장 정보가 등록 되었습니다.'.format(store_name)},201



app.py


import os
import psycopg2

from flask import Flask
from flask_restful import Api
from flask_sqlalchemy import SQLAlchemy

from config import app_config

from resources.store import StoreRegister

# set_env.bat 에서 설정한 application 이름 설정
app = Flask(os.getenv('FLASK_APP'))

# set_env.bat 에서 설정한 FLASK_ENV는 development
env_name = os.getenv('FLASK_ENV')
"""
app_config 는 config.py 에서 정의 app_config 이면 위에서 development를 설정하였기
때문에 config.py 의 Development 클래스의 정보가 설정 되게 된다.
"""
app.config.from_object(app_config[env_name])

# 데이터 변경사항에 대해 커밋 전후로 알림 여부
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

# Query Debug 여부
app.config['SQLALCHEMY_ECHO'] = False


"""
API Resource 등록
---------------------------------------------------------
POST /stores/create - 매장 등록
PATCH /stores - 벌크 매장 등록
GET /stores/search - 매장목록 조회
GET /stores/<int:store_id> - 매장정보조회
DELETE /stores/<int:store_id> - 매장정보삭제
"""
api = Api(app)
api.add_resource(StoreRegister, '/stores/create')

if __name__ == "__main__":
    from db_init import db
    db.init_app(app)
    app.run(debug=True,port=5000)


테스트


POSTMAN을 이용하여서 테스트를 진행 한다.

아래 그림에서 보면 Body 부분에 입력 파라미터를 설정 하면 된다.


매장등록 결과 DB 조회






반응형