gpt4 book ai didi

django - Django 中具有模型继承的 RESTful API

转载 作者:行者123 更新时间:2023-12-01 13:50:42 24 4
gpt4 key购买 nike

我需要用 Django 的 REST Framwork 实现一个注重模型继承的 RESTful API。我想this是构建 API 的一种巧妙方法。基本思想是仅通过呈现的属性集来区分对象类。但是 Django 如何做到这一点呢?让我们坚持一个简单的示例场景:

  • Animal 具有属性 age
  • Bird 通过属性 wing 扩展了 Animal,例如它的大小。
  • Dog 通过 tail 属性扩展了 Animal,例如它的长度。

示例要求:

  • 如果 /animals/ 列出所有动物及其一般属性就足够了,即它们的年龄。
  • 假设 ID=1 的动物是一只鸟,/animals/1 应该给出如下内容:

{
'age': '3',
'class': 'Bird',
'wing': 'big'
}
  • 假设 ID=2 的动物是一只狗,/animals/2 应该给出如下内容:

{
'age': '8',
'class': 'Dog',
'tail': 'long'
}

我没有成功实现 Django 的 REST 框架,主要是因为我没有成功添加/删除特定于类的字段。我特别想知道,在这种情况下,create 操作是如何实现的?

最佳答案

tl;dr: 它并不像看起来那么复杂。该解决方案提出了一个完全可重用的类,它以最少的代码实现了请求的用例,正如您可以从下面的 示例性用法 中看到的那样。


经过一些摆弄,我想出了以下解决方案,我相信,这是非常令人满意的。为此,我们将需要一个辅助函数和两个类,没有其他依赖项。

HyperlinkedModelSerializer

的扩展

假设查询从 Animal 类返回一个对象,实际上 是一只Birdget_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',)

views.py 中:

class AnimalViewSet(viewsets.ModelViewSet):
queryset = Animal.objects.all()
serializer_class = AnimalSerializer

关于django - Django 中具有模型继承的 RESTful API,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31923568/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com