2013-05-28 58 views
1

我正在寻找一些与我的应用程序的帮助。这里的示例代码第一(约2k行剥离下来...),我会尽量解释一下我在寻找更高版本:ndb模型,装饰器,嵌套函数

from google.appengine.ext import ndb 
import webapp2 
import json 

class User(ndb.Model): 
    company_    = ndb.KeyProperty(repeated=True) 

    @property 
    def company(self): 
    return {} if not self.company_ else self.company_ 

    @company.setter 
    def company(self, value): 
    if value: 
     self.company_ = self.company_.expand(value) if self.company_ else [value] 
    else: 
     self.company_ = [] 
    self.put() 

class Company(ndb.Model): 
    administrator   = ndb.KeyProperty(kind=User, repeated=True) 
    manager    = ndb.KeyProperty(kind=User, repeated=True) 

    # FAKE decorator 
    @staticmethod 
    def administrator(handler): 
    def check_requirements(self, *a, **kw): 
     if True: 
     return 
     else: 
     return handler(self, *a, **kw) 
    return check_requirements 

class BaseHandler(webapp2.RequestHandler): 
    def jwrite(self, **kw): 
    return self.response.out.write(json.dumps(kw)) 

class require(BaseHandler): 
    @staticmethod 
    def login(handler): 
    def check_requirements(self, *a, **kw): 
     if not self.auth.get_user_by_session(): 
     self.redirect('/', abort=True) 
     else: 
     return handler(self, *a, **kw) 
    return check_requirements 

class ApiHandler(BaseHandler): 
    @require.login 
    def post(self, model, action, key=''): 
    method = '_post_%s' % model 
    try: 
     getattr(self, method)(action, key) 
    except Exception as error: 
     return self.jwrite(error = error) 

    def _post_company(self, action, key): 

    if action == 'create': 
     data = dict(self.request.POST) 
     """ Company.create(data) method: 
      Populates Company instance with POST data. 
      Assigns first user that created the company 
      both administrator and manager roles. 
     """ 
     key_ = Company.create(data) 
     if key_: 
     self.user.company = key_ 
     return 

    elif action == 'delete': 

     @Company.administrator 
     def delete_all_user_companies(self): 
     ndb.delete_multi(self.user.company) 
     self.user.company = None 
     return 

     companies = ndb.get_multi(self.user.company) 
     if self.user.key in map(lambda c: c.administrator, companies): 
     delete_all_user_companies(self) 

    elif action == 'update': 

     @Company.manager 
     def update_company(self, key): 
     data = dict(self.request.POST) 
     """ Company.update(key, data) method: 
      Populates Company instance with POST data 
     """ 
     key_ = Company.update(key, data) 
     if key_: 
      return 

     company = ndb.Key(Company, key).get() 
     if self.user.key in company.manager.extend(company.administrator): 
     update_company(self) 

