Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Rest Framework - OPTIONS request - Get foreign key choices

Lets say I have two models in Django, and I'm using standard Viewsets and Serializers to expose endpoints.

ModelA has a ForeignKey field to ModelB. When I do an OPTIONS request to the ModelA endpoint it just lists it as a field, like below:

"actions": {
    "POST": {
        "pk": {
            "type": "integer",
            "required": false,
            "read_only": true,
            "label": "ID"
        },
        "created": {
            "type": "datetime",
            "required": false,
            "read_only": true,
            "label": "Created"
        },

        "foreign_key_field": {
            "type": "field",
            "required": true,
            "read_only": false,
            "label": "Wcp"
        },
    }
}

I would like it to have a 'choices' property which pulls down every single instance of Model B. This way in my Angular frontend I can create a dropdown of all the choice.

I think I need to make a new MetaData class or somthing. But I can't find any solid way of doing this. I also think this functionality has changed in 3.X of DRF so old answers around the internet no longer help.

Cheers, Dean

like image 598
Dean Sherwin Avatar asked Oct 05 '17 17:10

Dean Sherwin


People also ask

How do you save a foreign key field in Django REST framework?

Your code is using serializer only for validation, but it is possible use it to insert or update new objects on database calling serializer. save() . To save foreign keys using django-rest-framework you must put a related field on serializer to deal with it. Use PrimaryKeyRelatedField .

What is the difference between ModelSerializer and HyperlinkedModelSerializer?

The HyperlinkedModelSerializer class is similar to the ModelSerializer class except that it uses hyperlinks to represent relationships, rather than primary keys. By default the serializer will include a url field instead of a primary key field.

What is renderers in Django REST framework?

The rendering process takes the intermediate representation of template and context, and turns it into the final byte stream that can be served to the client. REST framework includes a number of built in Renderer classes, that allow you to return responses with various media types.

What is Restapi in Django?

REST APIs are an industry-standard way for web services to send and receive data. They use HTTP request methods to facilitate the request-response cycle and typically transfer data using JSON, and more rarely - HTML, XML and other formats.


Video Answer


2 Answers

Made some improvements to @ralfzen his answer to improve serialization. Also needed to use RelatedField instead of just ManyRelatedField

from django.utils.encoding import force_text
from rest_framework.views import APIView
from rest_framework.metadata import SimpleMetadata
from rest_framework.relations import ManyRelatedField, RelatedField


class MyMetaData(SimpleMetadata):

    def get_field_info(self, field):
        field_info = super(MyMetaData, self).get_field_info(field)
        if isinstance(field, (RelatedField, ManyRelatedField)):
            field_info['choices'] = [
                {
                    'value': choice_value,
                    'display_name': force_text(choice_name, strings_only=True)
                }
                for choice_value, choice_name in field.get_choices().items()
            ]
        return field_info


class MyView(APIView):
    metadata_class = MyMetaData
like image 93
Michel Rugenbrink Avatar answered Oct 06 '22 00:10

Michel Rugenbrink


I don't have a perfect answer as well, but my way of achieving this was mapping json items to their ids and storing such a collection like this (in angular):

$scope.nodes = response.data; //original data retrieved from django
$scope.nodesIds = response.data.map(function (v) { return v.id; }); //particular ids

doing this, you're able to use that map in ng-options in such a way:

<select ng-model="node_start" ng-options="node for node in nodesIds" />

Any better ideas?

//After thorough consideration (it turns out that this is a 'manual' approach)

from rest_framework.metadata import SimpleMetadata
class MyMetadata(SimpleMetadata):
    def get_serializer_info(self, serializer):
        orderedDict = super().get_serializer_info(serializer)
        orderedDict['foreign_key_field']['choices'] = [x.id for x in Node.objects.all()]
        return orderedDict

class ViewSet(ModelViewSet):
    metadata_class = MyMetadata

Please let me know if it works for you :)

//Finally (smart approach with changes in default DRF code)

If you want it fully automatically, all you have to do is adding three lines of code to metadata.py:

on top:

from rest_framework.relations import PrimaryKeyRelatedField

in get_field_info method:

if type(field) is PrimaryKeyRelatedField:
    field_info['choices'] = [x.id for x in field.queryset.all()]
like image 22
detoix Avatar answered Oct 05 '22 23:10

detoix