Skip to content

Instantly share code, notes, and snippets.

@danfairs
Created October 17, 2012 12:57
Show Gist options
  • Save danfairs/3905379 to your computer and use it in GitHub Desktop.
Save danfairs/3905379 to your computer and use it in GitHub Desktop.
Reverse filtering on ForeignKey/ToManyFields
from tastypie.fields import NOT_PROVIDED, ToManyField
class OrmToManyField(ToManyField):
def __init__(self, to, attribute, related_name=None, default=NOT_PROVIDED,
null=False, blank=False, readonly=False, full=False,
unique=False, help_text=None, orm_attribute=None):
super(OrmToManyField, self).__init__(
to, attribute, related_name=related_name, default=default,
null=null, blank=blank, readonly=readonly, full=full,
unique=unique, help_text=help_text
)
self.orm_attribute = orm_attribute
# Presented as a function, as we have a couple of Resource base classes which just invoke this directly
def _orm_check_filtering(cls, self, field_name, filter_type='exact',
filter_bits=None):
if filter_bits is None:
filter_bits = []
if not field_name in self._meta.filtering:
raise InvalidFilterError(
"The '%s' field does not allow filtering." % field_name
)
# Check to see if it's an allowed lookup type.
if not self._meta.filtering[field_name] in (ALL, ALL_WITH_RELATIONS):
# Must be an explicit whitelist.
if not filter_type in self._meta.filtering[field_name]:
raise InvalidFilterError(
"'%s' is not an allowed filter on the '%s' field." % (
filter_type, field_name
)
)
if self.fields[field_name].attribute is None:
raise InvalidFilterError(
"The '%s' field has no 'attribute' for searching with." % (
field_name
)
)
# The attribute that needs to be passed to the orm may be different
# from the attribute specified by the 'attribute' attribute.
try:
attribute = self.fields[field_name].orm_attribute
except AttributeError:
attribute = self.fields[field_name].attribute
# Check to see if it's a relational lookup and if that's allowed.
if len(filter_bits):
if not getattr(self.fields[field_name], 'is_related', False):
raise InvalidFilterError(
"The '%s' field does not support relations." % field_name
)
if not self._meta.filtering[field_name] == ALL_WITH_RELATIONS:
raise InvalidFilterError(
"Lookups are not allowed more than one level "
"deep on the '%s' field." % field_name
)
# Recursively descend through the remaining lookups in the filter,
# if any. We should ensure that all along the way, we're allowed
# to filter on that field by the related resource.
related_resource = self.fields[field_name].get_related_resource(
None
)
return [attribute] + related_resource.check_filtering(
filter_bits[0],
filter_type,
filter_bits[1:]
)
return [attribute]
class SeriesResource(BaseSeriesResource):
episodes = OrmToManyField(
'ss.api.timecode.episode.SeriesEpisodeResource',
'episode_set',
orm_attribute='episode',
full=True
)
class Meta(BaseSeriesResource.Meta):
filtering = {
'episodes': ALL_WITH_RELATIONS,
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment