Skip to content

Instantly share code, notes, and snippets.

@niwinz
Last active August 29, 2015 14:05
Show Gist options
  • Save niwinz/e49dcb8ef8fb4cdfe01d to your computer and use it in GitHub Desktop.
Save niwinz/e49dcb8ef8fb4cdfe01d to your computer and use it in GitHub Desktop.
Generic Views
from django.core.urlresolvers import reverse
from django.core.paginator import Paginator
from django.core.paginator import InvalidPage
from django.core.paginator import EmptyPage
from django.core.paginator import PageNotAnInteger
from django.views.generic import View
from django.template import RequestContext
from django.template.loader import render_to_string
from django.shortcuts import render_to_response
from . import http
from . import exceptions as exc
class GenericView(View):
response_cls = http.HttpResponse
content_type = "text/html"
permissions = ()
def handle_exception(self, exception):
"""
Ad-hoc exception handling for all derived views.
"""
ctx = {"exception": exception}
if isinstance(exception, exc.RedirectRequired):
return self.redirect(url=exception.detail)
elif isinstance(exception, exc.IntegrityError):
return http.HttpConflict(exception)
return exception
def init(self, request, *args, **kwargs):
pass
def get_context_data(self):
context = {"view": self}
context.update(self.kwargs)
return context
def __handle_permissions(self):
for fn in self.permissions:
result = fn(self)
if isinstance(result, http.HttpResponse):
return result
elif result == False:
raise exc.PermissionDenied("Forbidden")
if hasattr(self, "handle_permissions"):
return self.handle_permissions()
def dispatch(self, request, *args, **kwargs):
try:
self.init(request, *args, **kwargs)
result = self.__handle_permissions()
if isinstance(result, http.HttpResponse):
return result
return super().dispatch(request, *args, **kwargs)
except Exception as e:
response = self.handle_exception(e)
if isinstance(response, Exception):
raise
return response
def redirect(self, reverseurl=None, *, url=None, args=None, kwargs=None):
"""
Simple redirect helper.
"""
if not url:
url = reverse(reverseurl, args=args, kwargs=kwargs)
return http.HttpRedirect(url)
def render(self, template=None, context=None, data=None,
response_cls=None, content_type=None, status_code=None):
output_data = data or b""
if template:
_context = self.get_context_data()
_context.update(context or {})
context_instance = RequestContext(self.request)
output_data = render_to_string(template, _context,
context_instance=context_instance)
if content_type is None:
content_type = self.content_type
if not response_cls:
response_cls = self.response_cls
response = response_cls(output_data, content_type=content_type)
if status_code:
response.status_code = status_code
return response
class FormViewMixin(object):
# Simple helers for dealing with forms
def get_form_cls(self):
if not hasattr(self, "form_cls"):
raise exc.InternalError("form_cls attribute not defined")
return self.form_cls
def get_form(self, initial=None, **kwargs):
form_cls = self.get_form_cls()
if self.request.method == "POST":
return form_cls(self.request.POST, self.request.FILES, initial=initial, **kwargs)
return form_cls(initial=initial, **kwargs)
PAGINATOR_NUMBERS_AFTER_CURRENT = 3
PAGINATOR_NUMBERS_BEFORE_CURRENT = 3
PAGINATOR_NUMBERS_AT_BEGIN = 2
PAGINATOR_NUMBERS_AT_END = 1
class PaginatorMixin(object):
page_param = "page"
page_size = 30
def paginate(self, queryset, page_param='page', page_size=None, raise_when_overflow=False):
"""
Updates context with a member called 'page'. This member contains the queryset paginated.
:param QuerySet queryset: objects queryset to paginate
:param dict context: base context to update
:param str page_param: request get param used for page number
:return: context updated with pagination info
:rtype: dict
"""
if page_size is None:
page_size = self.page_size
paginator = Paginator(queryset, page_size)
page_num = self.request.GET.get(page_param) or 1
try:
page = paginator.page(page_num)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
page = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
page = paginator.page(paginator.num_pages)
if raise_when_overflow:
raise exc.NotFound("page not found")
paginator.after_current = PAGINATOR_NUMBERS_AFTER_CURRENT
paginator.before_current = PAGINATOR_NUMBERS_BEFORE_CURRENT
paginator.at_begin = PAGINATOR_NUMBERS_AT_BEGIN
paginator.at_end = PAGINATOR_NUMBERS_AT_END
return page
class AjaxMixin(object):
def handle_exception(self, exception):
"""
Ad-hoc exception handling for all derived views.
"""
ctx = {"_error": str(exception)}
if isinstance(exception, exc.WrongArguments):
return self.render_json(ctx, status_code=http.HTTP_400_BAD_REQUEST)
return super().handle_exception(exception)
def render_json(self, data, *, content_type=None, status_code=None):
if content_type is None:
content_type = "application/json"
return self.render(data=to_json(data),
content_type=content_type,
status_code=status_code)
class GenericTemplateView(GenericView):
tmpl_name = None
def get(self, request, *args, **kwargs):
if self.tmpl_name is None:
raise ValueError("tmpl_name attr must be a valid template name")
return self.render(self.tmpl_name)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment