Permalink
Browse files

Fixed #6735 -- Added class-based views.

This patch is the result of the work of many people, over many years.
To try and thank individuals would inevitably lead to many people
being left out or forgotten -- so rather than try to give a list that
will inevitably be incomplete, I'd like to thank *everybody* who
contributed in any way, big or small, with coding, testing, feedback
and/or documentation over the multi-year process of getting this into
trunk.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14254 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
1 parent fa2159f commit 0fcb09455729113f64a9873ca40bffd009b9bc5f @freakboy3742 freakboy3742 committed Oct 18, 2010
Showing with 5,029 additions and 83 deletions.
  1. +9 −0 django/views/generic/__init__.py
  2. +190 −0 django/views/generic/base.py
  3. +7 −1 django/views/generic/create_update.py
  4. +7 −0 django/views/generic/date_based.py
  5. +595 −0 django/views/generic/dates.py
  6. +142 −0 django/views/generic/detail.py
  7. +249 −0 django/views/generic/edit.py
  8. +138 −0 django/views/generic/list.py
  9. +7 −0 django/views/generic/list_detail.py
  10. +6 −0 django/views/generic/simple.py
  11. +4 −2 docs/index.txt
  12. +9 −0 docs/internals/deprecation.txt
  13. +81 −77 docs/intro/tutorial04.txt
  14. +1,391 −0 docs/ref/class-based-views.txt
  15. +1 −1 docs/ref/generic-views.txt
  16. +9 −1 docs/ref/index.txt
  17. +29 −0 docs/releases/1.3.txt
  18. +535 −0 docs/topics/class-based-views.txt
  19. +127 −0 docs/topics/generic-views-migration.txt
  20. +9 −1 docs/topics/index.txt
  21. 0 tests/regressiontests/generic_views/__init__.py
  22. +233 −0 tests/regressiontests/generic_views/base.py
  23. +352 −0 tests/regressiontests/generic_views/dates.py
  24. +71 −0 tests/regressiontests/generic_views/detail.py
  25. +233 −0 tests/regressiontests/generic_views/edit.py
  26. +47 −0 tests/regressiontests/generic_views/fixtures/generic-views-test-data.json
  27. +11 −0 tests/regressiontests/generic_views/forms.py
  28. +129 −0 tests/regressiontests/generic_views/list.py
  29. +41 −0 tests/regressiontests/generic_views/models.py
  30. +1 −0 tests/regressiontests/generic_views/templates/generic_views/about.html
  31. +1 −0 tests/regressiontests/generic_views/templates/generic_views/apple_detail.html
  32. +1 −0 tests/regressiontests/generic_views/templates/generic_views/artist_detail.html
  33. +1 −0 tests/regressiontests/generic_views/templates/generic_views/artist_form.html
  34. +1 −0 tests/regressiontests/generic_views/templates/generic_views/author_confirm_delete.html
  35. +1 −0 tests/regressiontests/generic_views/templates/generic_views/author_detail.html
  36. +1 −0 tests/regressiontests/generic_views/templates/generic_views/author_form.html
  37. +3 −0 tests/regressiontests/generic_views/templates/generic_views/author_list.html
  38. +3 −0 tests/regressiontests/generic_views/templates/generic_views/author_objects.html
  39. +1 −0 tests/regressiontests/generic_views/templates/generic_views/author_view.html
  40. +1 −0 tests/regressiontests/generic_views/templates/generic_views/book_archive.html
  41. +1 −0 tests/regressiontests/generic_views/templates/generic_views/book_archive_day.html
  42. +1 −0 tests/regressiontests/generic_views/templates/generic_views/book_archive_month.html
  43. +1 −0 tests/regressiontests/generic_views/templates/generic_views/book_archive_week.html
  44. +1 −0 tests/regressiontests/generic_views/templates/generic_views/book_archive_year.html
  45. +1 −0 tests/regressiontests/generic_views/templates/generic_views/book_detail.html
  46. +3 −0 tests/regressiontests/generic_views/templates/generic_views/book_list.html
  47. +1 −0 tests/regressiontests/generic_views/templates/generic_views/confirm_delete.html
  48. +1 −0 tests/regressiontests/generic_views/templates/generic_views/detail.html
  49. +1 −0 tests/regressiontests/generic_views/templates/generic_views/form.html
  50. +3 −0 tests/regressiontests/generic_views/templates/generic_views/list.html
  51. +1 −0 tests/regressiontests/generic_views/templates/generic_views/page_template.html
  52. +1 −0 tests/regressiontests/generic_views/templates/registration/login.html
  53. +5 −0 tests/regressiontests/generic_views/tests.py
  54. +186 −0 tests/regressiontests/generic_views/urls.py
  55. +145 −0 tests/regressiontests/generic_views/views.py
