Last active
August 29, 2015 14:05
-
-
Save niwinz/e49dcb8ef8fb4cdfe01d to your computer and use it in GitHub Desktop.
Generic Views
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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