将 Google App Engine 队列服务与 Django 结合使用

发布于 2024-10-14 19:52:39 字数 4253 浏览 3 评论 0原文

我正在尝试使用 Google App Engine 队列 API,但在测试时遇到问题。似乎在该过程的某些部分,CSRF 不起作用。

据我了解,api 执行调用 url 并在后台发出 http 请求的任务。

API 调用的完整 url 为 → http://localhost.localdomain: 8000/admin/cooking/recipe/36/chefworker/

当它引发此异常时:

Traceback (most recent call last):
  File "/home/mariocesar/Proyectos/Cooking/cooking/django/core/handlers/base.py", line 100, in get_response
    response = callback(request, *callback_args, **callback_kwargs)
  File "/home/mariocesar/Proyectos/Cooking/cooking/django/views/decorators/csrf.py", line 24, in wrapped_view
    resp.csrf_exempt = True
AttributeError: 'NoneType' object has no attribute 'csrf_exempt'

因此,GAE api 发出的执行请求中缺少 csrf 中间件、cookie、某些数据或响应本身后台任务。

如何在不禁用 Django 上的 CSRF 的情况下解决这个问题?但是,djangoappengine 完全可以吗?

下面是我正在使用的 models.py 和 admin.py 文件。

models.py

from django.db import models

class Recipe(models.Model):
    name = models.CharField(max_length=140)
    description = models.TextField()
    cooking_time = models.PositiveIntegerField()
    status = models.CharField(max_length=40)

    def __unicode__(self):
        return self.name

    def cookthis(self):
        import time
        self.status = 'The chef is cooking this recipe'
        self.save()
        time.sleep(obj.cooking_time)
        self.status = 'It\'s done ! the recipe is ready to serve'
        self.save()

admin.py

import logging

from django.contrib import admin, messages
from django.http import HttpResponse
from django.utils.functional import update_wrapper
from django.contrib.admin.util import unquote
from django.shortcuts import get_object_or_404, render_to_response
from django import template
from django.core.urlresolvers import reverse
from google.appengine.api import taskqueue
from google.appengine.api.taskqueue import TaskAlreadyExistsError

from cooking.models import Recipe
from django.views.decorators.csrf import csrf_exempt

class AdminRecipe(admin.ModelAdmin):
    def get_urls(self):
        from django.conf.urls.defaults import patterns, url

        def wrap(view):
            def wrapper(*args, **kwargs):
                return self.admin_site.admin_view(view)(*args, **kwargs)
            return update_wrapper(wrapper, view)

        info = self.model._meta.app_label, self.model._meta.module_name

        urlpatterns = super(AdminRecipe, self).get_urls()
        myurls = patterns('',
            url(r'^(.+)/cook/$',
                wrap(self.cook_view),
                name='%s_%s_chefworker' % info),
            url(r'^(.+)/chefworker/$',
                wrap(self.chefworker_worker),
                name='%s_%s_chefworker' % info),
        )
        return myurls + urlpatterns

    def cook_view(self, request, object_id, extra_context=None):
        obj = get_object_or_404(Recipe, pk=unquote(object_id))
        if request.POST:
            try:
                taskqueue.add(
                    name="recipie-%s" % obj.id,
                    url=reverse('admin:cooking_recipe_chefworker', args=(obj.id,))
                )
                messages.add_message(request, messages.INFO, 'Chef is cooking the recipe.')
            except TaskAlreadyExistsError:
                messages.add_message(request, messages.ERROR, 'chef is already cooking that recipe.')

        context_instance = template.RequestContext(request, current_app=self.admin_site.name)
        return render_to_response("admin/cooking/recipe/cook_view.html", {'object': obj}, context_instance=context_instance)

    #TODO: Add csrf token on form
    @csrf_exempt
    def chefworker_worker(self, request, object_id, extra_context=None):
        import time

        if request.POST:
            obj = get_object_or_404(Recipe, pk=unquote(object_id))
            obj.cookthis()

        return HttpResponse('done')

admin.site.register(Recipe, AdminRecipe)

重要说明: 很难调试这个错误,因为 dev_appserver 记录器只是引发 403 错误,没有其他信息;因此,我必须修补文件 google/appengine/api/taskqueue/taskqueue_stub.py 第 574 行并添加“logging.info('response --- \n%s' % result)”才能获取输出。

I am trying to use Google App Engine queues API, I am having problems on testing this. It seems that in some part of the process the CSRF it's not working.

as I understand the api executes the task calling the url and making and http request in background.

The complete url is the API is calling is → http://localhost.localdomain:8000/admin/cooking/recipe/36/chefworker/

When it raises this exception:

Traceback (most recent call last):
  File "/home/mariocesar/Proyectos/Cooking/cooking/django/core/handlers/base.py", line 100, in get_response
    response = callback(request, *callback_args, **callback_kwargs)
  File "/home/mariocesar/Proyectos/Cooking/cooking/django/views/decorators/csrf.py", line 24, in wrapped_view
    resp.csrf_exempt = True
AttributeError: 'NoneType' object has no attribute 'csrf_exempt'

So, the csrf middleware, the cookie, some data or the response itself is missing from the request that the GAE api makes to execute the task in the background.

How to solve this without disabling CSRF on Django? however, it's posible with djangoappengine at all?

Down are the models.py and admin.py files I am using.

models.py

from django.db import models

class Recipe(models.Model):
    name = models.CharField(max_length=140)
    description = models.TextField()
    cooking_time = models.PositiveIntegerField()
    status = models.CharField(max_length=40)

    def __unicode__(self):
        return self.name

    def cookthis(self):
        import time
        self.status = 'The chef is cooking this recipe'
        self.save()
        time.sleep(obj.cooking_time)
        self.status = 'It\'s done ! the recipe is ready to serve'
        self.save()