正如你所看到的,我有用户和公司的模型。用户可以有多个公司,公司可以有多个用户,可以是管理员或经理。你会注意到一些装饰和嵌套功能 - 其中大多数是假的(;但这就是我正在寻找...

我在做基本的登录检查@ require.login装饰(我'我把它放在单独的类中,只是因为它在代码中看起来更干净 - @ require.login vs @ BaseHandler.require_login)。我用它“保护”了API的post方法,现在我需要对角色进行额外的检查 - 管理员可以做一些管理者不能做的事情,我需要在其他几个地方做这个检查,所以我认为这将是一个装饰器功能的好地方,但我不知道如何编写它们。 :

  1. 这个装饰器会是个好地方吗?我应该把它放在公司类还是ApiHandler类的某个地方?我的第一个直觉是把它放在公司类,但我不知道如何处理范围 - 我需要在那里得到用户实例(self.user.company列表)...

  2. 接下来的事情是经理装饰者。我怎样写这个作为装饰:

    company = ndb.Key(Company, key).get() 
        if self.user.key in company.manager.extend(company.administrator): 
        update_company(self) 
    

    ,并以此为@ Company.manager@ requre.manager,根据不同的答案,我的第一个问题?

  3. 管理员这一点更复杂的另一种装饰 - 我要检查,如果用户是管理员为他的所有公司,并删除那些他在哪里,同时保持者,他是不是:

    companies = ndb.get_multi(self.user.company) 
        if self.user.key in map(lambda c: c.administrator, companies): 
        delete_all_user_companies(self) 
    

    我甚至不确定这个map()函数是否正确,如果代码可以工作,还没有尝试它 - 现在只是一个伪代码占位符...

  4. 最后一个问题:应该我关心POST请求黑客行为?根据上面的示例代码,是否有可能某些用户可以自定义POST请求并删除或更新不属于他的公司? !

任何帮助,意见或见解,将不胜感激(;感谢

回答

3

我相信我已经解决了这个:

from google.appengine.ext import ndb 
import webapp2 
import json 

class User(ndb.Model): 
    company_    = ndb.KeyProperty(repeated=True) 

    @property 
    def company(self): 
    return {} if not self.company_ else self.company_ 

    @company.setter 
    def company(self, value): 
    if value: 
     # self.company_ = self.company_.expand(value) if self.company_ else [value] 
     # Lists mutate when expanded. Code above was returning None 
     self.company_ = self.company_ + [value] if self.company_ else [value] 
    else: 
     self.company_ = [] 
    self.put() 

class Company(ndb.Model): 
    administrator   = ndb.KeyProperty(kind=User, repeated=True) 
    manager    = ndb.KeyProperty(kind=User, repeated=True) 

class BaseHandler(webapp2.RequestHandler): 
    def jwrite(self, **kw): 
    return self.response.out.write(json.dumps(kw)) 

class require(BaseHandler): 
    @staticmethod 
    def login(handler): 
    def check_requirements(self, *a, **kw): 
     if not self.auth.get_user_by_session(): 
     self.redirect('/', abort=True) 
     else: 
     return handler(self, *a, **kw) 
    return check_requirements 

class role(BaseHandler): 
    @staticmethod 
    def administrator(handler): 
    def check_requirements(self, *a, **kw): 
     # I didn't care much about optimizing queries 
     # since this isn't frequent operation. 
     # For more frequent calls, I'd consider projections. 
     companies = ndb.get_multi(*a) 
     # Next lines checks if current user is administrator 
     # for all companies passed to the function 
     if not self.user.key in reduce(lambda x, y: x if x != y else y, map(lambda c: c.administrator, companies)): 
     return self.jwrite(error = 'Permission denied. Administrator required.') 
     else: 
     return handler(self, *a, **kw) 
    return check_requirements 

    @staticmethod 
    def manager(handler): 
    def check_requirements(self, *a, **kw): 
     companies = ndb.get_multi(*a) 
     # Next lines checks if current user is manager 
     # or administrator (since admin has higher privileges) 
     # for all companies passed to the function 
     if not self.user.key in reduce(lambda x, y: x if x != y else y, map(lambda c: c.manager + c.administrator, companies)): 
     return self.jwrite(error = 'Permission denied. Manager or Administrator required.') 
     else: 
     return handler(self, *a, **kw) 
    return check_requirements 

class ApiHandler(BaseHandler): 
    @require.login 
    def post(self, model, action, key=''): 
    method = '_post_%s' % model 
    try: 
     getattr(self, method)(action, key) 
    except Exception as error: 
     return self.jwrite(error = error) 

    def _post_company(self, action, key): 

    if action == 'create': 
     data = dict(self.request.POST) 
     """ Company.create(data) method: 
      Populates Company instance with POST data. 
      Assigns first user that created the company 
      both administrator and manager roles. 
     """ 
     key_ = Company.create(data) 
     if key_: 
     self.user.company = key_ 
     return 

    elif action == 'delete': 

     @role.administrator 
     def delete_all_user_companies(self, *a): 
     ndb.delete_multi(*a) 
     self.user.company = None 
     return 

     delete_all_user_companies(self, self.user.company) 

    elif action == 'update': 

     @role.manager 
     def update_company(self, *a): 
     data = dict(self.request.POST) 
     """ Company.update(key, data) method: 
      Populates Company instance with POST data 
     """ 
     key_ = Company.update(key, data) 
     if key_: 
      return 

     update_company(self, ndb.Key(Company, key)) 

,并回答了我自己的问题:

  1. 我在公司类里面命名时遇到了问题 - 有属性管理员和装饰器都有相同的名称,于是我将deco为了方便起见,将API转换为新的类(角色)。当我编写装饰器时,我意识到我可以将它们用于任何其他模型(包括经理和管理员字段),所以我认为这是一个很好的调用(但是,写入装饰器需要一些时间并试验映射和减少数组,但我已经设法解决了这个问题,我不确定是否需要将参数传递给装饰器,也许我应该在装饰器之外进行查询,或者将匹配项传递给处理器函数,我将不得不考虑它。 。

  2. ...并删除那些他在哪里,同时保持者,他是不是管理员这就是为什么我做内部装饰疑问摆在首位,但仍然不知道,如果它的智能(。

  3. 我仍然可以使用这个答案。

希望这可以帮助别人......

+0

没关系接受你自己的问题! –