Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/django/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Django中带有模型继承的RESTful API_Django_Rest_Django Views_Django Rest Framework - Fatal编程技术网

Django中带有模型继承的RESTful API

Django中带有模型继承的RESTful API,django,rest,django-views,django-rest-framework,Django,Rest,Django Views,Django Rest Framework,我需要实现一个RESTful API,它关注Django的REST框架对模型的继承。我认为这是构建API的一种简洁方法。其基本思想是仅通过所呈现的属性集来区分对象类。但Django如何做到这一点?让我们坚持一个简单的示例场景: 类Animal具有属性age 类鸟通过属性翅膀扩展动物,例如其大小 类狗通过属性尾巴扩展动物,例如其长度 示范性要求: 如果/animals/列出所有动物的一般属性,即年龄,就足够了 假设ID=1的动物是一只鸟,/anists/1应该给出如下信息: 假设ID=

我需要实现一个RESTful API,它关注Django的REST框架对模型的继承。我认为这是构建API的一种简洁方法。其基本思想是仅通过所呈现的属性集来区分对象类。但Django如何做到这一点?让我们坚持一个简单的示例场景:

  • Animal
    具有属性
    age
  • 通过属性
    翅膀
    扩展
    动物
    ,例如其大小
  • 通过属性
    尾巴
    扩展
    动物
    ,例如其长度
示范性要求:

  • 如果
    /animals/
    列出所有动物的一般属性,即年龄,就足够了
  • 假设ID=1的动物是一只鸟,
    /anists/1
    应该给出如下信息:

  • 假设ID=2的动物是狗,
    /animates/2
    应该给出如下信息:

我没有实现Django的REST框架,主要是因为我没有成功地添加/删除特定于类的字段。我特别想知道,在这样的场景中创建操作是如何实现的?

tl;dr:它不像看上去那么复杂。这个解决方案提出了一个完全可重用的类,它用最少的代码实现所请求的用例,正如下面的示例用法所示


经过一番努力,我想出了下面的解决方案,我相信这是相当令人满意的。我们需要一个helper函数和两个类,没有进一步的依赖关系

对超链接ModelSerializer的扩展 假设一个查询从
Animal
类返回一个对象,它实际上是一只
Bird
。然后
get_actual
Animal
解析为对象形式
Bird
类:

def get_actual(obj):
    """Expands `obj` to the actual object type.
    """
    for name in dir(obj):
        try:
            attr = getattr(obj, name)
            if isinstance(attr, obj.__class__):
                return attr
        except:
            pass
    return obj
ModelField
定义了一个字段,用于命名序列化程序下的模型:

class ModelField(serializers.ChoiceField):
    """Defines field that names the model that underlies a serializer.
    """

    def __init__(self, *args, **kwargs):
        super(ModelField, self).__init__(*args, allow_null=True, **kwargs)

    def get_attribute(self, obj):
        return get_actual(obj)

    def to_representation(self, obj):
        return obj.__class__.__name__
HyperlinkedModelHierarchySerializer
发挥了神奇的作用:

