106、登陆认证装饰器、 配置文件 、路由系统

124 阅读5分钟

今日内容概要

  • 登陆认证装饰器
  • 配置文件
  • 路由系统

今日内容详细

登陆认证装饰器

1.flask路由下加装饰器,一定要加endpoint
	1.如果不指定endpoint,反向解析的名字都是函数名,不加装饰器没有问题,就是正常函数index,detail
  2.如果加了装饰器--->index,detail都变成了inner--->反向解析的名字都是函数名inner,报错了
  3.wrapper装饰器---->把它包的更像--->函数名变成了原来函数的函数名
  
2.装饰器使用位置,顺序:紧挨着函数,路由匹配成功了,才会执行该装饰器

代码


from flask import Flask,render_template,request,session,redirect
from functools import wraps

app = Flask(__name__,template_folder='templates')
app.debug = True
app.secret_key = 'sdfsdfsdfsdf'  # 等同于django的 配置文件有个很长的秘钥

USERS = {
    1:{'name':'张三','age':18,'gender':'男','text':"道路千万条"},
    2:{'name':'李四','age':28,'gender':'男','text':"安全第一条"},
    3:{'name':'王五','age':18,'gender':'女','text':"行车不规范"},
}

def login_outer(func_name):
    def inner(*args,**kwargs):
        if session.get('username'):
            res = func_name(*args, **kwargs)
            return res
        else:
            return redirect('/login')
    return inner

@app.route('/login',methods = ['GET','POST'],endpoint='login')
def login():
    if request.method == "GET":
        return render_template('login.html')
    else:
        # post请求校验用户名,密码
        username = request.form.get('username')
        password = request.form.get('password')
        if username == 'nana' and password =='123':
            # 登陆成功,存session
            session['username']=username
            return redirect('/index')
        else:
            return render_template('login.html',error='用户名密码错误')

@app.route('/index',endpoint='index')
@login_outer   # index= login_outer(index)
def index():
    # 登陆才能访问
    return render_template('index.html',users=USERS)

# /detail/{{k}}
@app.route('/detail/<int:pk>',endpoint='detail')
@login_outer   # detail= login_outer(detail)
def detail(pk):

    info = USERS.get(pk)
    return render_template('detail.html',info=info)

if __name__ == '__main__':
    app.run()

装饰器

1.装饰器的本质:在不改变被装饰对象原来的'调用方式''内部代码'的情况下给被装饰对象添加新的功能
2.装饰器的原则:对修改封闭,对扩展开放
3.演示
    def outer(func):
      def inner(*args,**kwargs):
          res= func(*args,**kwargs)
          return res
      return inner

    @outer   # index = outer(index)  -->将语法糖下面紧挨着的函数名当作outer的参数传进去,返回inner,当函数被调用(index()),执行inner()--->真正执行被调用函数
    def index():
        print('index')
        return 123

    print(index())
    
4.还有类装饰器: 1.装饰类的装饰器   2. 类作为装饰器
	1.装饰类的装饰器
    def outer(func):
      def inner(*args,**kwargs):
          res= func(*args,**kwargs)
          res.name = 'nana'
          return res
      return inner

      @outer  # Foo = outer(Foo)     # 其返回值inner,当类被实力话产生对象的时候(f = Foo())--->inner()-->类实例化得到对象,返回,以后f就是Foo的对象,但是可以里面多了属性或方法
    class Foo:
        pass

    f= Foo()
    print(f.name)  # nana
    
  2.类作为装饰器
  	class Person:
    def __call__(self,func,*args, **kwargs):
        def inner(*args, **kwargs):
            res = func(*args, **kwargs)
            return res

        return inner

  p = Person()


  @p    # test = p(test)  ==>对象加括号,自动调用__call__方法返回inner,以后test就是inner--->test(参数)--》执行的是inner
  def  test():
      print(test)  # <function Person.__call__.<locals>.inner at 0x1044c1900>
      print('ok')

配置文件

配置方式一:

from flask import Flask

app = Flask(__name__)

# 1.配置方式一:
app.debug=True  # Debug mode: on
app.secret_key='dsvfsvgfbfdbgfbdgbf'


@app.route('/')
def index():
    print(app.debug)  # True
    print(app.secret_key)  # dsvfsvgfbfdbgfbdgbf
    return "it's ok"

if __name__ == '__main__':
    app.run()

配置方式二

from flask import Flask

app = Flask(__name__)

# 配置方式二:
app.config['DEBUG'] =True
app.config['SECRET_KEY']='dcfssdvtgvb'



