Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/django/views/generic/edit.py: 44%

133 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1import warnings 

2 

3from django.core.exceptions import ImproperlyConfigured 

4from django.forms import Form 

5from django.forms import models as model_forms 

6from django.http import HttpResponseRedirect 

7from django.views.generic.base import ContextMixin, TemplateResponseMixin, View 

8from django.views.generic.detail import ( 

9 BaseDetailView, 

10 SingleObjectMixin, 

11 SingleObjectTemplateResponseMixin, 

12) 

13 

14 

15class FormMixin(ContextMixin): 

16 """Provide a way to show and handle a form in a request.""" 

17 

18 initial = {} 

19 form_class = None 

20 success_url = None 

21 prefix = None 

22 

23 def get_initial(self): 

24 """Return the initial data to use for forms on this view.""" 

25 return self.initial.copy() 

26 

27 def get_prefix(self): 

28 """Return the prefix to use for forms.""" 

29 return self.prefix 

30 

31 def get_form_class(self): 

32 """Return the form class to use.""" 

33 return self.form_class 

34 

35 def get_form(self, form_class=None): 

36 """Return an instance of the form to be used in this view.""" 

37 if form_class is None: 

38 form_class = self.get_form_class() 

39 return form_class(**self.get_form_kwargs()) 

40 

41 def get_form_kwargs(self): 

42 """Return the keyword arguments for instantiating the form.""" 

43 kwargs = { 

44 "initial": self.get_initial(), 

45 "prefix": self.get_prefix(), 

46 } 

47 

48 if self.request.method in ("POST", "PUT"): 

49 kwargs.update( 

50 { 

51 "data": self.request.POST, 

52 "files": self.request.FILES, 

53 } 

54 ) 

55 return kwargs 

56 

57 def get_success_url(self): 

58 """Return the URL to redirect to after processing a valid form.""" 

59 if not self.success_url: 

60 raise ImproperlyConfigured("No URL to redirect to. Provide a success_url.") 

61 return str(self.success_url) # success_url may be lazy 

62 

63 def form_valid(self, form): 

64 """If the form is valid, redirect to the supplied URL.""" 

65 return HttpResponseRedirect(self.get_success_url()) 

66 

67 def form_invalid(self, form): 

68 """If the form is invalid, render the invalid form.""" 

69 return self.render_to_response(self.get_context_data(form=form)) 

70 

71 def get_context_data(self, **kwargs): 

72 """Insert the form into the context dict.""" 

73 if "form" not in kwargs: 

74 kwargs["form"] = self.get_form() 

75 return super().get_context_data(**kwargs) 

76 

77 

78class ModelFormMixin(FormMixin, SingleObjectMixin): 

79 """Provide a way to show and handle a ModelForm in a request.""" 

80 

81 fields = None 

82 

83 def get_form_class(self): 

84 """Return the form class to use in this view.""" 

85 if self.fields is not None and self.form_class: 

86 raise ImproperlyConfigured( 

87 "Specifying both 'fields' and 'form_class' is not permitted." 

88 ) 

89 if self.form_class: 

90 return self.form_class 

91 else: 

92 if self.model is not None: 

93 # If a model has been explicitly provided, use it 

94 model = self.model 

95 elif getattr(self, "object", None) is not None: 

96 # If this view is operating on a single object, use 

97 # the class of that object 

98 model = self.object.__class__ 

99 else: 

100 # Try to get a queryset and extract the model class 

101 # from that 

102 model = self.get_queryset().model 

103 

104 if self.fields is None: 

105 raise ImproperlyConfigured( 

106 "Using ModelFormMixin (base class of %s) without " 

107 "the 'fields' attribute is prohibited." % self.__class__.__name__ 

108 ) 

109 

110 return model_forms.modelform_factory(model, fields=self.fields) 

111 

112 def get_form_kwargs(self): 

113 """Return the keyword arguments for instantiating the form.""" 

114 kwargs = super().get_form_kwargs() 

115 if hasattr(self, "object"): 

116 kwargs.update({"instance": self.object}) 

117 return kwargs 

118 

119 def get_success_url(self): 

120 """Return the URL to redirect to after processing a valid form.""" 

121 if self.success_url: 

122 url = self.success_url.format(**self.object.__dict__) 

123 else: 

124 try: 

125 url = self.object.get_absolute_url() 

126 except AttributeError: 

127 raise ImproperlyConfigured( 

128 "No URL to redirect to. Either provide a url or define" 

129 " a get_absolute_url method on the Model." 

130 ) 

131 return url 

132 

133 def form_valid(self, form): 

134 """If the form is valid, save the associated model.""" 

135 self.object = form.save() 

136 return super().form_valid(form) 

137 

138 

139class ProcessFormView(View): 

140 """Render a form on GET and processes it on POST.""" 

141 

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

