Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/django/views/generic/edit.py: 42%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

126 statements  

1from django.core.exceptions import ImproperlyConfigured 

2from django.forms import Form 

3from django.forms import models as model_forms 

4from django.http import HttpResponseRedirect 

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

6from django.views.generic.detail import ( 

7 BaseDetailView, 

8 SingleObjectMixin, 

9 SingleObjectTemplateResponseMixin, 

10) 

11 

12 

13class FormMixin(ContextMixin): 

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

15 

16 initial = {} 

17 form_class = None 

18 success_url = None 

19 prefix = None 

20 

21 def get_initial(self): 

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

23 return self.initial.copy() 

24 

25 def get_prefix(self): 

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

27 return self.prefix 

28 

29 def get_form_class(self): 

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

31 return self.form_class 

32 

33 def get_form(self, form_class=None): 

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

35 if form_class is None: 

36 form_class = self.get_form_class() 

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

38 

39 def get_form_kwargs(self): 

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

41 kwargs = { 

42 "initial": self.get_initial(), 

43 "prefix": self.get_prefix(), 

44 } 

45 

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

47 kwargs.update( 

48 { 

49 "data": self.request.POST, 

50 "files": self.request.FILES, 

51 } 

52 ) 

53 return kwargs 

54 

55 def get_success_url(self): 

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

57 if not self.success_url: 

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

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

60 

61 def form_valid(self, form): 

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

63 return HttpResponseRedirect(self.get_success_url()) 

64 

65 def form_invalid(self, form): 

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

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

68 

69 def get_context_data(self, **kwargs): 

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

71 if "form" not in kwargs: 

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

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

74 

75 

76class ModelFormMixin(FormMixin, SingleObjectMixin): 

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

78 

79 fields = None 

80 

81 def get_form_class(self): 

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

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

84 raise ImproperlyConfigured( 

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

86 ) 

87 if self.form_class: 

88 return self.form_class 

89 else: 

90 if self.model is not None: 

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

92 model = self.model 

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

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

95 # the class of that object 

96 model = self.object.__class__ 

97 else: 

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

99 # from that 

100 model = self.get_queryset().model 

101 

102 if self.fields is None: 

103 raise ImproperlyConfigured( 

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

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

106 ) 

107 

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

109 

110 def get_form_kwargs(self): 

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

112 kwargs = super().get_form_kwargs() 

113 if hasattr(self, "object"): 

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

115 return kwargs 

116 

117 def get_success_url(self): 

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

119 if self.success_url: 

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

121 else: 

122 try: 

123 url = self.object.get_absolute_url() 

124 except AttributeError: 

125 raise ImproperlyConfigured( 

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

127 " a get_absolute_url method on the Model." 

128 ) 

129 return url 

130 

131 def form_valid(self, form): 

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

133 self.object = form.save() 

134 return super().form_valid(form) 

135 

136 

137class ProcessFormView(View): 

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

139 

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

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

142 return self.render_to_response(self.get_context_data()) 

143 

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

145 """ 

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

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

148 """ 

149 form = self.get_form() 

150 if form.is_valid(): 

151 return self.form_valid(form) 

152 else: 

153 return self.form_invalid(form) 

154 

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

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

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

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

159 

160 

161class BaseFormView(FormMixin, ProcessFormView): 

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

163 

164 

165class FormView(TemplateResponseMixin, BaseFormView): 

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

167 

168 

169class BaseCreateView(ModelFormMixin, ProcessFormView): 

170 """ 

171 Base view for creating a new object instance. 

172 

173 This requires subclassing to provide a response mixin. 

174 """ 

175 

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

177 self.object = None 

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

179 

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

181 self.object = None 

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

183 

184 

185class CreateView(SingleObjectTemplateResponseMixin, BaseCreateView): 

186 """ 

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

188 """ 

189 

190 template_name_suffix = "_form" 

191 

192 

193class BaseUpdateView(ModelFormMixin, ProcessFormView): 

194 """ 

195 Base view for updating an existing object. 

196 

197 This requires subclassing to provide a response mixin. 

198 """ 

199 

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

201 self.object = self.get_object() 

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

203 

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

205 self.object = self.get_object() 

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

207 

208 

209class UpdateView(SingleObjectTemplateResponseMixin, BaseUpdateView): 

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

211 

212 template_name_suffix = "_form" 

213 

214 

215class DeletionMixin: 

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

217 

218 success_url = None 

219 

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

221 """ 

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

223 success URL. 

224 """ 

225 self.object = self.get_object() 

226 success_url = self.get_success_url() 

227 self.object.delete() 

228 return HttpResponseRedirect(success_url) 

229 

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

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

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

233 

234 def get_success_url(self): 

235 if self.success_url: 

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

237 else: 

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

239 

240 

241class BaseDeleteView(DeletionMixin, FormMixin, BaseDetailView): 

242 """ 

243 Base view for deleting an object. 

244 

245 This requires subclassing to provide a response mixin. 

246 """ 

247 

248 form_class = Form 

249 

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

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

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

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

254 # overly complex. 

255 self.object = self.get_object() 

256 form = self.get_form() 

257 if form.is_valid(): 

258 return self.form_valid(form) 

259 else: 

260 return self.form_invalid(form) 

261 

262 def form_valid(self, form): 

263 success_url = self.get_success_url() 

264 self.object.delete() 

265 return HttpResponseRedirect(success_url) 

266 

267 

268class DeleteView(SingleObjectTemplateResponseMixin, BaseDeleteView): 

269 """ 

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

271 response rendered by a template. 

272 """ 

273 

274 template_name_suffix = "_confirm_delete"