Django: handling HTTP requests

原文地址:
https://docs.djangoproject.com/en/2.0/topics/http/
https://docs.djangoproject.com/en/2.0/ref/request-response/

1. 关于HTTP请求和响应

原文地址:https://docs.djangoproject.com/en/2.0/ref/request-response/

当请求一个页面时,Django会创建出一个HttpRequest对象,该对象包含该请求的元数据。然后Django加载合适的(处理该请求的)view(视图处理函数),并将该对象作为视图函数的第一个参数传给它。视图处理函数会返回一个HttpResponse对象。

1.1 HttpRequest对象

1.1.1 属性

该对象包含的属性包括:
(如下这些数据应该是只读的)

  • HttpRequest.scheme:字符串,表示请求的模式(http或是https)
  • HttpRequest.body:byte string,表示原始的http request body;
  • HttpRequest.path:字符串,表示完整的请求路径,不包括http scheme或域,例如:/music/bands/the_beatles/
  • HttpRequest.path_info:
  • HttpRequest.method:字符串,表示请求的方法,例如“GET”,“POST”
  • HttpRequest.encoding:
  • HttpRequest.content_type:
  • HttpRequest.content_params:
  • HttpRequest.GET:一个类字典的对象,包含类所有的HTTP GET参数。
  • HttpRequest.POST:一个类字典对象,包含类所有的HTTP POST参数,包括form表单数据。如果你想获取原始的、非form表单数据,可以通过HttpRequest.body数据获取。对于一个POST方法,可能HttpRequest.POST为空(由于没有包含任何表单数据),因此,如果你想判定请求的方法,不要使用HttpRequest.POST来检查,而是应该使用request.method == "POST"这种形式判定。
  • HttpRequest.COOKIES:字典,包含所有的key,value(都是字符串)
  • HttpRequest.FILES:一个类字典对象,包含类所有的上传文件。FILES中的key是<input type="file" name="" />.中的name。value为一个UploadedFile对象。详情见:https://docs.djangoproject.com/en/2.0/topics/files/ 。只有当请求的方法为POST且 提交的表单 form 包含enctype="multipart/form-data"属性时,FILES才会包含值,否则为一个空的类字典对象。
  • HttpRequest.META:
  • HttpRequest.resolver_match:

被应用代码设置的属性:(Django不会设置这些属性,但是如果你的应用设置了这些属性,则会使用这些值)

  • HttpRequest.current_app:
  • HttpRequest.urlconf:

被中间层设置的属性:

1.1.2 方法

方法包括:

  • HttpRequest.get_host():
  • HttpRequest.get_port():
  • HttpRequest.get_full_path():
  • HttpRequest.build_absolute_uri:
  • HttpRequest.get_signed_cookie:
  • HttpRequest.is_secure():
  • HttpRequest.is_ajax():
  • HttpRequest.read(size=None)/HttpRequest.readline()/HttpRequest.readlines()
  • HttpRequest.__iter__()

1.2 QueryDict对象

在HttpRequest对象中,GET,POST属性都是django.http.QueryDict类型对象。该对象可以处理一个key 绑定多个值的情况(由于html 中存在select multiple这种多个value的情况)。在request.POST,request.GET中的django.http.QueryDict都是不可变的,如果要获取一个可变的版本,可通过QueryDict.copy()获取。https://docs.djangoproject.com/en/2.0/ref/request-response/#django.http.QueryDict.copy

QueryDict实现了所有的标准的字典的方法(它就是一个dictionary的子类)。但是有些的行为可能会存在一点差异(和标准的字典相比)。例如:

  • QueryDict.__init__(query_string=None, mutable=False, encoding=None) :注意request.POST,request.GET中的对象是不可变的,如果你想构造可变的版本,则可传入:mutable=True。
    使用示例:QueryDict('a=1&a=2&c=3')
  • QueryDict.__getitem__(key) :返回给定key的value,如果该key对应多个value,则返回最后一个。如果不存在key ,则抛出异常。
  • QueryDict.__setitem__(key, value)
  • QueryDict.__contains__(key)
  • QueryDict.get(key, default=None)
  • QueryDict.update(other_dict) :将新的值附加到当前列表中(而不是进行替换)。例如:
>>> q = QueryDict('a=1', mutable=True)
>>> q.update({'a': '2'})
>>> q.getlist('a')
['1', '2']
>>> q['a'] # returns the last
'2'
  • QueryDict.items():
  • QueryDict.values():
  • QueryDict.copy():
  • QueryDict.getlist(key, default=None):
  • QueryDict.setlist(key, list_):
  • QueryDict.appendlist(key, item):
  • 还有其他的,懒得粘了

1.3 HttpResponse 对象

HttpResponse是完全由你构造,填充了。

典型的用法是通过一个string来构造httpresponse,例如:

>>> from django.http import HttpResponse
>>> response = HttpResponse("Here's the text of the Web page.")
>>> response = HttpResponse("Text only, please.", content_type="text/plain")

另外,httpresponse是一个 file-like对象,你可以通过文件读写方法填充内容。

>>> response = HttpResponse()
>>> response.write("<p>Here's the text of the Web page.</p>")
>>> response.write("<p>Here's another paragraph.</p>")

另外,也可以通过iterator来填充内容。HttpResponse可以消费iterator,然后就内容保存为一个string。如果你想将你的response 通过streaming的方式传递给client,可以使用 StreamingHttpResponse。

1.3.1 设置响应头部

可想操作字典一样,设置头部,例如:

>>> response = HttpResponse()
>>> response['Age'] = 120
>>> del response['Age']

1.3.2 告知浏览器,响应是一个文件附件

to tell the browser to treat the response as a file attachment,通过content_type参数、Content-Disposition头部。例如:

>>> response = HttpResponse(my_data, content_type='application/vnd.ms-excel')
>>> response['Content-Disposition'] = 'attachment; filename="foo.xls"'

1.3.3 HttpResponse属性

包括:

  • HttpResponse.content:
  • HttpResponse.charset:
  • HttpResponse.status_code:
  • HttpResponse.reason_phrase:
  • HttpResponse.streaming:
  • HttpResponse.closed:

1.3.4 HttpResponse方法

  • HttpResponse.__init__(content='', content_type=None, status=200, reason=None, charset=None)
  • HttpResponse.__setitem__(header, value)
  • HttpResponse.set_cookie(key, value='', max_age=None, expires=None, path='/', domain=None, secure=None, httponly=False)
  • HttpResponse.write(content)
  • 其他方法,不粘贴了

1.3.5 HttpResponse的子类

包括:

  • HttpResponseRedirect:
  • HttpResponsePermanentRedirect:
  • HttpResponseNotModified:
  • HttpResponseBadRequest:
  • HttpResponseNotFound:
  • HttpResponseForbidden:
  • 其他,不粘贴了

1.4 JsonResponse 对象

HttpResponse的子类,继承了父类的大多数行为,但是,默认情况下,Content-Type 头部为application/json,内容是json-化的对象。

1.5 StreamingHttpResponse 对象与FileResponse 对象

StreamingHttpResponse是HttpResponse的子类。

2.django 对请求的路由处理

当用户请求一个基于Django的页面时,Django通过如下方式找到处理该请求的方法/类(或是没有找到,抛出异常):

  1. Django先确定将要使用的URLConf模块(通常来说,就是setting.py中指定的ROOT_URLCONF(该值一般指定来要使用的URL映射文件))。但是新来的请求HttpRequest对象自己携带来urlconf属性的话,就会使用该值。
  2. 根据ROOT_URLCONF,加载对应的python模块,在模块中寻找urlpatterns变量,该变量的值是一个django.urls.path() 或/和 django.urls.re_path()变量的列表;
  3. Django按顺序检查urlpatterns变量中的每项值,一旦找到一个匹配的URL,就停止,然后Django导入、调用对应的方法。调用时,传入:HttpRequest实例、URL匹配时匹配到的位置参数、其他调用传入的附加参数;

示例:

from django.urls import path

from . import views

urlpatterns = [
    path('articles/2003/', views.special_case_2003),
    path('articles/<int:year>/', views.year_archive),
    path('articles/<int:year>/<int:month>/', views.month_archive),
    path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail),
]

