~ubuntu-branches/ubuntu/quantal/python-django/quantal

« back to all changes in this revision

Viewing changes to django/views/decorators/http.py

  • Committer: Bazaar Package Importer
  • Author(s): Chris Lamb
  • Date: 2009-07-29 11:26:28 UTC
  • mfrom: (1.1.8 upstream) (4.1.5 sid)
  • Revision ID: james.westby@ubuntu.com-20090729112628-pg09ino8sz0sj21t
Tags: 1.1-1
* New upstream release.
* Merge from experimental:
  - Ship FastCGI initscript and /etc/default file in python-django's examples
    directory (Closes: #538863)
  - Drop "05_10539-sphinx06-compatibility.diff"; it has been applied
    upstream.
  - Bump Standards-Version to 3.8.2.

Show diffs side-by-side

added added

removed removed

Lines of Context:
7
7
except ImportError:
8
8
    from django.utils.functional import wraps  # Python 2.3, 2.4 fallback.
9
9
 
 
10
from calendar import timegm
 
11
from datetime import timedelta
 
12
from email.Utils import formatdate
 
13
 
10
14
from django.utils.decorators import decorator_from_middleware
 
15
from django.utils.http import parse_etags, quote_etag
11
16
from django.middleware.http import ConditionalGetMiddleware
12
 
from django.http import HttpResponseNotAllowed
 
17
from django.http import HttpResponseNotAllowed, HttpResponseNotModified, HttpResponse
 
18
 
13
19
 
14
20
conditional_page = decorator_from_middleware(ConditionalGetMiddleware)
15
21
 
36
42
require_GET.__doc__ = "Decorator to require that a view only accept the GET method."
37
43
 
38
44
require_POST = require_http_methods(["POST"])
39
 
require_POST.__doc__ = "Decorator to require that a view only accept the POST method."
 
 
b'\\ No newline at end of file'
 
45
require_POST.__doc__ = "Decorator to require that a view only accept the POST method."
 
46
 
 
47
def condition(etag_func=None, last_modified_func=None):
 
48
    """
 
49
    Decorator to support conditional retrieval (or change) for a view
 
50
    function.
 
51
 
 
52
    The parameters are callables to compute the ETag and last modified time for
 
53
    the requested resource, respectively. The callables are passed the same
 
54
    parameters as the view itself. The Etag function should return a string (or
 
55
    None if the resource doesn't exist), whilst the last_modified function
 
56
    should return a datetime object (or None if the resource doesn't exist).
 
57
 
 
58
    If both parameters are provided, all the preconditions must be met before
 
59
    the view is processed.
 
60
 
 
61
    This decorator will either pass control to the wrapped view function or
 
62
    return an HTTP 304 response (unmodified) or 412 response (preconditions
 
63
    failed), depending upon the request method.
 
64
 
 
65
    Any behavior marked as "undefined" in the HTTP spec (e.g. If-none-match
 
66
    plus If-modified-since headers) will result in the view function being
 
67
    called.
 
68
    """
 
69
    def decorator(func):
 
70
        def inner(request, *args, **kwargs):
 
71
            # Get HTTP request headers
 
72
            if_modified_since = request.META.get("HTTP_IF_MODIFIED_SINCE")
 
73
            if_none_match = request.META.get("HTTP_IF_NONE_MATCH")
 
74
            if_match = request.META.get("HTTP_IF_MATCH")
 
75
            if if_none_match or if_match:
 
76
                # There can be more than one ETag in the request, so we
 
77
                # consider the list of values.
 
78
                try:
 
79
                    etags = parse_etags(if_none_match or if_match)
 
80
                except ValueError:
 
81
                    # In case of invalid etag ignore all ETag headers.
 
82
                    # Apparently Opera sends invalidly quoted headers at times
 
83
                    # (we should be returning a 400 response, but that's a
 
84
                    # little extreme) -- this is Django bug #10681.
 
85
                    if_none_match = None
 
86
                    if_match = None
 
87
 
 
88
            # Compute values (if any) for the requested resource.
 
89
            if etag_func:
 
90
                res_etag = etag_func(request, *args, **kwargs)
 
91
            else:
 
92
                res_etag = None
 
93
            if last_modified_func:
 
94
                dt = last_modified_func(request, *args, **kwargs)
 
95
                if dt:
 
96
                    res_last_modified = formatdate(timegm(dt.utctimetuple()))[:26] + 'GMT'
 
97
                else:
 
98
                    res_last_modified = None
 
99
            else:
 
100
                res_last_modified = None
 
101
 
 
102
            response = None
 
103
            if not ((if_match and (if_modified_since or if_none_match)) or
 
104
                    (if_match and if_none_match)):
 
105
                # We only get here if no undefined combinations of headers are
 
106
                # specified.
 
107
                if ((if_none_match and (res_etag in etags or
 
108
                        "*" in etags and res_etag)) and
 
109
                        (not if_modified_since or
 
110
                            res_last_modified == if_modified_since)):
 
111
                    if request.method in ("GET", "HEAD"):
 
112
                        response = HttpResponseNotModified()
 
113
                    else:
 
114
                        response = HttpResponse(status=412)
 
115
                elif if_match and ((not res_etag and "*" in etags) or
 
116
                        (res_etag and res_etag not in etags)):
 
117
                    response = HttpResponse(status=412)
 
118
                elif (not if_none_match and if_modified_since and
 
119
                        request.method == "GET" and
 
120
                        res_last_modified == if_modified_since):
 
121
                    response = HttpResponseNotModified()
 
122
 
 
123
            if response is None:
 
124
                response = func(request, *args, **kwargs)
 
125
 
 
126
            # Set relevant headers on the response if they don't already exist.
 
127
            if res_last_modified and not response.has_header('Last-Modified'):
 
128
                response['Last-Modified'] = res_last_modified
 
129
            if res_etag and not response.has_header('ETag'):
 
130
                response['ETag'] = quote_etag(res_etag)
 
131
 
 
132
            return response
 
133
 
 
134
        return inner
 
135
    return decorator
 
136
 
 
137
# Shortcut decorators for common cases based on ETag or Last-Modified only
 
138
def etag(etag_func):
 
139
    return condition(etag_func=etag_func)
 
140
 
 
141
def last_modified(last_modified_func):
 
142
    return condition(last_modified_func=last_modified_func)
 
143