mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			330 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			330 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import inspect
 | |
| import re
 | |
| import warnings
 | |
| 
 | |
| from django.core.exceptions import ImproperlyConfigured
 | |
| from django.forms import models as model_forms
 | |
| from django.http import HttpResponseRedirect
 | |
| from django.utils import six
 | |
| from django.utils.deprecation import RemovedInDjango20Warning
 | |
| from django.utils.encoding import force_text
 | |
| from django.views.generic.base import TemplateResponseMixin, ContextMixin, View
 | |
| from django.views.generic.detail import (SingleObjectMixin,
 | |
|                         SingleObjectTemplateResponseMixin, BaseDetailView)
 | |
| 
 | |
| PERCENT_PLACEHOLDER_REGEX = re.compile(r'%\([^\)]+\)')  # RemovedInDjango20Warning
 | |
| 
 | |
| 
 | |
| class FormMixinBase(type):
 | |
|     def __new__(cls, name, bases, attrs):
 | |
|         get_form = attrs.get('get_form')
 | |
|         if get_form and inspect.isfunction(get_form):
 | |
|             try:
 | |
|                 inspect.getcallargs(get_form, None)
 | |
|             except TypeError:
 | |
|                 warnings.warn(
 | |
|                     "`%s.%s.get_form` method must define a default value for "
 | |
|                     "its `form_class` argument." % (attrs['__module__'], name),
 | |
|                     RemovedInDjango20Warning, stacklevel=2
 | |
|                 )
 | |
| 
 | |
|                 def get_form_with_form_class(self, form_class=None):
 | |
|                     if form_class is None:
 | |
|                         form_class = self.get_form_class()
 | |
|                     return get_form(self, form_class=form_class)
 | |
|                 attrs['get_form'] = get_form_with_form_class
 | |
|         return super(FormMixinBase, cls).__new__(cls, name, bases, attrs)
 | |
| 
 | |
| 
 | |
| class FormMixin(six.with_metaclass(FormMixinBase, ContextMixin)):
 | |
|     """
 | |
|     A mixin that provides a way to show and handle a form in a request.
 | |
|     """
 | |
| 
 | |
|     initial = {}
 | |
|     form_class = None
 | |
|     success_url = None
 | |
|     prefix = None
 | |
| 
 | |
|     def get_initial(self):
 | |
|         """
 | |
|         Returns the initial data to use for forms on this view.
 | |
|         """
 | |
|         return self.initial.copy()
 | |
| 
 | |
|     def get_prefix(self):
 | |
|         """
 | |
|         Returns the prefix to use for forms on this view
 | |
|         """
 | |
|         return self.prefix
 | |
| 
 | |
|     def get_form_class(self):
 | |
|         """
 | |
|         Returns the form class to use in this view
 | |
|         """
 | |
|         return self.form_class
 | |
| 
 | |
|     def get_form(self, form_class=None):
 | |
|         """
 | |
|         Returns an instance of the form to be used in this view.
 | |
|         """
 | |
|         if form_class is None:
 | |
|             form_class = self.get_form_class()
 | |
|         return form_class(**self.get_form_kwargs())
 | |
| 
 | |
|     def get_form_kwargs(self):
 | |
|         """
 | |
|         Returns the keyword arguments for instantiating the form.
 | |
|         """
 | |
|         kwargs = {
 | |
|             'initial': self.get_initial(),
 | |
|             'prefix': self.get_prefix(),
 | |
|         }
 | |
| 
 | |
|         if self.request.method in ('POST', 'PUT'):
 | |
|             kwargs.update({
 | |
|                 'data': self.request.POST,
 | |
|                 'files': self.request.FILES,
 | |
|             })
 | |
|         return kwargs
 | |
| 
 | |
|     def get_success_url(self):
 | |
|         """
 | |
|         Returns the supplied success URL.
 | |
|         """
 | |
|         if self.success_url:
 | |
|             # Forcing possible reverse_lazy evaluation
 | |
|             url = force_text(self.success_url)
 | |
|         else:
 | |
|             raise ImproperlyConfigured(
 | |
|                 "No URL to redirect to. Provide a success_url.")
 | |
|         return url
 | |
| 
 | |
|     def form_valid(self, form):
 | |
|         """
 | |
|         If the form is valid, redirect to the supplied URL.
 | |
|         """
 | |
|         return HttpResponseRedirect(self.get_success_url())
 | |
| 
 | |
|     def form_invalid(self, form):
 | |
