2010-07-31 37 views
9

Tôi đang cố sử dụng các trình trang trí để quản lý cách người dùng có thể hoặc không thể truy cập tài nguyên trong ứng dụng web (chạy trên Google App Engine). Xin lưu ý rằng tôi không cho phép người dùng đăng nhập bằng tài khoản Google của họ, do đó, việc đặt quyền truy cập cụ thể cho các tuyến đường cụ thể trong app.yaml không phải là một tùy chọn.Trình trang trí Python và kế thừa lớp

tôi đã sử dụng các nguồn sau:
- Bruce Eckel's guide to decorators
- SO : get-class-in-python-decorator2
- SO : python-decorators-and-inheritance
- SO : get-class-in-python-decorator

Tuy nhiên tôi vẫn còn một chút bối rối ...

Dưới đây là mã của tôi! Trong ví dụ sau, current_user là một phương thức @property thuộc về lớp RequestHandler. Nó trả về một đối tượng User (db.model) được lưu trữ trong kho dữ liệu, với một mức IntProperty().

class FoobarController(RequestHandler): 

    # Access decorator 
    def requiredLevel(required_level): 
     def wrap(func): 
      def f(self, *args): 
       if self.current_user.level >= required_level: 
        func(self, *args) 
       else: 
        raise Exception('Insufficient level to access this resource') 
      return f 
     return wrap 

    @requiredLevel(100) 
    def get(self, someparameters): 
     #do stuff here... 

    @requiredLevel(200) 
    def post(self): 
     #do something else here... 

Tuy nhiên, ứng dụng của tôi sử dụng các bộ điều khiển khác nhau cho các loại tài nguyên khác nhau. Để sử dụng các trang trí @requiredLevel trong tất cả các lớp con, tôi cần phải di chuyển nó vào lớp cha (RequestHandler):

class RequestHandler(webapp.RequestHandler): 

    #Access decorator 
    def requiredLevel(required_level): 
     #See code above 

Ý tưởng của tôi là để truy cập vào trang trí trong tất cả các lớp con điều khiển bằng cách sử dụng đoạn mã sau:

class FoobarController(RequestHandler): 

    @RequestHandler.requiredLevel(100) 
    def get(self): 
     #do stuff here... 

Tôi nghĩ mình đã đạt đến giới hạn kiến ​​thức về trang trí và thừa kế lớp :). Có suy nghĩ gì không?

+1

Tại sao nó là một phương pháp trên lớp học? Điều đó sẽ chỉ gây ra mọi thứ để phá vỡ, và nó sẽ chỉ hoạt động như một hàm bình thường bên trong lớp mà nó đã được định nghĩa. Trừ khi bạn đang ở trên 3.x, trong trường hợp đó nó có thể hoạt động tốt. –

+0

Trình trang trí là một phương thức trên lớp vì tôi chưa tìm cách viết mã trang trí là lớp 1/chấp nhận đối số và 2/có thể truy cập các phương thức vào chính lớp hiện tại. Đây có phải là ý của bạn không? Chủ yếu là tự học, tôi gặp khó khăn hoàn toàn hiểu được hướng dẫn, trang trí và thừa kế của Bruce Eckell. – jbmusso

+0

Bạn chỉ có thể sao chép-dán chức năng bên ngoài lớp, và nó sẽ hoạt động tốt và bình thường. Điều đó có đủ để trả lời câu hỏi của bạn không? –

Trả lời

4

Mã ban đầu của bạn, với hai chỉnh nhỏ, cũng sẽ hoạt động. Một cách tiếp cận dựa trên lớp có vẻ khá nặng trọng lượng cho một trang trí đơn giản như vậy:

class RequestHandler(webapp.RequestHandler): 

    # The decorator is now a class method. 
    @classmethod  # Note the 'klass' argument, similar to 'self' on an instance method 
    def requiredLevel(klass, required_level): 
     def wrap(func): 
      def f(self, *args): 
       if self.current_user.level >= required_level: 
        func(self, *args) 
       else: 
        raise Exception('Insufficient level to access this resource') 
      return f 
     return wrap 


class FoobarController(RequestHandler): 
    @RequestHandler.requiredLevel(100) 
    def get(self, someparameters): 
     #do stuff here... 

    @RequestHandler.requiredLevel(200) 
    def post(self): 
     #do something else here... 

Cách khác, bạn có thể sử dụng một @staticmethod thay vì:

class RequestHandler(webapp.RequestHandler): 

    # The decorator is now a static method. 
    @staticmethod  # No default argument required... 
    def requiredLevel(required_level): 

Lý do mã gốc đã không làm việc là requiredLevel được giả định là một phương thức instance, không sẵn sàng ở thời điểm khai báo lớp (khi bạn đang trang trí các phương thức khác), cũng không có sẵn từ đối tượng lớp (đặt trình trang trí vào lớp cơ sở RequestHandler của bạn) là một ý tưởng tuyệt vời, và cuộc gọi trang trí kết quả là độc đáo tự tài liệu).

Bạn có thể quan tâm để đọc tài liệu về @classmethod@staticmethod.

Ngoài ra, một chút soạn sẵn Tôi thích để đưa vào trang trí của tôi:

@staticmethod 
    def requiredLevel(required_level): 
     def wrap(func): 
      def f(self, *args): 
       if self.current_user.level >= required_level: 
        func(self, *args) 
       else: 
        raise Exception('Insufficient level to access this resource') 
      # This will maintain the function name and documentation of the wrapped function. 
      # Very helpful when debugging or checking the docs from the python shell: 
      wrap.__doc__ = f.__doc__ 
      wrap.__name__ = f.__name__ 
      return f 
     return wrap 
1

Sau khi đào qua StackOverflow và đọc kỹ Bruce Eckel's guide to decorators, tôi nghĩ rằng tôi đã tìm được giải pháp khả thi.

Nó liên quan đến việc thực hiện trang trí như là một lớp trong lớp phụ huynh:

class RequestHandler(webapp.RequestHandler): 

    # Decorator class : 
    class requiredLevel(object): 
     def __init__(self, required_level): 
      self.required_level = required_level 

     def __call__(self, f): 
      def wrapped_f(*f_args): 
       if f_args[0].current_user.level >= self.required_level: 
        return f(*f_args) 
       else: 
        raise Exception('User has insufficient level to access this resource') 
      return wrapped_f 

này làm việc! Sử dụng f_args [0] có vẻ hơi bẩn đối với tôi, tôi sẽ chỉnh sửa câu trả lời này nếu tôi tìm thấy thứ gì đó đẹp hơn.

Sau đó, bạn có thể trang trí các phương thức trong lớp con theo cách sau:

FooController(RequestHandler): 
    @RequestHandler.requiredLevel(100) 
    def get(self, id): 
     # Do something here 

    @RequestHandler.requiredLevel(250) 
    def post(self) 
     # Do some stuff here 

BarController(RequestHandler): 
    @RequestHandler.requiredLevel(500) 
    def get(self, id): 
     # Do something here 

Hãy viết ý kiến ​​hoặc đề xuất một nâng cao.

+0

bạn có thể sử dụng 'wrap_f (request_handler, * args, ** kwargs)' làm chữ ký hàm. Ngoài ra không cần phải đặt lớp trang trí vào lớp RequestHandler của bạn. Tôi sẽ đặt điều này ở cấp mô-đun. – nils

+0

Cảm ơn bạn đã bình luận! Tôi nghĩ rằng bạn chỉ cần làm cho tôi hiểu cách thức hoạt động thừa kế khác nhau (và tốt hơn rất nhiều). Tôi sẽ cập nhật mã của tôi cho phù hợp. – jbmusso

Các vấn đề liên quan