/lib/python/django/views/generic/edit.py

https://github.com/mozilla/moztrap-vendor-lib · Python · 280 lines · 130 code · 48 blank · 102 comment · 18 complexity · db2222bec88f27baeb01aa18cb4680ad MD5 · raw file

  1. import warnings
  2. from django.forms import models as model_forms
  3. from django.core.exceptions import ImproperlyConfigured
  4. from django.http import HttpResponseRedirect
  5. from django.utils.encoding import force_text
  6. from django.views.generic.base import TemplateResponseMixin, ContextMixin, View
  7. from django.views.generic.detail import (SingleObjectMixin,
  8. SingleObjectTemplateResponseMixin, BaseDetailView)
  9. class FormMixin(ContextMixin):
  10. """
  11. A mixin that provides a way to show and handle a form in a request.
  12. """
  13. initial = {}
  14. form_class = None
  15. success_url = None
  16. prefix = None
  17. def get_initial(self):
  18. """
  19. Returns the initial data to use for forms on this view.
  20. """
  21. return self.initial.copy()
  22. def get_prefix(self):
  23. """
  24. Returns the prefix to use for forms on this view
  25. """
  26. return self.prefix
  27. def get_form_class(self):
  28. """
  29. Returns the form class to use in this view
  30. """
  31. return self.form_class
  32. def get_form(self, form_class):
  33. """
  34. Returns an instance of the form to be used in this view.
  35. """
  36. return form_class(**self.get_form_kwargs())
  37. def get_form_kwargs(self):
  38. """
  39. Returns the keyword arguments for instantiating the form.
  40. """
  41. kwargs = {
  42. 'initial': self.get_initial(),
  43. 'prefix': self.get_prefix(),
  44. }
  45. if self.request.method in ('POST', 'PUT'):
  46. kwargs.update({
  47. 'data': self.request.POST,
  48. 'files': self.request.FILES,
  49. })
  50. return kwargs
  51. def get_success_url(self):
  52. """
  53. Returns the supplied success URL.
  54. """
  55. if self.success_url:
  56. # Forcing possible reverse_lazy evaluation
  57. url = force_text(self.success_url)
  58. else:
  59. raise ImproperlyConfigured(
  60. "No URL to redirect to. Provide a success_url.")
  61. return url
  62. def form_valid(self, form):
  63. """
  64. If the form is valid, redirect to the supplied URL.
  65. """
  66. return HttpResponseRedirect(self.get_success_url())
  67. def form_invalid(self, form):
  68. """
  69. If the form is invalid, re-render the context data with the
  70. data-filled form and errors.
  71. """
  72. return self.render_to_response(self.get_context_data(form=form))
  73. class ModelFormMixin(FormMixin, SingleObjectMixin):
  74. """
  75. A mixin that provides a way to show and handle a modelform in a request.
  76. """
  77. fields = None
  78. def get_form_class(self):
  79. """
  80. Returns the form class to use in this view.
  81. """
  82. if self.form_class:
  83. return self.form_class
  84. else:
  85. if self.model is not None:
  86. # If a model has been explicitly provided, use it
  87. model = self.model
  88. elif hasattr(self, 'object') and self.object is not None:
  89. # If this view is operating on a single object, use
  90. # the class of that object
  91. model = self.object.__class__
  92. else:
  93. # Try to get a queryset and extract the model class
  94. # from that
  95. model = self.get_queryset().model
  96. if self.fields is None:
  97. warnings.warn("Using ModelFormMixin (base class of %s) without "
  98. "the 'fields' attribute is deprecated." % self.__class__.__name__,
  99. PendingDeprecationWarning)
  100. return model_forms.modelform_factory(model, fields=self.fields)
  101. def get_form_kwargs(self):
  102. """
  103. Returns the keyword arguments for instantiating the form.
  104. """
  105. kwargs = super(ModelFormMixin, self).get_form_kwargs()
  106. kwargs.update({'instance': self.object})
  107. return kwargs
  108. def get_success_url(self):
  109. """
  110. Returns the supplied URL.
  111. """
  112. if self.success_url:
  113. url = self.success_url % self.object.__dict__
  114. else:
  115. try:
  116. url = self.object.get_absolute_url()
  117. except AttributeError:
  118. raise ImproperlyConfigured(
  119. "No URL to redirect to. Either provide a url or define"
  120. " a get_absolute_url method on the Model.")
  121. return url
  122. def form_valid(self, form):
  123. """
  124. If the form is valid, save the associated model.
  125. """
  126. self.object = form.save()
  127. return super(ModelFormMixin, self).form_valid(form)
  128. class ProcessFormView(View):
  129. """
  130. A mixin that renders a form on GET and processes it on POST.
  131. """
  132. def get(self, request, *args, **kwargs):
  133. """
  134. Handles GET requests and instantiates a blank version of the form.
  135. """
  136. form_class = self.get_form_class()
  137. form = self.get_form(form_class)
  138. return self.render_to_response(self.get_context_data(form=form))
  139. def post(self, request, *args, **kwargs):
  140. """
  141. Handles POST requests, instantiating a form instance with the passed
  142. POST variables and then checked for validity.
  143. """
  144. form_class = self.get_form_class()
  145. form = self.get_form(form_class)
  146. if form.is_valid():
  147. return self.form_valid(form)
  148. else:
  149. return self.form_invalid(form)
  150. # PUT is a valid HTTP verb for creating (with a known URL) or editing an
  151. # object, note that browsers only support POST for now.
  152. def put(self, *args, **kwargs):
  153. return self.post(*args, **kwargs)
  154. class BaseFormView(FormMixin, ProcessFormView):
  155. """
  156. A base view for displaying a form
  157. """
  158. class FormView(TemplateResponseMixin, BaseFormView):
  159. """
  160. A view for displaying a form, and rendering a template response.
  161. """
  162. class BaseCreateView(ModelFormMixin, ProcessFormView):
  163. """
  164. Base view for creating an new object instance.
  165. Using this base class requires subclassing to provide a response mixin.
  166. """
  167. def get(self, request, *args, **kwargs):
  168. self.object = None
  169. return super(BaseCreateView, self).get(request, *args, **kwargs)
  170. def post(self, request, *args, **kwargs):
  171. self.object = None
  172. return super(BaseCreateView, self).post(request, *args, **kwargs)
  173. class CreateView(SingleObjectTemplateResponseMixin, BaseCreateView):
  174. """
  175. View for creating a new object instance,
  176. with a response rendered by template.
  177. """
  178. template_name_suffix = '_form'
  179. class BaseUpdateView(ModelFormMixin, ProcessFormView):
  180. """
  181. Base view for updating an existing object.
  182. Using this base class requires subclassing to provide a response mixin.
  183. """
  184. def get(self, request, *args, **kwargs):
  185. self.object = self.get_object()
  186. return super(BaseUpdateView, self).get(request, *args, **kwargs)
  187. def post(self, request, *args, **kwargs):
  188. self.object = self.get_object()
  189. return super(BaseUpdateView, self).post(request, *args, **kwargs)
  190. class UpdateView(SingleObjectTemplateResponseMixin, BaseUpdateView):
  191. """
  192. View for updating an object,
  193. with a response rendered by template.
  194. """
  195. template_name_suffix = '_form'
  196. class DeletionMixin(object):
  197. """
  198. A mixin providing the ability to delete objects
  199. """
  200. success_url = None
  201. def delete(self, request, *args, **kwargs):
  202. """
  203. Calls the delete() method on the fetched object and then
  204. redirects to the success URL.
  205. """
  206. self.object = self.get_object()
  207. success_url = self.get_success_url()
  208. self.object.delete()
  209. return HttpResponseRedirect(success_url)
  210. # Add support for browsers which only accept GET and POST for now.
  211. def post(self, request, *args, **kwargs):
  212. return self.delete(request, *args, **kwargs)
  213. def get_success_url(self):
  214. if self.success_url:
  215. return self.success_url % self.object.__dict__
  216. else:
  217. raise ImproperlyConfigured(
  218. "No URL to redirect to. Provide a success_url.")
  219. class BaseDeleteView(DeletionMixin, BaseDetailView):
  220. """
  221. Base view for deleting an object.
  222. Using this base class requires subclassing to provide a response mixin.
  223. """
  224. class DeleteView(SingleObjectTemplateResponseMixin, BaseDeleteView):
  225. """
  226. View for deleting an object retrieved with `self.get_object()`,
  227. with a response rendered by template.
  228. """
  229. template_name_suffix = '_confirm_delete'