在会话中存储对象

此示例展示了如何在会话对象中跨请求持久化对象。

之前所述,Session 实例的持续时间与假想会话本身的持续时间相同。每次调用Request.getSession 时,如果请求的会话仍然处于活动状态,则会返回与之前返回的相同的Session 实例。因此,Session 实例可用于在会话存在期间保留其他对象。

演示如何实现比解释更容易,因此以下是一个示例

>>> from zope.interface import Interface, Attribute, implementer
>>> from twisted.python.components import registerAdapter
>>> from twisted.web.server import Session
>>> class ICounter(Interface):
...     value = Attribute("An int value which counts up once per page view.")
...
>>> @implementer(ICounter)
... class Counter(object):
...     def __init__(self, session):
...         self.value = 0
...
>>> registerAdapter(Counter, Session, ICounter)
>>> ses = Session(None, None)
>>> data = ICounter(ses)
>>> print(data)
<__main__.Counter object at 0x8d535ec>
>>> print(data is ICounter(ses))
True
>>>

什么?,我听到你说。

此示例中显示的是Session 用于持久化状态的接口和基于适配器的 API。这里有几个关键部分在交互

  • ICounter 是一个接口,它有多种用途。与所有接口一样,它记录了一类对象的 API(在本例中,只是value 属性)。它还用作会话对象中基本上是一个字典的键:该接口用于在会话中存储或检索值(在本例中为Counter 实例)。

  • Counter 是一个类,它实际上在此示例中保存会话数据。它实现了ICounter(同样,主要是为了文档目的)。它还有一个value 属性,如接口所声明的那样。

  • registerAdapter 调用设置了其三个参数之间的关系,以便适配将按照我们在此情况下的预期执行。

  • 适配由表达式ICounter(ses) 执行。这可以理解为:将ses 适配为ICounter。由于registerAdapter 调用,它大致等效于Counter(ses)。但是(由于Session 执行的某些操作),它还保存了创建的Counter 实例,以便在下一次执行此适配时返回它。这就是为什么最后一个语句产生True 的原因。

如果您仍然不清楚其中的一些细节,请不要担心,只需记住这一点:ICounter(ses) 会为您提供一个可以在其上持久化状态的对象。它可以是您想要的任何状态,无论多少,您可以在单个Session 实例上使用任意数量的不同Interface 类。

在这些概念依赖关系之外,将持久化状态实际添加到 Twisted Web 应用程序中只是一小步。以下是一个实现简单计数器的示例,它重用了上面示例中的定义

from twisted.web.resource import Resource

class CounterResource(Resource):
    def render_GET(self, request):
        session = request.getSession()
        counter = ICounter(session)
        counter.value += 1
        return (b"<!DOCTYPE html><html><head><meta charset='utf-8'>"
                b"<title></title></head><body>"
                b"Visit #" + str(counter.value).encode("utf-8") +
                b" for you!")

从这一方面来看非常简单,对吧?它所做的只是使用Request.getSession 和上面的适配,加上一些整数运算,为您提供基于会话的访问计数器。

以下是一个基于此示例的rpy 脚本 的完整源代码

cache()

from zope.interface import Interface, Attribute, implementer
from twisted.python.components import registerAdapter
from twisted.web.server import Session
from twisted.web.resource import Resource

class ICounter(Interface):
    value = Attribute("An int value which counts up once per page view.")

@implementer(ICounter)
class Counter(object):
    def __init__(self, session):
        self.value = 0

registerAdapter(Counter, Session, ICounter)

class CounterResource(Resource):
    def render_GET(self, request):
        session = request.getSession()
        counter = ICounter(session)
        counter.value += 1
        return (b"<!DOCTYPE html><html><head><meta charset='utf-8'>"
                b"<title></title></head><body>"
                b"Visit #" + str(counter.value).encode("utf-8") +
                b" for you!")

resource = CounterResource()

还有一点需要注意的是此示例顶部的cache() 调用。与之前的示例 中出现此调用一样,此 rpy 脚本是有状态的。这一次,是ICounter 定义和registerAdapter 调用只需要执行一次。如果我们不使用cache,每个请求都会定义一个新的、不同的名为ICounter 的接口。这些中的每一个都将是会话中的一个不同的键,因此计数器永远不会超过 1。