对于请求 /articles/2005/03/:匹配第三条, Django会调用views.month_archive(request, year=2005, month=3)
/articles/2003/ :匹配第一条,Django调用views.special_case_2003(request)
/articles/2003 :没有匹配;
/articles/2003/03/building-a-django-site/ :匹配最后一条,调用:views.article_detail(request, year=2003, month=3, slug="building-a-django-site").

在写URL匹配规则时,可以使用路径转化器,默认的转化器包括:

  1. str :匹配任意非空自负串,包括/。
  2. int :匹配0或是其他任意的正数,返回一个int值。
  3. slug :Matches any slug string consisting of ASCII letters or numbers, plus the hyphen and underscore characters. For example, building-your-1st-django-site.
  4. uuid : 匹配格式化的uuid。为了避免多个URL匹配到同一个页面,必须包括短横杠(dash),且字符必须是小写的。例如:075194d3-6885-417e-a8a8-6c931e272f00. Returns a UUID instance.
  5. path :匹配任意非空串,包括/。

自定义路径转化器,见:https://docs.djangoproject.com/en/2.0/topics/http/urls/

3. 请求函数的编写

一个处理请求的函数(view function),就是一个简单的python函数,接受一个web请求,然后返回一个web响应。响应可以是html内容,或是一个重定向,或是404错误,或是xml文档或是一个图片,或是其他任意的东西。函数代码也可以放在任意的地方,只要是在python path下。但是一般建议(约定上)放在views.py中。

示例:

from django.http import HttpResponse
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    html = "<html><body>It is now %s.</body></html>" % now
    return HttpResponse(html)

4. 视图装饰器

Django提供了多个装饰器,用于支持不同的Http特征。

4.1 对于http 请求方法的限制过滤

位于django.views.decorators.http 中的装饰器可以提供基于http方法的限制,限制对view 函数的调用。当条件不满足时,这些装饰器会返回django.http.HttpResponseNotAllowed错误。装饰器包括:

  1. require_http_methods(request_method_list) :接受特定的方法请求。例如:
from django.views.decorators.http import require_http_methods

@require_http_methods(["GET", "POST"])
def my_view(request):
    # I can assume now that only GET or POST requests make it this far
    # ...
    pass
  1. require_GET():
  2. require_POST()
  3. require_safe():只允许get,head方法。

4.2 Conditional view processing

位于django.views.decorators.http中的装饰器可以控制对于特定视图的处理caching行为。例如:

  • condition(etag_func=None, last_modified_func=None):
  • etag(etag_func):
  • last_modified(last_modified_func):

这些装饰器可以用于产生ETag,Last-Modified 头部,详细请求见:https://docs.djangoproject.com/en/2.0/topics/conditional-view-processing/

4.3 GZip 压缩

位于Django.views.decorators.gzip中的装饰器可以控制内容压缩行为。装饰器包括:

  • gzip_page():如果浏览器允许gzip压缩的话,该装饰器会压缩内容。

4.4 Vary headers

django.views.decorators.vary包中的装饰器可以用来控制caching行为(基于特定的头部)。装饰器包括:

  • vary_on_cookie(func):
  • vary_on_headers:Vary头部定义了:在构造cache key时,那个request header 应该考虑。

详细见:https://docs.djangoproject.com/en/2.0/topics/cache/#using-vary-headers

4.5 Caching

位于django.views.decorators.cache中的装饰器控制了服务端、客户端的cache,装饰器包括:

  • cache_control(**kwargs) :该装饰器控制http 响应中的Cache-Control头部。
  • never_cache(view_func) :该装饰器控制http 响应中增加Cache-Control: max-age=0, no-cache, no-store, must-revalidate 头部,表示不进行caching。

5. 上传文件

当Django处理文件上传时,文件数据存放在request.FILES变量中。本章主要描述文件如何存储到磁盘或内存中,并如何自定义行为。

对于上传文件的处理:

5.1 示例一:读取

from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import UploadFileForm

# Imaginary function to handle an uploaded file.
from somewhere import handle_uploaded_file