|         """
 | |
|         If the form is invalid, re-render the context data with the
 | |
|         data-filled form and errors.
 | |
|         """
 | |
|         return self.render_to_response(self.get_context_data(form=form))
 | |
| 
 | |
| 
 | |
| class ModelFormMixin(FormMixin, SingleObjectMixin):
 | |
|     """
 | |
|     A mixin that provides a way to show and handle a modelform in a request.
 | |
|     """
 | |
|     fields = None
 | |
| 
 | |
|     def get_form_class(self):
 | |
|         """
 | |
|         Returns the form class to use in this view.
 | |
|         """
 | |
|         if self.fields is not None and self.form_class:
 | |
|             raise ImproperlyConfigured(
 | |
|                 "Specifying both 'fields' and 'form_class' is not permitted."
 | |
|             )
 | |
|         if self.form_class:
 | |
|             return self.form_class
 | |
|         else:
 | |
|             if self.model is not None:
 | |
|                 # If a model has been explicitly provided, use it
 | |
|                 model = self.model
 | |
|             elif hasattr(self, 'object') and self.object is not None:
 | |
|                 # If this view is operating on a single object, use
 | |
|                 # the class of that object
 | |
|                 model = self.object.__class__
 | |
|             else:
 | |
|                 # Try to get a queryset and extract the model class
 | |
|                 # from that
 | |
|                 model = self.get_queryset().model
 | |
| 
 | |
|             if self.fields is None:
 | |
|                 raise ImproperlyConfigured(
 | |
|                     "Using ModelFormMixin (base class of %s) without "
 | |
|                     "the 'fields' attribute is prohibited." % self.__class__.__name__
 | |
|                 )
 | |
| 
 | |
|             return model_forms.modelform_factory(model, fields=self.fields)
 | |
| 
 | |
|     def get_form_kwargs(self):
 | |
|         """
 | |
|         Returns the keyword arguments for instantiating the form.
 | |
|         """
 | |
|         kwargs = super(ModelFormMixin, self).get_form_kwargs()
 | |
|         if hasattr(self, 'object'):
 | |
|             kwargs.update({'instance': self.object})
 | |
|         return kwargs
 | |
| 
 | |
|     def get_success_url(self):
 | |
|         """
 | |
|         Returns the supplied URL.
 | |
|         """
 | |
|         if self.success_url:
 | |
|             if PERCENT_PLACEHOLDER_REGEX.search(self.success_url):
 | |
|                 warnings.warn(
 | |
|                     "%()s placeholder style in success_url is deprecated. "
 | |
|                     "Please replace them by the {} Python format syntax.",
 | |
|                     RemovedInDjango20Warning, stacklevel=2
 | |
|                 )
 | |
|                 url = self.success_url % self.object.__dict__
 | |
|             else:
 | |
|                 url = self.success_url.format(**self.object.__dict__)
 | |
|         else:
 | |
|             try:
 | |
|                 url = self.object.get_absolute_url()
 | |
|             except AttributeError:
 | |
|                 raise ImproperlyConfigured(
 | |
|                     "No URL to redirect to.  Either provide a url or define"
 | |
|                     " a get_absolute_url method on the Model.")
 | |
|         return url
 | |
| 
 | |
|     def form_valid(self, form):
 | |
|         """
 | |
|         If the form is valid, save the associated model.
 | |
|         """
 | |
|         self.object = form.save()
 | |
|         return super(ModelFormMixin, self).form_valid(form)
 | |
| 
 | |
| 
 | |
| class ProcessFormView(View):
 | |
|     """
 | |
|     A mixin that renders a form on GET and processes it on POST.
 | |
|     """
 | |
|     def get(self, request, *args, **kwargs):
 | |
|         """
 | |
|         Handles GET requests and instantiates a blank version of the form.
 | |
|         """
 | |
|         form = self.get_form()
 | |
|         return self.render_to_response(self.get_context_data(form=form))
 | |
| 
 | |
|     def post(self, request, *args, **kwargs):
 | |
|         """
 | |
|         Handles POST requests, instantiating a form instance with the passed
 | |
|         POST variables and then checked for validity.
 | |
|         """
 | |
|         form = self.get_form()
 | |
|         if form.is_valid():
 | |
|             return self.form_valid(form)
 | |
|         else:
 | |
|             return self.form_invalid(form)
 | |
| 
 | |
|     # PUT is a valid HTTP verb for creating (with a known URL) or editing an
 | |
|     # object, note that browsers only support POST for now.
 | |
|     def put(self, *args, **kwargs):
 | |
|         return self.post(*args, **kwargs)
 | |
