1 | import copy
|
---|
2 | from django import http
|
---|
3 | from django.core.exceptions import ImproperlyConfigured
|
---|
4 | from django.template import RequestContext, loader
|
---|
5 | from django.utils.translation import ugettext_lazy as _
|
---|
6 | from django.utils.functional import update_wrapper
|
---|
7 | from django.utils.log import getLogger
|
---|
8 |
|
---|
9 | logger = getLogger('django.request')
|
---|
10 |
|
---|
11 | class classonlymethod(classmethod):
|
---|
12 | def __get__(self, instance, owner):
|
---|
13 | if instance is not None:
|
---|
14 | raise AttributeError("This method is available only on the view class.")
|
---|
15 | return super(classonlymethod, self).__get__(instance, owner)
|
---|
16 |
|
---|
17 | class View(object):
|
---|
18 | """
|
---|
19 | Intentionally simple parent class for all views. Only implements
|
---|
20 | dispatch-by-method and simple sanity checking.
|
---|
21 | """
|
---|
22 |
|
---|
23 | http_method_names = ['get', 'post', 'put', 'delete', 'head', 'options', 'trace']
|
---|
24 |
|
---|
25 | def __init__(self, **kwargs):
|
---|
26 | """
|
---|
27 | Constructor. Called in the URLconf; can contain helpful extra
|
---|
28 | keyword arguments, and other things.
|
---|
29 | """
|
---|
30 | # Go through keyword arguments, and either save their values to our
|
---|
31 | # instance, or raise an error.
|
---|
32 | for key, value in kwargs.iteritems():
|
---|
33 | # sanitize keyword arguments
|
---|
34 | if key in self.http_method_names:
|
---|
35 | raise TypeError(u"You tried to pass in the %s method name as a "
|
---|
36 | u"keyword argument to %s(). Don't do that."
|
---|
37 | % (key, self.__class__.__name__))
|
---|
38 | if not hasattr(self, key):
|
---|
39 | raise TypeError(u"%s() received an invalid keyword %r" % (
|
---|
40 | self.__class__.__name__, key))
|
---|
41 |
|
---|
42 | setattr(self, key, value)
|
---|
43 |
|
---|
44 |
|
---|
45 | def __call__(self, request, *args, **kwargs):
|
---|
46 | return self.dispatch(request, *args, **kwargs)
|
---|
47 |
|
---|
48 | @classonlymethod
|
---|
49 | def as_view(cls, **initkwargs):
|
---|
50 | """
|
---|
51 | Main entry point for a request-response process.
|
---|
52 | """
|
---|
53 | def view(request, *args, **kwargs):
|
---|
54 | self = cls(**initkwargs)
|
---|
55 | return self.dispatch(request, *args, **kwargs)
|
---|
56 |
|
---|
57 | # take name and docstring from class
|
---|
58 | update_wrapper(view, cls, updated=())
|
---|
59 |
|
---|
60 | # and possible attributes set by decorators
|
---|
61 | # like csrf_exempt from dispatch
|
---|
62 | update_wrapper(view, cls.dispatch, assigned=())
|
---|
63 | return view
|
---|
64 |
|
---|
65 | def dispatch(self, request, *args, **kwargs):
|
---|
66 | # Try to dispatch to the right method; if a method doesn't exist,
|
---|
67 | # defer to the error handler. Also defer to the error handler if the
|
---|
68 | # request method isn't on the approved list.
|
---|
69 | if request.method.lower() in self.http_method_names:
|
---|
70 | handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
|
---|
71 | else:
|
---|
72 | handler = self.http_method_not_allowed
|
---|
73 | self.request = request
|
---|
74 | self.args = args
|
---|
75 | self.kwargs = kwargs
|
---|
76 | return handler(request, *args, **kwargs)
|
---|
77 |
|
---|
78 | def http_method_not_allowed(self, request, *args, **kwargs):
|
---|
79 | allowed_methods = [m for m in self.http_method_names if hasattr(self, m)]
|
---|
80 | logger.warning('Method Not Allowed (%s): %s' % (request.method, request.path),
|
---|
81 | extra={
|
---|
82 | 'status_code': 405,
|
---|
83 | 'request': self.request
|
---|
84 | }
|
---|
85 | )
|
---|
86 | return http.HttpResponseNotAllowed(allowed_methods)
|
---|
87 |
|
---|
88 |
|
---|
89 | class TemplateResponseMixin(object):
|
---|
90 | """
|
---|
91 | A mixin that can be used to render a template.
|
---|
92 | """
|
---|
93 | template_name = None
|
---|
94 |
|
---|
95 | def render_to_response(self, context):
|
---|
96 | """
|
---|
97 | Returns a response with a template rendered with the given context.
|
---|
98 | """
|
---|
99 | return self.get_response(self.render_template(context))
|
---|
100 |
|
---|
101 | def get_response(self, content, **httpresponse_kwargs):
|
---|
102 | """
|
---|
103 | Construct an `HttpResponse` object.
|
---|
104 | """
|
---|
105 | return http.HttpResponse(content, **httpresponse_kwargs)
|
---|
106 |
|
---|
107 | def render_template(self, context):
|
---|
108 | """
|
---|
109 | Render the template with a given context.
|
---|
110 | """
|
---|
111 | context_instance = self.get_context_instance(context)
|
---|
112 | return self.get_template().render(context_instance)
|
---|
113 |
|
---|
114 | def get_context_instance(self, context):
|
---|
115 | """
|
---|
116 | Get the template context instance. Must return a Context (or subclass)
|
---|
117 | instance.
|
---|
118 | """
|
---|
119 | return RequestContext(self.request, context)
|
---|
120 |
|
---|
121 | def get_template(self):
|
---|
122 | """
|
---|
123 | Get a ``Template`` object for the given request.
|
---|
124 | """
|
---|
125 | names = self.get_template_names()
|
---|
126 | if not names:
|
---|
127 | raise ImproperlyConfigured(u"'%s' must provide template_name."
|
---|
128 | % self.__class__.__name__)
|
---|
129 | return self.load_template(names)
|
---|
130 |
|
---|
131 | def get_template_names(self):
|
---|
132 | """
|
---|
133 | Return a list of template names to be used for the request. Must return
|
---|
134 | a list. May not be called if get_template is overridden.
|
---|
135 | """
|
---|
136 | if self.template_name is None:
|
---|
137 | return []
|
---|
138 | else:
|
---|
139 | return [self.template_name]
|
---|
140 |
|
---|
141 | def load_template(self, names):
|
---|
142 | """
|
---|
143 | Load a list of templates using the default template loader.
|
---|
144 | """
|
---|
145 | return loader.select_template(names)
|
---|
146 |
|
---|
147 |
|
---|
148 | class TemplateView(TemplateResponseMixin, View):
|
---|
149 | """
|
---|
150 | A view that renders a template.
|
---|
151 | """
|
---|
152 | def get_context_data(self, **kwargs):
|
---|
153 | return {
|
---|
154 | 'params': kwargs
|
---|
155 | }
|
---|
156 |
|
---|
157 | def get(self, request, *args, **kwargs):
|
---|
158 | context = self.get_context_data(**kwargs)
|
---|
159 | return self.render_to_response(context)
|
---|
160 |
|
---|
161 |
|
---|
162 | class RedirectView(View):
|
---|
163 | """
|
---|
164 | A view that provides a redirect on any GET request.
|
---|
165 | """
|
---|
166 | permanent = True
|
---|
167 | url = None
|
---|
168 | query_string = False
|
---|
169 |
|
---|
170 | def get_redirect_url(self, **kwargs):
|
---|
171 | """
|
---|
172 | Return the URL redirect to. Keyword arguments from the
|
---|
173 | URL pattern match generating the redirect request
|
---|
174 | are provided as kwargs to this method.
|
---|
175 | """
|
---|
176 | if self.url:
|
---|
177 | args = self.request.META["QUERY_STRING"]
|
---|
178 | if args and self.query_string:
|
---|
179 | url = "%s?%s" % (self.url, args)
|
---|
180 | else:
|
---|
181 | url = self.url
|
---|
182 | return url % kwargs
|
---|
183 | else:
|
---|
184 | return None
|
---|
185 |
|
---|
186 | def get(self, request, *args, **kwargs):
|
---|
187 | url = self.get_redirect_url(**kwargs)
|
---|
188 | if url:
|
---|
189 | if self.permanent:
|
---|
190 | return http.HttpResponsePermanentRedirect(url)
|
---|
191 | else:
|
---|
192 | return http.HttpResponseRedirect(url)
|
---|
193 | else:
|
---|
194 | logger.warning('Gone: %s' % self.request.path,
|
---|
195 | extra={
|
---|
196 | 'status_code': 410,
|
---|
197 | 'request': self.request
|
---|
198 | })
|
---|
199 | return http.HttpResponseGone()
|
---|