Django在发送POST数据并访问模型时产生空白页面

2 投票
2 回答
1560 浏览
提问于 2025-04-16 18:05

我真是快抓狂了。我正在用Django 1.3开发一个网页应用(最近从1.2.5升级过来,希望能解决我的错误),结果在我的一个视图里遇到了一个奇怪的bug。

这个视图是通过AJAX请求从客户端调用的。如果我用GET请求调用这个视图,一切都很好。如果用POST请求但不带数据,情况也不错。但是,如果我用POST请求并且实际发送了数据(无论数据是什么),Django就会返回一个空白页面。

我喜欢动手尝试,所以我发现可以在我的视图里打印输出,并在调试控制台看到结果。于是我开始调试,发现了一个奇怪的现象:

c = Character.objects.get(id=int(character))

这是这个视图中处理模型的第一行代码。打印结果显示,character的值是正常的(这是请求中传递的ID),而且c确实得到了正确的数据库记录。尽管如此,只要调用了这一行,Django的请求输出就什么都没有。

如果我在处理模型之前提前返回,Django就能正确渲染请求的其余部分。处理模型后,虽然会发送200的响应,但内容似乎被省略了。

这种情况似乎只发生在发送POST数据时,无论这些POST数据是否被读取或使用。我对此完全感到困惑。我打算把我的代码贴在这里,希望有人能帮我找出背后发生的黑魔法。

这是视图本身,直接从urls.py调用:

@csrf_exempt
def ajax_update(request, character):
    #determine the timestamp to send the client *before* polling
    #the database

    update_time = int(time.time())

    #grab each update... thingy as json, and put it all together
    output = {
        "events": current_events(request, character, "default"),
        "character": character,
        "update_time": update_time,
    }

    return HttpResponse(json.dumps(output), mimetype = "application/json")

这是current_events的代码,包含了上面提到的那一行:

def current_events(request, character, type = "default", timestamp = 0):
    '''
    purpose: grab the last 3 days worth of events, or the last 10 events,
    whichever is a larger list.
    '''

    #TODO: actually do something with timestamp
    c = Character.objects.get(id=int(character))

    #DEBUG: What on earth is going on here?
    print c

    #TODO: See if this can be optimized into a single query
    event_links = EventLink.objects.filter(viewer=c)
    events = Event.objects.filter(id__in=event_links.values('event')).order_by('timestamp')

    t = loader.get_template('ajax/world-events.html')

    output = []

    for event in events:
        revisions = Event.objects.filter(original_event=event.id).order_by('timestamp')

        display_message = event.message

        history = []
        if len(revisions):
            history.append(event) #first item in the history is the original event
            history.extend(revisions)   
            display_message = history[-1].message

        output.append({
            "id": event.id,
            "writer": event.writer.id,
            "type": event.type,
            "timestamp": int(time.mktime(event.timestamp.timetuple())),
            "message": t.render(RequestContext(request, {"event_text": display_message, "event": event, "history": history}))
        })

    return output

最后,这是从客户端用jQuery调用代码的CoffeeScript:

auto_refresh_abort = 0
last_server_timestamp = 0
window.update_display = ->  
    #ajax call to grab events from the server
    $.ajax 
        type: 'POST'
        url: "/ajax/update/"+window.active_character
        data: 
            "last_update": last_server_timestamp
        dataType: "json"
        success: (output) ->
            auto_refresh_abort = 0
            update_character_log output.events
        error: (XMLHttpRequest, textStatus, errorThrown) ->
            auto_refresh_abort += 1
            console.log XMLHttpRequest

    return null

如果你觉得有帮助(这已经很多了),我可以贴出其他需要的代码示例,比如模型的代码。我可以确认,在我的测试案例中被查找的Character确实存在,而且它只是一个标准的Django ID,我没有做什么奇怪的事情。

提前谢谢大家。

更新:应要求,这里是输出应该包含的内容示例:

{'update_time': 1305984080, 'character': u'1', 'events': /*snip*/}

我已经确认代码执行到最后,并且传递给HttpResponse的输出确实包含这些数据。为了好玩,我在查询Character的那一行后面插入了一个返回"TASTY",所以输出的数据应该是:

{'update_time': 1305984080, 'character': u'1', 'events': 'TASTY'}

并确认这部分是有效的。我可以在HttpRequest之前“打印输出”到调试控制台,并且这部分内容会显示出来。

如果我注释掉处理Character的那一行,我的浏览器会收到这部分数据作为请求的内容。而且,如果我保持代码不变并且不发送POST数据,浏览器也会收到正确的数据。失败的具体情况是,当我在发送POST数据后操作模型时——在这种情况下,浏览器收到的HTTP头是完全一样的,但没有内容。它收到的头信息是:

Content-Type:application/json
Date:Sat, 21 May 2011 13:29:38 GMT
Server:WSGIServer/0.1 Python/2.6

但实际上没有内容被传递。

2 个回答

1

我想我可能找到了问题所在。当我把我的应用程序连接到Apache服务器,并停止使用Django的开发服务器时,那些奇怪的错误就消失了。现在,响应总是能正常发送。

我仍然不确定问题是出在我自己的代码上,还是开发服务器本身的奇怪毛病,所以我发这个回答,希望如果其他人在开发过程中遇到类似的错误,这能给他们一些启示。

看起来开发服务器对POST数据的HTTP头部要求很挑剔,不喜欢某些头部。如果我能找到具体导致这个问题的原因和情况,我会再补充更多细节。对我来说,一个临时的解决办法是使用Firefox浏览器进行开发,因为Django似乎比较喜欢这个浏览器的头部。在Apache的生产环境下,这个错误并没有影响。

1

这段内容其实是给你提供一种方法,帮助你避免出现问题,同时也能在问题发生时用来调试。

  1. 首先,使用curl或者Firefox的REST客户端插件来创建你的请求,插件可以在这里找到。

  2. 接下来,你需要写一些代码,让它返回一些固定的“假数据”作为响应,覆盖几种情况(或者所有可能的情况)。这样,你的角色和当前事件的函数只返回你知道是正确的“假数据”。

  3. 然后,使用REST客户端(或者更正式的测试方法)来验证这些响应是否正确。

  4. 接着,你编写客户端的ajax代码,并确认它能正确处理这些假数据的输出。

  5. 最后,你把这些假数据的调用重构成函数,这样可以直接调用真实的数据对象(你可以快速验证它们是否以正确的方式返回对象)。

通过这种方法,你可以有效地调试你的程序,否则你只能猜测程序的哪个部分在正常工作,而这可能在很多地方都容易出错。

撰写回答