143 """Handle GET requests: instantiate a blank version of the form.""" 

144 return self.render_to_response(self.get_context_data()) 

145 

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

147 """ 

148 Handle POST requests: instantiate a form instance with the passed 

149 POST variables and then check if it's valid. 

150 """ 

151 form = self.get_form() 

152 if form.is_valid(): 

153 return self.form_valid(form) 

154 else: 

155 return self.form_invalid(form) 

156 

157 # PUT is a valid HTTP verb for creating (with a known URL) or editing an 

158 # object, note that browsers only support POST for now. 

159 def put(self, *args, **kwargs): 

160 return self.post(*args, **kwargs) 

161 

162 

163class BaseFormView(FormMixin, ProcessFormView): 

164 """A base view for displaying a form.""" 

165 

166 

167class FormView(TemplateResponseMixin, BaseFormView): 

168 """A view for displaying a form and rendering a template response.""" 

169 

170 

171class BaseCreateView(ModelFormMixin, ProcessFormView): 

172 """ 

173 Base view for creating a new object instance. 

174 

175 Using this base class requires subclassing to provide a response mixin. 

176 """ 

177 

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

179 self.object = None 

180 return super().get(request, *args, **kwargs) 

181 

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

183 self.object = None 

184 return super().post(request, *args, **kwargs) 

185 

186 

187class CreateView(SingleObjectTemplateResponseMixin, BaseCreateView): 

188 """ 

189 View for creating a new object, with a response rendered by a template. 

190 """ 

191 

192 template_name_suffix = "_form" 

193 

194 

195class BaseUpdateView(ModelFormMixin, ProcessFormView): 

196 """ 

197 Base view for updating an existing object. 

198 

199 Using this base class requires subclassing to provide a response mixin. 

200 """ 

201 

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

203 self.object = self.get_object() 

204 return super().get(request, *args, **kwargs) 

205 

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

207 self.object = self.get_object() 

208 return super().post(request, *args, **kwargs) 

209 

210 

211class UpdateView(SingleObjectTemplateResponseMixin, BaseUpdateView): 

212 """View for updating an object, with a response rendered by a template.""" 

213 

214 template_name_suffix = "_form" 

215 

216 

217class DeletionMixin: 

218 """Provide the ability to delete objects.""" 

219 

220 success_url = None 

221 

222 def delete(self, request, *args, **kwargs): 

223 """ 

224 Call the delete() method on the fetched object and then redirect to the 

225 success URL. 

226 """ 

227 self.object = self.get_object() 

228 success_url = self.get_success_url() 

229 self.object.delete() 

230 return HttpResponseRedirect(success_url) 

231 

232 # Add support for browsers which only accept GET and POST for now. 

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

234 return self.delete(request, *args, **kwargs) 

235 

236 def get_success_url(self): 

237 if self.success_url: 

238 return self.success_url.format(**self.object.__dict__) 

239 else: 

240 raise ImproperlyConfigured("No URL to redirect to. Provide a success_url.") 

241 

242 

243# RemovedInDjango50Warning. 

244class DeleteViewCustomDeleteWarning(Warning): 

245 pass 

246 

247 

248class BaseDeleteView(DeletionMixin, FormMixin, BaseDetailView): 

249 """ 

250 Base view for deleting an object. 

251 

252 Using this base class requires subclassing to provide a response mixin. 

253 """ 

254 

255 form_class = Form 

256 

257 def __init__(self, *args, **kwargs): 

258 # RemovedInDjango50Warning. 

259 if self.__class__.delete is not DeletionMixin.delete: 

260 warnings.warn( 

261 f"DeleteView uses FormMixin to handle POST requests. As a " 

262 f"consequence, any custom deletion logic in " 

263 f"{self.__class__.__name__}.delete() handler should be moved " 

264 f"to form_valid().", 

265 DeleteViewCustomDeleteWarning, 

266 stacklevel=2, 

267 ) 

268 super().__init__(*args, **kwargs) 

269 

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

271 # Set self.object before the usual form processing flow. 

272 # Inlined because having DeletionMixin as the first base, for 

273 # get_success_url(), makes leveraging super() with ProcessFormView 

274 # overly complex. 

275 self.object = self.get_object() 

276 form = self.get_form() 

277 if form.is_valid(): 

278 return self.form_valid(form) 

279 else: 

280 return self.form_invalid(form) 

281 

282 def form_valid(self, form): 

283 success_url = self.get_success_url() 

284 self.object.delete() 

285 return HttpResponseRedirect(success_url) 

286 

287 

288class DeleteView(SingleObjectTemplateResponseMixin, BaseDeleteView): 

289 """ 

290 View for deleting an object retrieved with self.get_object(), with a 

291 response rendered by a template. 

292 """ 

293 

294 template_name_suffix = "_confirm_delete"