Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# Imports 

2 

3from django.http import HttpResponseRedirect 

4from django.utils.decorators import method_decorator 

5from django.utils.translation import ugettext_lazy as _ 

6from django.views.decorators.csrf import csrf_exempt 

7from superdjango.exceptions import IMustBeMissingSomething 

8from .base import BaseView 

9from .messages import MessageMixin 

10 

11# Exports 

12 

13__all__ = ( 

14 "ConfirmView", 

15 "CSRFExemptMixin", 

16 "FormMixin", 

17 "FormView", 

18) 

19 

20# Mixins 

21 

22 

23class CSRFExemptMixin(object): 

24 """Provides `CSRF`_ exemption for a view. 

25 

26 .. _CSRF: https://docs.djangoproject.com/en/stable/ref/csrf/ 

27 

28 .. note:: 

29 When used, this mixin should be the left-most class of the view. 

30 

31 """ 

32 

33 @method_decorator(csrf_exempt) 

34 def dispatch(self, request, *args, **kwargs): 

35 """Dispatch the request without CSRF protection.""" 

36 # noinspection PyUnresolvedReferences 

37 return super().dispatch(request, *args, **kwargs) 

38 

39 

40class FormMixin(MessageMixin): 

41 """Provides form-related methods and attributes.""" 

42 

43 cancel_text = None 

44 """The text to display as part of the cancel URL.""" 

45 

46 cancel_url = None 

47 """The URL to which the user should be sent when canceling the confirmation.""" 

48 

49 error_message = None 

50 """The message to be displayed for an invalid (failed) form submission.""" 

51 

52 form_class = None 

53 """The class to use for rendering forms.""" 

54 

55 submit_text = None 

56 """The text to display for the submit button.""" 

57 

58 success_message = None 

59 """The message to be displayed after a valid (successful) form submission.""" 

60 

61 success_url = None 

62 """The URL to which a user is sent after a successful (valid) form submission.""" 

63 

64 # noinspection PyUnusedLocal 

65 def form_valid(self, form): 

66 """Process a valid form and redirect to success URL.""" 

67 

68 message = self.get_success_message() 

69 if message is not None: 

70 self.messages.success(message) 

71 

72 return HttpResponseRedirect(self.get_success_url()) 

73 

74 def form_invalid(self, form): 

75 """Handle invalid form submission.""" 

76 # noinspection PyUnresolvedReferences 

77 context = self.get_context_data(form=form) 

78 

79 message = self.get_error_message() 

80 if message is not None: 

81 self.messages.error(message) 

82 

83 # noinspection PyUnresolvedReferences 

84 return self.render_to_response(context) 

85 

86 # noinspection PyUnusedLocal 

87 def get(self, request, *args, **kwargs): 

88 """Handle GET requests.""" 

89 form = self.get_form() 

90 

91 # noinspection PyUnresolvedReferences 

92 context = self.get_context_data(form=form) 

93 

94 # noinspection PyUnresolvedReferences 

95 return self.render_to_response(context) 

96 

97 def get_cancel_text(self): 

98 """Get the text to display for the cancel link or button. 

99 

100 :rtype: str 

101 

102 """ 

103 if self.cancel_text is not None: 

104 return self.cancel_text 

105 

106 return _("Cancel") 

107 

108 def get_cancel_url(self): 

109 """Get the URL for canceling the form. 

110 

111 :rtype: str | None 

112 

113 """ 

114 return self.cancel_url 

115 

116 def get_context_data(self, **kwargs): 

117 """Add form-specific values to the context. 

118 

119 - ``cancel_text`` 

120 - ``cancel_url`` 

121 - ``form`` 

122 - ``submit_text`` 

123 

124 """ 

125 

126 # noinspection PyUnresolvedReferences 

127 context = super().get_context_data(**kwargs) 

128 

129 context['cancel_text'] = self.get_cancel_text() 

130 context['cancel_url'] = self.get_cancel_url() 

131 context['submit_text'] = self.get_submit_text() 

132 

133 return context 

134 

135 def get_error_message(self): 

136 """Get the error message for an invalid form submission. 

137 

138 :rtype: str | None 

139 

140 """ 

141 return self.error_message 

142 

143 def get_form_class(self): 

144 """Get the form class used by this view.""" 

145 if self.form_class is not None: 

146 return self.form_class 

147 

148 raise IMustBeMissingSomething(self.__class__.__name__, "form_class", "get_form_class") 

149 

150 def get_form(self, data=None, files=None, **kwargs): 

151 """Get the form instance. 

152 

153 :param data: The form data. 

154 :type data: QueryDict 

155 

156 :param files: Files data. 

157 :type files: QueryDict 

158 

159 :returns: The instantiated form instance. 

160 

161 Any additional keyword arguments are passed directly to the form. 

162 

163 """ 

164 cls = self.get_form_class() 

165 return cls(data=data, files=files, **kwargs) 

166 

167 def get_submit_text(self): 

