Flask RESTful API - PATCH /stores (현재 위치 기준 지정한 반경내 모든 매장 정보 등록)

2019. 7. 3. 11:27Python/Flask

반응형


PATCH /stores

사용자가 지정한 위도,경도,반경 기준에 해당 하는 매장 정보를 저장 한다.
저장 시 기존에 저장한 매장 정보를 조회 하여서 일치하는 정보가 있는 경우 업데이트 하고 일치하는 정보가 없는 경우 저장 처리 한다.

Kakao API를 호출 하면 한번에 15건씩 조회 결과를 받을 수 있다 하지만 반경을 크게 주면 15개 이상의 매장이 검색 될 것이다.
게시판 페이징 처리 처럼 페이징 처리를 통하여 모든 데이터를 저정해야 한다.

아래 그림을 보면 대략 10km 기준으로 검색 해보면 40개 이상 매장이 검색 될 것이다.



Resource URL


Request 

{    
"place_name":"스타벅스",
"x":37.6125901,
"y":126.7284632,
"radius":"10000"
}

Response

{
"message": "총 47 건의 매장 정보가 등록 되었습니다."
}


소스코드 작성


앞에 코드에 메소드를 추가해 나가면 된다.


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

  • 기존 매장 정보 검색 메소드 추가
  • 매장 정보 등록을 위한 메소드 추가
  • app.py Resource 정보 등록


store.py - Model


Kakao API 조회 후 조회된 매장 정보가 기존에 등록된 경우 업데이트 처리를 위한 조회 메소드

@classmethod def find_store_by_store_name_with_adress(self, place_name, road_address):     """ 매장 정보 조회 param place_name: 장소명 param road_address: 도로명주소 :return: store: 조회된 매장 정보 객체 """ return db.session.query(self).filter_by(store_name=place_name).filter_by(road_address=road_address).first()


store.py - Resource


"""
PATCH /stores - 현재 위치 기준 지정한 반경내 모든 매장 정보 등록
"""
class StoresRegister(Resource):

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

    parser.add_argument('place_name',
        type=str,
        required=False
    )
    parser.add_argument('x',
        type=float,
        required=False
    )
    parser.add_argument('y',
        type=float,
        required=False
    )
    parser.add_argument('radius',
        type=float,
        required=False
    )

    def patch(self):
        # Request Parameter를 dictionary 형태로 저장 37.5950084,126.7656567
        data = StoresRegister.parser.parse_args()

        # 장소명
        place_name = data['place_name']

        # 위도
        y = data['y']

        # 경도
        x = data['x']

        # 뱐경
        radius = data['radius']

        # 저장 처리를 위한 매장 리스트
        store_list = []

        # 페이지당건수
        per_page_count = 15

        # 총 페이지
        total_page = 1

        # 페이지
        page = 1

        """
        Kakao API 검색 결과는 기본적으로 한번에 15건을 조회 결과로 리턴 해 준다.
        만약 건수가 15건이 넘어가는 경우 페이징 처리가 필요하다.
        이를 위해서 클로저 함수를 이용한다.
        """
        def make_stores(response):
            for store in response['documents']:
                """
                단일건으로 등록한 매장 정보의 경우 주소 정보로 위치 정보를 저장 하게 된다.
                실제로는 위치정보가 장소명으로 검색한 경우와 불일치 하므로 기존 매장 정보 검색 후 일치하는 정보가 있는 경우
                장소명으로 검색한 정보로 업데이트 처리 하기 위해 기존 매장 정보 검색 후 매장 정보를 설정한다.
                """
                store_info = StoreModel.find_store_by_store_name_with_adress(store['place_name'],store['road_address_name'])
                if store_info:
                    # Geometry 정보 설정
                    point = 'POINT({} {})'.format(store_info.lat, store_info.lon)

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

                    # 매장 리스트에 append 처리
                    store_list.append(StoreModel(store_info.store_id,store['place_name'],store['address_name'],store['road_address_name'],store['phone'],store['place_url'],None,store['x'],store['y'],geo))
                else:
                    # 검색 결과에서 카테고리 그룹코드가 CE7 인경우 카페 이므로 그룹코드가 CE7인 경우만 리슽에 저장
                    if store['category_group_code'] == 'CE7':
                        # Geometry 정보 설정
                        point = 'POINT({} {})'.format(store['x'], store['y'])

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

                        # 매장 리스트에 append 처리
                        store_list.append(StoreModel(None, store['place_name'],store['address_name'],store['road_address_name'],store['phone'],store['place_url'],None,store['x'],store['y'],geo))

        # Kakao API를 검색을 통해 위치 정보를 입수한다.
        response = find_place(y ,x, radius, place_name, 1)

        # 총 조회 데이터 건수
        total_count = response['meta']['total_count']

        # 총 페이지 계산
        if total_count % per_page_count == 0:
            total_page = int(total_count / per_page_count)
        else:
            total_page = int((total_count / per_page_count) + 1)

        # 조회결과를 저장 처리를 위한 리스트 변수 저장 클로저 함수 호출
        make_stores(response)

        # 조회 결과 가 없는 경우
        if total_count == 0:
            return {'message': '매장 정보가 존재 하지 않습니다.'}, 404
        else:
            for page in range(total_page):
                """
                page 기본값은 0 이지만 위에서 첫번째 페이지 건으로 조회 하였기 때문에
                그 다음 page 인 2page 조회 를 위해서 page + 2로 해준다.
                """
                page = page + 2

                # Kakao API를 검색을 통해 위치 정보를 입수한다.
                response = find_place(y ,x, radius, place_name, page)

                # 조회결과를 저장 처리를 위한 리스트 변수 저장 클로저 함수 호출
                make_stores(response)

        # 매장 정보 저장/업데이트 처리
        total_save_count = 0

        if len(store_list) > 0:
            total_save_count = len(store_list) - 1

            for store in store_list:
                StoreModel.save(store)

        return {'message':'총 {} 건의 매장 정보가 등록 되었습니다.'.format(total_save_count)},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,StoresRegister

# 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 - 매장목록 조회
GET /stores/<int:store_id> - 매장정보조회
DELETE /stores/<int:store_id> - 매장정보삭제
"""
api = Api(app)
api.add_resource(StoreRegister, '/stores/create')
api.add_resource(StoresRegister, '/stores')

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


테스트


아래 그림과 같이 장소명 현재 위치 정보를 입력 파라미터로 설정 한 후 테스트 한다.



반응형