admin.py

import logging

from django.contrib import admin, messages
from django.http import HttpResponse
from django.utils.functional import update_wrapper
from django.contrib.admin.util import unquote
from django.shortcuts import get_object_or_404, render_to_response
from django import template
from django.core.urlresolvers import reverse
from google.appengine.api import taskqueue
from google.appengine.api.taskqueue import TaskAlreadyExistsError

from cooking.models import Recipe
from django.views.decorators.csrf import csrf_exempt

class AdminRecipe(admin.ModelAdmin):
    def get_urls(self):
        from django.conf.urls.defaults import patterns, url

        def wrap(view):
            def wrapper(*args, **kwargs):
                return self.admin_site.admin_view(view)(*args, **kwargs)
            return update_wrapper(wrapper, view)

        info = self.model._meta.app_label, self.model._meta.module_name

        urlpatterns = super(AdminRecipe, self).get_urls()
        myurls = patterns('',
            url(r'^(.+)/cook/

IMPORTANT NOTE:
Was hard to debug this error, cause the dev_appserver logger was just raising 403 errors, no other info; so, I have to patch the file google/appengine/api/taskqueue/taskqueue_stub.py line 574 and add "logging.info('response --- \n%s' % result)" to get the output.

, wrap(self.cook_view), name='%s_%s_chefworker' % info), url(r'^(.+)/chefworker/

IMPORTANT NOTE:
Was hard to debug this error, cause the dev_appserver logger was just raising 403 errors, no other info; so, I have to patch the file google/appengine/api/taskqueue/taskqueue_stub.py line 574 and add "logging.info('response --- \n%s' % result)" to get the output.

, wrap(self.chefworker_worker), name='%s_%s_chefworker' % info), ) return myurls + urlpatterns def cook_view(self, request, object_id, extra_context=None): obj = get_object_or_404(Recipe, pk=unquote(object_id)) if request.POST: try: taskqueue.add( name="recipie-%s" % obj.id, url=reverse('admin:cooking_recipe_chefworker', args=(obj.id,)) ) messages.add_message(request, messages.INFO, 'Chef is cooking the recipe.') except TaskAlreadyExistsError: messages.add_message(request, messages.ERROR, 'chef is already cooking that recipe.') context_instance = template.RequestContext(request, current_app=self.admin_site.name) return render_to_response("admin/cooking/recipe/cook_view.html", {'object': obj}, context_instance=context_instance) #TODO: Add csrf token on form @csrf_exempt def chefworker_worker(self, request, object_id, extra_context=None): import time if request.POST: obj = get_object_or_404(Recipe, pk=unquote(object_id)) obj.cookthis() return HttpResponse('done') admin.site.register(Recipe, AdminRecipe)

IMPORTANT NOTE:
Was hard to debug this error, cause the dev_appserver logger was just raising 403 errors, no other info; so, I have to patch the file google/appengine/api/taskqueue/taskqueue_stub.py line 574 and add "logging.info('response --- \n%s' % result)" to get the output.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

手长情犹 2024-10-21 19:52:39

如果您启用了 CsrfViewMiddleware,Django 将在您的视图的所有 POST 中要求一个 csrf_token

Django 提供了一个装饰器 @csrf_exempt,您应该将其放置在任务队列视图上。这会关闭那些视图的中间件。

或者,您可以完全避免使用 CsrfViewMiddleware ,而是在需要时使用 @csrf_protect 装饰器。我不建议这样做——保护所有地方并为任务队列视图开辟少量豁免可能更安全。

(最后一点:上面的两个答案——你的观点有问题,或者你应该只使用 GET 来处理任务队列——我错了。你的观点没有任何问题,POST 是正确使用的动词对于任务队列任务。)

If you have the CsrfViewMiddleware enabled, Django will require a csrf_token in all POSTs to your views.

Django provides a decorator, @csrf_exempt, that you should place on your task queue views. This turns off the middleware just for those views.

Alternatively, you can avoid using CsrfViewMiddleware altogether and instead use the @csrf_protect decorator where you need it. I don't recommend doing this -- it's probably safer to protect everywhere and carve out a small number of exemptions for your task queue views.

(One last note: both answers above -- that something is wrong with your view, or that you should just use GET for the task queue -- strike me wrong. There's nothing wrong with your view, and POST is the right verb to use for task queue tasks.)

只有一腔孤勇 2024-10-21 19:52:39

查看source ,看起来只有当您的视图函数返回 None (或未显式返回,在这种情况下 Python 将隐式返回 None )时才会发生这种情况。看看你的代码,我不明白这是如何发生的——你确定这是你确切部署的代码吗?

另外,您可能不想在任务队列任务中使用 get_object_or_404 - 如果找不到该对象,它将抛出 404,这将导致任务出错并无限期重试。

您也不应该需要 CSRF 保护(根据您的 TODO);相反,请确保任务队列 URL 标记为仅限管理员,并且它只会由任务队列服务调用。

Looking at the source of csrf.py, it looks like this would only occur if your view function is returning None (or not explicitly returning, in which case Python would return None implicitly). Looking at your code, I don't see how that could occur, though - are you sure this is your exact deployed code?

Also, you probably don't want to use get_object_or_404 inside a task queue task - if it can't find the object, it'll throw a 404, which will cause the task to error and retry indefinitely.

You also shouldn't need CSRF protection (per your TODO); instead, make sure the task queue URL is marked admin-only, and it will only ever be called by the task queue service.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文