【问题标题】:Django Rest - "<ClientProfile: mike@tyson.com>" needs to have a value for field "clientprofile" before this many-to-many relationship can be usedDjango Rest - "<ClientProfile: mike@tyson.com>" 需要为字段 "clientprofile" 设置一个值,然后才能使用这种多对多关系
【发布时间】:2017-04-03 05:18:26
【问题描述】:

我正在尝试在可能的情况下实现 API:

  • 返回所有 ClientProfile 对象,其中包含一个包含 Location 对象列表的嵌套 ManytoMany 字段(我真的不在乎它是 PK 还是实际对象,实际对象会很好。)
  • 能够 POST 一个 ClientProfile 对象,并使用 Location 对象的 PK 指定应该与它关联的 Location 对象,如下所示:

            {
            "user": {
                "email": "mike@tyson.com"
            },
            "first_name": "Mike",
            "last_name": "Tyson",
            "locations": [
                1,5,21
    
            ]
        }
    
  • 我看到人们对普通的 Django 说“你需要实现一个保存方法”。我是 Django 和 Django REST 的新手,所以不确定在哪里或如何为此实施解决方案..

请把我从噩梦中拯救出来!

点冻结输出:

appdirs==1.4.0
chardet==2.3.0
Django==1.10.5
django-csvimport==2.5
djangorestframework==3.5.4
packaging==16.8
pyparsing==2.1.10
six==1.10.0

错误:

    Traceback (most recent call last):
  File "/venv/project/userauth/tests/User.py", line 28, in setUp
    self.response = self.client.post(path="/client-profile/", format="json", data=self.data)
  File "/venv/lib/python3.5/site-packages/rest_framework/test.py", line 290, in post
    path, data=data, format=format, content_type=content_type, **extra)
  File "/venv/lib/python3.5/site-packages/rest_framework/test.py", line 212, in post
    return self.generic('POST', path, data, content_type, **extra)
  File "/venv/lib/python3.5/site-packages/django/test/client.py", line 409, in generic
    return self.request(**r)
  File "/venv/lib/python3.5/site-packages/rest_framework/test.py", line 279, in request
    return super(APIClient, self).request(**kwargs)
  File "/venv/lib/python3.5/site-packages/rest_framework/test.py", line 231, in request
    request = super(APIRequestFactory, self).request(**kwargs)
  File "/venv/lib/python3.5/site-packages/django/test/client.py", line 494, in request
    six.reraise(*exc_info)
  File "/venv/lib/python3.5/site-packages/django/utils/six.py", line 686, in reraise
    raise value
  File "/venv/lib/python3.5/site-packages/django/core/handlers/exception.py", line 39, in inner
    response = get_response(request)
  File "/venv/lib/python3.5/site-packages/django/core/handlers/base.py", line 187, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/venv/lib/python3.5/site-packages/django/core/handlers/base.py", line 185, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/venv/lib/python3.5/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
    return view_func(*args, **kwargs)
  File "/venv/lib/python3.5/site-packages/django/views/generic/base.py", line 68, in view
    return self.dispatch(request, *args, **kwargs)
  File "/venv/lib/python3.5/site-packages/rest_framework/views.py", line 483, in dispatch
    response = self.handle_exception(exc)
  File "/venv/lib/python3.5/site-packages/rest_framework/views.py", line 443, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/venv/lib/python3.5/site-packages/rest_framework/views.py", line 480, in dispatch
    response = handler(request, *args, **kwargs)
  File "/venv/project/userauth/views/ClientProfile.py", line 25, in post
    serializer.save()
  File "/venv/lib/python3.5/site-packages/rest_framework/serializers.py", line 214, in save
    self.instance = self.create(validated_data)
  File "/venv/project/userauth/serializers/ClientProfile.py", line 20, in create
    client_profile = ClientProfile.objects.create(user=user, **validated_data)
  File "/venv/lib/python3.5/site-packages/django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/venv/lib/python3.5/site-packages/django/db/models/query.py", line 397, in create
    obj = self.model(**kwargs)
  File "/venv/lib/python3.5/site-packages/django/db/models/base.py", line 550, in __init__
    setattr(self, prop, kwargs[prop])
  File "/venv/lib/python3.5/site-packages/django/db/models/fields/related_descriptors.py", line 499, in __set__
    manager = self.__get__(instance)
  File "/venv/lib/python3.5/site-packages/django/db/models/fields/related_descriptors.py", line 476, in __get__
    return self.related_manager_cls(instance)
  File "/venv/lib/python3.5/site-packages/django/db/models/fields/related_descriptors.py", line 783, in __init__
    (instance, self.source_field_name))
ValueError: "<ClientProfile: mike@tyson.com>" needs to have a value for field "clientprofile" before this many-to-many relationship can be used.

