如何使用rest框架将子对象正确添加到父集合中

时间:2015-01-27 20:57:27

标签: django django-rest-framework

我有以下两个模型:Blueprint和Workload。 Blueprint模型实例应该具有与之关联的Workload集合。 最初,应该允许独立创建Blueprint和Workload实例。 一个完整的典型场景如下:

1)创建一个新的Blueprint实例

2)创建一个新的Workload实例

3)创建的Workload实例被添加到Blueprint的“工作负载”集合

python REPL版本是这样的:

blueprint = Blueprint(name="bluepint 1")
blueprint.save()
workload = Workload)name="workload 1")
workload.save()
blueprint.workloads.add(blueprint)
blueprint.save()

在我的python客户端中,我可以独立创建Blueprint和Workload的实例,而且没有问题。

我的问题是:将正确的HTTP方法和相应的URL语法添加到Blueprint的“工作负载”集合中是什么。

这是models.py:

class Workload(models.Model):
    name = models.CharField(max_length=120, blank=True, default='')
    description = models.TextField()
    image = models.CharField(max_length=120, blank=True, default='')
    flavor = models.CharField(max_length=120, blank=True, default='')
    blueprint = models.ForeignKey('Blueprint', related_name='workloads', null=True)

    class Meta:
        ordering = ('name',)
        unique_together = ('blueprint', 'name')

    def __unicode__(self):
        return '%d: %s' % (self.name, self.description)


class Blueprint(models.Model):
    name = models.CharField(max_length=120, blank=True, default='')
    description = models.TextField()

    class Meta:
        ordering = ('name',)

这是serializers.py:

# region Workload Serializer
class WorkloadSerializer(serializers.Serializer):
    pk = serializers.IntegerField(read_only=True)
    name = serializers.CharField(required=False, allow_blank=True, max_length=120)
    description = serializers.CharField(style={'type': 'textarea'})
    image = serializers.CharField(required=False, allow_blank=True, max_length=120)
    flavor = serializers.CharField(required=False, allow_blank=True, max_length=120)


    def create(self, validated_data):
        """
        Create and return a new `Snippet` instance, given the validated data.
        """
        return Workload.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        Update and return an existing `Snippet` instance, given the validated data.
        """
        instance.name = validated_data.get('name', instance.name)
        instance.description = validated_data.get('description', instance.description)
        instance.image = validated_data.get('image', instance.image)
        instance.flavor = validated_data.get('flavor', instance.flavor)
        instance.save()
        return instance
# endregion


# region Blueprint Serializer
class BlueprintSerializer(serializers.ModelSerializer):
    workloads = serializers.StringRelatedField(many=True, required=False)
    pk = serializers.IntegerField(read_only=True)
    name = serializers.CharField(required=False, allow_blank=True, max_length=120)
    description = serializers.CharField(style={'type': 'textarea'})

    def create(self, validated_data):
        """
        Create and return a new `Snippet` instance, given the validated data.
        """
        return Blueprint.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        Update and return an existing `Snippet` instance, given the validated data.
        """
        instance.name = validated_data.get('name', instance.name)
        instance.description = validated_data.get('description', instance.description)
        instance.save()
        return instance

    class Meta:
        model = Blueprint
        fields = ('name', 'description', 'workloads')
# endregion

-Eugene

1 个答案:

答案 0 :(得分:0)

我不是REST专家,但就REST而言,我相信你的网址应该是这样的:

GET  /api/blueprint/                #list the blueprints
POST /api/blueprint/                #add new blueprint
GET  /api/blueprint/1/              #detail information about blueprint with id=1
PUT  /api/blueprint/1/              #update blueprint with id=1
GET  /api/blueprint/1/workloads/    #list all workloads of blueprint with id 1
POST /api/blueprint/1/workloads/    #add new workload to blueprint with id 1 workloads
GET  /api/blueprint/1/workloads/1   #detail information about workload with id=1 and blueprint id = 1
PUT  /api/blueprint/1/workloads/1   #update information about workload with id=1 and blueprint id = 1

所以你在REST上下文中的复制可能会像:

#request
http POST /api/blueprint/ name="bluepint 1"

#response
{
    "id": 1,
    "name": "bluepint 1",
    "description": ""
}

#request
http POST /api/blueprint/1/workloads/ name="workload 1"

#response
{
    "id": 1,
    "name": "workload 1",
    "description": "",
    "image": "",
    "flavor": "",
    "blueprint": 1
}

为了制作这样的网址结构,您应该结帐drf-extensionsdrf-nested-routers

另一种方法是将自定义detail端点添加到/api/blueprint/,例如:

http POST /api/blueprint/1/add_workload/ name="workload 2"

如果你使用Viewsets,这将会像这样:

#inside BlueprintViewset
@detail_route(methods=['post'])
def add_workload(self, request, pk):
    serializer = WorkloadSerializer(data=request.data)
    if serializer.is_valid():
        serializer.save(blueprint=pk)
        return Response(serializer.data, status=status.HTTP_201_CREATED)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)