@@ -1,3 +1,12 @@
+from django.views.generic.base import View, TemplateView, RedirectView
+from django.views.generic.dates import (ArchiveIndexView, YearArchiveView, MonthArchiveView,
+ WeekArchiveView, DayArchiveView, TodayArchiveView,
+ DateDetailView)
+from django.views.generic.detail import DetailView
+from django.views.generic.edit import CreateView, UpdateView, DeleteView
+from django.views.generic.list import ListView
+
+
class GenericViewError(Exception):
"""A problem in a generic view."""
pass
@@ -0,0 +1,190 @@
+import copy
+from django import http
+from django.core.exceptions import ImproperlyConfigured
+from django.template import RequestContext, loader
+from django.utils.translation import ugettext_lazy as _
+from django.utils.functional import update_wrapper
+from django.utils.log import getLogger
+
+logger = getLogger('django.request')
+
+class classonlymethod(classmethod):
+ def __get__(self, instance, owner):
+ if instance is not None:
+ raise AttributeError("This method is available only on the view class.")
+ return super(classonlymethod, self).__get__(instance, owner)
+
+class View(object):
+ """
+ Intentionally simple parent class for all views. Only implements
+ dispatch-by-method and simple sanity checking.
+ """
+
+ http_method_names = ['get', 'post', 'put', 'delete', 'head', 'options', 'trace']
+
+ def __init__(self, **kwargs):
+ """
+ Constructor. Called in the URLconf; can contain helpful extra
+ keyword arguments, and other things.
+ """
+ # Go through keyword arguments, and either save their values to our
+ # instance, or raise an error.
+ for key, value in kwargs.iteritems():
+ setattr(self, key, value)
+
+ @classonlymethod
+ def as_view(cls, **initkwargs):
+ """
+ Main entry point for a request-response process.
+ """
+ # sanitize keyword arguments
+ for key in initkwargs:
+ if key in cls.http_method_names:
+ raise TypeError(u"You tried to pass in the %s method name as a "
+ u"keyword argument to %s(). Don't do that."
+ % (key, cls.__name__))
+ if not hasattr(cls, key):
+ raise TypeError(u"%s() received an invalid keyword %r" % (
+ cls.__name__, key))
+
+ def view(request, *args, **kwargs):
+ self = cls(**initkwargs)
+ return self.dispatch(request, *args, **kwargs)
+
+ # take name and docstring from class
+ update_wrapper(view, cls, updated=())
+
+ # and possible attributes set by decorators
+ # like csrf_exempt from dispatch
+ update_wrapper(view, cls.dispatch, assigned=())
+ return view
+
+ def dispatch(self, request, *args, **kwargs):
+ # Try to dispatch to the right method for that; if it doesn't exist,
+ # defer to the error handler. Also defer to the error handler if the
+ # request method isn't on the approved list.
+ if request.method.lower() in self.http_method_names:
+ handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
+ else:
+ handler = self.http_method_not_allowed
+ self.request = request
+ self.args = args
+ self.kwargs = kwargs
+ return handler(request, *args, **kwargs)
+
+ def http_method_not_allowed(self, request, *args, **kwargs):
+ allowed_methods = [m for m in self.http_method_names if hasattr(self, m)]
+ return http.HttpResponseNotAllowed(allowed_methods)
+
+
+class TemplateResponseMixin(object):
+ """
+ A mixin that can be used to render a template.
+ """
+ template_name = None
+
+ def render_to_response(self, context):
+ """
+ Returns a response with a template rendered with the given context.
+ """
+ return self.get_response(self.render_template(context))
+
+ def get_response(self, content, **httpresponse_kwargs):
+ """
+ Construct an `HttpResponse` object.
+ """
+ return http.HttpResponse(content, **httpresponse_kwargs)
+
+ def render_template(self, context):
+ """
+ Render the template with a given context.
+ """
+ context_instance = self.get_context_instance(context)
+ return self.get_template().render(context_instance)
+
+ def get_context_instance(self, context):
+ """
+ Get the template context instance. Must return a Context (or subclass)
+ instance.
+ """
+ return RequestContext(self.request, context)
+
+ def get_template(self):
+ """
+ Get a ``Template`` object for the given request.
+ """
+ names = self.get_template_names()
+ if not names:
+ raise ImproperlyConfigured(u"'%s' must provide template_name."
+ % self.__class__.__name__)
+ return self.load_template(names)
+
+ def get_template_names(self):
+ """
+ Return a list of template names to be used for the request. Must return
+ a list. May not be called if get_template is overridden.
+ """
+ if self.template_name is None:
+ return []
+ else:
+ return [self.template_name]
+
+ def load_template(self, names):
+ """
+ Load a list of templates using the default template loader.
+ """
+ return loader.select_template(names)
+
+
+class TemplateView(TemplateResponseMixin, View):
+ """
+ A view that renders a template.
+ """
+ def get_context_data(self, **kwargs):
+ return {
+ 'params': kwargs
+ }
+
+ def get(self, request, *args, **kwargs):
+ context = self.get_context_data(**kwargs)
+ return self.render_to_response(context)
+
+
+class RedirectView(View):
+ """
+ A view that provides a redirect on any GET request.
+ """
+ permanent = True
+ url = None
+ query_string = False
+
+ def get_redirect_url(self, **kwargs):
+ """
+ Return the URL redirect to. Keyword arguments from the
+ URL pattern match generating the redirect request
+ are provided as kwargs to this method.
+ """
+ if self.url:
+ args = self.request.META["QUERY_STRING"]
+ if args and self.query_string:
+ url = "%s?%s" % (self.url, args)
+ else:
+ url = self.url
+ return url % kwargs
+ else:
+ return None
+
+ def get(self, request, *args, **kwargs):
+ url = self.get_redirect_url(**kwargs)
+ if url:
+ if self.permanent:
+ return http.HttpResponsePermanentRedirect(url)
+ else:
+ return http.HttpResponseRedirect(url)
+ else:
+ logger.warning('Gone: %s' % self.request.path,
+ extra={
+ 'status_code': 410,
+ 'request': self.request
+ })
+ return http.HttpResponseGone()
@@ -8,6 +8,12 @@
from django.views.generic import GenericViewError
from django.contrib import messages
+import warnings
+warnings.warn(
+ 'Function-based generic views have been deprecated; use class-based views instead.',
+ PendingDeprecationWarning
+)
+
def apply_extra_context(extra_context, context):
"""
@@ -111,7 +117,7 @@ def create_object(request, model=None, template_name=None,
form = form_class(request.POST, request.FILES)
if form.is_valid():
new_object = form.save()
-
+
msg = ugettext("The %(verbose_name)s was created successfully.") %\
{"verbose_name": model._meta.verbose_name}
messages.success(request, msg, fail_silently=True)
@@ -7,6 +7,13 @@
from django.db.models.fields import DateTimeField
from django.http import Http404, HttpResponse
+import warnings
+warnings.warn(
+ 'Function-based generic views have been deprecated; use class-based views instead.',
+ PendingDeprecationWarning
+)
+
+
def archive_index(request, queryset, date_field, num_latest=15,
template_name=None, template_loader=loader,
extra_context=None, allow_empty=True, context_processors=None,
Oops, something went wrong. Retry.

0 comments on commit 0fcb094

Please sign in to comment.