型号:

class ClientProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='client_profile')
    enabled = models.BooleanField(_('Enabled?'), default=False)
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    locations = models.ManyToManyField(Location, related_name='client_profile')

    def __str__(self):
        return self.user.email


class Location(models.Model):
    company = models.ForeignKey(Company)
    display_name = models.CharField(max_length=50, null=True)
    address1 = models.CharField(max_length=50, null=True)
    address2 = models.CharField(max_length=50, null=True)
    city = models.CharField(max_length=50, null=True)
    county = models.CharField(max_length=50, null=True)
    postcode = models.CharField(max_length=50, null=True)
    country = models.CharField(max_length=50, null=True)
    tel = models.CharField(max_length=50, null=True)
    email = models.CharField(max_length=50, null=True)

序列化器:

class ClientProfileSerializer(serializers.ModelSerializer):
    user = UserSerializer(required=True)
    locations = LocationSerializer(required=False, many=True)

    class Meta:
        model = ClientProfile
        fields = ('id', 'user', 'first_name', 'last_name', 'locations')

    def create(self, validated_data):
        user_data = validated_data.pop('user')
        user = User.objects.create(email=user_data['email'], is_client=True)
        client_profile = ClientProfile.objects.create(user=user, **validated_data)

        return client_profile


class LocationSerializer(serializers.ModelSerializer):
    class Meta:
        model = Location
        fields = ('id', 'company', 'display_name', 'address1', 'address2',
                  'city', 'county', 'postcode', 'country', 'tel', 'email')

观看次数:

class ClientProfileList(APIView):
    """
    List all ClientProfiles, or create a new ClientProfile.
    """
    def get(self, request, format=None):
        if self.request.user.is_client:
            client_profiles = ClientProfile.objects.all()
            serializer = ClientProfileSerializer(client_profiles, many=True)
            return Response(serializer.data)
        else:
            return Response(status=status.HTTP_401_UNAUTHORIZED)

    def post(self, request, format=None):
        serializer = ClientProfileSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class ClientProfileDetail(APIView):
    """
    Retrieve, update or delete a ClientProfile instance.
    """
    def get_object(self, pk):
        try:
            return ClientProfile.objects.get(pk=pk)
        except ClientProfile.DoesNotExist:
            raise Http404

    def get(self, request, pk, format=None):
        client_profile = self.get_object(pk)
        serializer = ClientProfileSerializer(client_profile)
        return Response(serializer.data)

    def put(self, request, pk, format=None):
        client_profile = self.get_object(pk)
        serializer = ClientProfileSerializer(client_profile, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk, format=None):
        client_profile = self.get_object(pk)
        client_profile.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
class LocationList(APIView):
    """
    List all Locations, or create a new Location.
    """
    def get(self, request, format=None):
        if self.request.user.is_authenticated:
            if self.request.user.is_client:
                locations = Location.objects.all()
                serializer = LocationSerializer(locations, many=True)
                return Response(serializer.data)
            else:
                return Response(status=status.HTTP_403_FORBIDDEN)
        else:
            return Response(status=status.HTTP_401_UNAUTHORIZED)

    def post(self, request, format=None):
        serializer = LocationSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

class LocationDetail(APIView):
    """
    Retrieve, update or delete a Location instance.
    """
    def get_object(self, pk):
        try:
            return Location.objects.get(pk=pk)
        except Location.DoesNotExist:
            raise Http404

    def get(self, request, pk, format=None):
        location = self.get_object(pk)
        serializer = LocationSerializer(location)
        return Response(serializer.data)

    def put(self, request, pk, format=None):
        location = self.get_object(pk)
        serializer = LocationSerializer(location, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk, format=None):
        location = self.get_object(pk)
        location.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

【问题讨论】:

    标签: python django api django-rest-framework many-to-many


    【解决方案1】:

    具有ManyToMany 字段的Django 模型必须先保存,然后才能添加相关实例。 (要在 ManyToMany 关系中添加条目,Django 需要父模型实例 pk,它仅在模型创建后设置。)

    因此,必须先保存您的 ClientProfile,然后才能添加任何相关的 Location 实例。

    要修复此错误,只需更新您的 ClientProfileSerializer.create 方法:

    class ClientProfileSerializer(serializers.ModelSerializer):
    
        def create(self, validated_data):
            user_data = validated_data.pop('user')
            user = User.objects.create(email=user_data['email'], is_client=True)
            locations = validated_data.pop('locations')
            client_profile = ClientProfile.objects.create(user=user, **validated_data)
            client_profile.locations.add(*locations)
            return client_profile
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-06-01
      • 1970-01-01
      • 2023-03-24
      • 1970-01-01
      • 1970-01-01
      • 2019-08-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多