def get_field_info(self, field): field_info = super(LinkMetaData, self).get_field_info(field) field_info = OrderedDict() field_info['type'] = self.label_lookup[field] field_info['required'] = getattr(field, 'required', False) attrs = [ 'read_only', 'label', 'help_text', 'min_length', 'max_length', 'min_value', 'max_value' ] for attr in attrs: value = getattr(field, attr, None) if value is not None and value != '': field_info[attr] = force_text(value, strings_only=True) if getattr(field, 'child', None): field_info['child'] = self.get_field_info(field.child) elif getattr(field, 'fields', None): field_info['children'] = self.get_serializer_info(field) if not field_info.get('read_only') and hasattr(field, 'choices'): field_info['choices'] = [] for data, junk in field.choices.items(): if data and (data != 'None'): data = ast.literal_eval(data) field_info['choices'].append( { 'value': data['id'], 'display_name': force_text( data['name'], strings_only=True) } ) return field_info
def get_field_info(self, field): """ Given an instance of a serializer field, return a dictionary of metadata about it. """ field_info = OrderedDict() field_info['type'] = self.label_lookup[field] field_info['required'] = getattr(field, 'required', False) attrs = [ 'read_only', 'label', 'help_text', 'min_length', 'max_length', 'min_value', 'max_value' ] for attr in attrs: value = getattr(field, attr, None) if value is not None and value != '': field_info[attr] = force_text(value, strings_only=True) if not field_info.get('read_only') and hasattr(field, 'choices'): field_info['choices'] = [ { 'value': choice_value, 'display_name': force_text(choice_name, strings_only=True) } for choice_value, choice_name in field.choices.items() ] return field_info
class BindingDict(object): """ This dict-like object is used to store fields on a serializer. This ensures that whenever fields are added to the serializer we call `field.bind()` so that the `field_name` and `parent` attributes can be set correctly. """ def __init__(self, serializer): self.serializer = serializer self.fields = OrderedDict() def __setitem__(self, key, field): self.fields[key] = field field.bind(field_name=key, parent=self.serializer) def __getitem__(self, key): return self.fields[key] def __delitem__(self, key): del self.fields[key] def items(self): return self.fields.items() def keys(self): return self.fields.keys() def values(self): return self.fields.values()
def _merge_fields_and_pk(pk, fields): fields_and_pk = OrderedDict() fields_and_pk['pk'] = pk fields_and_pk[pk.name] = pk fields_and_pk.update(fields) return fields_and_pk
def get_field_info(self, field): """ Given an instance of a serializer field, return a dictionary of metadata about it. """ field_info = OrderedDict() field_info['type'] = self.label_lookup[field] field_info['required'] = getattr(field, 'required', False) attrs = [ 'read_only', 'label', 'help_text', 'min_length', 'max_length', 'min_value', 'max_value' ] for attr in attrs: value = getattr(field, attr, None) if value is not None and value != '': field_info[attr] = force_text(value, strings_only=True) if not field_info.get('read_only') and hasattr(field, 'choices'): field_info['choices'] = [{ 'value': choice_value, 'display_name': force_text(choice_name, strings_only=True) } for choice_value, choice_name in field.choices.items()] return field_info
def get_field_info(self, field): """ Custom get field info so extra parameters of custom fields are displayed as well. """ field_info = OrderedDict() field_info['type'] = self.label_lookup[field] field_info['required'] = getattr(field, 'required', False) attrs = [ 'read_only', 'label', 'help_text', 'min_length', 'max_length', 'min_value', 'max_value' ] # This is the custom part, load extra field attrs if necessary. if hasattr(field, 'extra_field_attrs'): attrs += field.extra_field_attrs for attr in attrs: value = getattr(field, attr, None) if value is not None and value != '': field_info[attr] = force_text(value, strings_only=True) if not field_info.get('read_only') and hasattr(field, 'choices'): field_info['choices'] = [ { 'value': choice_value, 'display_name': force_text(choice_name, strings_only=True) } for choice_value, choice_name in field.choices.items() ] return field_info
def __init__(self, choices, **kwargs): # Allow either single or paired choices style: # choices = [1, 2, 3] # choices = [(1, 'First'), (2, 'Second'), (3, 'Third')] pairs = [ isinstance(item, (list, tuple)) and len(item) == 2 for item in choices ] if all(pairs): self.choices = OrderedDict([(key, display_value) for key, display_value in choices]) else: self.choices = OrderedDict([(item, item) for item in choices]) # Map the string representation of choices to the underlying value. # Allows us to deal with eg. integer choices while supporting either # integer or string input, but still get the correct datatype out. self.choice_strings_to_values = dict([ (six.text_type(key), key) for key in self.choices.keys() ]) super(ChoiceField, self).__init__(**kwargs) if not self.required: self.choices[''] = '' self.choice_strings_to_values[''] = ''
def test_active_model_json_renderer(self): parents = [ParentModel.objects.create() for x in range(3)] children = [ ChildModel.objects.create(parent=parents[1], old_parent=parents[2]), ChildModel.objects.create(parent=parents[1], old_parent=parents[2]), ChildModel.objects.create(parent=parents[0], old_parent=parents[1]) ] obj = ChildSideloadSerializer(children, many=True).data expected = { 'child_models': [ OrderedDict([('id', c.id), ('parent_id', c.parent.id), ('old_parent_id', c.old_parent.id)]) for c in children ], 'parent_models': [ OrderedDict([('id', p.id), ('text', p.text), ('child_ids', p.child_ids), ('old_child_ids', p.old_child_ids)]) for p in parents ] } result = convert_to_active_model_json(obj) assert result == expected
def to_internal_value(self, data): """ Dict of native values <- Dict of primitive datatypes. """ ret = OrderedDict() errors = OrderedDict() fields = [ field for field in self.fields.values() if (not field.read_only) or (field.default is not empty) ] for field in fields: validate_method = getattr(self, 'validate_' + field.field_name, None) primitive_value = field.get_value(data) try: validated_value = field.run_validation(primitive_value) if validate_method is not None: validated_value = validate_method(validated_value) except ValidationError as exc: errors[field.field_name] = exc.detail except SkipField: pass else: set_value(ret, field.source_attrs, validated_value) if errors: raise ValidationError(errors) return ret
def get_initial(self): if self._initial_data is not None: return OrderedDict([ (field_name, field.get_value(self._initial_data)) for field_name, field in self.fields.items() if field.get_value(self._initial_data) is not empty and not field.read_only ]) return OrderedDict([(field.field_name, field.get_initial()) for field in self.fields.values() if not field.read_only])
def to_representation(self, instance): """ Overrides to nest the primary record and add sideloads. """ ret = OrderedDict() base_data = self.base_serializer.__class__( instance, many=True, context=self.context ).data ret[pluralize(self.base_key)] = base_data ret.update(self.get_sideload_objects(instance)) return ret
def to_representation(self, instance): """ Overrides the DRF method to add a root key and sideloads. """ # self.base_serializer.instance = instance base_result = self.base_serializer.data if self.is_nested: return base_result ret = OrderedDict() key = self.base_key ret[key] = base_result ret.update(self.get_sideload_objects(instance)) return ret
def test_compact(self): renderer = JSONRenderer() data = OrderedDict([('a', 1), ('b', 2)]) context = {'indent': 4} assert (renderer.render( data, renderer_context=context) == b'{\n "a": 1,\n "b": 2\n}')
class ChoiceField(Field): default_error_messages = { 'invalid_choice': _('`{input}` is not a valid choice.') } def __init__(self, choices, **kwargs): # Allow either single or paired choices style: # choices = [1, 2, 3] # choices = [(1, 'First'), (2, 'Second'), (3, 'Third')] pairs = [ isinstance(item, (list, tuple)) and len(item) == 2 for item in choices ] if all(pairs): self.choices = OrderedDict([(key, display_value) for key, display_value in choices]) else: self.choices = OrderedDict([(item, item) for item in choices]) # Map the string representation of choices to the underlying value. # Allows us to deal with eg. integer choices while supporting either # integer or string input, but still get the correct datatype out. self.choice_strings_to_values = dict([ (six.text_type(key), key) for key in self.choices.keys() ]) super(ChoiceField, self).__init__(**kwargs) def to_internal_value(self, data): try: return self.choice_strings_to_values[six.text_type(data)] except KeyError: self.fail('invalid_choice', input=data) def to_representation(self, value): return self.choice_strings_to_values[six.text_type(value)]
def format_keys(obj, format_type=None): """ Takes either a dict or list and returns it with camelized keys only if REST_EMBER_FORMAT_KEYS is set. :format_type: Either 'camelize' or 'underscore' """ if (getattr(settings, 'REST_EMBER_FORMAT_KEYS', False) and format_type in ('camelize', 'underscore')): if isinstance(obj, dict): formatted = OrderedDict() for key, value in obj.items(): if format_type == 'camelize': formatted[inflection.camelize(key, False)]\ = format_keys(value, format_type) elif format_type == 'underscore': formatted[inflection.underscore(key)]\ = format_keys(value, format_type) return formatted if isinstance(obj, list): return [format_keys(item, format_type) for item in obj] else: return obj else: return obj
def _get_reverse_relationships(opts): """ Returns an `OrderedDict` of field names to `RelationInfo`. """ reverse_relations = OrderedDict() for relation in opts.get_all_related_objects(): accessor_name = relation.get_accessor_name() reverse_relations[accessor_name] = RelationInfo( model_field=None, related=relation.model, to_many=relation.field.rel.multiple, has_through_model=False ) # Deal with reverse many-to-many relationships. for relation in opts.get_all_related_many_to_many_objects(): accessor_name = relation.get_accessor_name() reverse_relations[accessor_name] = RelationInfo( model_field=None, related=relation.model, to_many=True, has_through_model=( (getattr(relation.field.rel, 'through', None) is not None) and not relation.field.rel.through._meta.auto_created ) ) return reverse_relations
def to_representation(self, obj): answer = {} self.tryKeysOnDict(answer, 'guid', obj, ['guid']) self.tryKeysOnDict(answer, 'title', obj, ['title']) self.tryKeysOnDict(answer, 'description', obj, ['description']) self.tryKeysOnDict(answer, 'user', obj, ['author', 'owner_nick']) self.tryKeysOnDict(answer, 'tags', obj, ['tags']) self.tryKeysOnDict(answer, 'created_at', obj, ['created_at']) self.tryKeysOnDict(answer, 'endpoint', obj, ['endpoint', 'end_point']) self.tryKeysOnDict(answer, 'link', obj, ['permalink']) self.tryKeysOnDict(answer, 'category_name', obj, ['category_name']) self.tryKeysOnDict(answer, 'parameters', obj, ['parameters']) self.tryKeysOnDict(answer, 'result', obj, ['result']) self.tryKeysOnDict(answer, 'timestamp', obj, ['timestamp']) self.tryKeysOnDict(answer, 'category_id', obj, ['category', 'category_id']) try: if 'format' in obj and obj['format'].startswith('application/json'): answer['result'] = json.loads(answer['result']) except AttributeError: # TODO: ver esto, plis pass if answer['tags']: answer['tags'] = map(lambda x: x['tag__name'] if type(x) == dict else x, answer['tags']) if answer['link']: domain = self.context['request'].auth['microsite_domain'] answer['link'] = domain + answer['link'] return OrderedDict(answer)
def _get_forward_relationships(opts): """ Returns an `OrderedDict` of field names to `RelationInfo`. """ forward_relations = OrderedDict() for field in [field for field in opts.fields if field.serialize and field.rel]: forward_relations[field.name] = RelationInfo( model_field=field, related_model=_resolve_model(field.rel.to), to_many=False, has_through_model=False ) # Deal with forward many-to-many relationships. for field in [field for field in opts.many_to_many if field.serialize]: forward_relations[field.name] = RelationInfo( model_field=field, related_model=_resolve_model(field.rel.to), to_many=True, has_through_model=( not field.rel.through._meta.auto_created ) ) return forward_relations
def get_paginated_response(self, data): return Response(OrderedDict([ ('count', self.count), ('next', self.get_next_link()), ('previous', self.get_previous_link()), ('results', data) ]))
def get_meta(self): pagination = { 'pages': self.page.paginator.num_pages, 'first': None, 'previous': None, 'next': None, 'last': None, 'before': 0, 'more': 0, } if self.page.has_previous(): pagination['first'] = 1 if self.page.previous_page_number() > 1: pagination['previous'] = self.page.previous_page_number() if self.page.has_next(): pagination['last'] = self.page.paginator.num_pages if self.page.next_page_number() < self.page.paginator.num_pages: pagination['next'] = self.page.next_page_number() if self.page.start_index(): pagination['before'] = self.page.start_index() - 1 pagination['more'] = self.page.paginator.count - self.page.end_index() return OrderedDict([('count', self.page.paginator.count), ('pages', pagination['pages']), ('first', pagination['first']), ('previous', pagination['previous']), ('next', pagination['next']), ('last', pagination['last']), ('before', pagination['before']), ('more', pagination['more'])])
def _get_reverse_relationships(opts): """ Returns an `OrderedDict` of field names to `RelationInfo`. """ # Note that we have a hack here to handle internal API differences for # this internal API across Django 1.7 -> Django 1.8. # See: https://code.djangoproject.com/ticket/24208 reverse_relations = OrderedDict() for relation in opts.get_all_related_objects(): accessor_name = relation.get_accessor_name() related = getattr(relation, 'related_model', relation.model) reverse_relations[accessor_name] = RelationInfo( model_field=None, related_model=related, to_many=relation.field.rel.multiple, has_through_model=False) # Deal with reverse many-to-many relationships. for relation in opts.get_all_related_many_to_many_objects(): accessor_name = relation.get_accessor_name() related = getattr(relation, 'related_model', relation.model) reverse_relations[accessor_name] = RelationInfo( model_field=None, related_model=related, to_many=True, has_through_model=( (getattr(relation.field.rel, 'through', None) is not None) and not relation.field.rel.through._meta.auto_created)) return reverse_relations
def get_api_root_view(self): """ Return a view to use as the API root. """ api_root_dict = OrderedDict() list_name = self.routes[0].name for prefix, viewset, basename in self.registry: api_root_dict[prefix] = list_name.format(basename=basename) class APIRoot(views.APIView): _ignore_model_permissions = True def get(self, request, *args, **kwargs): ret = OrderedDict() namespace = get_resolver_match(request).namespace for key, url_name in api_root_dict.items(): if namespace: url_name = namespace + ':' + url_name try: ret[key] = reverse(url_name, args=args, kwargs=kwargs, request=request, format=kwargs.get('format', None)) except NoReverseMatch: # Don't bail out if eg. no list routes exist, only detail routes. continue return Response(ret) return APIRoot.as_view()
def get_paginated_response(self, data): return Response( OrderedDict([('count', self.page.paginator.count), ('next', self.get_next_link()), ('previous', self.get_previous_link()), ('facets', self.facets), ('suggestions', self.suggestions), ('results', data)]))
def _get_fields(opts): fields = OrderedDict() for field in [ field for field in opts.fields if field.serialize and not field.rel ]: fields[field.name] = field return fields
def test_default_display_value(self): class TestSerializer(serializers.ModelSerializer): class Meta: model = DisplayValueModel serializer = TestSerializer() expected = OrderedDict([('1', 'Red Color'), ('2', 'Yellow Color'), ('3', 'Green Color')]) self.assertEqual(serializer.fields['color'].choices, expected)
def get_paginated_response(self, data): print "Entra en paginado" return Response(OrderedDict([ ('total', self.count), ('next', self.get_next_link()), ('previous', self.get_previous_link()), ('items', data ) ]))
def get_field_info(model): """ Given a model class, returns a `FieldInfo` instance containing metadata about the various field types on the model. """ # Deal with the primary key. pk = model.id if not issubclass(model, mongoengine.EmbeddedDocument) else None # Deal with regular fields. fields = OrderedDict() for field_name in model._fields_ordered: fields[field_name] = model._fields[field_name] # Deal with forward relationships. # Pass forward relations since there is no relations on mongodb forward_relations = OrderedDict() # Deal with reverse relationships. # Pass reverse relations since there is no relations on mongodb reverse_relations = OrderedDict() # Shortcut that merges both regular fields and the pk, # for simplifying regular field lookup. fields_and_pk = OrderedDict() fields_and_pk["pk"] = pk fields_and_pk[getattr(pk, "name", "pk")] = pk fields_and_pk.update(fields) # Shortcut that merges both forward and reverse relationships relations = OrderedDict(list(forward_relations.items()) + list(reverse_relations.items())) return FieldInfo(pk, fields, forward_relations, reverse_relations, fields_and_pk, relations)
def get_paginated_response(self, data): return Response(OrderedDict([ ('count', self.page.paginator.count), ('next', self.get_next_link()), ('previous', self.get_previous_link()), ('current_page', self.page.number), ('total_pages', self.page.paginator.num_pages), ('results', data) ]))
def get_paginated_response(self, data): return OrderedDict([('start_index', self.page.start_index()), ('end_index', self.page.end_index()), ('num_pages', self.page.paginator.num_pages), ('current_page', self.page.number), ('count', self.page.paginator.count), ('next', self.get_next_link()), ('previous', self.get_previous_link()), ('results', data)])
def __init__(self, renderer, serialized_data, serializer=None): self.renderer = renderer self.serialized_data = serialized_data if serializer: self.serializer = get_serializer(serializer) else: self.serializer = get_serializer(serialized_data.serializer) self.hash = OrderedDict({"data": []}) self.included_set = set()
def choices(self): queryset = self.get_queryset() if queryset is None: # Ensure that field.choices returns something sensible # even when accessed with a read-only field. return {} return OrderedDict([(six.text_type(self.to_representation(item)), six.text_type(item)) for item in queryset])
def get_fields(self): """ Get the required fields for serializing the result. """ fields = getattr(self.Meta, "fields", []) exclude = getattr(self.Meta, "exclude", []) if fields and exclude: raise ImproperlyConfigured( "Cannot set both `fields` and `exclude`.") ignore_fields = getattr(self.Meta, "ignore_fields", []) indices = getattr(self.Meta, "index_classes") declared_fields = copy.deepcopy(self._declared_fields) prefix_field_names = len(indices) > 1 field_mapping = OrderedDict() # overlapping fields on multiple indices is supported by internally prefixing the field # names with the index class to which they belong or, optionally, a user-provided alias # for the index. for index_cls in self.Meta.index_classes: prefix = "" if prefix_field_names: prefix = "_%s__" % self._get_index_class_name(index_cls) for field_name, field_type in six.iteritems(index_cls.fields): orig_name = field_name field_name = "%s%s" % (prefix, field_name) # This has become a little more complex, but provides convenient flexibility for users if not exclude: if orig_name not in fields and field_name not in fields: continue elif orig_name in exclude or field_name in exclude or orig_name in ignore_fields or field_name in ignore_fields: continue # Look up the field attributes on the current index model, # in order to correctly instantiate the serializer field. model = index_cls().get_model() kwargs = self._get_default_field_kwargs(model, field_type) kwargs['prefix_field_names'] = prefix_field_names field_mapping[field_name] = self._field_mapping[field_type]( **kwargs) # Add any explicitly declared fields. They *will* override any index fields # in case of naming collision!. if declared_fields: for field_name in declared_fields: if field_name in field_mapping: warnings.warn( "Field '{field}' already exists in the field list. This *will* " "overwrite existing field '{field}'".format( field=field_name)) field_mapping[field_name] = declared_fields[field_name] return field_mapping
def determine_metadata(self, request, view): metadata = OrderedDict() metadata['name'] = view.get_view_name() metadata['description'] = view.get_view_description() metadata['renders'] = [renderer.media_type for renderer in view.renderer_classes] metadata['parses'] = [parser.media_type for parser in view.parser_classes] if hasattr(view, 'get_serializer'): actions = self.determine_actions(request, view) if actions: metadata['actions'] = actions return metadata
def choices(self): queryset = self.child_relation.queryset iterable = queryset.all() if (hasattr(queryset, 'all')) else queryset items_and_representations = [ (item, self.child_relation.to_representation(item)) for item in iterable ] return OrderedDict([ (six.text_type(item_representation), six.text_type(item) + ' - ' + six.text_type(item_representation)) for item, item_representation in items_and_representations ])
def get_serializer_info(self, serializer): """ Given an instance of a serializer, return a dictionary of metadata about its fields. """ if hasattr(serializer, 'child'): # If this is a `ListSerializer` then we want to examine the # underlying child serializer instance instead. serializer = serializer.child return OrderedDict([(field_name, self.get_field_info(field)) for field_name, field in serializer.fields.items() ])
def get(self, request, *args, **kwargs): ret = OrderedDict() for key, url_name in api_root_dict.items(): try: ret[key] = reverse(url_name, request=request, format=kwargs.get('format', None)) except NoReverseMatch: # Don't bail out if eg. no list routes exist, only detail routes. continue return Response(ret)
def post(self, request, *args, **kwargs): lng, lat = request.data.get('longitude'), request.data.get('latitude') err = self._validate_lat_long(lat, lng) if err: return views.Response(err, status=400) try: point = Point(x=lng, y=lat) except TypeError: # pragma: no cover # this is one wiered edgecase return views.Response( { rest_settings.api_settings.NON_FIELD_ERRORS_KEY: [ "Invalid value given for longitude or latitude", ], }, status=400) try: data = WardBoundary.objects.values( 'area', 'area__name', 'area__code', 'area__constituency', 'area__constituency__name', 'area__constituency__code', 'area__constituency__county', 'area__constituency__county__name', 'area__constituency__county__code', ).get(mpoly__contains=point) return views.Response( OrderedDict([ ('ward', data['area']), ('ward_name', data['area__name']), ('ward_code', data['area__code']), ('constituency', data['area__constituency']), ('constituency_name', data['area__constituency__name']), ('constituency_code', data['area__constituency__code']), ('county', data['area__constituency__county']), ('county_name', data['area__constituency__county__name']), ('county_code', data['area__constituency__county__code']), ])) except WardBoundary.DoesNotExist: return views.Response( { rest_settings.api_settings.NON_FIELD_ERRORS_KEY: [ "No ward contains the coordinates ({}, {})".format( lng, lat) ] }, status=400)
def __init__(self, choices, **kwargs): # Allow either single or paired choices style: # choices = [1, 2, 3] # choices = [(1, 'First'), (2, 'Second'), (3, 'Third')] pairs = [ isinstance(item, (list, tuple)) and len(item) == 2 for item in choices ] if all(pairs): self.choices = OrderedDict([(key, display_value) for key, display_value in choices]) else: self.choices = OrderedDict([(item, item) for item in choices]) # Map the string representation of choices to the underlying value. # Allows us to deal with eg. integer choices while supporting either # integer or string input, but still get the correct datatype out. self.choice_strings_to_values = dict([(six.text_type(key), key) for key in self.choices.keys()]) super(ChoiceField, self).__init__(**kwargs)
def get_paginated_response(self, data): response = Response( OrderedDict([ ('count', len(data)), ('time-to-update-participant-number', Settings.manager.first().update_time_participant_number), ('next', None), ('previous', None), ('results', data) ])) if not self.show_all_elements(): response.data.update( super(StandardResultsSetPagination, self).get_paginated_response(data).data) return response
def get_field_info(self, field): """ Given an instance of a serializer field, return a dictionary of metadata about it. """ field_info = OrderedDict() field_info["type"] = self.label_lookup[field] field_info["required"] = getattr(field, "required", False) attrs = ["read_only", "label", "help_text", "min_length", "max_length", "min_value", "max_value"] for attr in attrs: value = getattr(field, attr, None) if value is not None and value != "": field_info[attr] = force_text(value, strings_only=True) if not field_info.get("read_only") and hasattr(field, "choices"): field_info["choices"] = [ {"value": choice_value, "display_name": force_text(choice_name, strings_only=True)} for choice_value, choice_name in field.choices.items() ] return field_info
def __init__(self, serializer): self.serializer = serializer self.fields = OrderedDict()
class JsonApiAdapter(object): def __init__(self, renderer, serialized_data, serializer=None): self.renderer = renderer self.serialized_data = serialized_data if serializer: self.serializer = get_serializer(serializer) else: self.serializer = get_serializer(serialized_data.serializer) self.hash = OrderedDict({"data": []}) self.included_set = set() def serializable_hash(self): if isinstance(self.serialized_data, list): for obj in self.serialized_data: result = JsonApiAdapter(self.renderer, obj, self.serializer).serializable_hash() self.hash["data"].append(result.get("data")) if result.get("included"): if "included" not in self.hash: self.hash["included"] = [] for result in result.get("included"): set_key = "-".join([result.get("type"), result.get("id")]) if set_key not in self.included_set: self.hash["included"].append(result) self.included_set.add(set_key) else: self.hash["data"] = self.attributes_for_serialized_data( self.serialized_data, self.serializer) self.add_resource_relationships( self.hash["data"], self.serialized_data, self.serializer) return self.hash def add_relationships(self, resource, rel_name, relationship): dash_name = dasherize(rel_name) if dash_name not in resource["relationships"]: resource["relationships"][dash_name] = OrderedDict({ "data": [] }) if relationship.get("data"): for data in relationship.get("data"): try: rel_id = data.get("id") # Serialized data except AttributeError: rel_id = data # Only IDs resource["relationships"][dash_name]["data"].append( OrderedDict([ ("id", force_text(rel_id)), ("type", relationship.get("type")), ]) ) def add_relationship(self, resource, rel_name, relationship): dash_name = dasherize(rel_name) if dash_name not in resource["relationships"]: resource["relationships"][dash_name] = OrderedDict({ "data": None }) if relationship.get("data"): try: rel_id = relationship.get("data").get("id") # Serialized data except AttributeError: rel_id = relationship.get("data") # Only ID resource["relationships"][dasherize(rel_name)]["data"] = \ OrderedDict([ ("id", force_text(rel_id)), ("type", relationship.get("type")), ]) def add_included(self, rel_name, relationship, parent=None): included_serializer = self.get_included_serializer( relationship.get("parent_serializer"), rel_name) if not included_serializer: return serialized_data = relationship.get("data") if not isinstance(serialized_data, list): serialized_data = [serialized_data] included_data = [] for item in serialized_data: if isinstance(item, six.integer_types): # Only ID data = self.get_included_data( rel_name, item, included_serializer) if data: included_data.append(data) resource_path = ".".join([parent, rel_name] if parent else [rel_name]) if self.include_assoc(resource_path): if "included" not in self.hash: self.hash["included"] = [] for data in included_data: attrs = self.attributes_for_serialized_data( data, included_serializer) self.add_resource_relationships( attrs, data, included_serializer, add_included=False) if attrs not in self.hash.get("included"): self.hash["included"].append(attrs) if self.include_nested_assoc(resource_path): for data in included_data: relationships = self.get_relationships_data( data, included_serializer) for rel_name, relationship in six.iteritems(relationships): self.add_included(rel_name, relationship, resource_path) def get_included_serializer(self, serializer, rel_name): return getattr(serializer.Meta, "include", {}).get(rel_name) def get_included_data(self, rel_name, pk, included_serializer): model = included_serializer.Meta.model obj = model.objects.get(pk=pk) return included_serializer.to_representation(obj) def attributes_for_serialized_data(self, serialized_data, serializer): if isinstance(serialized_data, list): result = [] for obj in serialized_data: result.append(self.resource_object_for(obj, serializer)) else: result = self.resource_object_for(serialized_data, serializer) return result def resource_object_for(self, obj, serializer): attributes = self.get_attributes_data(obj, serializer) result = OrderedDict([ ("id", force_text(attributes.pop("id"))), ("type", attributes.pop("type")), ]) if attributes: result["attributes"] = attributes return result def add_resource_relationships(self, attrs, serialized_data, serializer, add_included=True): relationships = self.get_relationships_data(serialized_data, serializer) if relationships and "relationships" not in attrs: attrs["relationships"] = OrderedDict() for rel_name, relationship in six.iteritems(relationships): if isinstance(relationship.get("data"), list): self.add_relationships(attrs, rel_name, relationship) else: self.add_relationship(attrs, rel_name, relationship) if add_included: self.add_included(rel_name, relationship) def include_assoc(self, assoc): return self.check_assoc("{}$".format(assoc)) def include_nested_assoc(self, assoc): return self.check_assoc("{}.".format(assoc)) def check_assoc(self, assoc): include_opt = self.renderer.request.query_params.get("include") if not include_opt: return False include_opt = include_opt.split(",") for opt in include_opt: if re.match(r"^{}".format( dasherize(assoc.replace(".", "\."))), opt): return True return False def get_attributes_data(self, obj, serializer): model = serializer.Meta.model resource_type = get_resource_type(model) attributes = OrderedDict([("id", None), ("type", resource_type)]) for field_name, field in six.iteritems(serializer.get_fields()): if isinstance(field, (RelatedField, ManyRelatedField)): continue attributes[dasherize(field_name)] = obj.get(field_name) return attributes def get_relationships_data(self, serialized_data, serializer): relationships = OrderedDict() for field_name, field in serializer.get_fields().items(): if isinstance(field, (RelatedField, ManyRelatedField)): relationships[field_name] = { "field": field, "parent_serializer": serializer, "data": serialized_data.get(field_name) } related_field = get_serializer(field) relationships[field_name]["type"] = get_resource_type( get_model(related_field, field_name, serializer)) return relationships
def get_field_info(model): """ Given a model class, returns a `FieldInfo` instance containing metadata about the various field types on the model. """ opts = model._meta.concrete_model._meta # Deal with the primary key. pk = opts.pk while pk.rel and pk.rel.parent_link: # If model is a child via multitable inheritance, use parent's pk. pk = pk.rel.to._meta.pk # Deal with regular fields. fields = OrderedDict() for field in [field for field in opts.fields if field.serialize and not field.rel]: fields[field.name] = field # Deal with forward relationships. forward_relations = OrderedDict() for field in [field for field in opts.fields if field.serialize and field.rel]: forward_relations[field.name] = RelationInfo( model_field=field, related=_resolve_model(field.rel.to), to_many=False, has_through_model=False ) # Deal with forward many-to-many relationships. for field in [field for field in opts.many_to_many if field.serialize]: forward_relations[field.name] = RelationInfo( model_field=field, related=_resolve_model(field.rel.to), to_many=True, has_through_model=( not field.rel.through._meta.auto_created ) ) # Deal with reverse relationships. reverse_relations = OrderedDict() for relation in opts.get_all_related_objects(): accessor_name = relation.get_accessor_name() reverse_relations[accessor_name] = RelationInfo( model_field=None, related=relation.model, to_many=relation.field.rel.multiple, has_through_model=False ) # Deal with reverse many-to-many relationships. for relation in opts.get_all_related_many_to_many_objects(): accessor_name = relation.get_accessor_name() reverse_relations[accessor_name] = RelationInfo( model_field=None, related=relation.model, to_many=True, has_through_model=( (getattr(relation.field.rel, 'through', None) is not None) and not relation.field.rel.through._meta.auto_created ) ) # Shortcut that merges both regular fields and the pk, # for simplifying regular field lookup. fields_and_pk = OrderedDict() fields_and_pk['pk'] = pk fields_and_pk[pk.name] = pk fields_and_pk.update(fields) # Shortcut that merges both forward and reverse relationships relations = OrderedDict( list(forward_relations.items()) + list(reverse_relations.items()) ) return FieldInfo(pk, fields, forward_relations, reverse_relations, fields_and_pk, relations)