目录
2-2-2 方式二、继承 views.MethodView 类 - 常用方式
三、 路由的正则支持 - 基于BaseConverter实现正则规则
一、基本使用
@app.route('/detail/<int:nid>',methods=['GET'],endpoint='detail')
1-1 默认转换器
DEFAULT_CONVERTERS = { 'default': UnicodeConverter, 'string': UnicodeConverter, 'any': AnyConverter, 'path': PathConverter, 'int': IntegerConverter, 'float': FloatConverter, 'uuid': UUIDConverter, }
二、add_url_rule - 装饰器本质分析及使用
2-1 route装饰器 源码分析
route的装饰器本质是 add_url_rule 函数的使用
@app.route("/login", methods=['GET',"POST"],endpoint='loginname') def login(): return '登录' # 效果同上装饰器的使用 app.add_url_rule('/login', 'loginname', login, methods=['GET',"POST"]) ''' def add_url_rule(self, rule, endpoint=None, view_func=None, provide_automatic_options=None, **options): '''
2-1-1 add_url_rule使用效果测试
from flask import Flask, request, render_template, redirect, session, url_for app = Flask(__name__) app.debug = True app.secret_key = 'test' # 模拟数据库内信息 USER_LIST = { 1: {'name': '张三', 'age': 18, 'gender': '男', 'text': "道路千万条"}, 2: {'name': '李四', 'age': 28, 'gender': '男', 'text': "安全第一条"}, 3: {'name': '王五', 'age': 18, 'gender': '女', 'text': "行车不规范"}, } from functools import wraps def login_decorator(func, ): # @wraps(func) def wrapper(*args, **kwargs): if 'user' in session: res = func(*args, **kwargs) return res return redirect('/login') return wrapper @app.route('/') @login_decorator def index(): return 'ok' # @app.route('/login', methods=['GET', 'POST'],endpoint=loginname) def login(): if request.method == 'GET': return render_template('login.html') else: # 取出表单提交的name数据 name = request.form.get('name') pwd = request.form.get('pwd') if name == 'test' and pwd == '123': # 登录成功 写session session['user'] = name return 'login success' else: # django中传字典,这里传**字典 # return render_template('login.html',**{'error':'用户名或密码错误'}) return render_template('login.html', error='用户名或密码错误') app.add_url_rule('/login', 'loginname', login, methods=['GET', 'POST']) if __name__ == '__main__': app.run()
错误信息分析:单独使用add_url_rule时,必须手动指定endpoint参数
错误信息分析:单独使用add_url_rule时,必须手动基于函数内存地址
2-2 基于add_url_rule的CBV使用方式
注意!!基于flask库下的views,切记导入
2-2-1 方式一、继承view.View类
使用思路总结
- 创建继承 views.View 的类
- 必须在该类内重写 dispatch_request 方法
- 类外app.add_url_rule('/路由', view_func=类名.as_view(name='别名,即同endpoint'))
View基类总结
- 类内
- methods = ['GET', 'POST'] - 指定该类视图支持的请求类别
- decorators = [login_auth, ] - 指定该类视图外包裹的装饰器,注意!前置名作为里层包裹。
实例分析
from flask import Flask, views app = Flask(__name__) app.debug = True # 装饰器,用于类内decorators,包裹类视图 def auth(func): def inner(*args, **kwargs): print('before') result = func(*args, **kwargs) print('after') return result return inner class IndexView(views.View): methods = ['GET'] decorators = [auth, ] def dispatch_request(self): print('Index') return 'Index!' app.add_url_rule('/index', view_func=IndexView.as_view(name='index')) # name=endpoint
源码分析
class View(object): """Alternative way to use view functions. A subclass has to implement :meth:`dispatch_request` which is called with the view arguments from the URL routing system. If :attr:`methods` is provided the methods do not have to be passed to the :meth:`~flask.Flask.add_url_rule` 使用视图函数的另一种方法。 子类必须实现:meth: ' dispatch_request ',它使用来自URL路由系统的视图参数调用。 如果提供:attr: 'methods',则不必传递给:meth:' ~flask.Flask.add_url_rule ' method explicitly:: 明确的使用方法 class MyView(View): methods = ['GET'] def dispatch_request(self, name): return 'Hello %s!' % name app.add_url_rule('/hello/<name>', view_func=MyView.as_view('myview')) When you want to decorate a pluggable view you will have to either do that when the view function is created (by wrapping the return value of :meth:`as_view`) or you can use the :attr:`decorators` attribute:: 当你想装饰一个可插入视图时,你必须在视图函数创建时(通过包装返回值:meth: ' as_view ') 或者你可以使用:attr: ' decorators '属性:: class SecretView(View): methods = ['GET'] decorators = [superuser_required] def dispatch_request(self): ... The decorators stored in the decorators list are applied one after another when the view function is created. Note that you can *not* use the class based decorators since those would decorate the view class and not the generated view function! 在创建视图函数时,存储在装饰器列表中的装饰器将一个接一个地应用。 注意,您不能使用基于类的装饰器,因为它们将装饰视图类而不是生成的视图函数! """ #: A list of methods this view can handle. methods = None #: Setting this disables or force-enables the automatic options handling. provide_automatic_options = None #: The canonical way to decorate class-based views is to decorate the #: return value of as_view(). However since this moves parts of the #: logic from the class declaration to the place where it's hooked #: into the routing system. #: #: You can place one or more decorators in this list and whenever the #: view function is created the result is automatically decorated. #: #: .. versionadded:: 0.8 decorators = () def dispatch_request(self): """Subclasses have to override this method to implement the actual view function code. This method is called with all the arguments from the URL rule. 子类必须重写此方法以实现实际的视图函数代码。 使用URL规则中的所有参数调用此方法。 """ raise NotImplementedError() @classmethod def as_view(cls, name, *class_args, **class_kwargs): """Converts the class into an actual view function that can be used with the routing system. Internally this generates a function on the fly which will instantiate the :class:`View` on each request and call the :meth:`dispatch_request` method on it. The arguments passed to :meth:`as_view` are forwarded to the constructor of the class. """ def view(*args, **kwargs): self = view.view_class(*class_args, **class_kwargs) return self.dispatch_request(*args, **kwargs) if cls.decorators: view.__name__ = name view.__module__ = cls.__module__ for decorator in cls.decorators: view = decorator(view) # We attach the view class to the view function for two reasons: # first of all it allows us to easily figure out what class-based # view this thing came from, secondly it's also used for instantiating # the view class so you can actually replace it with something else # for testing purposes and debugging. 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
2-2-2 方式二、继承 views.MethodView 类 - 常用方式
使用思路总结
- 创建继承 views.MethodView 的类 (该类继承于View类)
- 必须在该类内重写 get 和 post方法
- 类外app.add_url_rule('/路由', view_func=类名.as_view(name='别名,即同endpoint'))
View基类总结
- 类内
- methods = ['GET', 'POST'] - 指定该类视图支持的请求类别
- decorators = [login_auth, ] - 指定该类视图外包裹的装饰器,注意!前置名作为里层包裹。
实例分析
from flask import Flask, views app = Flask(__name__) app.debug = True # 装饰器,用于类内decorators,包裹类视图 def auth(func): def inner(*args, **kwargs): print('before') result = func(*args, **kwargs) print('after') return result return inner class IndexView(views.MethodView): methods = ['GET'] decorators = [auth, ] def get(self): return 'Index.GET' def post(self): return 'Index.POST' app.add_url_rule('/index', view_func=IndexView.as_view(name='index')) # name=endpoint
源码分析
class MethodView(with_metaclass(MethodViewType, View)): """A class-based view that dispatches request methods to the corresponding class methods. For example, if you implement a ``get`` method, it will be used to handle ``GET`` requests. :: class CounterAPI(MethodView): def get(self): return session.get('counter', 0) def post(self): session['counter'] = session.get('counter', 0) + 1 return 'OK' app.add_url_rule('/counter', view_func=CounterAPI.as_view('counter')) """ def dispatch_request(self, *args, **kwargs): meth = getattr(self, request.method.lower(), None) # If the request method is HEAD and we don't have a handler for it # retry with GET. if meth is None and request.method == 'HEAD': meth = getattr(self, 'get', None) assert meth is not None, 'Unimplemented method %r' % request.method return meth(*args, **kwargs)
2-3 app.add_url_rule参数
- rule - URL规则
- view_func - 视图函数名称
- defaults = None - 默认值, 当URL中无参数,函数需要参数时,使用defaults = {'k': 'v'}
为函数提供参数- endpoint = None - 名称,用于反向生成URL,即: url_for('名称')
- methods = None - 允许的请求方式,如:["GET", "POST"]
- strict_slashes = None - 对URL最后的 / 符号是否严格要求
@app.route('/index', strict_slashes=False) # 访问 http://www.xx.com/index/ 或 http://www.xx.com/index 均可 @app.route('/index', strict_slashes=True) # 仅访问 http://www.xx.com/index
- redirect_to = None - 重定向到指定地址
- @app.route('/index/<int:nid>', redirect_to='/home/<nid>')
- subdomain = None - 子域名访问
# 修改主机内host文件,确保域名能够DNS解析 # C:\Windows\System32\drivers\etc\hosts 127.0.0.1 www.test.com 127.0.0.1 admin.test.com 127.0.0.1 buy.test.com from flask import Flask, views, url_for app = Flask(import_name=__name__) app.config['SERVER_NAME'] = 'test.com:5000' @app.route("/", subdomain="admin") def static_index(): """Flask supports static subdomains This is available at static.your-domain.tld""" return "static.your-domain.tld" # 可以传入任意的字符串,如传入的字符串为aa,显示为 aa.test.com @app.route("/dynamic", subdomain="<username>") def username_index(username): """Dynamic subdomains are also supported Try going to user1.your-domain.tld/dynamic""" return username + ".your-domain.tld" if __name__ == '__main__': app.run() 访问方式: http://www.test.com:5000/dynamic http://admin.test.com:5000/dynamic http://buy.test.com:5000/dynamic
三、 路由的正则支持 - 基于BaseConverter实现正则规则
思路总结
- 创建继承 BaseConverter 的类
- 注册:app.url_map.converters['regex'] = RegexConverter
- 使用:@app.route('/index/<regex("\d+"):nid>') 正则表达式会当作第二个参数传递到类中
实例分析
from flask import Flask, views, url_for from werkzeug.routing import BaseConverter app = Flask(import_name=__name__) class RegexConverter(BaseConverter): """ 自定义URL匹配正则表达式 """ def __init__(self, map, regex): super(RegexConverter, self).__init__(map) self.regex = regex def to_python(self, value): """ 路由匹配时,匹配成功后传递给视图函数中参数的值 """ return int(value) def to_url(self, value): """ 使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数 """ val = super(RegexConverter, self).to_url(value) return val # 添加到flask中 app.url_map.converters['regex'] = RegexConverter @app.route('/index/<regex("\d+"):nid>') def index(nid): print(url_for('index', nid='888')) return 'Index' if __name__ == '__main__': app.run()
源码分析
class BaseConverter(object): """Base class for all converters.""" regex = '[^/]+' weight = 100 def __init__(self, map): self.map = map def to_python(self, value): return value def to_url(self, value): return url_quote(value, charset=self.map.charset)