Created
April 27, 2015 05:01
-
-
Save beardedeagle/3b438db5917289299dc9 to your computer and use it in GitHub Desktop.
Field selection mixins for Tastypie (dehydrate, full_dehydrate and db level filter)
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
################################################################### | |
# mixin that utilizes dehydrate to implement field selection # | |
# Tested with python 2.7.5, django 1.8 and django-tastypie 0.12.1 # | |
################################################################### | |
class fieldSelectMixin(object): | |
""" | |
Mixin to allow field selection, dehydrate method. | |
""" | |
def dehydrate(self, bundle): | |
selectedFields = bundle.request.GET.get('fields') | |
debug = bundle.request.GET.get('debug', False) | |
if selectedFields: | |
selectedFields = selectedFields.split(',') | |
removedFields = [field for field in bundle.data.keys() if field not in selectedFields] | |
for field in removedFields: | |
del bundle.data[field] | |
if debug: | |
bundle.data['selectedFields'] = [field for field in selectedFields if field in bundle.data.keys()] | |
bundle.data['removedFields'] = removedFields | |
return bundle | |
################################################################### | |
# mixin that utilizes full_dehydrate to implement field selection # | |
# Tested with python 2.7.5, django 1.8 and django-tastypie 0.12.1 # | |
################################################################### | |
class fieldSelectMixin(object): | |
""" | |
Mixin to allow field selection, full_dehydrate method. | |
""" | |
def full_dehydrate(self, bundle, for_list=False): | |
""" | |
Given a bundle with an object instance, extract the information from it | |
to populate the resource. | |
""" | |
use_in = ['all', 'list' if for_list else 'detail'] | |
# Get values if fields is set in query | |
selectedFields = bundle.request.GET.get('fields') | |
# If selectedFields has data turn it into a list | |
if selectedFields: | |
selectedFields = selectedFields.split(',') | |
# Dehydrate each field. | |
for field_name, field_object in self.fields.items(): | |
# If it's not for use in this mode, skip | |
field_use_in = getattr(field_object, 'use_in', 'all') | |
if callable(field_use_in): | |
if not field_use_in(bundle): | |
continue | |
else: | |
if field_use_in not in use_in: | |
continue | |
# If fields is set in query paramaters filter field_name | |
# and discard any not in selectedFields | |
if selectedFields: | |
if field_name not in selectedFields: | |
continue | |
# A touch leaky but it makes URI resolution work. | |
if getattr(field_object, 'dehydrated_type', None) == 'related': | |
field_object.api_name = self._meta.api_name | |
field_object.resource_name = self._meta.resource_name | |
bundle.data[field_name] = field_object.dehydrate(bundle, for_list=for_list) | |
# Check for an optional method to do further dehydration. | |
method = getattr(self, "dehydrate_%s" % field_name, None) | |
if method: | |
bundle.data[field_name] = method(bundle) | |
bundle = self.dehydrate(bundle) | |
return bundle | |
################################################################### | |
# mixin that performs field selection at the db level # | |
# Tested with python 2.7.5, django 1.8 and django-tastypie 0.12.1 # | |
################################################################### | |
class fieldSelectMixin(object): | |
""" | |
Mixin to allow field selection, db level filter method. | |
Tested with python 2.7.5, django 1.8 and django-tastypie 0.12.1. | |
""" | |
def select_fields(self, bundle, queryset): | |
""" | |
Given an optional field list in GET query, tells the model queryset | |
to only select a list of fields from db. | |
""" | |
if hasattr(bundle.request, 'GET'): | |
selectedFields = bundle.request.GET.get('fields') | |
# If selectedFields has data turn it into a list | |
if selectedFields: | |
selectedFields = selectedFields.split(',') | |
queryset = queryset.only(*selectedFields) | |
return queryset | |
def full_dehydrate(self, bundle, for_list=False): | |
""" | |
Given a bundle with an object instance, extract the information from it | |
to populate the resource. | |
""" | |
use_in = ['all', 'list' if for_list else 'detail'] | |
field_list = self.fields.items() | |
# Get values if fields is set in query | |
if hasattr(bundle.request, 'GET'): | |
selectedFields = bundle.request.GET.get('fields') | |
# If selectedFields has data turn it into a list | |
if selectedFields: | |
selectedFields = selectedFields.split(',') | |
# Iterate only on the selected fields | |
field_list = [(f_n, f_o) for f_n, f_o in field_list if f_n in selectedFields] | |
# Dehydrate each field. | |
for field_name, field_object in field_list: | |
# If it's not for use in this mode, skip | |
field_use_in = getattr(field_object, 'use_in', 'all') | |
if callable(field_use_in): | |
if not field_use_in(bundle): | |
continue | |
else: | |
if field_use_in not in use_in: | |
continue | |
# A touch leaky but it makes URI resolution work. | |
if getattr(field_object, 'dehydrated_type', None) == 'related': | |
field_object.api_name = self._meta.api_name | |
field_object.resource_name = self._meta.resource_name | |
bundle.data[field_name] = field_object.dehydrate(bundle, for_list=for_list) | |
# Check for an optional method to do further dehydration. | |
method = getattr(self, "dehydrate_%s" % field_name, None) | |
if method: | |
bundle.data[field_name] = method(bundle) | |
bundle = self.dehydrate(bundle) | |
return bundle | |
def obj_get_list(self, bundle, **kwargs): | |
""" | |
Fetches the list of objects available on the resource. | |
This needs to be implemented at the user level. | |
``ModelResource`` includes a full working version specific to Django's | |
``Models``. | |
""" | |
filters = {} | |
if hasattr(bundle.request, 'GET'): | |
# Grab a mutable copy. | |
filters = bundle.request.GET.copy() | |
# Update with the provided kwargs. | |
filters.update(kwargs) | |
applicable_filters = self.build_filters(filters=filters) | |
try: | |
objects = self.select_fields(bundle, self.apply_filters(bundle.request, applicable_filters)) | |
return self.authorized_read_list(objects, bundle) | |
except ValueError: | |
raise BadRequest("Invalid resource lookup (mismatched type).") | |
def obj_get(self, bundle, **kwargs): | |
""" | |
Fetches an individual object on the resource. | |
This needs to be implemented at the user level. If the object can not | |
be found, this should raise a ``NotFound`` exception. | |
``ModelResource`` includes a full working version specific to Django's | |
``Models``. | |
""" | |
try: | |
object_list = self.select_fields(bundle, self.get_object_list(bundle.request).filter(**kwargs)) | |
stringified_kwargs = ', '.join(["%s=%s" % (k, v) for k, v in kwargs.items()]) | |
if len(object_list) <= 0: | |
raise self._meta.object_class.DoesNotExist("Couldn't find an instance of '%s' which matched '%s'." % (self._meta.object_class.__name__, stringified_kwargs)) | |
elif len(object_list) > 1: | |
raise MultipleObjectsReturned("More than '%s' matched '%s'." % (self._meta.object_class.__name__, stringified_kwargs)) | |
bundle.obj = object_list[0] | |
self.authorized_read_detail(object_list, bundle) | |
return bundle.obj | |
except ValueError: | |
raise NotFound("Invalid resource lookup (mismatched type).") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment