Python Web 框架工具包 werkzeug「建议收藏」

Python Web 框架工具包 werkzeug「建议收藏」一、什么是werkzeugwerkzeug官方的介绍说是一个WSGI工具包,不是一个web服务器,也不是一个web框架,它可以作为一个Web框架的底层库,因为它封装好了很多Web框架的东西,例如Request,Response等等;例如我最常用的Flask框架就是一Werkzeug为基础开发的。Werkzeug教程基础中文文档二、Werkzeug实现密码校…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

一、什么是werkzeug 

werkzeug 官方的介绍说是一个 WSGI 工具包,不是一个web服务器,也不是一个web框架,它可以作为一个 Web 框架的底层库,因为它封装好了很多 Web 框架的东西,例如 Request,Response 等等;例如我最常用的 Flask 框架就是一 Werkzeug 为基础开发的。

Werkzeug 教程基础中文文档

二、Werkzeug实现密码校验功能

使用Werkzeug实现密码散列,最关键的是使用其中security模块的generater_password_hash()和check_password_hash()。

from werkzeug.security import generate_password_hash,check_password_hash

class User(db.Model):
 __tablename__ = 'users'
 id = db.Column(db.Integer, primary_key=True)#id列
 username = db.Column(db.String(64), unique=True, index=True)  #username列
 role_id = db.Column(db.Integer, db.ForeignKey('roles.id')) #role_id列
 password_hash = db.Column(db.String(128)) #密码hash列
 
 @property
 def password(self): #访问password属性
  raise AttributeError('password is not a readable attribute')
 
 # 对保存到数据库中的密码进行加盐 
 # 什么是盐?
 # 通过一个随机字符串或指定字符串与原有字符串进行混淆处理,等到一个新的字符串,这个过程称为加盐处理。
 @password.setter
 def password(self,password): #设置password属性
  self.password_hash = generate_password_hash(password)  #默认加盐了
 
 # 输入密码时进行校验
 def verify_password(self,password):#验证密码
  return check_password_hash(self.password_hash,password)
 
 def __repr__(self):
  return '<user r="">' % self.username</user>

1、generate_password_hash(password, method=pbkdf2:sha1, slat_length=8)

method指定哈希变化的算法,默认sha1,salt_length指定‘盐的长度’,默认是8位。password是必须指定的。

返回计算得出的128位哈希值

2、check_password_hash(hash,password)

hash是密码散列值(一般存于数据库),password是输入需比较的密码。

返回的是True或者False

三、local 模块

1、简介

local模块中,Werkzeug实现了类似Python标准库中threading.local的功能。threading.local是线程局部变量,也就是每个线程的私有变量,具有线程隔离性,可以通过线程安全的方式获取或者改变线程中的变量。参照threading.local,Werkzeug实现了比thread.local更多的功能。

总结起来: 以上文档解释了对于“并发”问题,多线程并不是唯一的方式,在Python中还有“协程”。“协程”的一个显著特点在于是一个线程执行,一个线程可以存在多个协程。也可以理解为:协程会复用线程。对于WSGI应用来说,如果每一个线程处理一个请求,那么thread.local完全可以处理,但是如果每一个协程处理一个请求,那么一个线程中就存在多个请求,用thread.local变量处理起来会造成多个请求间数据的相互干扰。

对于上面问题,Werkzeug库解决的办法是local模块。local模块实现了四个类:

  • Local
  • LocalStack
  • LocalProxy
  • LocalManager

本文重点介绍前两个类的实现。

2、Local类

Local类能够用来存储线程的私有变量。在功能上这个threading.local类似。与之不同的是,Local类支持Python的协程。在Werkzeug库的local模块中,Local类实现了一种数据结构,用来保存线程的私有变量,对于其具体形式,可以参考它的构造函数:

class Local(object):
    __slots__ = ('__storage__', '__ident_func__')
    def __init__(self):
        object.__setattr__(self, '__storage__', {})
        object.__setattr__(self, '__ident_func__', get_ident)

从上面类定义可以看出,Local类具有两个属性:__storage____ident_func__。从构造函数来看,__storage__是一个字典,而__ident_func__是一个函数,用来识别当前线程或协程。

1. __ident_func__

关于当前线程或协程的识别,local模块引入get_ident函数。如果支持协程,则从greenlet库中导入相关函数,否则从thread库中导入相关函数。调用get_ident将返回一个整数,这个整数可以确定当前线程或者协程。

try:
    from greenlet import getcurrent as get_ident
except ImportError:
    try:
        from thread import get_ident
    except ImportError:
        from _thread import get_ident

2. __storage__

__storage__是一个字典,用来存储不同的线程/协程,以及这些线程/协程中的变量。以下是一个简单的多线程的例子,用来说明__storage__的具体结构。

import threading
from werkzeug.local import Local

l = Local()
l.__storage__
def add_arg(arg, i):
    l.__setattr__(arg, i)
for i in range(3):
    arg = 'arg' + str(i)
    t = threading.Thread(target=add_arg, args=(arg, i))
    t.start()
l.__storage__

上面的例子,具体分析为:

  • 首先,代码创建了一个Local的实例l,并且访问它的__storage__属性。由于目前还没有数据,所以l.__storage__的结果为{};
  • 代码创建了3个线程,每个线程均运行add_arg(arg, i)函数。这个函数会为每个线程创建一个变量,并对其赋值;
  • 最后,再次访问l.__storage__。这次,l实例中将包含3个线程的信息。其结果为:
{20212: {'arg0': 0}, 20404: {'arg1': 1}, 21512: {'arg2': 2}}

从以上结果可以看出,__storage__这个字典的键表示不同的线程(通过get_ident函数获得线程标识数值),而值表示对应线程中的变量。这种结构将不同的线程分离开来。当某个线程要访问该线程的变量时,便可以通过get_ident函数获得线程标识数值,进而可以在字典中获得该键对应的值信息了。

3、LocalStack类

LocalStack类和Local类类似,但是它实现了栈数据结构。

LocalStack类初始化的时候,便会创建一个Local实例,这个实例用于存储线程/协程的变量。与此同时,LocalStack类还实现了pushpoptop等方法或属性。调用这些属性或者方法时,该类会根据当前线程或协程的标识数值,在Local实例中对相应的数值进行操作。以下还是以一个多线程的例子进行说明:

from werkzeug.local import LocalStack, LocalProxy
import logging, random, threading, time

# 定义logging配置
logging.basicConfig(level=logging.DEBUG,
    format='(%(threadName)-10s) %(message)s',
)

# 生成一个LocalStack实例_stack
_stack = LocalStack()

# 定义一个RequestConetxt类,它包含一个上下文环境。
# 当调用这个类的实例时,它会将这个上下文对象放入
# _stack栈中去。当退出该上下文环境时,栈会pop其中
# 的上下文对象。

class RequestConetxt(object):
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c
    def __enter__(self):
        _stack.push(self)
    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_tb is None:
            _stack.pop()
    def __repr__(self):
        return '%s, %s, %s' % (self.a, self.b, self.c)

# 定义一个可供不同线程调用的方法。当不同线程调用该
# 方法时,首先会生成一个RequestConetxt实例,并在这
# 个上下文环境中先将该线程休眠一定时间,之后打印出
# 目前_stack中的信息,以及当前线程中的变量信息。
# 以上过程会循环两次。

def worker(i):
    with request_context(i):
        for j in range(2):
            pause = random.random()
            logging.debug('Sleeping %0.02f', pause)
            time.sleep(pause)
            logging.debug('stack: %s' % _stack._local.__storage__.items())
            logging.debug('ident_func(): %d' % _stack.__ident_func__())
            logging.debug('a=%s; b=%s; c=%s' % 
                          (LocalProxy(lambda: _stack.top.a),
                           LocalProxy(lambda: _stack.top.b),
                           LocalProxy(lambda: _stack.top.c))
                          )
    logging.debug('Done')

# 调用该函数生成一个RequestConetxt对象
def request_context(i):
    i = str(i+1)
    return RequestConetxt('a'+i, 'b'+i, 'c'+i)

# 在程序最开始显示_stack的最初状态
logging.debug('Stack Initial State: %s' % _stack._local.__storage__.items())

# 产生两个线程,分别调用worker函数
for i in range(2):
    t = threading.Thread(target=worker, args=(i,))
    t.start()
main_thread = threading.currentThread()
for t in threading.enumerate():
    if t is not main_thread:
        t.join()

# 在程序最后显示_stack的最终状态
logging.debug('Stack Finally State: %s' % _stack._local.__storage__.items())

以上例子的具体分析过程如下:

  • 首先,先创建一个LocalStack实例_stack,这个实例将存储线程/协程的变量信息;
  • 在程序开始运行时,先检查_stack中包含的信息;
  • 之后创建两个线程,分别执行worker函数;
  • worker函数首先会产生一个上下文对象,这个上下文对象会放入_stack中。在这个上下文环境中,程序执行一些操作,打印一些数据。当退出上下文环境时,_stack会pop该上下文对象。
  • 在程序结束时,再次检查_stack中包含的信息。

运行上面的测试例子,产生结果如下:

(MainThread) Stack Initial State: []
(Thread-1  ) Sleeping 0.31
(Thread-2  ) Sleeping 0.02
(Thread-2  ) stack: [(880, {'stack': [a1, b1, c1]}), (13232, {'stack': [a2, b2, c2]})]
(Thread-2  ) ident_func(): 13232
(Thread-2  ) a=a2; b=b2; c=c2
(Thread-2  ) Sleeping 0.49
(Thread-1  ) stack: [(880, {'stack': [a1, b1, c1]}), (13232, {'stack': [a2, b2, c2]})]
(Thread-1  ) ident_func(): 880
(Thread-1  ) a=a1; b=b1; c=c1
(Thread-1  ) Sleeping 0.27
(Thread-2  ) stack: [(880, {'stack': [a1, b1, c1]}), (13232, {'stack': [a2, b2, c2]})]
(Thread-2  ) ident_func(): 13232
(Thread-2  ) a=a2; b=b2; c=c2
(Thread-2  ) Done
(Thread-1  ) stack: [(880, {'stack': [a1, b1, c1]})]
(Thread-1  ) ident_func(): 880
(Thread-1  ) a=a1; b=b1; c=c1
(Thread-1  ) Done
(MainThread) Stack Finally State: []

注意:

  • 当两个线程在运行时,_stack中会存储这两个线程的信息,每个线程的信息都保存在类似{'stack': [a1, b1, c1]}的结构中(注:stack键对应的是放入该栈中的对象,此处为了方便打印了该对象的一些属性)。
  • 当线程在休眠和运行中切换时,通过线程的标识数值进行区分不同线程,线程1运行时它通过标识数值只会对属于该线程的数值进行操作,而不会和线程2的数值混淆,这样便起到线程隔离的效果(而不是通过锁的方式)。
  • 由于是在一个上下文环境中运行,当线程执行完毕时,_stack会将该线程存储的信息删除掉。在上面的运行结果中可以看出,当线程2运行结束后,_stack中只包含线程1的相关信息。当所有线程都运行结束,_stack的最终状态将为空。

四、wrappers模块

1、简介

Werkzeug库中的wrappers模块主要对requestresponse进行封装。request包含了客户端发往服务器的所有请求信息,response包含了web应用返回给客户端的所有信息。wrappers模块对请求和响应的封装简化了客户端、服务器和web应用通信的流程。本文主要介绍wrappers模块中重要的类。

2、BaseRequest

BaseRequest是一个非常基础的请求类,它可以和其他的“混合”类结合在一起构建复杂的请求类。只要传递一个环境变量environ(由WSGI服务器根据请求产生),便可以构造一个BaseRequest实例。其构造函数如下:

def __init__(self, environ, populate_request=True, shallow=False):
    self.environ = environ
    if populate_request and not shallow:
        self.environ['werkzeug.request'] = self
    self.shallow = shallow

初始化后,形成的实例request便具有了一些属性可以访问,这些属性只能以“只读”的方式访问。例如:

  • url_charset
  • want_form_data_parsed
  • stream
  • args
  • data
  • form
  • values
  • files
  • cookies
  • headers
  • path
  • full_path
  • script_root
  • url
  • base_url
  • url_root
  • host_url
  • host
  • access_route
  • remote_addr

BaseRequest中还有两个类方法比较常用:

from_values(cls, *args, kwargs)**

@classmethod

def from_values(cls, *args, **kwargs):
    """Create a new request object based on the values provided.  If
    environ is given missing values are filled from there.  This method is
    useful for small scripts when you need to simulate a request from an URL.
    Do not use this method for unittesting, there is a full featured client
    object (:class:`Client`) that allows to create multipart requests,
    support for cookies etc.
    This accepts the same options as the
    :class:`~werkzeug.test.EnvironBuilder`.
    .. versionchanged:: 0.5
       This method now accepts the same arguments as
       :class:`~werkzeug.test.EnvironBuilder`.  Because of this the
       `environ` parameter is now called `environ_overrides`.
    :return: request object
    """

    from werkzeug.test import EnvironBuilder
    charset = kwargs.pop('charset', cls.charset)
    kwargs['charset'] = charset
    builder = EnvironBuilder(*args, **kwargs)
    try:
        return builder.get_request(cls)
    finally:
        builder.close()

这个类方法可以根据提供的参数构建一个请求。

application(cls, f)

@classmethod

def application(cls, f):
    """Decorate a function as responder that accepts the request as first
    argument.  This works like the :func:`responder` decorator but the
    function is passed the request object as first argument and the
    request object will be closed automatically::
        @Request.application
        def my_wsgi_app(request):
            return Response('Hello World!')
    :param f: the WSGI callable to decorate
    :return: a new WSGI callable
    """

    #: return a callable that wraps the -2nd argument with the request
    #: and calls the function with all the arguments up to that one and
    #: the request.  The return value is then called with the latest
    #: two arguments.  This makes it possible to use this decorator for
    #: both methods and standalone WSGI functions.

    def application(*args):
        request = cls(args[-2])
        with request:
            return f(*args[:-2] + (request,))(*args[-2:])
    return update_wrapper(application, f)

这个类方法是一个装饰器,可以用来装饰WSGI可调用对象或函数。

以上属性和方法的具体用法可以参考 Request——werkzeug文档

3、BaseResponse

BaseResponse类是一个响应类,用它可以封装一个response对象。response对象最大的特点是它是一个WSGI应用。

在之前介绍WSGI规范的文章中曾介绍过Web服务器网关,它简化了服务器和web应用之间的通信过程,它要求服务器和web应用要遵循WSGI规范进行开发。对于web应用而言,应用应该实现一个函数或者一个可调用对象,这样WSGI服务器可以通过调用myWebApp(environ, start_response)从web应用获得响应内容。

response响应对象就是这样一个WSGI应用对象。在其实现过程中有一个__call__方法,可以实现对一个response对象的调用。代码如下:

def __call__(self, environ, start_response):
    """Process this response as WSGI application.
    :param environ: the WSGI environment.
    :param start_response: the response callable provided by the WSGI
                           server.
    :return: an application iterator
    """

    app_iter, status, headers = self.get_wsgi_response(environ)
    start_response(status, headers)
    return app_iter

这样,我们就可以很清楚地理解WSGI应用的实现过程。下面是一个非常简单的WSGI应用。

from werkzeug.wrappers import Request, Response

def application(environ, start_response):
    request = Request(environ)
    response = Response("Hello %s!" % request.args.get('name', 'World!'))
    return response(environ, start_response)

上面的小例子的实现步骤分析:

  1. 根据传入web应用的environ构造请求对象request
  2. web应用构造响应对象response
  3. 调用响应对象response。调用过程中产生三个值:app_iterstatusheaders,其中statusheaders作为参数传递给函数start_response用于生成响应报文首行的相关信息,而app_iter作为响应的内容(它是一个可迭代对象)返回给WSGI网关
  4. WSGI网关将返回的信息组成响应首行、响应首部、响应主体等,形成响应报文发回给客户端。

BaseResponse类中还有一些属性和方法,以下属性和方法的具体用法可以参考Response——werkzeug文档

  • 属性
    • status_code
    • status
    • data
    • is_stream
    • is_sequence
    • ······
  • 方法
    • call_on_close(func)
    • close()
    • freeze()
    • force_type() 类方法
    • from_app() 类方法
    • set_data()
    • get_data()
    • _ensure_sequence()
    • make_sequence()
    • iter_encoded()
    • calculate_content_length()
    • set_cookie()
    • delete_cookie()
    • get_wsgi_headers(environ)
    • get_app_iter(environ)
    • get_wsgi_response(environ)
    • __call__(environ, start_response)
    • ······

4、Mixin类

BaseRequest类和BaseResponse类是请求和响应最基础的类。wrappers模块中还提供了一些Mixin类,用于扩展请求类和响应类。

有关请求类的Mixin类主要有:

  • AcceptMixin类 ——请求报文中关于客户端希望接收的数据类型的类。
  • ETagRequestMixin类 ——请求报文中关于Etag和Cache的类。
  • UserAgentMixin类 ——请求报文中关于user_agent的类。
  • AuthorizationMixin类 ——请求报文中关于认证的类。
  • CommonRequestDescriptorsMixin类 ——通过这个类可以获取请求首部中的相关信息。

有关响应类的Mixin类主要有:

  • ETagResponseMixin类 ——为响应增加Etag和Cache控制的类。
  • ResponseStreamMixin类 ——为响应可迭代对象提供一个“只写”的接口的类。
  • CommonResponseDescriptorsMixin类 ——通过这个类可以获取响应首部中的相关信息。
  • WWWAuthenticateMixin类 ——为响应提供认证的类。

5、RequestResponse

终于讲到Request类和Response类了。

Request类继承自BaseRequest类,并且结合一些请求相关的Mixin类,具体如下:

class Request(BaseRequest, AcceptMixin, ETagRequestMixin,
              UserAgentMixin, AuthorizationMixin,
              CommonRequestDescriptorsMixin)

Response类继承自BaseResponse类,并且结合一些响应相关的Mixin类,具体如下:Python

class Response(BaseResponse, ETagResponseMixin, ResponseStreamMixin,
               CommonResponseDescriptorsMixin,
               WWWAuthenticateMixin)

至此,可以从wrappers模块中引入Request类和Response用于构建请求对象和响应对象。

五、routing模块

Werkzeug库的routing模块的主要功能在于URL解析。对于WSGI应用来讲,不同的URL对应不同的视图函数,routing模块则会对请求信息的URL进行解析并匹配,触发URL对应的视图函数,以此生成一个响应信息。routing模块的解析和匹配功能主要体现在三个类上:RuleMapMapAdapter

1、Rule

Rule类继承自RuleFactory类。一个Rule的实例代表一个URL模式,一个WSGI应用可以处理很多不同的URL模式,这也就是说可以产生很多不同的Rule实例。这些Rule实例最终会作为参数传递给Map类,形成一个包含所有URL模式的对象,通过这个对象可以解析并匹配请求对应的视图函数。

关于Rule类有一些常用的方法:

  • empty() ——在实际情况中,Rule实例会和一个Map实例进行绑定。通过empty()方法可以将Rule实例和Map实例解除绑定。
  • get_empty_kwargs() ——在empty()方法中调用,可以获得之前Rule实例的参数,以便重新构造一个Rule实例。
  • get_rules(map) ——这个方法是对RuleFactory类中get_rules方法的重写,返回Rule实例本身。
  • refresh() ——当修改Rule实例(URL规则)后可以调用该方法,以便更新Rule实例和Map实例的绑定关系。
  • bind(map, rebind=False) ——将Rule实例和一个Map实例进行绑定,这个方法会调用complie()方法,会给Rule实例生成一个正则表达式。
  • complie() ——根据Rule实例的URL模式,生成一个正则表达式,以便后续对请求的path进行匹配。
  • match(path) ——将Rule实例和给定的path进行匹配。在调用complie()方法生成的正则表达式将会对path进行匹配。如果匹配,将返回这个path中的参数,以便后续过程使用。如果不匹配,将会由其他的Rule实例和这个path进行匹配。

注意: 在对给定的URL进行匹配的过程中,会使用一些Converters。关于Converters的信息后续加以介绍。

2、Map

通过Map类构造的实例可以存储所有的URL规则,这些规则是Rule类的实例。Map实例可以 通过后续的调用和给定的URL进行匹配。

关于Map类有一些常用的方法:

  • add(rulefactory) ——这个方法在构造Map实例的时候就会调用,它会将所有传入Map类中的Rule实例和该Map实例建立绑定关系。该方法还会调用Rule实例的bind方法。
  • bind方法 ——这个方法会生成一个MapAdapter实例,传入MapAdapter的包括一些请求信息,这样可以调用MapAdapter实例的方法匹配给定URL。
  • bind_to_environ方法 ——通过解析请求中的environ信息,然后调用上面的bind方法,最终会生成一个MapAdapter实例。

3、MapAdapter

MapAdapter类执行URL匹配的具体工作。关于MapAdapter类有一些常用的方法:

  • dispatch方法 ——该方法首先会调用MapAdapter实例的match()方法,如果有匹配的Rule,则会执行该Rule对应的视图函数。
  • match方法 ——该方法将会进行具体的URL匹配工作。它会将请求中的url和MapAdapter实例中的所有Rule进行匹配,如果有匹配成功的,则返回该Rule对应的endpoint和一些参数rvendpoint一般会对应一个视图函数,返回的rv可以作为参数传入视图函数中。

4、示例

为了说明routing模块的工作原理,这里使用Werkzeug文档中的一个例子,稍加改动后如下所示:

from werkzeug.routing import Map, Rule, NotFound, RequestRedirect, HTTPException

url_map = Map([
    Rule('/', endpoint='blog/index'),
    Rule('/<int:year>/', endpoint='blog/archive'),
    Rule('/<int:year>/<int:month>/', endpoint='blog/archive'),
    Rule('/<int:year>/<int:month>/<int:day>/', endpoint='blog/archive'),
    Rule('/<int:year>/<int:month>/<int:day>/<slug>',
         endpoint='blog/show_post'),
    Rule('/about', endpoint='blog/about_me'),
    Rule('/feeds/', endpoint='blog/feeds'),
    Rule('/feeds/<feed_name>.rss', endpoint='blog/show_feed')
])

def application(environ, start_response):
    urls = url_map.bind_to_environ(environ)
    try:
        endpoint, args = urls.match()
    except HTTPException, e:
        return e(environ, start_response)
    start_response('200 OK', [('Content-Type', 'text/plain')])
    return  ['Rule points to %r with arguments %r' % (endpoint, args)]

if __name__ == '__main__':
    from werkzeug.serving import run_simple
    run_simple('localhost', 4000, application)

这里我们使用werkzeug自带的服务器模块构造了一个Web服务器,并且设计了一个简单的WSGI应用——application。这个Web服务器可以根据URL的不同返回不同的结果。关于服务器的构造这里不再赘述,以下部分简单对URL Routing过程进行分析:

1. 设计URL模式

设计URL模式的过程就是构造Rule实例的过程。上面的例子中我们构造了8个Rule实例,分别对应8个不同的URL模式。每个Rule实例还对应一个endpoint,这个endpoint可以和视图函数进行对应,以便访问某个URL时,可以触发与之对应的视图函数。下面的例子展示了endpoint和视图函数的对应关系。

from werkzeug.wrappers import Response
from werkzeug.routing import Map, Rule

def on_index(request):
    return Response('Hello from the index')

url_map = Map([Rule('/', endpoint='index')])
views = {'index': on_index}

2. 构造Map实例

构造Map实例时,会调用它的add(rulefactory)方法。这个方法会在Map实例和各个Rule实例之间建立绑定关系,并通过调用Rule实例的bind()方法为每个Rule实例生成一个正则表达式。

例如,对于'/about'这个URL,它对应的正则表达式为:

1

‘^\\|\\/about$’

对于'/<int:year>/<int:month>/<int:day>/'这个URL,它对应的正则表达式为:

1

‘^\\|\\/(?P<year>\\d+)\\/(?P<month>\\d+)\\/(?P<day>\\d+)(?<!/)(?P<__suffix__>/?)$’

3. 构造MapAdapter实例

在设计WSGI应用时,上述例子通过url_map.bind_to_environ(environ)构建了一个MapAdapter实例。这个实例将请求的相关信息和已经创建好的Map实例放在一起,以便进行URL匹配。

进行URL匹配的过程是通过调用MapAdapter实例的match()方法进行的。实质上,这个方法会将请求中的path传入到所有Rule实例的match(path)方法中,经过正则表达式的匹配来分析path是否和某个Rule实例匹配。如果匹配则返回对应的endpoint和其他的参数,这可以作为参数传入视图函数。

4. 访问URL可得相关结果

之后,访问URL可以得到相对应的结果。

例如,访问http://localhost:4000/2017/,可以得到:

Rule points to 'blog/archive' with arguments {'year': 2017}

访问http://localhost:4000/2017/3/20/,可以得到:

Rule points to 'blog/archive' with arguments {'month': 3, 'day': 20, 'year': 2017}

访问http://localhost:4000/about,可以得到:

Rule points to 'blog/about_me' with arguments {}

六、utils模块

utils是一个工具模块,很多方法都用在responserequest模块中。当设计中间件的时候也需要调用某些方法。

secure_filename() 函数处理文件名,该函数只返回ASCII字符,非ASCII字符会被过滤掉。中文文件名需要转换成英文,可以使用pypinyin库 来转换。

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/184953.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号