" 在使用这种多对多关系之前,"id" 字段需要有一个值,django,django-rest-framework"/>

首页 > 解决方案 > 删除时:ValueError:“" 在使用这种多对多关系之前,"id" 字段需要有一个值

问题描述

我很确定人们会说这个问题已经有了答案,但我找不到任何答案。在其他问题中,在创建具有多对多关系的对象时会出现问题。

就我而言,当我尝试删除记录时会发生这种情况......嗯?!

我有用户模型和组(from django.contrib.auth.models import Group),它们之间具有标准的多对多关系(django 标准)。

在我在序列化程序中使用 Meta 属性之前,一切正常depth(顺便说一句,这非常方便!)。

我的用户序列化程序:

from rest_framework import serializers
from ..models.base_user import User


class BaseUserSerializer(serializers.ModelSerializer):

    class Meta:
        model = User
        depth = 1
        fields = (
            'id',
            'username',
            'email',
            'first_name',
            'last_name',
            'is_active',
            'groups',
        )

我的组序列化程序:

from django.contrib.auth.models import Group
from rest_framework import serializers


class BaseGroupSerializer(serializers.ModelSerializer):

    class Meta:
        model = Group
        depth = 1
        fields = (
            'id',
            'name',
            'user_set',
        )
    
    def to_representation(self, instance):
        response = super().to_representation(instance)
        # By using the 'depth' Meta attribute, the nested User records were serialized
        # with their password. By doing this, we remove the 'password' field from the
        # representation of the Groups and their nested Users.
        for user in response.get("user_set"):
            user.pop("password", None)
        return response

我的 API 函数视图:

@api_view(http_method_names=["DELETE"])
def user_delete(request, id):
    """
    Delete the User that corresponds to 'id'.
    """
    user = User.objects.get(id=id)
    user.delete()
    serializer = BaseUserSerializer(user)
    return Response(serializer.data)

错误 :

Internal Server Error: /api/users/45/delete/
Traceback (most recent call last):
  File "C:\Program Files\Python37\lib\site-packages\django\core\handlers\exception.py", line 47, in inner
    response = get_response(request)
  File "C:\Program Files\Python37\lib\site-packages\django\core\handlers\base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\Program Files\Python37\lib\site-packages\django\views\decorators\csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "C:\Program Files\Python37\lib\site-packages\django\views\generic\base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "C:\Program Files\Python37\lib\site-packages\rest_framework\views.py", line 509, in dispatch
    response = self.handle_exception(exc)
  File "C:\Program Files\Python37\lib\site-packages\rest_framework\views.py", line 469, in handle_exception
    self.raise_uncaught_exception(exc)
  File "C:\Program Files\Python37\lib\site-packages\rest_framework\views.py", line 480, in raise_uncaught_exception
    raise exc
  File "C:\Program Files\Python37\lib\site-packages\rest_framework\views.py", line 506, in dispatch
    response = handler(request, *args, **kwargs)
  File "C:\Program Files\Python37\lib\site-packages\rest_framework\decorators.py", line 50, in handler
    return func(*args, **kwargs)
  File "E:\Documents\Projects\my-project\my-project\backend\base\viewsets\base_user_viewset.py", line 142, in user_delete
    return Response(serializer.data)
  File "C:\Program Files\Python37\lib\site-packages\rest_framework\serializers.py", line 548, in data
    ret = super().data
  File "C:\Program Files\Python37\lib\site-packages\rest_framework\serializers.py", line 246, in data
    self._data = self.to_representation(self.instance)
  File "C:\Program Files\Python37\lib\site-packages\rest_framework\serializers.py", line 502, in to_representation
    attribute = field.get_attribute(instance)
  File "C:\Program Files\Python37\lib\site-packages\rest_framework\fields.py", line 457, in get_attribute
    return get_attribute(instance, self.source_attrs)
  File "C:\Program Files\Python37\lib\site-packages\rest_framework\fields.py", line 97, in get_attribute
    instance = getattr(instance, attr)
  File "C:\Program Files\Python37\lib\site-packages\django\db\models\fields\related_descriptors.py", line 536, in __get__
    return self.related_manager_cls(instance)
  File "C:\Program Files\Python37\lib\site-packages\django\db\models\fields\related_descriptors.py", line 853, in __init__
    (instance, self.pk_field_names[self.source_field_name]))
ValueError: "<User: Test USER (testuser)>" needs to have a value for field 
"id" before this many-to-many relationship can be used.
[27/Jan/2021 15:59:58] "DELETE /api/users/45/delete/ HTTP/1.1" 500 133778

如果我depth = 1从 中删除BaseUserSerializer,则没有任何错误。我不明白它为什么会发生,以及如何解决它。

注意:用户仍然被有效删除,但由于有 ValueError 而不是正常响应,我的前端没有按预期运行......

任何帮助表示赞赏:)

标签: djangodjango-rest-framework

解决方案


就个人而言,我会避免让DELETE请求路径返回用户详细信息。相反,它可以根据 HTTP 响应的状态返回 id 或成功/失败。但是,如果您想让您拥有的东西起作用,您需要在删除用户之前生成响应,否则所有这些关系都会被破坏,从而导致您遇到错误。

@api_view(http_method_names=["DELETE"])
def user_delete(request, id):
    """
    Delete the User that corresponds to 'id'.
    """
    user = User.objects.get(id=id)
    # Generate the response before deleting the user and it's relationships.
    response = Response(BaseUserSerializer(user).data)
    user.delete()
    return response

此外,您可以使用以下代码删除 BaseGroupSerializer.to_representation 中的 hack。这将告诉 DRF 具体使用哪个序列化程序,从而限制 BaseUserSerializer 中的可用字段。我实际上建议不要使用depth,因为当您将模型添加到数据库时,它们会自动添加到您的 API 中。我的直觉是只通过显式更改来更改 API。

class BaseGroupSerializer(serializers.ModelSerializer):
    user_set = BaseUserSerializer(many=True)
    class Meta:
        model = Group
        depth = 1
        fields = (
            'id',
            'name',
            'user_set',
        )

推荐阅读