def _add_metrics(self, queryset_or_obj, method): """Parse the ?metric[METRIC]=PERIOD query param, validate it, and run ``method`` for each each requested object. This is used to share code between add_metric_to_object and get_metrics_queryset. """ metrics_requested = self.parse_metric_query_params( self.request.query_params) if metrics_requested: metric_map = self.metric_map for metric, period in metrics_requested.items(): if metric not in metric_map: raise InvalidQueryStringError( "Invalid metric in query string: '{}'".format(metric), parameter='metrics') if period not in self.VALID_METRIC_PERIODS: raise InvalidQueryStringError( "Invalid period for metric: '{}'".format(period), parameter='metrics') metric_class = metric_map[metric] if period == 'total': after = None else: after = timezone.now() - self.TIMEDELTA_MAP[period] queryset_or_obj = method(queryset_or_obj, metric_class, metric, after) return queryset_or_obj
def process_related_counts_parameters(self, params, value): """ Processes related_counts parameter. Can either be a True/False value for fetching counts on all fields, or a comma-separated list for specifying individual fields. Ensures field for which we are requesting counts is a relationship field. """ if utils.is_truthy(params) or utils.is_falsy(params): return params field_counts_requested = [val for val in params.split(',')] countable_fields = {field for field in self.parent.fields if getattr(self.parent.fields[field], 'json_api_link', False) or getattr(getattr(self.parent.fields[field], 'field', None), 'json_api_link', None)} for count_field in field_counts_requested: # Some fields will hide relationships, e.g. HideIfWithdrawal # Ignore related_counts for these fields fetched_field = self.parent.fields.get(count_field) hidden = fetched_field and isinstance(fetched_field, HideIfWithdrawal) and getattr(value, 'is_retracted', False) if not hidden and count_field not in countable_fields: raise InvalidQueryStringError( detail="Acceptable values for the related_counts query param are 'true', 'false', or any of the relationship fields; got '{0}'".format( params), parameter='related_counts' ) return field_counts_requested
def get_meta_information(self, meta_data, value): """ For retrieving meta values, otherwise returns {} """ meta = {} for key in meta_data or {}: if key == 'count': show_related_counts = self.context['request'].query_params.get( 'related_counts', False) if utils.is_truthy(show_related_counts): meta[key] = website_utils.rapply(meta_data[key], _url_val, obj=value, serializer=self.parent) elif utils.is_falsy(show_related_counts): continue if not utils.is_truthy(show_related_counts): raise InvalidQueryStringError( detail= "Acceptable values for the related_counts query param are 'true' or 'false'; got '{0}'" .format(show_related_counts), parameter='related_counts') else: meta[key] = website_utils.rapply(meta_data[key], _url_val, obj=value, serializer=self.parent) return meta
def to_representation(self, value): """ Returns nested dictionary in format {'links': {'self.link_type': ... } If no meta information, self.link_type is equal to a string containing link's URL. Otherwise, the link is represented as a links object with 'href' and 'meta' members. """ url = super(JSONAPIHyperlinkedIdentityField, self).to_representation(value) meta = {} for key in self.meta or {}: if key == 'count': show_related_counts = self.context['request'].query_params.get( 'related_counts', False) if utils.is_truthy(show_related_counts): meta[key] = _rapply(self.meta[key], _url_val, obj=value, serializer=self.parent) elif utils.is_falsy(show_related_counts): continue else: raise InvalidQueryStringError( detail= "Acceptable values for the related_counts query param are 'true' or 'false'; got '{0}'" .format(show_related_counts), parameter='related_counts') return {'links': {self.link_type: {'href': url, 'meta': meta}}}
def to_representation(self, obj, envelope='data'): """Serialize to final representation. :param obj: Object to be serialized. :param envelope: Key for resource object. """ ret = {} meta = getattr(self, 'Meta', None) type_ = getattr(meta, 'type_', None) assert type_ is not None, 'Must define Meta.type_' data = { 'id': '', 'type': type_, 'attributes': {}, 'relationships': {}, 'embeds': {}, 'links': {}, } embeds = self.context.get('embed', {}) context_envelope = self.context.get('envelope', envelope) if context_envelope == 'None': context_envelope = None enable_esi = self.context.get('enable_esi', False) is_anonymous = is_anonymized(self.context['request']) to_be_removed = set() if is_anonymous and hasattr(self, 'non_anonymized_fields'): # Drop any fields that are not specified in the `non_anonymized_fields` variable. allowed = set(self.non_anonymized_fields) existing = set(self.fields.keys()) to_be_removed = existing - allowed fields = [field for field in self.fields.values() if not field.write_only and field.field_name not in to_be_removed] invalid_embeds = self.invalid_embeds(fields, embeds) invalid_embeds = invalid_embeds - to_be_removed if invalid_embeds: raise InvalidQueryStringError(parameter='embed', detail='The following fields are not embeddable: {}'.format( ', '.join(invalid_embeds))) for field in fields: try: attribute = field.get_attribute(obj) except SkipField: continue nested_field = getattr(field, 'field', None) if attribute is None: # We skip `to_representation` for `None` values so that # fields do not have to explicitly deal with that case. data['attributes'][field.field_name] = None else: try: representation = field.to_representation(attribute) except SkipField: continue if getattr(field, 'json_api_link', False) or getattr(nested_field, 'json_api_link', False): # If embed=field_name is appended to the query string or 'always_embed' flag is True, directly embed the # results in addition to adding a relationship link if embeds and (field.field_name in embeds or getattr(field, 'always_embed', None)): if enable_esi: try: result = field.to_esi_representation(attribute, envelope=envelope) except SkipField: continue else: try: # If a field has an empty representation, it should not be embedded. result = self.context['embed'][field.field_name](obj) except SkipField: result = None if result: data['embeds'][field.field_name] = result else: data['embeds'][field.field_name] = {'error': 'This field is not embeddable.'} try: if not (is_anonymous and hasattr(field, 'view_name') and field.view_name in self.views_to_hide_if_anonymous): data['relationships'][field.field_name] = representation except SkipField: continue elif field.field_name == 'id': data['id'] = representation elif field.field_name == 'links': data['links'] = representation else: data['attributes'][field.field_name] = representation if not data['relationships']: del data['relationships'] if not data['embeds']: del data['embeds'] if context_envelope: ret[context_envelope] = data if is_anonymous: ret['meta'] = {'anonymous': True} else: ret = data return ret
def to_representation(self, obj, envelope='data'): """Serialize to final representation. :param obj: Object to be serialized. :param envelope: Key for resource object. """ ret = {} meta = getattr(self, 'Meta', None) type_ = getattr(meta, 'type_', None) assert type_ is not None, 'Must define Meta.type_' data = collections.OrderedDict([ ('id', ''), ('type', type_), ('attributes', collections.OrderedDict()), ('relationships', collections.OrderedDict()), ('embeds', {}), ('links', {}), ]) embeds = self.context.get('embed', {}) fields = [ field for field in self.fields.values() if not field.write_only ] invalid_embeds = self.invalid_embeds(fields, embeds) if invalid_embeds: raise InvalidQueryStringError( parameter='embed', detail='The following fields are not embeddable: {}'.format( ', '.join(invalid_embeds))) for field in fields: try: attribute = field.get_attribute(obj) except SkipField: continue nested_field = getattr(field, 'field', None) if getattr(field, 'json_api_link', False) or getattr( nested_field, 'json_api_link', False): # If embed=field_name is appended to the query string or 'always_embed' flag is True, directly embed the # results rather than adding a relationship link if attribute is None: continue if embeds and (field.field_name in embeds or getattr(field, 'always_embed', None)): result = self.context['embed'][field.field_name](obj) if result: data['embeds'][field.field_name] = result else: try: data['relationships'][ field.field_name] = field.to_representation( attribute) except SkipField: continue elif field.field_name == 'id': data['id'] = field.to_representation(attribute) elif field.field_name == 'links': data['links'] = field.to_representation(attribute) else: if attribute is None: # We skip `to_representation` for `None` values so that # fields do not have to explicitly deal with that case. data['attributes'][field.field_name] = None else: data['attributes'][ field.field_name] = field.to_representation(attribute) if not data['relationships']: del data['relationships'] if not data['embeds']: del data['embeds'] if envelope: ret[envelope] = data else: ret = data return ret