Flask类视图

Posted Flask学习笔记

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flask类视图相关的知识,希望对你有一定的参考价值。

类视图和add_url_rule1.视图2.标准类视图1.app.add_rul_rule()与视图关系2.使用类继承3.方法提示3.基于调度的类视图4.装饰器视图(函数装饰器/类装饰器)

类视图和add_url_rule

1.视图

当一个url请求进入后台时,一般有两种方式来进行处理:视图函数和类视图.视图函数直接使用一个函数来进行处理并返回数据给浏览器.类视图则是使用类来进行处理并返回的,所以当需要进行的处理比较简单,则可以考虑使用前者,处理比较复杂就考虑使用后者,但是最终还是需要看使用环境和需求而定.

类视图:支持继承,处理复杂的请求

2.标准类视图

  1. 定义:类需要继承Flask.views.View,并且重写dispatch_request方法

  2. dispatch_request:当访问到对应的url时,就会实例化这个类,并执行dispatch_request方法,并将这个方法的返回值返回给浏览器.dispatch_request其实也是一个视图函数,所以它的返回值跟视图函数一样,必须是html模板\字符串\response对象\response对象的子类或者固定格式的元组

  3. 类注册:add_url_rule方法进行注册,注册方式与视图函数基本一致,不过参数view_func跟视图函数的使用有点区别,需要使用视图类的as_view方法进行转换,同时指定一个视图名称.

  4. http://docs.jinkan.org/docs/flask/views.html

实现一个简单的类视图:

 from flask import Flask,views,render_template
 
 app=Flask(__name__)
 app.config.update({'DEBUG':True, 'TEMPLATES_AUTO_RELOAD':True})
 
 @app.route('/')
 def index():
     return 'index'
 
 # 假设从数据库中载入大量的对象,并渲染到前端
 users = [
    {'admin':{
         'id':1,
         'passwd':'123'}
    },
    {'Jack': {
         'id': 2,
         'passwd': '123'}
    }
 ]
 
 class Get_User_Information(views.View):
     def dispatch_request(self):
         cont = {
             'users':users
        }
         return render_template('users.html', **cont)
 
 
 # 注册到网站
 app.add_url_rule('/users/', endpoint='users', view_func=Get_User_Information.as_view('users'))
 
 if __name__ == '__main__':
     app.run()
  • 继承了flask.views.View 类,并复写了dispath_request()方法

users.html

 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <title>Users</title>
 </head>
 <body>
    {{ users }}
    {{ url_for('users') }}
 </body>
 </html>

1.app.add_rul_rule()与视图关系

语法:

 def add_url_rule(
  self,
   rule,
   endpoint=None,
   view_func=None,
   provide_automatic_options=None,
   **options
 ):

其中的view_func 接受的是一个函数,并不是类,当使用的是类视图时,需要转化类到函数才能正常的接受使用.

 app.add_url_rule('/users/', endpoint='users', view_func=Get_User_Information.as_view('users'))

如上,使用了类的as_view 方法,这是一个类方法,Flask 内部实现的,返回的对象是一个 函数对象

 @classmethod
  def as_view(cls, name, *class_args, **class_kwargs):
      def view(*args, **kwargs):
          self = view.view_class(*class_args, **class_kwargs)   # 类本身去处理变量
          return self.dispatch_request(*args, **kwargs)               # 调用类本身的`dispath_request` 方法
 
      if cls.decorators:
          view.__name__ = name
          view.__module__ = cls.__module__
          for decorator in cls.decorators:
              view = decorator(view)
 
      view.view_class = cls     # 类本身
      view.__name__ = name
      view.__doc__ = cls.__doc__
      view.__module__ = cls.__module__
      view.methods = cls.methods
      view.provide_automatic_options = cls.provide_automatic_options
      return view

可以看到这是一个类方法. as_view 返回的是view 函数对象,view 是其内部定义的一个函数,返回的是一个cls.dispatch_request() 对象.这是一个典型的闭包结构.

类似与这样:

 class My_Define(object):
  info = {}
  def get(self):
      return '1'
 
  @classmethod
  def class_method(cls, name, *clsargs, **clskwargs):
      def view(*args, **kwargs):
          self = view.view_class(*clsargs, **clskwargs)
          return self.get(*args, **kwargs)
      view.view_class = cls
      view.__name__ = name
      cls.info['cls'] = cls.__name__
      return view
 
 
 class My_Define_sub(My_Define):
  def get(self):
      return self.__class__
 
 print(My_Define_sub.__dict__)
 My_Define_sub.class_method('test')
 print(My_Define_sub.info)
 print(type(My_Define_sub.class_method('test')))
 print(My_Define_sub.class_method('test').__name__)
 """
 {'__module__': '__main__', 'get': <function My_Define_sub.get at 0x7fd8baafed40>, '__doc__': None}
 {'cls': 'My_Define_sub'}
 <class 'function'>
 test
 """

可以看到它返回的对象确实是一个函数类型的对象.并且cls 是子类本身.

在这里name 的值是必须的.当add_url_ruleendpoint 没有定义的时候,url_for 要获取name 的值,这个定义在add_url_rule中:

 def add_url_rule(
  self,
  rule,
  endpoint=None,
  view_func=None,
  provide_automatic_options=None,
  **options
 ):
 
  if endpoint is None:
      endpoint = _endpoint_from_view_func(view_func)
     
 # _endpoint_from_view_func(view_func)定义
 def _endpoint_from_view_func(view_func):
  assert view_func is not None, "expected view func if endpoint is not provided."
  return view_func.__name__

可以看到没有定义endpoint 默认返回的是view_func.__name__ ,即返回as_view('test').__name__ ,这也就是必须填写as_view(name) 的原因.

2.使用类继承

大量的相同的内容,可以抽象成一个父类,然后在子类就可以用很少的代码完成工作.

 from flask import Flask,views,render_template
 app = Flask(__name__)
 
 @app.route('/')
 def index():
  return '1'
 
 ads={'ada1':'picture1', 'ads2':'picture2'}
 
 class Page1_Views(views.View):
  def dispatch_request(self):
      cont = {
          'ads':ads
      }
      return render_template('page1.html', **cont)
 
 class Page2_views(views.View):
  def dispatch_request(self):
      cont = {
          'ads':ads
      }
      return render_template('page2.html', **cont)
 
 # 注册
 app.add_url_rule('/page1/', endpoint('page1'), view_func=Page1_Views.as_view('page1'))
 app.add_url_rule('/pages2/', endpoint('page2'), view_func=Page2_views.as_view('page2'))
 
 # 大量的page都需要同样的数据
 
 class Ads_view(views.View):
  def get_templte_name(self):
      raise NotImplementedError()
 
  def render_tamplate_view(self, cont):
      return render_template(self.get_tempalte_name(), **cont)
 
  def dispatch_request(self):
      cont = {
          'ads':ads
      }
      return self.render_tamplate_view(**cont)
 
 
 class Page_view(Ads_view):
  def get_template_name(self):
      return 'page3.html'
 
     
 
 
 
 if __name__=='__main__':
  app.run(debug=True)

3.方法提示

http请求有多种方式get/post,如果要在标准类视图中控制,需要添加methods属性来控制

 class MyView(View):
  methods = ['GET', 'POST']
 
  def dispatch_request(self):
      if request.method == 'POST':
          ...
      ...
 
 app.add_url_rule('/myview', view_func=MyView.as_view('myview'))

3.基于调度的类视图

  1. 定义:类需要继承Flask.views.MethodView,并且定义对应的请求方法

  2. 请求方法:当对应的url访问进来后就会根据url的请求方式查找对应的请求方法,并将该方法的返回值返回给浏览器,如果没有定义对应的请求方法就会报错,提示Method Not Allowed

  3. 类注册:注册方式和标准类视图是相同的

一个简单的基于调度的类视图:

 from flask import Flask,render_template,views,request
 app = Flask(__name__)
 
 @app.route('/')
 def index():
     return "Hello World"
 
 class Login_View(views.MethodView):
     def get(self):
         return render_template('login.html')
 
     def post(self):
         # request.form.get 获取form 表单的数据,get()中是表单的name值
         username = request.form.get('username')
         passwd = request.form.get('password')
         if username == 'admin' and passwd == '123456':
             return "successful"
         else:
             return 'failed'
 
 app.add_url_rule('/login/', view_func=Login_View.as_view('login'))
 
 if __name__=='__main__':
     app.run(debug=True)

login.html

 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <title>login</title>
 </head>
 <body>
     <div>
         <form action="" method="post">
             <label for="#">username</label>
             <input type="text" name="username">
             <label for="#">password</label>
             <input type="password" name="password">
             <label for="#">submit</label>
             <input type="submit" name="submit">
         </form>
     </div>
 </body>
 </html>

美化:

