【问题标题】:Posting a one-to-many relationship发布一对多关系
【发布时间】:2013-03-04 13:41:15
【问题描述】:

我正在尝试通过 Django REST 框架向我的 Django 模型公开一个 API。

我有一个对象Observation。一个观察可以包含多个已观察到的事物。所以我是这样表示的:

class Observation(models.Model):

    photo_file = models.ImageField( upload_to=img_dir,   blank=True, null=True )
    titestamp = models.DateTimeField(blank=True, null=True)
    latitude = models.FloatField()
    longitude = models.FloatField()


class ObservedThing(models.Model):
    thing = models.ForeignKey(Thing) # the thing being observed
    observation = models.ForeignKey(Observation, related_name='observed_thing')
    value = models.FloatField()

据我了解,这是一对多的关系。

我现在有一个 API 视图:

class ObsvList(generics.ListCreateAPIView):
    """
    API endpoint that represents a list of observations.
    """
    model = Observation
    serializer_class = ObsvSerializer

以及对应的序列化器:

class ObsvSerializer(serializers.ModelSerializer):

    observed_thing = serializers.PrimaryKeyRelatedField(many=True)

    class Meta:
        model = Observation

我必须做些什么才能发布一个检测到几件事情的观察?我想不明白。非常感谢。

【问题讨论】:

    标签: django one-to-many django-rest-framework


    【解决方案1】:

    (答案或多或少从另一个similar but less clear question复制)

    要在单个 POST 中创建多个相关对象,需要可写的嵌套序列化器,但目前尚不可用。

    完全支持work in progress,但同时一个(hacky)解决方案是在每种情况下覆盖视图中的create 方法:

    class FooListCreateView(ListCreateAPIView):
        model = Foo
        serializer_class = FooSerializer
    
        def create(self, request, *args, **kwargs):
            data=request.DATA
    
            f = Foo.objects.create()
    
            # ... create nested objects from request data ...  
    
            # ...
            return Response(serializer.data, 
                            status=status.HTTP_201_CREATED,
                            headers=headers)
    

    可能并不理想,但它对我有用,直到出现正确的方法。

    另一种选择是使用单独的 POST 单独创建相关的 Observation 对象,并使用 PrimaryKeyRelatedField or HyperlinkedRelatedField 在最终的 ObservedThing POST 中建立关联。

    【讨论】:

    • 啊哈,这就是我要找的。非常感谢,已采纳。
    【解决方案2】:

    我知道这个帖子已经有了答案,但我开始努力解决这个问题,因为这篇文章是我的灵感之一,我想分享我的最终解决方案。它可能对某人有用。我有模型,所以父类:

    #parent model class
    class Parent(models.Model):
    
        id = models.AutoField(primary_key=True)
        field = models.CharField(max_length=45)
    
        class Meta:
            managed = False
            db_table = 'parent'
    

    然后,子类:

    #child model class
    class Child(models.Model):
    
        id = models.AutoField(primary_key=True)
        field = models.CharField(max_length=45)
        parent = models.ForeignKey(Parent, related_name='children')
    
        class Meta:
            managed = False
            db_table = 'child'
    

    我必须定义序列化器,因为我不想创建路由器可访问的 url 来直接管理 Children 对象,但我想通过父 ModelViewSet 的 ModelViewSet 创建它们,这就是我需要的:

    class ChildSerializer(serializers.ModelSerializer):
        class Meta:
            model = Child
            read_only_fields = ('id',)
    
    class ParentSerializer(serializers.ModelSerializer):
        class Meta:
            model = Banner
            read_only_fields = ('id',)
    
    class ParentSerializerNested(ParentSerializer):
        children = ChildSerializer(many=True)
    

    然后我准备创建 ModelViewSet,覆盖/扩展 create/update mixins,并使其通用化,以便在其他情况下重用它:

    class ParentChildViewSet(viewsets.ModelViewSet):
    
        def create(self, request, *args, **kwargs):
            serializer = self.serializer_parent(data=request.DATA,
                                                files=request.FILES)
    
            try:
                if serializer.is_valid():
                    with transaction.commit_on_success():
                        self.pre_save(serializer.object)
                        parent = serializer.save(force_insert=True)
                        self.post_save(parent, created=True)
    
                        # need to insert children records
                        for child in request.DATA[self.child_field]:
                            child[self.parent_field] = parent.id
                            child_record = self.serializer_child(data=child)
                            if child_record.is_valid():
                                child_record.save(force_insert=True)
                            else:
                                raise ValidationError('Child validation failed')
    
                        headers = self.get_success_headers(serializer.data)
    
                        serializer.data[self.child_field] = self.serializer_child(
                            self.model_child.objects.filter(
                                **{self.parent_field: parent.id}).all(),
                                many=True).data
                        return Response(serializer.data,
                                        status=status.HTTP_201_CREATED,
                                        headers=headers)
            except ValidationError:
                pass
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    

    所以我可以将它重用于我的应用程序中的每个嵌套关系案例,如下所示:

    class ParentViewSet(ParentChildViewSet):
        child_field = 'children'
        parent_field = 'parent'
        model = Parent
        model_child = Child
        serializer_class = ParentSerializerNested
        serializer_parent = ParentSerializer
        serializer_child = ChildSerializer
    

    最后,路由:

    router = routers.DefaultRouter()
    router.register(r'parents', ParentViewSet)
    

    它就像一个魅力!

    【讨论】:

    • 感谢您的回答,但您在这里使用的是ModelViewSet ,我认为使用genericView 会更酷。
    【解决方案3】:
    thing = models.ManyToManyField('Thing')
    

    您需要使用多对多关系来创建一个临时表,该表将自动存储键和关联数据。

    【讨论】:

    • 谢谢,但我的中间表是ObservedThing 一张。 ManyToManyField 仅在您没有任何其他信息附加到该关系时才有效。我能做的就是拥有thing = models.ManyToManyField(Thing, through='ObservedThing'),但这仍然不能解决我原来的问题。
    猜你喜欢
    • 2015-05-19
    • 2020-07-13
    • 2018-08-01
    • 2019-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-13
    相关资源
    最近更新 更多