Skip to content

Instantly share code, notes, and snippets.

@beardedeagle
Created April 27, 2015 05:01
Show Gist options
  • Save beardedeagle/3b438db5917289299dc9 to your computer and use it in GitHub Desktop.
Save beardedeagle/3b438db5917289299dc9 to your computer and use it in GitHub Desktop.
Field selection mixins for Tastypie (dehydrate, full_dehydrate and db level filter)
###################################################################
# 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