Created
October 17, 2012 12:57
-
-
Save danfairs/3905379 to your computer and use it in GitHub Desktop.
Reverse filtering on ForeignKey/ToManyFields
This file contains hidden or 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 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