在Django/REST中保存多对多模型? [英] Save a many-to-many model in Django/REST?

查看:151
本文介绍了在Django/REST中保存多对多模型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为我的Django应用程序编写REST API,并且无法获取POST请求以在一种模型上工作. 这是有问题的模型:

I'm writing a REST API for my Django app, and can't get POST requests to work on one model. Here's the model in question:

class ProjectNode(models.Model):
    name = models.CharField(max_length=60)
    place = models.CharField(max_length=150)
    time_spent = models.BigIntegerField()
    parent_project = models.ForeignKey(Project, related_name='tasks')
    workers = models.ManyToManyField(User, related_name='tasks_can_do')

    def __str__(self):
        return self.name

User模型目前仅保存一个name字段. 这是我的ProjectNode的序列化器:

The User model just holds a name field at the moment. Here's my serializer for ProjectNode:

class ProjectNodeSerializer(serializers.ModelSerializer):
    class Meta:
        model = ProjectNode
        fields = ('id', 'name', 'place', 'time_spent', 'workers',)

这是API视图(来自views.py):

And here's the API view (from views.py):

class WebProjectNodeListView(generics.ListCreateAPIView):
    queryset = ProjectNode.objects.all()
    serializer_class = ProjectNodeSerializer

    def pre_save(self, obj):
        obj.parent_project = Project.objects.get(pk=self.request.DATA['parent_project'])
        for worker_pk in self.request.DATA['workers']:
            obj.workers.add(User.objects.get(pk=worker_pk))
        obj.final_worker = User.objects.get(pk=self.request.DATA['final_workers'])

昨天我起初尝试了一个更简单的版本,该版本仅具有Project ForeignKey关系,而且似乎可以正常工作,所以我认为使用add也可以工作,但是在测试API时出现错误使用httpie(我已经添加了一些用户和项目,并确保我正确获得了他们的ID). 这是请求:

I tried a simpler version yesterday at first, which only had the Project ForeignKey relationship, and it seemed to work, so I thought that using add would work too, but I get an error when testing out the API with httpie (I already added some users and projects, and am sure I get their id's correctly). Here's the request:

http POST :8000/api/tasks/ name="newtask" place="home" time_spent:=50 parent_project:=1 workers:=[1]

我收到此错误:

"<ProjectNode: newtask>" needs to have a value for field "projectnode" before this many-to-many relationship can be used.

回溯也指向以下代码行:

And the traceback also points to this line of code:

obj.workers.add(User.objects.get(id=worker_pk))

现在,我感觉这是因为我试图在数据库中创建ProjectNode对象之前更新User对象上的关系,但是我不确定如何解决此问题?

Now, I get the feeling that this is because I'm trying to update the relationship on the User object before a ProjectNode object is created in the database, but I'm not sure how to resolve this?

推荐答案

DRF无法创建嵌套序列化程序对象或多对多"字段的模型. 因此有必要在创建ProjectNode之前重写Serializer的创建方法并创建/获取M2M模型. 尝试在序列化程序中覆盖create(self, validated_data),然后在此方法中使用数据.

DRF doesn't works create models which are nested serializers objects or Many to Many fields. So is necessary to override Serializer create method and create/get M2M models before create ProjectNode. Try to override create(self, validated_data) in your serializer and work with your data inside this method..

示例:

我的模型Project与ProjectImages具有M2M关系.在ProjectSerializer中,我像这样重写了create方法.

My model Project has M2M relation with ProjectImages. In ProjectSerializer I override create method like this.

 def create(self, validated_data):
        try:
            # Remove nested and M2m relationships from validated_data
            images = validated_data.pop('projectimage_set') if 'projectimage_set' in validated_data else []

            # Create project model
            instance = Project(**validated_data)
            if status:
                instance.set_status(status)

            project = instance.save()

            # Create relations
            for image in images:
                ProjectImage.objects.create(project=project, **image)

        except exceptions.ValidationError as e:
            errors_messages = e.error_dict if hasattr(e, 'error_dict') else e.error_list
            raise serializers.ValidationError(errors_messages)

        return project

希望获得帮助!

这篇关于在Django/REST中保存多对多模型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