태터데스크 관리자

도움말
닫기
적용하기   첫페이지 만들기

태터데스크 메시지

저장하였습니다.

도깨비: 요청/응답 json 포맷 사용 + mongodb 저장소 붙이기

Daily life/Hard study 2012.08.02 22:20

지난번에 작업 - http://blog.dgoon.net/449 - 하다 말았던 부분을 이어서 한다. 도깨비에 추가로 할 작업은 다음 두가지이다.

1. 일단 요청/응답 포맷을 json으로 바꾼다.
2. persistent layer를 붙인다(mongodb?)

-----
query string 이나 parameter 는 버린다.
POST 로 요청을 받고 -> Content-type 이 application/json 인지 확인하고 -> body 를 읽어서 json.loads 를 한다.
요청이 가져야 하는 값들은 여기서 나오는 json 에 모두 포함되어 있어야 한다. 몇 가지 사소한 것들을 적어둔다.

a. 요청 타입(POST/GET)은 지난번에 @require_http_methods("POST") 로 강제했다
b. content-type은 request.META.get('CONTENT-TYPE') 에 들어있다.
c. body는 request.read() 를 하면 나온다

필수 데이터가 들어있는지 확인하는 코드(check_required)와 content-type을 확인하는 코드(check-content-type)을 함수로 분리하면 더미 로직은 get/put 둘 모두,

        check_content_type(request, "application/json")
        body = request.read()
        req_dict = simplejson.loads(body)
        check_required(GET_REQUIRED_FIELDS, req_dict.keys())
        print req_dict


이 정도로 간단해진다. 조건이 맞지 않는 경우 check_required, check_content_type 함수들은 직접 예외를 생성해서 던진다. 테스트 요청을 날리던 get.py, put.py 에서는 content-type 만 application/json 으로 바꾸고, urlencode 하던 부분을 simplejson.dumps 로만 바꾸면 된다.

{'app_key': 'deadbeef', 'condition': {}}
[02/Aug/2012 21:06:05] "POST /api/v1/get/ HTTP/1.1" 200 2
{'content': 'THIS_IS_CONTENT', 'tag_list': ['tag1', 'tag2', 'tag3'], 'meta': {}, 'app_key': 'deadbeef'}
[02/Aug/2012 21:06:16] "POST /api/v1/put/ HTTP/1.1" 200 2

이전보다 코드도 짧아지고, tag_list 도 리스트로 제대로 들어온다. 에잉 훨 낫네.

-----
persistent layer를 붙인다. 전에 튜토리얼을 따라가본 몽고디비를 쓰고 싶다. 그때는 c++ 클라이언트였는데, 파이썬이라고 뭐 딱히 다르진 않지 싶다. 더 쉽겠지...

http://www.mongodb.org/display/DOCS/Drivers -> http://www.mongodb.org/display/DOCS/Python+Language+Center

여기서 찾아보면 몇 가지 라이브러리가 있다. PyMongo 를 선택. 일단 언제나처럼 pip,easy_install,apt-get 순서로 패키지가 있나 뒤져본다.

sudo pip install pymongo

빙고. 파이썬 쉘 띄워서 import pymongo 해보니 뭔가 임포트 된다. 이걸 쓰면 되겠다.

http://api.mongodb.org/python/current/tutorial.html

여기 튜토리얼이니 잠깐 읽어보면 어렵지 않아요~ 지난번에 삽질하면서 katy 에 몽고디비 설치는 해 두었으니 그대로 활용하자. db=tokebi, collection=twit 으로 간다. store_mongodb.py 를 만들고, MongoStore 클래스에 몽고디비 접근 구현 코드를 둔다. 인터페이스는 mongo_store.get/put 이며 api_get, api_put 이 받은 요청을 거의 그대로 받는다.


api_get:
        app_key, condition = get_items(req_dict, GET_REQUIRED_FIELDS)             
        store = MongoStore('twit')
        retrieved = store.get(app_key, condition)

