1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
|
from django.conf import settings
from django.urls import path
from django.core.exceptions import ObjectDoesNotExist
from django.core.paginator import Paginator
from django.http import HttpResponse, Http404
from django.views.decorators.csrf import csrf_exempt
from .constants import OK, NO_CONTENT
from .exceptions import NotFound, BadRequest
from .resources import Resource
class DjangoResource(Resource):
"""
A Django-specific ``Resource`` subclass.
Doesn't require any special configuration, but helps when working in a
Django environment.
"""
def serialize_list(self, data):
if data is None:
return super(DjangoResource, self).serialize_list(data)
if getattr(self, 'paginate', False):
page_size = getattr(self, 'page_size', getattr(settings, 'RESTLESS_PAGE_SIZE', 10))
paginator = Paginator(data, page_size)
page_number = self.request.GET.get('p', 1)
if page_number not in paginator.page_range:
raise BadRequest('Invalid page number')
self.page = paginator.page(page_number)
data = self.page.object_list
return super(DjangoResource, self).serialize_list(data)
def wrap_list_response(self, data):
response_dict = super(DjangoResource, self).wrap_list_response(data)
if hasattr(self, 'page'):
next_page = self.page.has_next() and self.page.next_page_number() or None
previous_page = self.page.has_previous() and self.page.previous_page_number() or None
response_dict['pagination'] = {
'num_pages': self.page.paginator.num_pages,
'count': self.page.paginator.count,
'page': self.page.number,
'start_index': self.page.start_index(),
'end_index': self.page.end_index(),
'next_page': next_page,
'previous_page': previous_page,
'per_page': self.page.paginator.per_page,
}
return response_dict
# Because Django.
@classmethod
def as_list(self, *args, **kwargs):
return csrf_exempt(super(DjangoResource, self).as_list(*args, **kwargs))
@classmethod
def as_detail(self, *args, **kwargs):
return csrf_exempt(super(DjangoResource, self).as_detail(*args, **kwargs))
def is_debug(self):
return settings.DEBUG
def build_response(self, data, status=OK):
if status == NO_CONTENT:
# Avoid crashing the client when it tries to parse nonexisting JSON.
content_type = 'text/plain'
else:
content_type = 'application/json'
resp = HttpResponse(data, content_type=content_type, status=status)
return resp
def build_error(self, err):
# A bit nicer behavior surrounding things that don't exist.
if isinstance(err, (ObjectDoesNotExist, Http404)):
err = NotFound(msg=str(err))
return super(DjangoResource, self).build_error(err)
@classmethod
def build_url_name(cls, name, name_prefix=None):
"""
Given a ``name`` & an optional ``name_prefix``, this generates a name
for a URL.
:param name: The name for the URL (ex. 'detail')
:type name: string
:param name_prefix: (Optional) A prefix for the URL's name (for
resolving). The default is ``None``, which will autocreate a prefix
based on the class name. Ex: ``BlogPostResource`` ->
``api_blog_post_list``
:type name_prefix: string
:returns: The final name
:rtype: string
"""
if name_prefix is None:
name_prefix = 'api_{}'.format(
cls.__name__.replace('Resource', '').lower()
)
name_prefix = name_prefix.rstrip('_')
return '_'.join([name_prefix, name])
@classmethod
def urls(cls, name_prefix=None):
"""
A convenience method for hooking up the URLs.
This automatically adds a list & a detail endpoint to your URLconf.
:param name_prefix: (Optional) A prefix for the URL's name (for
resolving). The default is ``None``, which will autocreate a prefix
based on the class name. Ex: ``BlogPostResource`` ->
``api_blogpost_list``
:type name_prefix: string
:returns: A list of ``url`` objects for ``include(...)``
"""
return [
path('', cls.as_list(), name=cls.build_url_name('list', name_prefix)),
path('<pk>/', cls.as_detail(), name=cls.build_url_name('detail', name_prefix)),
]
|