def upload_file(request):
    if request.method == 'POST':
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            handle_uploaded_file(request.FILES['file'])
            return HttpResponseRedirect('/success/url/')
    else:
        form = UploadFileForm()
    return render(request, 'upload.html', {'form': form})

def handle_uploaded_file(f):
    with open('some/file/name.txt', 'wb+') as destination:
        for chunk in f.chunks():
            destination.write(chunk)

5.2 示例二:保存到model 中

保存到model中,可以使用FileField字段类型。当调用form.save函数时,文件数据将会保存到upload_to参数指定的位置

from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import UploadFileForm
from .models import ModelWithFileField

def upload_file(request):
    if request.method == 'POST':
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            instance = ModelWithFileField(file_field=request.FILES['file'])
            instance.save()
            return HttpResponseRedirect('/success/url/')
    else:
        form = UploadFileForm()
    return render(request, 'upload.html', {'form': form})

5.3 upload handler

当用户上传一个文件时,Django将文件数据传递给upload handler。 upload handler在setting 中的FILE_UPLOAD_HANDLERS变量指定,其初始配置是:

["django.core.files.uploadhandler.MemoryFileUploadHandler",
 "django.core.files.uploadhandler.TemporaryFileUploadHandler"]

Django对于文件的默认处理是:小文件放到内存中,大文件放到磁盘中。

你可以自定义自己的文件处理器,例如增加用户级配置管理,压缩或是将文件存储到其他地方(而非本地)。详见:https://docs.djangoproject.com/en/2.0/ref/files/uploads/#custom-upload-handlers

5.4 upload handler上传数据存储位置

在你对上传的文件做处理前,Django需要将文件存储在某个地方,方便你后续业务的处理。

默认情况下,当文件小于2.5M时,Django会将文件存储在内存中。

如果文件过大,Django会将文件存放到一个临时文件中(位于系统的临时目录中)。在类unix系统中,零时文件类似于:/tmp/tmpzfp6I6.upload。

https://docs.djangoproject.com/en/2.0/ref/settings/#file-upload-settings

6. Django 快捷函数

django.shortcuts 包中提供了一些便捷功能,来增加MVC各层之间的交互功能。例如:

  1. render(request, template_name, context=None, content_type=None, status=None, using=None) :html 文件渲染,将参数填入模版到中。
  2. redirect(to, permanent=False, *args, **kwargs):重定向;
  3. get_object_or_404():在model manager上调用get 方法,如果模型不存在,则返回http404;
  4. get_list_or_404():通过model manager获取过滤后的结果,如果结果为空,则返回http 404;

7. 通用视图

https://docs.djangoproject.com/en/2.0/topics/http/generic-views/

8. 中间层(middleware)

https://docs.djangoproject.com/en/2.0/topics/http/middleware/

Django 的中间层是一种框架,hook into 用户Django 请求/响应处理过程种。它的特点是轻量级的、低层次的‘插件’系统,会全局改变Django的输入输出。

每个中间层组件都完成某种特定的功能,例如AuthenticationMiddleware中间件通过session,将用户与请求绑定。

本章主要描述中间件如何工作且怎么激活中间件、怎么编写自己的中间件。当然,Django也提供来一些内置的中间见,详见:https://docs.djangoproject.com/en/2.0/ref/middleware/

8.1 编写自己的中间件

中间件工厂是一个可调用对象,接受一个get_response 可调用对象,返回一个中间件。

中间件是一个可调用对象,然后返回一个response(和view一样)

示例:

def simple_middleware(get_response):
    # One-time configuration and initialization.

    def middleware(request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.

        response = get_response(request)

        # Code to be executed for each request/response after
        # the view is called.

        return response

    return middleware

或是,也可以是一个类实例是可调用的类,例如:

class SimpleMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        # One-time configuration and initialization.

    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.

        response = self.get_response(request)

        # Code to be executed for each request/response after
        # the view is called.

        return response

Django提供的get_response可调用对象可以是真正的view,或是链上的下一个中间件。当前的中间件不需要get_response具体是那种

上述只是一个简化的实例。对于中间件链上的最后一个中间件的get_response实参不会是真正实际的view,而是一个包装器方法,该方法来源于view middleware处理器。

注意:

  • 中间件工厂方法只接受一个参数,即get_response。因此,在__init__()函数中,不能有其他的参数;
  • 对于每次请求,__call__() 方法都会被调用一次,但是__init__()只有在服务器启动时调用一次。

在启动阶段,动态标记那个中间件为“unused":通过在__init__()函数中,抛出MiddlewareNotUsed异常,则Django会将该中间件从链上剔除。

8.2 激活使用中间件

为了启用某个中间件,在MIDDLEWARE 列表(settings.py)中声明你要使用的中间件列表。

在MIDDLEWARE中,每个中间件都表示为一个字符串(该串为中间件工厂的全路径,或是函数名)。例如:(默认配置)

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

在Django项目中,中间件不是必须的, MIDDLEWARE变量可为空,但是,建议你至少使用CommonMiddleware中间件。

8.2.1 中间件的执行顺序和分层

在调用视图函数之前,Django将依次按顺序调用MIDDLEWARE中定义的中间件。一层层调用中间件(像洋葱模型一样)。如果中间的某个中间件做short-cut短路处理,直接返回类response,而不是调用它的get_response,则后续的中间件是看不到请求的。

8.3 中间件hooks

Django中间件必须至少包含以下方法之一:process_request,process_response,process_view 和 process_exception。 这些是由WSGI处理程序收集的方法,然后按列出的顺序调用。

8.3.1process_view

(1)函数声明:
process_view(request, view_func, view_args, view_kwargs)

(2)参数说明:
request:表示HttpRequst 对象。
view_func: 表示Django将要使用的python函数;
view_args,view_kargs:表示传递给view_func的参数。

(3)返回值
process_view函数在Django调用view之间调用。该函数应该返回None或是HttpResponse对象。

如果返回None,则Django会继续处理该请求,执行其他中间件的process_view函数、view函数。

如果返回HttpResponse对象,则Django就不会往后处理了,直接返回给response。

8.3.2 process_exception

当view函数抛出异常时,就会调用中间件的process_exception函数。

8.3.3 process_template_response

(1)函数声明:process_template_response(request, response)

(2)函数参数:
request:http request对象;
response:TemplateResponse对象。该对象由Django view或是其他middleware返回。

process_template_response是在view函数被调用处理完后被调用。如果response实例有render()方法,则表明该response是一个TemplateResponse 对象,或者说等同于一个TemplateResponse 对象。

(3)返回值
该函数必须返回一个实现了render方法的response对象。它可以通过修改response.template_name 或response.context_data ,修改传入的response对象。或是创建一个全新的TemplateResponse对象或是一个类TemplateResponse对象。

你无需显示的对response进行渲染(通过render方法),当所有的template response 中间件都被调用后,response会自动的渲染一次。

process_template_response方法的执行顺序和中间件定义的顺序相反。

各个钩子函数的调用时机如下图所示:
在这里插入图片描述

在这里插入图片描述

8.4 处理流式响应

和httpresponse不同的是StreamingHttpResponse 没有content属性。因此,中间件不能假设所有的response 都有一个content属性。当需要获取content时,你需要对response进行测试,如下:

if response.streaming:
    response.streaming_content = wrap_streaming_content(response.streaming_content)
else:
    response.content = alter_content(response.content)

9. Sessions

https://docs.djangoproject.com/en/2.0/topics/http/sessions/

Django 完全支持匿名session。默认将session数据存储在服务器端,并将cookies中数据的发送和接收过程抽象处理啊。Cookies包含类一个session ID(而不是数据本身)(除非你想将session数据存放到cookie中,详见https://docs.djangoproject.com/en/2.0/topics/http/sessions/#cookie-session-backend)

9.1 启用/禁用session

session是基于中间件实现的。为了允许session机制,你应该做如下配置:

  • 编辑setting中的MIDDLEWARE值,使得包含如下串:django.contrib.sessions.middleware.SessionMiddleware 。通过Django-admin startproject 配置的项目默认包含了该值。

如果你不想使用session,则可在MIDDLEWARE中移除SessionMiddleware中间件串,并在INSTALLED_APPS变量中,移除django.contrib.sessions。

9.2 配置session引擎

默认情况下,Django将session数据存储在数据库中(对应的model为:django.contrib.sessions.models.Session)。但是这可以配置。

如果你想将session存储到数据库中,你需要在INSTALLED_APPS中配置django.contrib.sessions。在配置好后,运行manage.py migrate来安装存储session数据的单个数据表。

9.2.1 将session存储到cache中

将session存储到cache中可以提高存取性能。

在将session存储到cache前,首先检查是否配置来cache。详细见:https://docs.djangoproject.com/en/2.0/topics/cache/

注意:本地cache存储不是线程安全的,因此可能不适合在生成环境下配置本地cache。但是可以配置到像memcache这种cache中。

如果你在CACHES中定义了多个cache,则Django会使用默认的cache。如果要使用其他的cache,将ESSION_CACHE_ALIAS配置成你要使用的cache。

当你配置后cache后,你可以进行如下配置:

  • 设置SESSION_ENGINEdjango.contrib.sessions.backends.cache,配置简单的cache存储。Session将会直接存储到你的cache中。但是,当cache 存储空间满或是服务器重启后,session数据可能不会进行持久化处理;
  • 为了进行持久化,设置SESSION_ENGINEdjango.contrib.sessions.backends.cached_db。这是,会进行写直达(write through)。每次写cache时,也会写入到数据库中。如果数据不在cache中,则会直接到数据库中读取。如果你要使用改种模式,则还需要继续进行配置,详见:https://docs.djangoproject.com/en/2.0/topics/http/sessions/#using-database-backed-sessions

9.2.2 将session存储到文件中

将session存储到文件中,将设置SESSION_ENGINEdjango.contrib.sessions.backends.file

文件式的存储还需要进行配置 SESSION_FILE_PATH,配置文件路径,详见:https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-SESSION_FILE_PATH

9.2.3 将session存储到cookie中

将session存储到cookie中,将设置SESSION_ENGINEdjango.contrib.sessions.backends.signed_cookies

9.3 在view中使用session

当启用了SessionMiddleware 后,每个HttpRequest 对象(view的第一个参数)就会有session属性(类字典的对象),然后你可以在view中,通过request.session的方式读写session的值。

9.3.1 backends.base.SessionBase类

该类是所有session对象的基类。该类具有如下方法、及方法的使用示例:

  • __getitem__(key) :使用方式,例如:fav_color = request.session['fav_color']
  • __setitem__(key, value) :使用方式,例如:request.session['fav_color'] = 'blue'
  • __delitem__(key) :可以使用del request.session['fav_color'],但是当key不存在时,会抛出异常;
  • __contains__(key)'fav_color' in request.session
  • get(key, default=None)fav_color = request.session.get('fav_color', 'red')
  • pop(key, default=__not_given)fav_color = request.session.pop('fav_color', 'blue')
  • keys()
  • items()
  • set_expiry(value)
  • flush():删除情况session数据;
  • get_expiry_date()
  • 其他

9.3.2 session的序列化

默认情况下,Django将session组成成json格式。你可以通过SESSION_SERIALIZER 设置来指定使用特定的序列化格式,详见:https://docs.djangoproject.com/en/2.0/topics/http/sessions/#custom-serializers

9.4 在view外使用session

可以通过SessionStore 在 view外使用session。例如:

>>> from django.contrib.sessions.backends.db import SessionStore
>>> s = SessionStore()
>>> # stored as seconds since epoch since datetimes are not serializable in JSON.
>>> s['last_login'] = 1376587691
>>> s.create()
>>> s.session_key
'2b1189a188b44ad18c35e113ac6ceead'
>>> s = SessionStore(session_key='2b1189a188b44ad18c35e113ac6ceead')
>>> s['last_login']
1376587691

如果你使用db 作为session后端引擎,则可通过model的方式访问操作session。session 相关的model定义在django/contrib/sessions/models.py中。使用示例:

>>> from django.contrib.sessions.models import Session
>>> s = Session.objects.get(pk='2b1189a188b44ad18c35e113ac6ceead')
>>> s.expire_date
datetime.datetime(2005, 8, 20, 13, 35, 12)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值