class HyperlinkedModelHierarchySerializer(serializers.HyperlinkedModelSerializer):
    """Extends the `HyperlinkedModelSerializer` to properly handle class hierearchies.

    For an hypothetical model `BaseModel`, serializers from this
    class are capable of also handling those models that are derived
    from `BaseModel`.

    The `Meta` class must whitelist the derived `models` to be
    allowed. It also must declare the `model_dependent_fields`
    attribute and those fields must also be added to its `fields`
    attribute, for example:

        wing = serializers.CharField(allow_null=True)
        tail = serializers.CharField(allow_null=True)

        class Meta:
            model = Animal
            models = (Bird, Dog)
            model_dependent_fields = ('wing', 'tail')
            fields = ('model', 'id', 'name') + model_dependent_fields
            read_only_fields = ('id',)

    The `model` field is defined by this class.
    """
    model = ModelField(choices=[])

    def __init__(self, *args, **kwargs):
        """Instantiates and filters fields.

        Keeps all fields if this serializer is processing a CREATE
        request. Retains only those fields that are independent of
        the particular model implementation otherwise.
        """
        super(HyperlinkedModelHierarchySerializer, self).__init__(*args, **kwargs)
        # complete the meta data
        self.Meta.models_by_name = {model.__name__: model for model in self.Meta.models}
        self.Meta.model_names = self.Meta.models_by_name.keys()
        # update valid model choices,
        # mark the model as writable if this is a CREATE request
        self.fields['model'] = ModelField(choices=self.Meta.model_names, read_only=bool(self.instance))
        def remove_missing_fields(obj):
            # drop those fields model-dependent fields that `obj` misses
            unused_field_keys = set()
            for field_key in self.Meta.model_dependent_fields:
                if not hasattr(obj, field_key):
                    unused_field_keys |= {field_key}
            for unused_field_key in unused_field_keys:
                self.fields.pop(unused_field_key)
        if not self.instance is None:
            # processing an UPDATE, LIST, RETRIEVE or DELETE request
            if not isinstance(self.instance, QuerySet):
                # this is an UPDATE, RETRIEVE or DELETE request,
                # retain only those fields that are present on the processed instance
                self.instance = get_actual(self.instance)
                remove_missing_fields(self.instance)
            else:
                # this is a LIST request, retain only those fields
                # that are independent of the particular model implementation
                for field_key in self.Meta.model_dependent_fields:
                    self.fields.pop(field_key)

    def validate_model(self, value):
        """Validates the `model` field.
        """
        if self.instance is None:
            # validate for CREATE
            if value not in self.Meta.model_names:
                raise serializers.ValidationError('Must be one of: ' + (', '.join(self.Meta.model_names)))
            else:
                return value
        else:
            # model cannot be changed
            return get_actual(self.instance).__class__.__name__

    def create(self, validated_data):
        """Creates instance w.r.t. the value of the `model` field.
        """
        model = self.Meta.models_by_name[validated_data.pop('model')]
        for field_key in self.Meta.model_dependent_fields:
            if not field_key in model._meta.get_all_field_names():
                validated_data.pop(field_key)
                self.fields.pop(field_key)
        return model.objects.create(**validated_data)
示范用法 这就是我们如何使用它。在
serializers.py中

class AnimalSerializer(HyperlinkedModelHierarchySerializer):

    wing = serializers.CharField(allow_null=True)
    tail = serializers.CharField(allow_null=True)

    class Meta:
        model = Animal
        models = (Bird, Dog)
        model_dependent_fields = ('wing', 'tail')
        fields = ('model', 'id', 'name') + model_dependent_fields
        read_only_fields = ('id',)
class AnimalViewSet(viewsets.ModelViewSet):
    queryset = Animal.objects.all()
    serializer_class = AnimalSerializer
views.py
中:

class AnimalSerializer(HyperlinkedModelHierarchySerializer):

    wing = serializers.CharField(allow_null=True)
    tail = serializers.CharField(allow_null=True)

    class Meta:
        model = Animal
        models = (Bird, Dog)
        model_dependent_fields = ('wing', 'tail')
        fields = ('model', 'id', 'name') + model_dependent_fields
        read_only_fields = ('id',)
class AnimalViewSet(viewsets.ModelViewSet):
    queryset = Animal.objects.all()
    serializer_class = AnimalSerializer

请记住,如果对这样的模型使用类继承,则任何查询都会有多个表和隐式连接。您最好使用。@dukebody:是的,但是对于抽象基类
Animal
,我不能像
Animal.objects.all()
这样进行查询,而且我也不能将
Dog.objects.all()
Bird.objects.all()
,这将使处理
Animal
对象变得非常不方便。对!你可能想考虑在动物模型中使用一个单独的“额外”JSON字段,用于特定的动物属性,或者甚至类似于[Django REST框架MungEngEng](),如果你的动物与其他关系领域无关,那么DukByth:你能更详细地解释一下“额外”的意思吗?
Animal
模型中的JSON字段?请参阅。Django有json字段实现: