Django Rest Framework - OPTIONS请求 - 获取外键选择

时间:2017-10-05 17:15:12

标签: python django django-rest-framework

假设我在Django中有两个模型,我使用标准的Viewsets和Serializers来公开端点。

ModelA有一个到ModelB的ForeignKey字段。当我向ModelA端点发出OPTIONS请求时,它只是将其列为一个字段,如下所示:

"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"
        },
    }
}

我希望它有一个“选择”属性,可以下拉模型B的每个实例。这样,在我的Angular前端,我可以创建所有选项的下拉列表。

我想我需要创建一个新的MetaData类或者一些东西。但我找不到任何可靠的方法来做到这一点。我还认为这个功能在DRF的3.X中发生了变化,因此互联网上的旧答案不再有用。

干杯, 迪恩

3 个答案:

答案 0 :(得分:1)

我也没有完美的答案,但我实现这一目标的方法是将json项目映射到它们的ID并存储像这样的集合(角度):

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

这样做,您就能够以这种方式在ng-options中使用该地图:

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

有更好的想法吗?

//经过深思熟虑(事实证明这是一种“手动”方法)

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

请告诉我它是否适合您:)

//最后(改进默认DRF代码的智能方法)

如果您想完全自动完成,您只需向metadata.py添加三行代码:

在上面:

from rest_framework.relations import PrimaryKeyRelatedField
get_field_info方法中的

if type(field) is PrimaryKeyRelatedField:
    field_info['choices'] = [x.id for x in field.queryset.all()]

答案 1 :(得分:0)

我解决了这个问题,感谢@detoix这样的提示:

from rest_framework.relations import ManyRelatedField
from rest_framework.metadata import SimpleMetadata


class MyMetaData(SimpleMetadata):

    def get_field_info(self, field):
        field_info = super(MyMetaData, self).get_field_info(field)
        if isinstance(field, ManyRelatedField):
            field_info['choices'] = field.get_choices()
        return field_info

在views.py中:

class ViewSet(ModelViewSet):
    metadata_class = MyMetadata

答案 2 :(得分:0)

对@ralfzen做了一些改进,以改善序列化。 还需要使用RelatedField而不是仅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