2010-02-28 4 views
5

배경App Engine은 (파이썬) 데이터 저장소 Precall API 후크

는 그래서 내가 GAE에 대한 응용 프로그램을 만들고있어 가정 해 봅시다, 나는 API Hooks를 사용하고 싶습니다.

큰 수정 : :이 질문의 원래 버전에서는 사용 사례에 대해 설명했지만 일부 사람들은 API 후크에 적합하지 않다고 지적했습니다. 수여 됨! 내가 도와 줬다고 생각해. 하지만 지금은 제 문제가 학문적입니다. 실제로 나는 후크를 사용하는 방법을 여전히 모르고 있으며, 나는하고 싶습니다. 내 질문을 다시 작성하여 훨씬 더 일반적인 것으로 만들었습니다.


코드

그래서 내가 만드는이 같은 모델 :

class Model(db.Model): 
    user = db.UserProperty(required=True) 
    def pre_put(self): 
     # Sets a value, raises an exception, whatever. Use your imagination 

그리고 나는 db_hooks.py 만들 :

from google.appengine.api import apiproxy_stub_map 

def patch_appengine(): 
    def hook(service, call, request, response): 
     assert service == 'datastore_v3' 
     if call == 'Put': 
      for entity in request.entity_list(): 
       entity.pre_put() 

    apiproxy_stub_map.apiproxy.GetPreCallHooks().Append('preput', 
                 hook, 
                 'datastore_v3') 

존재 TDD-썩는을, 나는이 모든 것을 GAEUnit을 사용하고있다. 그래서 gaeunit.py에서 t 바로 위에있다. 그는 주요 방법, 내가 추가 :

import db_hooks 
db_hooks.patch_appengine() 

그리고 나서 나는 인스턴스를 만들고 모델을 넣는 테스트를 쓴다.


patch_appengine() 동안

가 확실히 호출되는 질문, 후크는하지 않습니다. 내가 뭘 놓치고 있니? pre_put 함수가 실제로 호출되도록하려면 어떻게해야합니까?

답변

1

나는 후크가 실제로이 문제를 해결할 것이라고 생각하지 않습니다. 후크는 AppEngine 애플리케이션의 컨텍스트에서만 실행되지만 사용자는 Google 계정 설정을 사용하여 애플리케이션 외부에서 애칭을 변경할 수 있습니다. 그들이 그렇게한다면, 그것은 당신의 훅에서 어떠한 로직 구현도 유발하지 않을 것입니다.

나는 귀하의 응용 프로그램이 Users 엔티티에서 노출 된 것과 별개의 고유 한 별명을 관리하는 것이 실제 솔루션이라고 생각합니다.

+0

아, 네가 절대적으로 옳다. 그렇다면 계정 당 고유성을 적용하는 것은 어떻습니까? 풋볼 전에 확인해 봤으면 좋겠어? –

+0

고유성을 적용하려면 데이터 저장소에 엔터티를 만드는 첫 번째 put 전에 확인해야합니다. 사용자가 해당 값을 변경하지 못하도록하면 (좋은 생각입니다) 작성을 확인하기 만하면됩니다. –

+0

사실, 그리고 두 경우 모두, 내가 넣기 전에 원하는 검사와 업데이트를 작성한 다음 put() 대신에 사용하는 메소드를 작성하려고합니다. 고맙습니다. –

2

후크는 현재 수행중인 작업에 대해 약간 낮은 수준입니다. 당신이 원하는 것은 커스텀 속성 클래스이다. DerivedProperty는 aetycoon에서 티켓 일뿐입니다.

그러나 사용자 개체의 '닉네임'입력란은 아마도 사용자가 원하는 것일 수 없습니다. the docs 개는 Gmail 계정을 사용하는 경우 전자 메일 필드의 사용자 부분 일뿐입니다. 그들의 전체 이메일 주소. 대신 사용자가 자신의 별명을 설정하도록 할 수 있습니다.

2

여기서 문제는 hook() 함수의 컨텍스트 내에서 entity이 예상 한대로 db.Model의 인스턴스가 아니라는 것입니다.

이 문맥에서 entity은 혼동스럽게 엔티티 (entity_pb)라고하는 프로토콜 버퍼 클래스입니다.실제 엔터티의 JSON 표현처럼 생각하면 모든 데이터가 있고 인스턴스를 작성할 수 있지만 콜백을 기다리고있는 메모리 상주 인스턴스에 대한 참조가 없습니다. 다양한 put/delete 모든 방법을 패치

원숭이는 내가 아는 한 설정 모델 수준의 콜백에 가장 좋은 방법은 안전하게이 작업을 수행하는 방법에 대한 많은 자원이있을 것 같지 않기 때문에

† 새로운 비동기 호출은, 여기 & after_delete 후크 before_delete, before_put를 구현하는 BASEMODEL의, after_put : 필요에 따라

class HookedModel(db.Model): 

    def before_put(self): 
     logging.error("before put") 

    def after_put(self): 
     logging.error("after put") 

    def before_delete(self): 
     logging.error("before delete") 

    def after_delete(self): 
     logging.error("after delete") 

    def put(self): 
     return self.put_async().get_result() 

    def delete(self): 
     return self.delete_async().get_result() 

    def put_async(self): 
     return db.put_async(self) 

    def delete_async(self): 
     return db.delete_async(self) 

는 after_xxx 방법을 HookedModel에서 모델 클래스를 상속하고 before_xxx 우선합니다.

매우 표준적인 레이아웃을 사용하는 경우 main.py과 같이 응용 프로그램에 전역으로로드되는 어딘가에 다음 코드를 배치하십시오.

def normalize_entities(entities): 
    if not isinstance(entities, (list, tuple)): 
     entities = (entities,) 
    return [e for e in entities if hasattr(e, 'before_put')] 

# monkeypatch put_async to call entity.before_put 
db_put_async = db.put_async 
def db_put_async_hooked(entities, **kwargs): 
    ents = normalize_entities(entities) 
    for entity in ents: 
     entity.before_put() 
    a = db_put_async(entities, **kwargs) 
    get_result = a.get_result 
    def get_result_with_callback(): 
     for entity in ents: 
      entity.after_put() 
     return get_result() 
    a.get_result = get_result_with_callback 
    return a 
db.put_async = db_put_async_hooked 


# monkeypatch delete_async to call entity.before_delete 
db_delete_async = db.delete_async 
def db_delete_async_hooked(entities, **kwargs): 
    ents = normalize_entities(entities) 
    for entity in ents: 
     entity.before_delete() 
    a = db_delete_async(entities, **kwargs) 
    get_result = a.get_result 
    def get_result_with_callback(): 
     for entity in ents: 
      entity.after_delete() 
     return get_result() 
    a.get_result = get_result_with_callback 
    return a 
db.delete_async = db_delete_async_hooked 

저장하거나 model.put를 통해 인스턴스를 파괴 할 수있다() 또는 db.put의(), db.put_async() 등, 방법과를 얻을 : 이것은 우리의 후크를 호출하는 부분이다 원하는 효과.

† 더 나은 솔루션이 있는지 알고 싶습니다.?

관련 문제