| 
 | |
| 
 | |
| class BaseFormView(FormMixin, ProcessFormView):
 | |
|     """
 | |
|     A base view for displaying a form
 | |
|     """
 | |
| 
 | |
| 
 | |
| class FormView(TemplateResponseMixin, BaseFormView):
 | |
|     """
 | |
|     A view for displaying a form, and rendering a template response.
 | |
|     """
 | |
| 
 | |
| 
 | |
| class BaseCreateView(ModelFormMixin, ProcessFormView):
 | |
|     """
 | |
|     Base view for creating an new object instance.
 | |
| 
 | |
|     Using this base class requires subclassing to provide a response mixin.
 | |
|     """
 | |
|     def get(self, request, *args, **kwargs):
 | |
|         self.object = None
 | |
|         return super(BaseCreateView, self).get(request, *args, **kwargs)
 | |
| 
 | |
|     def post(self, request, *args, **kwargs):
 | |
|         self.object = None
 | |
|         return super(BaseCreateView, self).post(request, *args, **kwargs)
 | |
| 
 | |
| 
 | |
| class CreateView(SingleObjectTemplateResponseMixin, BaseCreateView):
 | |
|     """
 | |
|     View for creating a new object instance,
 | |
|     with a response rendered by template.
 | |
|     """
 | |
|     template_name_suffix = '_form'
 | |
| 
 | |
| 
 | |
| class BaseUpdateView(ModelFormMixin, ProcessFormView):
 | |
|     """
 | |
|     Base view for updating an existing object.
 | |
| 
 | |
|     Using this base class requires subclassing to provide a response mixin.
 | |
|     """
 | |
|     def get(self, request, *args, **kwargs):
 | |
|         self.object = self.get_object()
 | |
|         return super(BaseUpdateView, self).get(request, *args, **kwargs)
 | |
| 
 | |
|     def post(self, request, *args, **kwargs):
 | |
|         self.object = self.get_object()
 | |
|         return super(BaseUpdateView, self).post(request, *args, **kwargs)
 | |
| 
 | |
| 
 | |
| class UpdateView(SingleObjectTemplateResponseMixin, BaseUpdateView):
 | |
|     """
 | |
|     View for updating an object,
 | |
|     with a response rendered by template.
 | |
|     """
 | |
|     template_name_suffix = '_form'
 | |
| 
 | |
| 
 | |
| class DeletionMixin(object):
 | |
|     """
 | |
|     A mixin providing the ability to delete objects
 | |
|     """
 | |
|     success_url = None
 | |
| 
 | |
|     def delete(self, request, *args, **kwargs):
 | |
|         """
 | |
|         Calls the delete() method on the fetched object and then
 | |
|         redirects to the success URL.
 | |
|         """
 | |
|         self.object = self.get_object()
 | |
|         success_url = self.get_success_url()
 | |
|         self.object.delete()
 | |
|         return HttpResponseRedirect(success_url)
 | |
| 
 | |
|     # Add support for browsers which only accept GET and POST for now.
 | |
|     def post(self, request, *args, **kwargs):
 | |
|         return self.delete(request, *args, **kwargs)
 | |
| 
 | |
|     def get_success_url(self):
 | |
|         if self.success_url:
 | |
|             if PERCENT_PLACEHOLDER_REGEX.search(self.success_url):
 | |
|                 warnings.warn(
 | |
|                     "%()s placeholder style in success_url is deprecated. "
 | |
|                     "Please replace them by the {} Python format syntax.",
 | |
|                     RemovedInDjango20Warning, stacklevel=2
 | |
|                 )
 | |
|                 return self.success_url % self.object.__dict__
 | |
|             else:
 | |
|                 return self.success_url.format(**self.object.__dict__)
 | |
|         else:
 | |
|             raise ImproperlyConfigured(
 | |
|                 "No URL to redirect to. Provide a success_url.")
 | |
| 
 | |
| 
 | |
| class BaseDeleteView(DeletionMixin, BaseDetailView):
 | |
|     """
 | |
|     Base view for deleting an object.
 | |
| 
 | |
|     Using this base class requires subclassing to provide a response mixin.
 | |
|     """
 | |
| 
 | |
| 
 | |
| class DeleteView(SingleObjectTemplateResponseMixin, BaseDeleteView):
 | |
|     """
 | |
|     View for deleting an object retrieved with `self.get_object()`,
 | |
|     with a response rendered by template.
 | |
|     """
 | |
|     template_name_suffix = '_confirm_delete'
 |