Django uses request and response objects to pass state through the system.
When a page is requested, Django creates an HttpRequest object that contains metadata about the request. Then Django loads the appropriate view, passing the HttpRequest as the first argument to the view function. Each view is responsible for returning an HttpResponse object.
Weve used these objects often throughout the book; this appendix explains the complete APIs for HttpRequest and HttpResponse objects.
HttpRequest represents a single HTTP request from some user-agent.
Much of the important information about the request is available as attributes on the HttpRequest instance (see Table H-1). All attributes except session should be considered read-only.
Attribute | Description |
---|---|
path | A string representing the full path to the requested page, not including the domain for example, "/music/bands/the_beatles/" . |
method | A string representing the HTTP method used in the request. This is guaranteed to be uppercase. For example: if request.method == 'GET': do_something() elif request.method == 'POST': do_something_else() |
GET | A dictionary-like object containing all given HTTP GET parameters. See the upcoming QueryDict documentation. |
POST | A dictionary-like object containing all given HTTP POST parameters. See the upcoming QueryDict documentation. Its possible that a request can come in via POST with an empty POST dictionary if, say, a form is requested via the POST HTTP method but does not include form data. Therefore, you shouldnt use if request.POST to check for use of the POST method; instead, use if request.method == "POST" (see the method entry in this table). Note: POST does not include file-upload information. See FILES . |
REQUEST | For convenience, a dictionary-like object that searches POST first, and then GET . Inspired by PHPs $_REQUEST . For example, if GET = {"name": "john"} and POST = {"age": '34'} , REQUEST["name"] would be "john" , and REQUEST["age"] would be "34" . Its strongly suggested that you use GET and POST instead of REQUEST , because the former are more explicit. |
COOKIES | A standard Python dictionary containing all cookies. Keys and values are strings. See Chapter 12 for more on using cookies. |
FILES | A dictionary-like object containing all uploaded files. Each key in FILES is the name from the <input type="file" name="" /> . Each value in FILES is a standard Python dictionary with the following three keys:
Note that FILES will contain data only if the request method was POST and the <form> that posted to the request had enctype="multipart/form-data" . Otherwise, FILES will be a blank dictionary-like object. |
META | A standard Python dictionary containing all available HTTP headers. Available headers depend on the client and server, but here are some examples:
Any HTTP headers are available in META as keys prefixed with HTTP_ , for example:
|
user | A django.contrib.auth.models.User object representing the currently logged-in user. If the user isnt currently logged in, user will be set to an instance of django.contrib.auth.models.AnonymousUser . You can tell them apart with is_authenticated() , like so: if request.user.is_authenticated(): # Do something for logged-in users. else: # Do something for anonymous users. user is available only if your Django installation has the AuthenticationMiddleware activated. For the complete details of authentication and users, see Chapter 12. |
session | A readable and writable, dictionary-like object that represents the current session. This is available only if your Django installation has session support activated. See Chapter 12. |
raw_post_data | The raw HTTP POST data. This is useful for advanced processing. |
Request objects also have a few useful methods, as shown in Table H-2.
Method | Description |
---|---|
__getitem__(key) | Returns the GET/POST value for the given key, checking POST first, and then GET. Raises KeyError if the key doesnt exist. This lets you use dictionary-accessing syntax on an HttpRequest instance. For example, request["foo"] is the same as checking request.POST["foo"] and then request.GET["foo"] . |
has_key() | Returns True or False , designating whether request.GET or request.POST has the given key. |
get_full_path() | Returns the path , plus an appended query string, if applicable. For example, "/music/bands/the_beatles/?print=true" |
is_secure() | Returns True if the request is secure; that is, if it was made with HTTPS. |
In an HttpRequest object, the GET and POST attributes are instances of django.http.QueryDict . QueryDict is a dictionary-like class customized to deal with multiple values for the same key. This is necessary because some HTML form elements, notably <select multiple="multiple"> , pass multiple values for the same key.
QueryDict instances are immutable, unless you create a copy() of them. That means you cant change attributes of request.POST and request.GET directly.
QueryDict implements the all standard dictionary methods, because its a subclass of dictionary. Exceptions are outlined in Table H-3.
Method | Differences from Standard dict Implementation |
---|---|
__getitem__ | Works just like a dictionary. However, if the key has more than one value, __getitem__() returns the last value. |
__setitem__ | Sets the given key to [value] (a Python list whose single element is value ). Note that this, as other dictionary functions that have side effects, can be called only on a mutable QueryDict (one that was created via copy() ). |
get() | If the key has more than one value, get() returns the last value just like __getitem__ . |
update() | Takes either a QueryDict or standard dictionary. Unlike the standard dictionarys update method, this method appends to the current dictionary items rather than replacing them: >>> q = QueryDict('a=1') >>> q = q.copy() # to make it mutable >>> q.update({'a': '2'}) >>> q.getlist('a') ['1', '2'] >>> q['a'] # returns the last ['2'] |
items() | Just like the standard dictionary items() method, except this uses the same last-value logic as __getitem()__ : >>> q = QueryDict('a=1&a=2&a=3') >>> q.items() [('a', '3')] |
values() | Just like the standard dictionary values() method, except this uses the same last-value logic as __getitem()__ . |
In addition, QueryDict has the methods shown in Table H-4.
Method | Description |
---|---|
copy() | Returns a copy of the object, using copy.deepcopy() from the Python standard library. The copy will be mutable that is, you can change its values. |
getlist(key) | Returns the data with the requested key, as a Python list. Returns an empty list if the key doesnt exist. Its guaranteed to return a list of some sort. |
setlist(key, list_) | Sets the given key to list_ (unlike __setitem__() ). |
appendlist(key, item) | Appends an item to the internal list associated with key . |
setlistdefault(key, l) | Just like setdefault , except it takes a list of values instead of a single value. |
lists() | Like items() , except it includes all values, as a list, for each member of the dictionary. For example: >>> q = QueryDict('a=1&a=2&a=3') >>> q.lists() [('a', ['1', '2', '3'])] |
urlencode() | Returns a string of the data in query-string format (e.g., "a=2&b=3&b=5" ). |
For example, given this HTML form:
<form action="/foo/bar/" method="post"> <input type="text" name="your_name" /> <select multiple="multiple" name="bands"> <option value="beatles">The Beatles</option> <option value="who">The Who</option> <option value="zombies">The Zombies</option> </select> <input type="submit" /> </form>
if the user enters "John Smith" in the your_name field and selects both The Beatles and The Zombies in the multiple select box, heres what Djangos request object would have:
>>> request.GET {} >>> request.POST {'your_name': ['John Smith'], 'bands': ['beatles', 'zombies']} >>> request.POST['your_name'] 'John Smith' >>> request.POST['bands'] 'zombies' >>> request.POST.getlist('bands') ['beatles', 'zombies'] >>> request.POST.get('your_name', 'Adrian') 'John Smith' >>> request.POST.get('nonexistent_field', 'Nowhere Man') 'Nowhere Man'
Implementation Note:
The GET , POST , COOKIES , FILES , META , REQUEST , raw_post_data , and user attributes are all lazily loaded. That means Django doesnt spend resources calculating the values of those attributes until your code requests them.
In contrast to HttpRequest objects, which are created automatically by Django, HttpResponse objects are your responsibility. Each view you write is responsible for instantiating, populating, and returning an HttpResponse .
The HttpResponse class lives at django.http.HttpResponse .
Typically, youll construct an HttpResponse to pass the contents of the page, as a string, to the HttpResponse constructor:
>>> response = HttpResponse("Here's the text of the Web page.") >>> response = HttpResponse("Text only, please.", mimetype="text/plain")
But if you want to add content incrementally, you can use response as a filelike object:
>>> response = HttpResponse() >>> response.write("<p>Here's the text of the Web page.</p>") >>> response.write("<p>Here's another paragraph.</p>")
You can pass HttpResponse an iterator rather than passing it hard-coded strings. If you use this technique, follow these guidelines:
The iterator should return strings.
If an HttpResponse has been initialized with an iterator as its content, you cant use the HttpResponse instance as a filelike object. Doing so will raise Exception .
Finally, note that HttpResponse implements a write() method, which makes is suitable for use anywhere that Python expects a filelike object. See Chapter 11 for some examples of using this technique.
You can add and delete headers using dictionary syntax:
>>> response = HttpResponse() >>> response['X-DJANGO'] = "It's the best." >>> del response['X-PHP'] >>> response['X-DJANGO'] "It's the best."
You can also use has_header(header) to check for the existence of a header.
Avoid setting Cookie headers by hand; instead, see Chapter 12 for instructions on how cookies work in Django.
Django includes a number of HttpResponse subclasses that handle different types of HTTP responses (see Table H-5). Like HttpResponse , these subclasses live in django.http .
Class | Description |
---|---|
HttpResponseRedirect | The constructor takes a single argument: the path to redirect to. This can be a fully qualified URL (e.g., 'http://search.yahoo.com/' ) or an absolute URL with no domain (e.g., '/search/' ). Note that this returns an HTTP status code 302. |
HttpResponsePermanentRedirect | Like HttpResponseRedirect , but it returns a permanent redirect (HTTP status code 301) instead of a found redirect (status code 302). |
HttpResponseNotModified | The constructor doesnt take any arguments. Use this to designate that a page hasnt been modified since the users last request. |
HttpResponseBadRequest | Acts just like HttpResponse but uses a 400 status code. |
HttpResponseNotFound | Acts just like HttpResponse but uses a 404 status code. |
HttpResponseForbidden | Acts just like HttpResponse but uses a 403 status code. |
HttpResponseNotAllowed | Like HttpResponse , but uses a 405 status code. It takes a single, required argument: a list of permitted methods (e.g., ['GET', 'POST'] ). |
HttpResponseGone | Acts just like HttpResponse but uses a 410 status code. |
HttpResponseServerError | Acts just like HttpResponse but uses a 500 status code. |
You can, of course, define your own HttpResponse subclass to support different types of responses not supported out of the box.
Returning HTTP error codes in Django is easy. Weve already mentioned the HttpResponseNotFound , HttpResponseForbidden , HttpResponseServerError , and other subclasses. Just return an instance of one of those subclasses instead of a normal HttpResponse in order to signify an error, for example:
def my_view(request): # ... if foo: return HttpResponseNotFound('<h1>Page not found</h1>') else: return HttpResponse('<h1>Page was found</h1>')
Because a 404 error is by far the most common HTTP error, theres an easier way to handle it.
When you return an error such as HttpResponseNotFound , youre responsible for defining the HTML of the resulting error page:
return HttpResponseNotFound('<h1>Page not found</h1>')
For convenience, and because its a good idea to have a consistent 404 error page across your site, Django provides an Http404 exception. If you raise Http404 at any point in a view function, Django will catch it and return the standard error page for your application, along with an HTTP error code 404.
Heres an example:
from django.http import Http404 def detail(request, poll_id): try: p = Poll.objects.get(pk=poll_id) except Poll.DoesNotExist: raise Http404 return render_to_response('polls/detail.html', {'poll': p})
In order to use the Http404 exception to its fullest, you should create a template that is displayed when a 404 error is raised. This template should be called 404.html , and it should be located in the top level of your template tree.
When you raise an Http404 exception, Django loads a special view devoted to handling 404 errors. By default, its the view django.views.defaults.page_not_found , which loads and renders the template 404.html .
This means you need to define a 404.html template in your root template directory. This template will be used for all 404 errors.
This page_not_found view should suffice for 99% of Web applications, but if you want to override the 404 view, you can specify handler404 in your URLconf, like so:
from django.conf.urls.defaults import * urlpatterns = patterns('', ... ) handler404 = 'mysite.views.my_custom_404_view'
Behind the scenes, Django determines the 404 view by looking for handler404 . By default, URLconfs contain the following line:
from django.conf.urls.defaults import *
That takes care of setting handler404 in the current module. As you can see in django/conf/urls/defaults.py , handler404 is set to 'django.views.defaults.page_not_found' by default.
There are three things to note about 404 views:
The 404 view is also called if Django doesnt find a match after checking every regular expression in the URLconf.
If you dont define your own 404 view and simply use the default, which is recommended you still have one obligation: to create a 404.html template in the root of your template directory. The default 404 view will use that template for all 404 errors.
If DEBUG is set to True (in your settings module), then your 404 view will never be used, and the traceback will be displayed instead.
Similarly, Django executes special-case behavior in the case of runtime errors in view code. If a view results in an exception, Django will, by default, call the view django.views.defaults.server_error , which loads and renders the template 500.html .
This means you need to define a 500.html template in your root template directory. This template will be used for all server errors.
This server_error view should suffice for 99% of Web applications, but if you want to override the view, you can specify handler500 in your URLconf, like so:
from django.conf.urls.defaults import * urlpatterns = patterns('', ... ) handler500 = 'mysite.views.my_custom_error_view'
鍏充簬鏈瘎娉ㄧ郴缁
鏈珯浣跨敤涓婁笅鏂囧叧鑱旂殑璇勬敞绯荤粺鏉ユ敹闆嗗弽棣堜俊鎭備笉鍚屼簬涓鑸鏁寸珷鍋氳瘎娉ㄧ殑鍋氭硶锛 鎴戜滑鍏佽浣犲姣忎竴涓嫭绔嬬殑鈥滄枃鏈潡鈥濆仛璇勬敞銆備竴涓滄枃鏈潡鈥濈湅璧锋潵鏄繖鏍风殑锛
涓涓滄枃鏈潡鈥濇槸涓涓钀斤紝涓涓垪琛ㄩ」锛屼竴娈典唬鐮侊紝鎴栬呭叾浠栦竴灏忔鍐呭銆 浣犻変腑瀹冧細楂樹寒搴︽樉绀:
瑕佸鏂囨湰鍧楀仛璇勬敞锛屼綘鍙渶瑕佺偣鍑诲畠鏃佽竟鐨勬爣璇嗗潡:
鎴戜滑浼氫粩缁嗛槄璇绘瘡涓瘎璁猴紝濡傛灉鍙兘鐨勮瘽鎴戜滑涔熶細鎶婅瘎娉ㄨ冭檻鍒版湭鏉ョ殑鐗堟湰涓幓:
濡傛灉浣犳効鎰忎綘鐨勮瘎娉ㄨ閲囩敤锛岃纭繚鐣欎笅浣犵殑鍏ㄥ悕 (娉ㄦ剰涓嶆槸鏄电О鎴栫畝绉帮級
Many, many thanks to Jack Slocum; the inspiration and much of the code for the comment system comes from Jack's blog, and this site couldn't have been built without his wonderful
YAHOO.ext
library. Thanks also to Yahoo for YUI itself.