importosfromflaskimportFlask,requestfromgoogle.appengine.apiimportwrap_wsgi_appfromgoogle.appengine.extimportdeferredfromgoogle.appengine.extimportndbmy_key=os.environ.get("GAE_VERSION","Missing")app=Flask(__name__)app.wsgi_app=wrap_wsgi_app(app.wsgi_app,use_deferred=True)classCounter(ndb.Model):count=ndb.IntegerProperty(indexed=False)defdo_something_later(key,amount):entity=Counter.get_or_insert(key,count=0)entity.count+=amountentity.put()@app.route("/counter/increment")defincrement_counter():# Use default URL and queue name, no task name, execute ASAP.deferred.defer(do_something_later,my_key,10)# Use default URL and queue name, no task name, execute after 1 minute.deferred.defer(do_something_later,my_key,10,_countdown=60)# Providing non-default task queue argumentsdeferred.defer(do_something_later,my_key,10,_url="/custom/path",_countdown=120)return"Deferred counter increment."@app.route("/counter/get")defview_counter():counter=Counter.get_or_insert(my_key,count=0)returnstr(counter.count)@app.route("/custom/path",methods=["POST"])defcustom_deferred():print("Executing deferred task.")# request.environ contains the WSGI `environ` dictionary (See PEP 0333)returndeferred.Handler().post(request.environ)
Django
importosfromdjango.confimportsettingsfromdjango.core.wsgiimportget_wsgi_applicationfromdjango.httpimportHttpResponsefromdjango.urlsimportpathfromgoogle.appengine.apiimportwrap_wsgi_appfromgoogle.appengine.extimportdeferredfromgoogle.appengine.extimportndbmy_key=os.environ.get("GAE_VERSION","Missing")classCounter(ndb.Model):count=ndb.IntegerProperty(indexed=False)defdo_something_later(key,amount):entity=Counter.get_or_insert(key,count=0)entity.count+=amountentity.put()defincrement_counter(request):# Use default URL and queue name, no task name, execute ASAP.deferred.defer(do_something_later,my_key,10)# Use default URL and queue name, no task name, execute after 1 minute.deferred.defer(do_something_later,my_key,10,_countdown=60)# Providing non-default task queue argumentsdeferred.defer(do_something_later,my_key,10,_url="/custom/path",_countdown=120)returnHttpResponse("Deferred counter increment.")defview_counter(request):counter=Counter.get_or_insert(my_key,count=0)returnHttpResponse(str(counter.count))defcustom_deferred(request):print("Executing deferred task.")# request.environ contains the WSGI `environ` dictionary (See PEP 0333)response,status,headers=deferred.Handler().post(request.environ)returnHttpResponse(response,status=status.value)urlpatterns=(path("counter/get",view_counter,name="view_counter"),path("counter/increment",increment_counter,name="increment_counter"),path("custom/path",custom_deferred,name="custom_deferred"),)settings.configure(DEBUG=True,SECRET_KEY="thisisthesecretkey",ROOT_URLCONF=__name__,MIDDLEWARE_CLASSES=("django.middleware.common.CommonMiddleware","django.middleware.csrf.CsrfViewMiddleware","django.middleware.clickjacking.XFrameOptionsMiddleware",),ALLOWED_HOSTS=["*"],)app=wrap_wsgi_app(get_wsgi_application(),use_deferred=True)
没有任何框架
importosimportrefromgoogle.appengine.apiimportwrap_wsgi_appfromgoogle.appengine.extimportdeferredfromgoogle.appengine.extimportndbmy_key=os.environ.get("GAE_VERSION","Missing")classCounter(ndb.Model):count=ndb.IntegerProperty(indexed=False)defdo_something_later(key,amount):entity=Counter.get_or_insert(key,count=0)entity.count+=amountentity.put()defIncrementCounter(environ,start_response):# Use default URL and queue name, no task name, execute ASAP.deferred.defer(do_something_later,my_key,10)# Use default URL and queue name, no task name, execute after 1 minute.deferred.defer(do_something_later,my_key,10,_countdown=60)# Providing non-default task queue argumentsdeferred.defer(do_something_later,my_key,10,_url="/custom/path",_countdown=120)start_response("200 OK",[("Content-Type","text/html")])return[b"Deferred counter increment."]defViewCounter(environ,start_response):counter=Counter.get_or_insert(my_key,count=0)start_response("200 OK",[("Content-Type","text/html")])return[str(counter.count).encode("utf-8")]classCustomDeferredHandler(deferred.Handler):"""Deferred task handler that adds additional logic."""defpost(self,environ):print("Executing deferred task.")returnsuper().post(environ)routes={"counter/increment":IncrementCounter,"counter/get":ViewCounter,"custom/path":CustomDeferredHandler(),}classWSGIApplication:def__call__(self,environ,start_response):path=environ.get("PATH_INFO","").lstrip("/")forregex,handlerinroutes.items():match=re.search(regex,path)ifmatchisnotNone:returnhandler(environ,start_response)start_response("404 Not Found",[("Content-Type","text/plain")])return[b"Not found"]app=wrap_wsgi_app(WSGIApplication(),use_deferred=True)
[[["易于理解","easyToUnderstand","thumb-up"],["解决了我的问题","solvedMyProblem","thumb-up"],["其他","otherUp","thumb-up"]],[["很难理解","hardToUnderstand","thumb-down"],["信息或示例代码不正确","incorrectInformationOrSampleCode","thumb-down"],["没有我需要的信息/示例","missingTheInformationSamplesINeed","thumb-down"],["翻译问题","translationIssue","thumb-down"],["其他","otherDown","thumb-down"]],["最后更新时间 (UTC):2025-08-20。"],[[["\u003cp\u003eThe Deferred API for Python 3 on Google App Engine no longer requires setting \u003ccode\u003ebuiltins.deferred\u003c/code\u003e in \u003ccode\u003eapp.yaml\u003c/code\u003e; instead, you must use \u003ccode\u003euse_deferred=True\u003c/code\u003e when calling \u003ccode\u003ewrap_wsgi_app()\u003c/code\u003e.\u003c/p\u003e\n"],["\u003cp\u003eThe default behavior of the Deferred API in Python 3 remains consistent with Python 2, using the \u003ccode\u003e/_ah/queue/deferred\u003c/code\u003e URL and the default queue, but keep in mind that Cloud Tasks will differ.\u003c/p\u003e\n"],["\u003cp\u003eWhen using the local development server for testing, you need to set \u003ccode\u003eDEFERRED_USE_CROSS_COMPATIBLE_PICKLE_PROTOCOL: 'True'\u003c/code\u003e in your \u003ccode\u003eapp.yaml\u003c/code\u003e to make sure the Deferred API works.\u003c/p\u003e\n"],["\u003cp\u003eCustom URLs for deferred task execution in Python 3 require using the \u003ccode\u003edeferred.Handler\u003c/code\u003e class's \u003ccode\u003epost\u003c/code\u003e or \u003ccode\u003erun_from_request\u003c/code\u003e methods, as the \u003ccode\u003eTaskHandler\u003c/code\u003e from Python 2 is no longer available.\u003c/p\u003e\n"],["\u003cp\u003eThe way a Python 3 App uses the Deferred API, including its handling of requests and accessing the \u003ccode\u003eenviron\u003c/code\u003e dictionary, is based on the web framework that is being migrated to, as shown in the provided examples with Flask, Django and without a framework.\u003c/p\u003e\n"]]],[],null,["# Deferred API for Python 3\n\nThis page describes how to use the Deferred API, one of the legacy bundled services,\nwith the [Python 3 runtime](/appengine/docs/standard/python3) for\nthe standard environment. Your app can access the bundled services\nthrough the [**App Engine services SDK for Python 3**](https://github.com/GoogleCloudPlatform/appengine-python-standard).\n\nOverview\n--------\n\nPreviously, the Deferred package [`google.appengine.ext.deferred`](/appengine/docs/legacy/standard/python/refdocs/google.appengine.ext.deferred.deferred)\ndepended on the webapp framework in Python 2. Since the webapp framework has\nbeen removed in the App Engine services SDK for Python 3, you need to\nmake some changes when upgrading your Python 2 app to Python 3.\n\nEnabling the Deferred API\n-------------------------\n\nTo enable the Deferred API for Python 3, you no longer need to set\n[`builtins.deferred`](/appengine/docs/legacy/standard/python/config/appref#builtins)\nto `on` in the `app.yaml` file. Instead, to enable the API, you must pass\n`use_deferred=True` in the call to `wrap_wsgi_app()`.\n| **Important:** When using the [local development server](/appengine/docs/standard/python3/services/access#testing_and_deployment) to test Python 3 apps that use the [Deferred API](/appengine/docs/legacy/standard/python/refdocs/google.appengine.ext.deferred.deferred), you must set the following environment variable in your `app.yaml`: \n| `DEFERRED_USE_CROSS_COMPATIBLE_PICKLE_PROTOCOL: 'True'`\n\nSimilarites and differences\n---------------------------\n\nBy default, the Deferred API for Python 3 uses the same URL `/_ah/queue/deferred`\nand the same [default queue](/appengine/docs/standard/python/taskqueue/push/migrating-push-queues#creating_queues)\nas it did in Python 2. Note that for apps migrating to [Cloud Tasks](/tasks), the default queue is\n[not created automatically](/appengine/docs/legacy/standard/python/taskqueue/push/migrating-push-queues#creating_queues)\nand the [deferred tasks library is not available](/appengine/docs/legacy/standard/python/taskqueue/push/migrating-push-queues#features-not-available).\n\nIf your app uses the default `/_ah/queue/deferred` endpoint, using\n[`deferred.defer()` in Python 3](/appengine/docs/standard/python3/reference/services/bundled/google/appengine/ext/deferred/deferred)\nremains the same as\n[Python 2](/appengine/docs/legacy/standard/python/taskqueue/push/creating-tasks#using_the_instead_of_a_worker_service).\nIf your app uses a custom URL for execution of deferred tasks, you need to make\nsome changes since the `TaskHandler` class in the `deferred` module for Python 2\nhas been removed in the Python 3 version of this API.\n\nTo set a custom URL for execution of deferred tasks, the app can override either\nthe `post` or the `run_from_request` method in the\n[`deferred.Handler` class](/appengine/docs/standard/python3/reference/services/bundled/google/appengine/ext/deferred/Handler)\n(formerly `deferred.TaskHandler` in Python 2), and pass the `environ` parameter\nwhich represents a dictionary containing WSGI request parameters. The `post` method can then be\ncalled from the custom endpoint (as shown in the [Python 3 samples](#python-3-examples)).\n\nThe end-to-end usage of the Python 3 Deferred API, such as routing of requests and\naccessing the [`environ` dictionary](https://www.python.org/dev/peps/pep-0333/#id19),\ndepends on the web framework the app is migrating to. Compare code changes made\nfrom the Python 2 example to the Python 3 examples in the following sections.\n\nPython 3 examples\n-----------------\n\nThe following example shows how to execute a deferred task using a\ndefault endpoint and a custom endpoint in a Flask app and Django app. \n\n### Flask\n\n import os\n\n from flask import Flask, request\n from google.appengine.api import wrap_wsgi_app\n from google.appengine.ext import deferred\n from google.appengine.ext import ndb\n\n my_key = os.environ.get(\"GAE_VERSION\", \"Missing\")\n\n app = Flask(__name__)\n app.wsgi_app = wrap_wsgi_app(app.wsgi_app, use_deferred=True)\n\n\n class Counter(ndb.Model):\n count = ndb.IntegerProperty(indexed=False)\n\n\n def do_something_later(key, amount):\n entity = Counter.get_or_insert(key, count=0)\n entity.count += amount\n entity.put()\n\n\n @app.route(\"/counter/increment\")\n def increment_counter():\n # Use default URL and queue name, no task name, execute ASAP.\n deferred.defer(do_something_later, my_key, 10)\n\n # Use default URL and queue name, no task name, execute after 1 minute.\n deferred.defer(do_something_later, my_key, 10, _countdown=60)\n\n # Providing non-default task queue arguments\n deferred.defer(do_something_later, my_key, 10, _url=\"/custom/path\", _countdown=120)\n\n return \"Deferred counter increment.\"\n\n\n @app.route(\"/counter/get\")\n def view_counter():\n counter = Counter.get_or_insert(my_key, count=0)\n return str(counter.count)\n\n\n @app.route(\"/custom/path\", methods=[\"POST\"])\n def custom_deferred():\n print(\"Executing deferred task.\")\n # request.environ contains the WSGI `environ` dictionary (See PEP 0333)\n return deferred.Handler().post(request.environ)\n\n### Django\n\n import os\n\n from django.conf import settings\n from django.core.wsgi import get_wsgi_application\n from django.http import HttpResponse\n from django.urls import path\n from google.appengine.api import wrap_wsgi_app\n from google.appengine.ext import deferred\n from google.appengine.ext import ndb\n\n my_key = os.environ.get(\"GAE_VERSION\", \"Missing\")\n\n\n class Counter(ndb.Model):\n count = ndb.IntegerProperty(indexed=False)\n\n\n def do_something_later(key, amount):\n entity = Counter.get_or_insert(key, count=0)\n entity.count += amount\n entity.put()\n\n\n def increment_counter(request):\n # Use default URL and queue name, no task name, execute ASAP.\n deferred.defer(do_something_later, my_key, 10)\n\n # Use default URL and queue name, no task name, execute after 1 minute.\n deferred.defer(do_something_later, my_key, 10, _countdown=60)\n\n # Providing non-default task queue arguments\n deferred.defer(do_something_later, my_key, 10, _url=\"/custom/path\", _countdown=120)\n\n return HttpResponse(\"Deferred counter increment.\")\n\n\n def view_counter(request):\n counter = Counter.get_or_insert(my_key, count=0)\n return HttpResponse(str(counter.count))\n\n\n def custom_deferred(request):\n print(\"Executing deferred task.\")\n # request.environ contains the WSGI `environ` dictionary (See PEP 0333)\n response, status, headers = deferred.Handler().post(request.environ)\n return HttpResponse(response, status=status.value)\n\n\n urlpatterns = (\n path(\"counter/get\", view_counter, name=\"view_counter\"),\n path(\"counter/increment\", increment_counter, name=\"increment_counter\"),\n path(\"custom/path\", custom_deferred, name=\"custom_deferred\"),\n )\n\n settings.configure(\n DEBUG=True,\n SECRET_KEY=\"thisisthesecretkey\",\n ROOT_URLCONF=__name__,\n MIDDLEWARE_CLASSES=(\n \"django.middleware.common.CommonMiddleware\",\n \"django.middleware.csrf.CsrfViewMiddleware\",\n \"django.middleware.clickjacking.XFrameOptionsMiddleware\",\n ),\n ALLOWED_HOSTS=[\"*\"],\n )\n\n app = wrap_wsgi_app(get_wsgi_application(), use_deferred=True)\n\n### Without any framework\n\n import os\n import re\n\n from google.appengine.api import wrap_wsgi_app\n from google.appengine.ext import deferred\n from google.appengine.ext import ndb\n\n my_key = os.environ.get(\"GAE_VERSION\", \"Missing\")\n\n\n class Counter(ndb.Model):\n count = ndb.IntegerProperty(indexed=False)\n\n\n def do_something_later(key, amount):\n entity = Counter.get_or_insert(key, count=0)\n entity.count += amount\n entity.put()\n\n\n def IncrementCounter(environ, start_response):\n # Use default URL and queue name, no task name, execute ASAP.\n deferred.defer(do_something_later, my_key, 10)\n\n # Use default URL and queue name, no task name, execute after 1 minute.\n deferred.defer(do_something_later, my_key, 10, _countdown=60)\n\n # Providing non-default task queue arguments\n deferred.defer(do_something_later, my_key, 10, _url=\"/custom/path\", _countdown=120)\n\n start_response(\"200 OK\", [(\"Content-Type\", \"text/html\")])\n return [b\"Deferred counter increment.\"]\n\n\n def ViewCounter(environ, start_response):\n counter = Counter.get_or_insert(my_key, count=0)\n start_response(\"200 OK\", [(\"Content-Type\", \"text/html\")])\n return [str(counter.count).encode(\"utf-8\")]\n\n\n class CustomDeferredHandler(deferred.Handler):\n \"\"\"Deferred task handler that adds additional logic.\"\"\"\n\n def post(self, environ):\n print(\"Executing deferred task.\")\n return super().post(environ)\n\n\n routes = {\n \"counter/increment\": IncrementCounter,\n \"counter/get\": ViewCounter,\n \"custom/path\": CustomDeferredHandler(),\n }\n\n\n class WSGIApplication:\n def __call__(self, environ, start_response):\n path = environ.get(\"PATH_INFO\", \"\").lstrip(\"/\")\n for regex, handler in routes.items():\n match = re.search(regex, path)\n if match is not None:\n return handler(environ, start_response)\n\n start_response(\"404 Not Found\", [(\"Content-Type\", \"text/plain\")])\n return [b\"Not found\"]\n\n\n app = wrap_wsgi_app(WSGIApplication(), use_deferred=True)\n\nCode samples\n------------\n\nTo view the complete code samples from this guide, see\n[GitHub](https://github.com/GoogleCloudPlatform/python-docs-samples/tree/main/appengine/standard_python3/bundled-services)."]]