python - Django Rest Framework - JSON Post 请求 ManyTomany 字段问题

标签 python json django post django-rest-framework

TLDR;使用 Django Rest Framework,如何仅使用所述 M2M 字段的 ID/PK 对具有多对多关系字段的 API 资源进行 POST(应用程序/json 内容)?

我目前正在为一个使用 Django Rest Framework 的大学项目构建应用程序模型。以下模型代表应用程序上的志愿者(我隐藏了一些小细节以避免污染代码)

class Volunteer(models.Model):

first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
state = models.CharField(max_length=2, choices=STATE_CHOICES)
city = models.CharField(max_length=255)
gender = models.CharField(max_length=2, choices=GENDER_CHOICES, blank=True, null=True)
phone = models.CharField(max_length=13) 
email = models.EmailField(max_length=100)
description = models.TextField(max_length=500)  
photo = models.ImageField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
interest_areas = models.ManyToManyField(VolunteeringArea, blank=True)

class Meta:
    verbose_name_plural = "Volunteers"
    ordering = ("first_name", "last_name")

def __str__(self):
    return self.first_name + ' ' + self.last_name

def get_absolute_url(self):
    return reverse('volunteers:volunteer-detail', kwargs={'pk': self.id})

相应的序列化器如下所示:

class VolunteerSerializer(serializers.ModelSerializer):

    interest_areas = VolunteeringAreaSerializer(many=True)

    class Meta:
        model = Volunteer
        fields = ('id', 'first_name', 'last_name', 'phone', 'email', 'state', 'gender', 'interest_areas', 'city', 'description', 'photo')
        read_only_fields = ('created_at', )


    def to_representation(self, instance):
        representation = super(VolunteerSerializer, self).to_representation(instance)
        representation['interest_areas'] = VolunteeringAreaSerializer(instance.interest_areas.all(), many=True).data
        return representation 

正如您可能注意到的,每个志愿者可能有许多感兴趣的领域(多对多领域)。下面的模型代表了它:

class VolunteeringArea(models.Model):

    title = models.CharField(max_length=100)
    description = models.TextField(max_length=512)
    created_at = models.DateTimeField(auto_now_add=True)
    is_active = models.BooleanField(default=False)

    class Meta:
        verbose_name_plural = "VolunteeringAreas"

    def __str__(self):
        return self.title

    def slug(self):
        return slugify(self.title

)    

相应的序列化器是:

class VolunteeringAreaSerializer(serializers.ModelSerializer):

    class Meta:

        model = VolunteeringArea
        fields = ('id', 'title', 'description')
        read_only_fields = ('created_at', 'is_active') 

我的问题是,当尝试通过 POST (application/json) 创建一个新的志愿者并提供interested_area的ID时,我收到一条回复说我应该提供一个dict,但应用程序收到了一个int。我意识到应用程序需要一个完整的字典,其中包含interested_area 中的所有字段,但我只想提供 ID/PK,它将创建对象。

我在互联网上寻找了几个小时,似乎我应该首先创建没有interest_areas的对象,然后将它们添加到对象中。我尝试在模块的views.py上定义一个create方法(与框架定义的原始create方法相同),但它似乎陷入了serializer.is_valid(),很可能表明提供的值interested_area 字段无效。

def post(self, request, format=None):
    return self.create(request)


def create(self, request, *args, **kwargs):

    serializer = self.get_serializer(data=request.data)
    serializer.is_valid(raise_exception=True)
    self.perform_create(serializer)
    headers = self.get_success_headers(serializer.data)
    return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

def get_serializer(self, *args, **kwargs):

    serializer_class = self.get_serializer_class()
    kwargs['context'] = self.get_serializer_context()
    return serializer_class(*args, **kwargs)

我的错误是什么,我该如何使其工作?

为了提供信息,这是请求的正文内容,使用 Postman:

{
    "interest_areas": [0,1],
    "first_name":"NameOfThePerson",
    "last_name":"LastNameOfThePerson",
    "phone":"RandomCellphoneNumber",
    "email":"<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="3a485b545e55575f575b53567a485b545e55575f575b535614595557" rel="noreferrer noopener nofollow">[email protected]</a>",
    "gender":"Gender",
    "state":"State",
    "city":"City",
    "description":"randomDescription"
}

感谢您提前提供的帮助!

最佳答案

您收到错误是因为您已将 VolunteeringAreaSerializer 包含为 NestedSerialiser。要仅使用 VolunteeringArea 的 ID,请像这样使用 PrimaryKeyRelatedField 和bulk_create。

class VolunteerSerializer(serializers.ModelSerializer):

    interest_areas = serializers.PrimaryKeyRelatedField(many=True, 
                     queryset=models.VolunteeringArea.objects.all())


    class Meta:
        model = Volunteer
        fields = ('id', 'first_name', 'last_name', 'phone', 'email', 'state', 
              'gender', 'interest_areas', 'city', 'description', 'photo')

    def create(self, validated_data):
        areas = validated_data.pop('interest_areas', [])
        # create your volunteer here..
        #......
        ThroughModel = Volunteer.interest_areas.through
        # bulk-create through model instance fields
        ThroughModel.objects.bulk_create([
                ThroughModel(volunteer_id=<freshly_created_volunteer_id>, 
                         interest_area_id=area_id)
                        for area_id in areas
                      ])

        return volunteer

关于python - Django Rest Framework - JSON Post 请求 ManyTomany 字段问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53029707/

相关文章:

python - 山脉的衰减函数

json - 使用jq根据对象中变量的值选择对象

c# - 将json字典反序列化为c#

ruby - Make error installing ruby​​ gem json v1.8.3 : BFD (GNU Binutils for Debian) 2. 22 内部错误

css - Django Material 导航栏问题

python - 从 Twisted 上传到 GCS

python - 为 Python 3.6 元类提供 __classcell__ 示例

python - 如何将 gRPC 服务器/客户端部署到 heroku?

django - 如何正确配置 djcelery 结果后端到数据库

django - (Django) 在不同域上的两个站点之间共享身份验证