首页 > 解决方案 > 禁止 Django 直接分配到多对多集合的反面。使用 .set()

问题描述

我有两个模型,StudyConditionsstudies,并且在研究中声明了两者之间的多对多关系。我正在努力创造新的条件StudyConditionViewSet并将它们与研究联系起来。使用下面的 JSON 对象执行发布请求时,我收到错误 Direct assignment to the reverse side of a many-to-many set is disabled 。使用 .set()。这是不可能的,即我只能在 my 中执行此操作StudyViewSet,还是我缺少/误解了什么?关于这个具体案例,我看到的问题并不多。我已经尝试了 study_obj.conditions.add(new_condition) 和 new_condition.studies.add(study_obj),将条件添加到第一个研究中,并将研究添加到第二个中的条件。我也尝试过使用 set,但没有成功。

编辑: 我很笨,从一个教程中复制并粘贴了一些错误的代码,我的视图中有 StudentSerializer 而不是 StudyConditionSerializer,但这看起来不是问题,仍然出现错误。

{
    "name": "Post w/ studies",
    "studies": [
        {
            "id": "1"
        },
        {
            "id": "2"
        }
    ]
}
models.py

class Study(models.Model):
    featured = models.BooleanField(default=False)
    conditions = models.ManyToManyField('StudyCondition', related_name='studies', db_table='Study_Condition')


    class Meta:
        db_table = 'Study'

class StudyCondition(models.Model):
    name = models.TextField(null=False)

    class Meta:
        db_table = 'Condition'
views.py
class StudyConditionViewSet(viewsets.ViewSet):
    def list(self, request):
        queryset = StudyCondition.objects.all()
        result = StudyConditionSerializer(queryset, many=True)
        if result:
            return Response(result.data)
        else:
            return Response(data=result.data, status=200)

    def retrieve(self, request, pk=None):
        queryset = StudyCondition.objects.all()
        condition = get_object_or_404(queryset, pk=pk)
        result = StudyConditionSerializer(condition)
        return Response(result.data)

    def create(self, request):
        data = request.data
        new_condition= StudyCondition.objects.create(**data)
        new_condition.save()
        for study in data["studies"]:
            study_obj = Study.objects.get(id=study["id"])
            study_obj.conditions.add(new_condition)
            # new_condition.studies.add(study_obj)

        study_ser = StudyConditionSerializer(new_condition)
        if study_ser.is_valid():
            return Response(data=study_ser.data, status=201)
        else:
            return Response(data=study_ser.errors, status=409)

serializers.py

class StudyConditionSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField(label='conditionID', required=False)
    name = serializers.CharField()
    studies = serializers.SerializerMethodField()

    class Meta:
        model = StudyCondition
        fields = ['id', 'name', 'studies']

        def to_representation(self, instance: StudyCondition) -> dict:
            ret = super().to_representation(instance)
            return ret

        def get_queryset(self) -> QuerySet:
            qs = StudyCondition.objects.all()
            return qs

        def create(self, validated_data: dict) -> StudyCondition:
            return StudyCondition.objects.create(**validated_data)

    def get_studies(self, obj):
        studies = obj.studies.all()
        response = StudyListMiscSerializer(studies, many=True).data
        return response
Traceback
Environment:


Request Method: POST
Request URL: http://localhost:8000/api/studies/conditions/





Traceback (most recent call last):
  File "/usr/local/lib/python3.10/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/usr/local/lib/python3.10/site-packages/django/core/handlers/base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/usr/local/lib/python3.10/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "/usr/local/lib/python3.10/site-packages/rest_framework/viewsets.py", line 125, in view
    return self.dispatch(request, *args, **kwargs)
  File "/usr/local/lib/python3.10/site-packages/rest_framework/views.py", line 509, in dispatch
    response = self.handle_exception(exc)
  File "/usr/local/lib/python3.10/site-packages/rest_framework/views.py", line 469, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/usr/local/lib/python3.10/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
    raise exc
  File "/usr/local/lib/python3.10/site-packages/rest_framework/views.py", line 506, in dispatch
    response = handler(request, *args, **kwargs)
  File "/studies/views.py", line 112, in create
    new_condition= StudyCondition.objects.create(**data)
  File "/usr/local/lib/python3.10/site-packages/django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/usr/local/lib/python3.10/site-packages/django/db/models/query.py", line 451, in create
    obj = self.model(**kwargs)
  File "/usr/local/lib/python3.10/site-packages/django/db/models/base.py", line 498, in __init__
    _setattr(self, prop, kwargs[prop])
  File "/usr/local/lib/python3.10/site-packages/django/db/models/fields/related_descriptors.py", line 545, in __set__
    raise TypeError(

Exception Type: TypeError at /api/studies/conditions/
Exception Value: Direct assignment to the reverse side of a many-to-many set is prohibited. Use studies.set() instead.

标签: pythondjangodjango-rest-framework

解决方案


根据您的回溯,问题现在很清楚了。request.datastudies就是它失败的原因StudyCondition.objects.create(**data)。所以要修复,只需在创建StudyCondition.

就像是:

    def create(self, request):
        data = request.data
        studies = data.pop('studies')

        new_condition = StudyCondition.objects.create(**data)
        new_condition.save()

        for study in studies:
            ...

推荐阅读