api_put:
        app_key, content, tag_list, meta = get_items(req_dict, PUT_REQUIRED_FIELDS)
        store = MongoStore('twit')
        ack = store.put(app_key, content, tag_list, meta)

api_get, api_put 에 각각 위의 코드가 추가되었다. 테스트 요청을 날려보면서 디버깅을 좀 해야 했는데, python get.py localhost 2223 > ~/public_html/get_error.html 등으로 저장한 뒤에 브라우저로 열어보면 좀 쉽다. 몽고디비에 쓰고 읽을 때 필요한 자료구조는 뭘까 하고 튜토리얼을 봤더니,

In PyMongo we use dictionaries to represent documents. ...

저장 단위가 파이썬 맵이므로 구현은 아주 간단하다. 심지어 문서를 꺼내오는 조건도 사전이므로,몽고디비 접근은 거의 자명하다.

#-*- encoding: utf-8 -*-from pymongo import Connection
CONN = Connection()

def doc2dict(d):
    ret = dict(d)
    ret['_id'] = str(ret['_id'])
    return ret

class MongoStore(object):
    def __init__(self, collection):
        self.collection = CONN.tokebi[collection]

    def get(self, app_key, condition):
        cond = condition.copy()
        cond['app_key'] = app_key
        return map(doc2dict, self.collection.find(condition))

    def put(self, app_key, content, tag_list, meta):
        doc = {
            'app_key': app_key,
            'content': content,
            'tag_list': tag_list,
            'meta': meta,
        }
        return self.collection.insert(doc)


api_get, api_put 에서 응답을 적절히 json 포맷으로 만들어 준다면 테스트 요청을 날리는 get.py 에서는 여러개의 문서 리스트를 응답으로 얻게 된다. 받은 결과를 화면에 찍어보면,

dgoon@katy:~/works/tokebi/sample_query$ python get.py localhost 2223
{'Content-type': 'application/json', 'Accept': 'text/plain'}
{'condition': {}, 'app_key': 'deadbeef'}
0
        content THIS_IS_CONTENT
        _id 501a74c272653e2217c9b255
        meta {}
        app_key deadbeef
        tag_list ['tag1', 'tag2', 'tag3']
1
        content THIS_IS_CONTENT
        _id 501a74d472653e222068f2b8
        meta {}
        app_key deadbeef
        tag_list ['tag1', 'tag2', 'tag3']
2
        content 두만강푸른물에
        _id 501a750372653e222068f2b9
        meta {}
        app_key deadbeef
        tag_list [u'\uac15\uc0b0\uc5d0', u'\ub178\ub798']
3
        content 티비유치원 하나둘셋
        _id 501a768072653e225e6cb3c1
        meta {}
        app_key deadbeef
        tag_list ['hana', 'dul', 'set']


이렇게 확인할 수 있다. 한글도 잘 들어가는 것을 확인할 수 있다. 2번 문서의 tag_list에 들어있는 것은,

>>> for k in [u'\uac15\uc0b0\uc5d0', u'\ub178\ub798']:
...     print k
...
강산에
노래

이렇게 유효한 한글이다. PyMongo 튜토리얼에서 설명되어 있는데 - BSON strings are UTF-8 encoded 라고 한다. Unicode문자열은 encode 되어 저장된다고. 그리고 simplejson 으로 직렬화 할때는 다시 유니코드가 되어서 응답을 받은 쪽에서는 한글이 모두 유니코드로 떨어지게 된다. 나중에 좀 헷갈릴 것 같다.


-> http://git.dgoon.net/?p=tokebi.git;a=commit;h=da49398646c0812a0d33d71cbec23e5052f5ca09


-----

추가적으로 해야 하는 작업은 ... 잊고 있었는데, 나 말고 다른 사람도 이걸 쓰게 하려면


1. user_id 같은걸 넣어야 하고,


최신 엔트리를 가져오려면,


2. created_at 이 필요하고,


사람을 가리거나 정렬이 필요하니


3. 적절한 인덱스 가 필요하다.


다음 작업은 이상 3개가 되겠다.

Trackbacks 0 : Comments 6