app.py

 from flask import Flask,render_template,views,request
 app = Flask(__name__)
 
 @app.route('/')
 def index():
     return "Hello World"
 
 class Login_View(views.MethodView):
 
     def __render(self, error_info=None):
         return render_template('login.html',error=error_info)
 
     def get(self):
         return self.__render()
 
     def post(self):
         # request.form.get 获取form 表单的数据,get()中是表单的name值
         username = request.form.get('username')
         passwd = request.form.get('password')
         if username == 'admin' and passwd == '123456':
             return "successful"
         else:
             return self.__render(error_info='failed')
 
 app.add_url_rule('/login/', view_func=Login_View.as_view('login'))
 
 if __name__=='__main__':
     app.run(debug=True)

login.html

 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <title>login</title>
 </head>
 <body>
     <div>
         <form action="" method="post">
             <label for="#">username</label>
             <input type="text" name="username">
             <label for="#">password</label>
             <input type="password" name="password">
             <label for="#">submit</label>
             <input type="submit" name="submit">
         </form>
         <p>
            {% if error %}
                 <p style="color: red;">please try again</p>
            {% endif %}
         </p>
     </div>
 </body>
 </html>

4.装饰器视图(函数装饰器/类装饰器)

函数装饰器:

在使用装饰器时,需要使用內建模块functions.wraps(func) 来转化一下,原因是:

 import functools
 
 def check_user_info(func):
  def wrapper(*args, **kwargs):
      return func(*args, **kwargs)
  return wrapper
 
 @check_user_info
 def my_check(name):
  print(name)
 
 def check_user_info1(func):
  @functools.wraps(func)
  def wrapper(*args, **kwargs):
      return func(*args, **kwargs)
  return wrapper
 
 @check_user_info1
 def my_check1(name):
  print(name)
 
 
 print(my_check.__name__)
 print(my_check1.__name__)
 """
 wrapper
 my_check1
 """
 # 可以看到不转换函数的 __name__ 和本身并不对应.
 from flask import Flask,request,render_template,views
 import functools
 
 app = Flask(__name__)
 app.config.update({'DEBUG':True, 'TEMPLATES_AUTO_RELOAD':True})
 
 def setting1(func):
  @functools.wraps(func)
  def wrapper(*args, **kwargs):
      username = request.args.get('username')
      password = request.args.get('password')
      if username == 'admin' and password == '123456':
          return 'hello'
      else:
          return func(*args, **kwargs)
  return wrapper
 
 
 @app.route('/')
 def index():
  return "Hello World"
 
 @app.route('/setting1/')
 @setting1
 def setting1():
  return 'function decorator'
 
 if __name__=='__main__':
  app.run()

在类视图中使用装饰器

 from flask import Flask,request,render_template,views
 import functools
 
 app = Flask(__name__)
 app.config.update({'DEBUG':True, 'TEMPLATES_AUTO_RELOAD':True})
 
 user_info = {
  'admin':'123456'
 }
 
 # 定义一个函数装饰器.用于判断登陆的用户名,密码是否正确
 # 这里需要应用一下 functions.wrapper 否则被装饰的函数的 __name__ 会改变
 def check_user_info(func):
  @functools.wraps(func)
  def wrapper(*args, **kwargs):
      username = request.form.get('username')
      password = request.form.get('password')
      for k,v in user_info.items():
          if k == username and v == password:
              # return func(*args, **kwargs)
              return 'success'
          else:
              return 'please login.'
  return wrapper
 
 
 class Setting_View(views.MethodView):
  def __rander(self):
      return render_template('setting.html')
 
  def get(self):
      return self.__rander()
 
  @check_user_info
  def post(self):
      return self.__rander()
 
 app.add_url_rule('/setting/', view_func=Setting_View.as_view('setting'))
 
 
 
 if __name__=='__main__':
  app.run()

setting.html

 <!DOCTYPE html>
 <html lang="en">
 <head>
  <meta charset="UTF-8">
  <title>Document</title>
 </head>
 <body>
  <form action="" method="post">
      <label for="">username</label>
      <input type="text" name='username'>
      <label for="">password</label>
      <input type="password" name='password'>
      <label for="">submit</label>
      <input type="submit" name='submit'>
  </form>
 
 </body>
 </html>


以上是关于Flask类视图的主要内容,如果未能解决你的问题,请参考以下文章

flask类视图

Flask类视图

Flask视图:视图函数,类视图,蓝图使用方法整理

9Flask实战第9天:类视图

Flask03-2-视图函数和视图类

Flask 视图函数和视图类