@app.route('/')
def index():
    print(app.debug)  # True
    print(app.secret_key)  # dcfssdvtgvb
    # print(app.config)  # <Config {'ENV': 'production', 'DEBUG': False,N_COOKIE_NAME': 'session',....}>

    return "it's ok"


if __name__ == '__main__':
    app.run()

配置方式三

from flask import Flask

app = Flask(__name__)


# 配置方式三:通过py文件配置
# app.config.from_pyfile("python文件名称")
app.config.from_pyfile("settings.py")

@app.route('/')
def index():
    print(app.debug)  # True
    print(app.secret_key)  # asdfasdfasdfasd333

    return "it's ok"


if __name__ == '__main__':
    app.run()

settings.py

DEBUG=True
SECRET_KEY="asdfasdfasdfasd333"

其他

1.通过环境变量配置
app.config.from_envvar("环境变量名称")
#app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS'])
环境变量的值为python文件名称名称,内部调用from_pyfile方法

2.JSON文件
app.config.from_json("json文件名称")
JSON文件名称,必须是json格式,因为内部会执行json.loads

3.
app.config.from_mapping({'DEBUG': True})
字典格式

4.
app.config.from_object("python类或类的路径")
app.config.from_object('pro_flask.settings.TestingConfig')

# settings.py
  class Config(object):
      DEBUG = False
      TESTING = False
      DATABASE_URI = 'sqlite://:memory:'

  class ProductionConfig(Config):
      DATABASE_URI = 'mysql://user@localhost/foo'

  class DevelopmentConfig(Config):
      DEBUG = True  # # 开发环境,debug 是true的,连数据库连的是sqlite

  class TestingConfig(Config):
      TESTING = True


PS: 从sys.path中已经存在路径开始写
PS: settings.py文件默认路径要放在程序root_path目录,如果instance_relative_config为True,则就是instance_path目录(Flask对象init方法的参数)

5.配置中心

flask3.png

路由系统

1.典型写法
	@app.route('/detail/<int:nid>',methods=['GET'],endpoint='detail')
  """
  rule:路径
  methods :请求方式,列表
  endpoint: 路径别名
  """
  
2.转换器
  DEFAULT_CONVERTERS = {
      'default':          UnicodeConverter,
      'string':           UnicodeConverter,
      'any':              AnyConverter,
      'path':             PathConverter,
      'int':              IntegerConverter,
      'float':            FloatConverter,
      'uuid':             UUIDConverter,
  }
  # 最常使用:string  int  path
  ps:@app.route('/<path:aa>', methods=['GET'])  

路由系统本质-->读源码

1.入口:@app.route('/',methods=['GET'])的route
2.点进去,发现是一个装饰器
    @setupmethod
    def route(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]:
        def decorator(f: T_route) -> T_route:
            endpoint = options.pop("endpoint", None)
            self.add_url_rule(rule, endpoint, f, **options)
            return f

        return decorator
    """
    @app.route('/',methods=['GET'])   # index = app.route('/',methods=['GET'])(index) -->其返回值是decorator--->index = decorator(index)
def index():
    return "it's ok"
    """ 
3.分析decorator  
def decorator(f: T_route) -> T_route:
  endpoint = options.pop("endpoint", None) # 从options弹出,如果没有,就是None --->@app.route(传了就有,不传就是None)  
  self.add_url_rule(rule, endpoint, f, **options) # self就是app对象
  return f

4.核心:self.add_url_rule(rule, endpoint, f, **options)--->self就是app对象
app.add_url_rule('路由地址', '路由别名', 视图函数, **options)--->跟django很像
"""
也就是说可以不用@app.route(.....),而使用app.add_url_rule('路由地址', '路由别名', 视图函数, **options)
eg:
    def index():
        return "it's ok"
        
    app.add_url_rule('/', 'index', index)

"""
5.add_url_rule的参数详解
  """
     rule, URL规则,路径地址
      view_func, 视图函数名称
      defaults = None, 默认值, 当URL中无参数,函数需要参数时,使用defaults = {'k': 'v'}
      为函数提供参数
      endpoint = None, 名称,用于反向生成URL,即: url_for('名称')
      methods = None, 允许的请求方式,如:["GET", "POST"]
      #对URL最后的 / 符号是否严格要求
      strict_slashes = None
      #重定向到指定地址
      redirect_to = None, 
  """
	def index(name):
    print(name)
    return "it's ok"

  def home():
      return 'home'
  app.add_url_rule('/', 'index', index,defaults={'name':'nana'})
  app.add_url_rule('/home', 'home', home,strict_slashes=True)
      
    
6.endpoint 不传会怎么样?
	不传会以视图函数的名字作为值,但是如果加了装饰器,所有视图函数名字都是inner,就会出错,使用wrapper装饰器再装饰装饰器