Ticket #14557: base.py

File base.py, 6.6 KB (added by pyrou, 14 years ago)
Line 
1import copy
2from django import http
3from django.core.exceptions import ImproperlyConfigured
4from django.template import RequestContext, loader
5from django.utils.translation import ugettext_lazy as _
6from django.utils.functional import update_wrapper
7from django.utils.log import getLogger
8
9logger = getLogger('django.request')
10
11class 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
17class 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
89class 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
148class 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
162class 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()
Back to Top