168 """Get the text to display for the submit button. 

169 

170 :rtype: str 

171 

172 """ 

173 if self.submit_text is not None: 

174 return self.submit_text 

175 

176 return _("Submit") 

177 

178 def get_success_message(self): 

179 """Get the message to be displayed after a successful form submision. 

180 

181 :rtype: str | None 

182 

183 """ 

184 return self.success_message 

185 

186 def get_success_url(self): 

187 """Get the URL to which the user should be sent on successful form submit. 

188 

189 :rtype: str 

190 

191 """ 

192 if self.success_url is not None: 

193 return self.success_url 

194 

195 # noinspection PyUnresolvedReferences 

196 if 'next' in self.request.GET: 

197 # noinspection PyUnresolvedReferences 

198 return self.request.GET.get("next") 

199 

200 raise IMustBeMissingSomething(self.__class__.__name__, "success_url", "get_success_url") 

201 

202 # noinspection PyUnusedLocal 

203 def post(self, request, *args, **kwargs): 

204 """Handle POST requests, i.e form submission.""" 

205 form = self.get_form(data=request.POST, files=request.FILES) 

206 

207 if form.is_valid(): 

208 return self.form_valid(form) 

209 

210 return self.form_invalid(form) 

211 

212# Views 

213 

214 

215class ConfirmView(FormMixin, BaseView): 

216 """Provide a simple confirmation page before proceeding with another action. 

217 

218 Note that ``form_valid()`` and ``form_invalid()`` are ignored, and that no form or form class is used. 

219 

220 The template is used to provide the confirmation: 

221 

222 .. code-block:: html 

223 

224 <form method="post"> 

225 {% csrf_token %} 

226 

227 <p>{%trans "Are you sure you want to do this?" %}</p> 

228 

229 {% html "form_submit" %} 

230 </form> 

231 

232 In order for the view to do something, the ``submit()`` method must be overridden: 

233 

234 .. code-block:: python 

235 

236 from superdjango.ui.views import ConfirmView 

237 

238 class MyConfirmView(ConfirmView): 

239 pattern_name = "my_confirm_view" 

240 pattern_value = 'my/confirmation/' 

241 

242 def submit(self, request): 

243 # Do something after the form has been submitted. 

244 

245 Use the ``confirmed_keyword`` if you need to post data to the confirmation view prior to actual confirmation. 

246 

247 .. code-block:: python 

248 

249 from superdjango.ui.views import ConfirmView 

250 

251 class MyConfirmView(ConfirmView): 

252 confirmed_keyword = "confirmed" 

253 pattern_name = "my_confirm_view" 

254 pattern_value = 'my/confirmation/' 

255 

256 def submit(self, request): 

257 # Do something after the form has been submitted. 

258 

259 The template would also need to include the confirmation keyword: 

260 

261 .. code-block:: html 

262 

263 <form method="post"> 

264 {% csrf_token %} 

265 

266 <input type="hidden" name="{{ confirmed_keyword }}" value="1"> 

267 

268 <p>{%trans "Are you sure you want to do this?" %}</p> 

269 

270 {% html "form_submit" %} 

271 </form> 

272 

273 """ 

274 

275 confirmed_keyword = "confirmed" 

276 """The keyword submitted via POST that may used to confirm the next action.""" 

277 

278 # noinspection PyUnusedLocal 

279 def get(self, request, *args, **kwargs): 

280 """Handle GET requests.""" 

281 # noinspection PyAttributeOutsideInit 

282 self.request = request 

283 

284 context = self.get_context_data() 

285 return self.render_to_response(context) 

286 

287 def get_context_data(self, **kwargs): 

288 """Adds ``confirmed_keyword``.""" 

289 context = super().get_context_data(**kwargs) 

290 

291 context['confirmed_keyword'] = self.confirmed_keyword 

292 

293 return context 

294 

295 # noinspection PyUnusedLocal 

296 def post(self, request, *args, **kwargs): 

297 """Handle POST by calling the ``submit()`` method. 

298 

299 The presence of ``confirmed_keyword`` is checked prior to calling ``submit()``. This allows data to be posted to 

300 the view *without* the confirmation in order to re-use the view for confirming additional actions. 

301 

302 """ 

303 # noinspection PyAttributeOutsideInit 

304 self.request = request 

305 

306 confirmed = request.POST.get(self.confirmed_keyword, None) 

307 if confirmed: 

308 self.submit(request) 

309 return HttpResponseRedirect(self.get_success_url()) 

310 

311 context = self.get_context_data() 

312 

313 return self.render_to_response(context) 

314 

315 def submit(self, request): 

316 """Do whatever is required after the action has been confirmed. 

317 

318 :param request: The current request instance. 

319 

320 .. tip:: 

321 ``submit()`` does nothing by default. Override this method if you need the confirmation view to do 

322 something after the user has confirmed. 

323 

324 """ 

325 pass 

326 

327 

328class FormView(FormMixin, BaseView): 

329 """Display and handle form submission.""" 

330 pass